Skip to content
Source
Sign inSign up
Open History to see the last commit

FQN.ts

87 lines2.3 KB

1import type { NamespaceNode } from "./Namespace.ts";
2​
3/**
4 * Separator used in FQN strings.
5 */
6export const FQN_SEPARATOR = "/";
7​
8/**
9 * Encode an FQN for safe filesystem storage.
10 * Replaces `/` (FQN separator) with `__` to avoid subdirectory creation.
11 */
12export const encodeFqn = (fqn: string): string => fqn.replaceAll(FQN_SEPARATOR, "__");
13​
14/**
15 * Decode a filename back to FQN.
16 */
17export const decodeFqn = (filename: string): string => filename.replaceAll("__", FQN_SEPARATOR);
18​
19/**
20 * Convert a NamespaceNode chain to an array of namespace IDs, from root to leaf.
21 *
22 * @example
23 * ```ts
24 * const ns = { Id: "Child", Parent: { Id: "Parent", Parent: { Id: "Root" } } };
25 * toPath(ns); // ["Root", "Parent", "Child"]
26 * ```
27 */
28export const toPath = (ns: NamespaceNode | undefined): string[] => {
29 if (!ns) return [];
30 const path: string[] = [];
31 let current: NamespaceNode | undefined = ns;
32 while (current) {
33 path.unshift(current.Id);
34 current = current.Parent;
35 }
36 return path;
37};
38​
39/**
40 * Create a fully-qualified name (FQN) from a namespace and logical ID.
41 * The FQN is a flat key suitable for state storage.
42 *
43 * @example
44 * ```ts
45 * const ns = { Id: "Child", Parent: { Id: "Parent" } };
46 * toFqn(ns, "MyResource"); // "Parent/Child/MyResource"
47 * toFqn(undefined, "MyResource"); // "MyResource"
48 * ```
49 */
50export const toFqn = (ns: NamespaceNode | undefined, logicalId: string): string => {
51 const path = toPath(ns);
52 path.push(logicalId);
53 return path.join(FQN_SEPARATOR);
54};
55​
56/**
57 * Parse an FQN back into namespace path segments and the logical ID.
58 *
59 * @example
60 * ```ts
61 * parseFqn("Parent/Child/MyResource"); // { path: ["Parent", "Child"], logicalId: "MyResource" }
62 * parseFqn("MyResource"); // { path: [], logicalId: "MyResource" }
63 * ```
64 */
65export const parseFqn = (fqn: string): { path: string[]; logicalId: string } => {
66 const parts = fqn.split(FQN_SEPARATOR);
67 const logicalId = parts.pop()!;
68 return { path: parts, logicalId };
69};
70​
71/**
72 * Reconstruct a NamespaceNode chain from a path array.
73 *
74 * @example
75 * ```ts
76 * fromPath(["Parent", "Child"]);
77 * // { Id: "Child", Parent: { Id: "Parent" } }
78 * ```
79 */
80export const fromPath = (path: string[]): NamespaceNode | undefined => {
81 if (path.length === 0) return undefined;
82 let node: NamespaceNode | undefined;
83 for (const id of path) {
84 node = { Id: id, Parent: node };
85 }
86 return node;
87};