@jfdevelops/react-layout 0.17.4 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/create-config/component-keys.cjs +44 -0
  2. package/dist/create-config/component-keys.cjs.map +1 -0
  3. package/dist/create-config/component-keys.mjs +39 -0
  4. package/dist/create-config/component-keys.mjs.map +1 -0
  5. package/dist/create-config/define-layout.cjs +42 -5
  6. package/dist/create-config/define-layout.cjs.map +1 -1
  7. package/dist/create-config/define-layout.d.cts +12 -10
  8. package/dist/create-config/define-layout.d.cts.map +1 -1
  9. package/dist/create-config/define-layout.d.mts +12 -10
  10. package/dist/create-config/define-layout.d.mts.map +1 -1
  11. package/dist/create-config/define-layout.mjs +43 -6
  12. package/dist/create-config/define-layout.mjs.map +1 -1
  13. package/dist/create-config/for-paths/builder.cjs +192 -0
  14. package/dist/create-config/for-paths/builder.cjs.map +1 -0
  15. package/dist/create-config/for-paths/builder.d.cts +77 -0
  16. package/dist/create-config/for-paths/builder.d.cts.map +1 -0
  17. package/dist/create-config/for-paths/builder.d.mts +77 -0
  18. package/dist/create-config/for-paths/builder.d.mts.map +1 -0
  19. package/dist/create-config/for-paths/builder.mjs +192 -0
  20. package/dist/create-config/for-paths/builder.mjs.map +1 -0
  21. package/dist/create-config/for-paths/get-component-for-path.cjs +55 -0
  22. package/dist/create-config/for-paths/get-component-for-path.cjs.map +1 -0
  23. package/dist/create-config/for-paths/get-component-for-path.d.cts +55 -0
  24. package/dist/create-config/for-paths/get-component-for-path.d.cts.map +1 -0
  25. package/dist/create-config/for-paths/get-component-for-path.d.mts +55 -0
  26. package/dist/create-config/for-paths/get-component-for-path.d.mts.map +1 -0
  27. package/dist/create-config/for-paths/get-component-for-path.mjs +55 -0
  28. package/dist/create-config/for-paths/get-component-for-path.mjs.map +1 -0
  29. package/dist/create-config/for-paths/index.d.cts +2 -0
  30. package/dist/create-config/for-paths/index.d.mts +2 -0
  31. package/dist/create-config/for-resource.cjs.map +1 -1
  32. package/dist/create-config/for-resource.d.cts +19 -19
  33. package/dist/create-config/for-resource.d.cts.map +1 -1
  34. package/dist/create-config/for-resource.d.mts +19 -19
  35. package/dist/create-config/for-resource.d.mts.map +1 -1
  36. package/dist/create-config/for-resource.mjs.map +1 -1
  37. package/dist/create-config/for-resources/scoped-layout.d.cts +14 -14
  38. package/dist/create-config/for-resources/scoped-layout.d.cts.map +1 -1
  39. package/dist/create-config/for-resources/scoped-layout.d.mts +14 -14
  40. package/dist/create-config/for-resources/scoped-layout.d.mts.map +1 -1
  41. package/dist/create-config/get-component.cjs +60 -11
  42. package/dist/create-config/get-component.cjs.map +1 -1
  43. package/dist/create-config/get-component.d.cts +75 -6
  44. package/dist/create-config/get-component.d.cts.map +1 -1
  45. package/dist/create-config/get-component.d.mts +75 -6
  46. package/dist/create-config/get-component.d.mts.map +1 -1
  47. package/dist/create-config/get-component.mjs +61 -11
  48. package/dist/create-config/get-component.mjs.map +1 -1
  49. package/dist/create-config/index.d.cts +6 -1
  50. package/dist/create-config/index.d.mts +6 -1
  51. package/dist/create-config/is-sub-resource-key.cjs +15 -0
  52. package/dist/create-config/is-sub-resource-key.cjs.map +1 -0
  53. package/dist/create-config/is-sub-resource-key.d.cts +30 -0
  54. package/dist/create-config/is-sub-resource-key.d.cts.map +1 -0
  55. package/dist/create-config/is-sub-resource-key.d.mts +30 -0
  56. package/dist/create-config/is-sub-resource-key.d.mts.map +1 -0
  57. package/dist/create-config/is-sub-resource-key.mjs +15 -0
  58. package/dist/create-config/is-sub-resource-key.mjs.map +1 -0
  59. package/dist/create-config/paths/generate.cjs +77 -0
  60. package/dist/create-config/paths/generate.cjs.map +1 -0
  61. package/dist/create-config/paths/generate.mjs +77 -0
  62. package/dist/create-config/paths/generate.mjs.map +1 -0
  63. package/dist/create-config/paths/index.d.cts +2 -0
  64. package/dist/create-config/paths/index.d.mts +2 -0
  65. package/dist/create-config/paths/introspect.cjs +123 -0
  66. package/dist/create-config/paths/introspect.cjs.map +1 -0
  67. package/dist/create-config/paths/introspect.mjs +118 -0
  68. package/dist/create-config/paths/introspect.mjs.map +1 -0
  69. package/dist/create-config/paths/resolve.cjs +130 -0
  70. package/dist/create-config/paths/resolve.cjs.map +1 -0
  71. package/dist/create-config/paths/resolve.mjs +129 -0
  72. package/dist/create-config/paths/resolve.mjs.map +1 -0
  73. package/dist/create-config/paths/types.d.cts +104 -0
  74. package/dist/create-config/paths/types.d.cts.map +1 -0
  75. package/dist/create-config/paths/types.d.mts +104 -0
  76. package/dist/create-config/paths/types.d.mts.map +1 -0
  77. package/dist/create-config/paths/variables.cjs +32 -0
  78. package/dist/create-config/paths/variables.cjs.map +1 -0
  79. package/dist/create-config/paths/variables.d.cts +32 -0
  80. package/dist/create-config/paths/variables.d.cts.map +1 -0
  81. package/dist/create-config/paths/variables.d.mts +32 -0
  82. package/dist/create-config/paths/variables.d.mts.map +1 -0
  83. package/dist/create-config/paths/variables.mjs +29 -0
  84. package/dist/create-config/paths/variables.mjs.map +1 -0
  85. package/dist/create-config/types.d.cts +28 -8
  86. package/dist/create-config/types.d.cts.map +1 -1
  87. package/dist/create-config/types.d.mts +28 -8
  88. package/dist/create-config/types.d.mts.map +1 -1
  89. package/dist/errors/invalid-component.cjs +21 -0
  90. package/dist/errors/invalid-component.cjs.map +1 -0
  91. package/dist/errors/invalid-component.d.cts +18 -0
  92. package/dist/errors/invalid-component.d.cts.map +1 -0
  93. package/dist/errors/invalid-component.d.mts +18 -0
  94. package/dist/errors/invalid-component.d.mts.map +1 -0
  95. package/dist/errors/invalid-component.mjs +21 -0
  96. package/dist/errors/invalid-component.mjs.map +1 -0
  97. package/dist/errors/invalid-config.cjs +13 -0
  98. package/dist/errors/invalid-config.cjs.map +1 -0
  99. package/dist/errors/invalid-config.d.cts +15 -0
  100. package/dist/errors/invalid-config.d.cts.map +1 -0
  101. package/dist/errors/invalid-config.d.mts +15 -0
  102. package/dist/errors/invalid-config.d.mts.map +1 -0
  103. package/dist/errors/invalid-config.mjs +13 -0
  104. package/dist/errors/invalid-config.mjs.map +1 -0
  105. package/dist/errors/invalid-path.cjs +18 -0
  106. package/dist/errors/invalid-path.cjs.map +1 -0
  107. package/dist/errors/invalid-path.d.cts +16 -0
  108. package/dist/errors/invalid-path.d.cts.map +1 -0
  109. package/dist/errors/invalid-path.d.mts +16 -0
  110. package/dist/errors/invalid-path.d.mts.map +1 -0
  111. package/dist/errors/invalid-path.mjs +18 -0
  112. package/dist/errors/invalid-path.mjs.map +1 -0
  113. package/dist/errors/invalid-resource.cjs +18 -0
  114. package/dist/errors/invalid-resource.cjs.map +1 -0
  115. package/dist/errors/invalid-resource.d.cts +16 -0
  116. package/dist/errors/invalid-resource.d.cts.map +1 -0
  117. package/dist/errors/invalid-resource.d.mts +16 -0
  118. package/dist/errors/invalid-resource.d.mts.map +1 -0
  119. package/dist/errors/invalid-resource.mjs +18 -0
  120. package/dist/errors/invalid-resource.mjs.map +1 -0
  121. package/dist/errors/invalid-sub-resource.cjs +18 -0
  122. package/dist/errors/invalid-sub-resource.cjs.map +1 -0
  123. package/dist/errors/invalid-sub-resource.d.cts +17 -0
  124. package/dist/errors/invalid-sub-resource.d.cts.map +1 -0
  125. package/dist/errors/invalid-sub-resource.d.mts +17 -0
  126. package/dist/errors/invalid-sub-resource.d.mts.map +1 -0
  127. package/dist/errors/invalid-sub-resource.mjs +18 -0
  128. package/dist/errors/invalid-sub-resource.mjs.map +1 -0
  129. package/dist/errors/missing-path-variable.cjs +18 -0
  130. package/dist/errors/missing-path-variable.cjs.map +1 -0
  131. package/dist/errors/missing-path-variable.d.cts +16 -0
  132. package/dist/errors/missing-path-variable.d.cts.map +1 -0
  133. package/dist/errors/missing-path-variable.d.mts +16 -0
  134. package/dist/errors/missing-path-variable.d.mts.map +1 -0
  135. package/dist/errors/missing-path-variable.mjs +18 -0
  136. package/dist/errors/missing-path-variable.mjs.map +1 -0
  137. package/dist/errors/react-layout-error.cjs +56 -0
  138. package/dist/errors/react-layout-error.cjs.map +1 -0
  139. package/dist/errors/react-layout-error.d.cts +46 -0
  140. package/dist/errors/react-layout-error.d.cts.map +1 -0
  141. package/dist/errors/react-layout-error.d.mts +46 -0
  142. package/dist/errors/react-layout-error.d.mts.map +1 -0
  143. package/dist/errors/react-layout-error.mjs +54 -0
  144. package/dist/errors/react-layout-error.mjs.map +1 -0
  145. package/dist/index.cjs +15 -0
  146. package/dist/index.d.cts +14 -2
  147. package/dist/index.d.mts +14 -2
  148. package/dist/index.mjs +8 -1
  149. package/dist/props.d.cts +14 -4
  150. package/dist/props.d.cts.map +1 -1
  151. package/dist/props.d.mts +14 -4
  152. package/dist/props.d.mts.map +1 -1
  153. package/dist/resource.cjs.map +1 -1
  154. package/dist/resource.d.cts +2 -2
  155. package/dist/resource.d.cts.map +1 -1
  156. package/dist/resource.d.mts +2 -2
  157. package/dist/resource.d.mts.map +1 -1
  158. package/dist/resource.mjs.map +1 -1
  159. package/dist/utils.d.cts +2 -1
  160. package/dist/utils.d.cts.map +1 -1
  161. package/dist/utils.d.mts +2 -1
  162. package/dist/utils.d.mts.map +1 -1
  163. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.mjs","names":[],"sources":["../../../src/create-config/paths/generate.ts"],"sourcesContent":["import {\n COMPONENT_SLOT_KEYS,\n isConfigNode,\n readComponentKeys,\n readSubResourceKeys,\n SHARED_BRANCH_KEYS,\n} from '../component-keys';\nimport {\n createPathVariable,\n type ComponentPathVariable,\n type ResourcePathVariable,\n type SubResourcePathVariable,\n} from './variables';\n\n/** Recursion cap for generated paths. Must stay aligned with `MaxPathDepth` in `./types`. */\nexport const MAX_PATH_DEPTH = 6;\n\nconst RESOURCE_VARIABLE: ResourcePathVariable = createPathVariable('resource');\nconst SUB_RESOURCE_VARIABLE: SubResourcePathVariable =\n createPathVariable('subResource');\nconst COMPONENT_VARIABLE: ComponentPathVariable =\n createPathVariable('component');\n\n/** Component tails on one node: its slots, its shared branches, and `$component`. */\nfunction collectComponentTails(node: Record<string, unknown>): string[] {\n const tails: string[] = [];\n\n for (const key of COMPONENT_SLOT_KEYS) {\n if (node[key] !== undefined) {\n tails.push(key);\n }\n }\n\n if (readComponentKeys(node).length > 0) {\n tails.push(COMPONENT_VARIABLE);\n }\n\n for (const branch of SHARED_BRANCH_KEYS) {\n const branchNode = node[branch];\n\n if (!isConfigNode(branchNode)) {\n continue;\n }\n\n tails.push(branch);\n\n for (const key of COMPONENT_SLOT_KEYS) {\n if (branchNode[key] !== undefined) {\n tails.push(`${branch}.${key}`);\n }\n }\n\n if (readComponentKeys(branchNode).length > 0) {\n tails.push(`${branch}.${COMPONENT_VARIABLE}`);\n }\n }\n\n return tails;\n}\n\n/**\n * Component tails valid after a `$subResource` segment: one variable stands for the whole\n * sub-resource chain, so every descendant's tails are reachable.\n */\nfunction collectDescendantComponentTails(\n node: Record<string, unknown>,\n depth: number,\n): string[] {\n if (depth === 0) {\n return [];\n }\n\n const tails = new Set<string>();\n\n for (const key of readSubResourceKeys(node)) {\n const subNode = node[key];\n\n if (!isConfigNode(subNode)) {\n continue;\n }\n\n for (const tail of collectComponentTails(subNode)) {\n tails.add(tail);\n }\n\n for (const tail of collectDescendantComponentTails(subNode, depth - 1)) {\n tails.add(tail);\n }\n }\n\n return [...tails];\n}\n\n/** Every path tail valid below one config node. */\nfunction collectEntryPaths(\n node: Record<string, unknown>,\n depth: number,\n): string[] {\n if (depth === 0) {\n return [];\n }\n\n const paths = new Set<string>(collectComponentTails(node));\n const subResourceKeys = readSubResourceKeys(node);\n\n if (subResourceKeys.length > 0) {\n paths.add(SUB_RESOURCE_VARIABLE);\n\n for (const tail of collectDescendantComponentTails(node, depth)) {\n paths.add(`${SUB_RESOURCE_VARIABLE}.${tail}`);\n }\n }\n\n for (const key of subResourceKeys) {\n const subNode = node[key];\n\n paths.add(key);\n\n if (!isConfigNode(subNode)) {\n continue;\n }\n\n for (const nested of collectEntryPaths(subNode, depth - 1)) {\n paths.add(`${key}.${nested}`);\n }\n }\n\n return [...paths];\n}\n\n/**\n * Paths a config accepts, with resource and sub-resource segments available both as\n * literals and as variables. Runtime twin of `ParameterizedResourcePath`.\n */\nexport function generateResourceConfigPaths(\n config: Record<string, unknown>,\n): string[] {\n const paths = new Set<string>();\n\n for (const resource of Object.keys(config)) {\n const resourceNode = config[resource];\n\n if (!isConfigNode(resourceNode)) {\n continue;\n }\n\n paths.add(resource);\n paths.add(RESOURCE_VARIABLE);\n\n for (const tail of collectEntryPaths(resourceNode, MAX_PATH_DEPTH)) {\n paths.add(`${resource}.${tail}`);\n paths.add(`${RESOURCE_VARIABLE}.${tail}`);\n }\n }\n\n return [...paths];\n}\n"],"mappings":";;;;;AAeA,MAAa,iBAAiB;AAE9B,MAAM,oBAA0C,mBAAmB,UAAU;AAC7E,MAAM,wBACJ,mBAAmB,aAAa;AAClC,MAAM,qBACJ,mBAAmB,WAAW;;AAGhC,SAAS,sBAAsB,MAAyC;CACtE,MAAM,QAAkB,CAAC;CAEzB,KAAK,MAAM,OAAO,qBAChB,IAAI,KAAK,SAAS,QAChB,MAAM,KAAK,GAAG;CAIlB,IAAI,kBAAkB,IAAI,EAAE,SAAS,GACnC,MAAM,KAAK,kBAAkB;CAG/B,KAAK,MAAM,UAAU,oBAAoB;EACvC,MAAM,aAAa,KAAK;EAExB,IAAI,CAAC,aAAa,UAAU,GAC1B;EAGF,MAAM,KAAK,MAAM;EAEjB,KAAK,MAAM,OAAO,qBAChB,IAAI,WAAW,SAAS,QACtB,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK;EAIjC,IAAI,kBAAkB,UAAU,EAAE,SAAS,GACzC,MAAM,KAAK,GAAG,OAAO,GAAG,oBAAoB;CAEhD;CAEA,OAAO;AACT;;;;;AAMA,SAAS,gCACP,MACA,OACU;CACV,IAAI,UAAU,GACZ,OAAO,CAAC;CAGV,MAAM,wBAAQ,IAAI,IAAY;CAE9B,KAAK,MAAM,OAAO,oBAAoB,IAAI,GAAG;EAC3C,MAAM,UAAU,KAAK;EAErB,IAAI,CAAC,aAAa,OAAO,GACvB;EAGF,KAAK,MAAM,QAAQ,sBAAsB,OAAO,GAC9C,MAAM,IAAI,IAAI;EAGhB,KAAK,MAAM,QAAQ,gCAAgC,SAAS,QAAQ,CAAC,GACnE,MAAM,IAAI,IAAI;CAElB;CAEA,OAAO,CAAC,GAAG,KAAK;AAClB;;AAGA,SAAS,kBACP,MACA,OACU;CACV,IAAI,UAAU,GACZ,OAAO,CAAC;CAGV,MAAM,QAAQ,IAAI,IAAY,sBAAsB,IAAI,CAAC;CACzD,MAAM,kBAAkB,oBAAoB,IAAI;CAEhD,IAAI,gBAAgB,SAAS,GAAG;EAC9B,MAAM,IAAI,qBAAqB;EAE/B,KAAK,MAAM,QAAQ,gCAAgC,MAAM,KAAK,GAC5D,MAAM,IAAI,GAAG,sBAAsB,GAAG,MAAM;CAEhD;CAEA,KAAK,MAAM,OAAO,iBAAiB;EACjC,MAAM,UAAU,KAAK;EAErB,MAAM,IAAI,GAAG;EAEb,IAAI,CAAC,aAAa,OAAO,GACvB;EAGF,KAAK,MAAM,UAAU,kBAAkB,SAAS,QAAQ,CAAC,GACvD,MAAM,IAAI,GAAG,IAAI,GAAG,QAAQ;CAEhC;CAEA,OAAO,CAAC,GAAG,KAAK;AAClB;;;;;AAMA,SAAgB,4BACd,QACU;CACV,MAAM,wBAAQ,IAAI,IAAY;CAE9B,KAAK,MAAM,YAAY,OAAO,KAAK,MAAM,GAAG;EAC1C,MAAM,eAAe,OAAO;EAE5B,IAAI,CAAC,aAAa,YAAY,GAC5B;EAGF,MAAM,IAAI,QAAQ;EAClB,MAAM,IAAI,iBAAiB;EAE3B,KAAK,MAAM,QAAQ,kBAAkB,eAA4B,GAAG;GAClE,MAAM,IAAI,GAAG,SAAS,GAAG,MAAM;GAC/B,MAAM,IAAI,GAAG,kBAAkB,GAAG,MAAM;EAC1C;CACF;CAEA,OAAO,CAAC,GAAG,KAAK;AAClB"}
@@ -0,0 +1,2 @@
1
+ import { ComponentPathVariable, ExtractPathVariables, PATH_VARIABLE_IDENTIFIER, PathHasVariables, PathHead, PathTail, PathVariable, PathVariableIdentifier, ResourcePathVariable, SubResourcePathVariable } from "./variables.cjs";
2
+ import { ConfigResourceKeys, ConfigSubResourceKeys, ParameterizedEntryPath, ParameterizedResourcePath, PathComponentKeys, PathResourceCandidates, PathSubResourceKeys, PathVariables, PathVariablesForResource, ResolveConfigNode, ResolveParameterizedPathValue, ResourceSubResourceKeys } from "./types.cjs";
@@ -0,0 +1,2 @@
1
+ import { ComponentPathVariable, ExtractPathVariables, PATH_VARIABLE_IDENTIFIER, PathHasVariables, PathHead, PathTail, PathVariable, PathVariableIdentifier, ResourcePathVariable, SubResourcePathVariable } from "./variables.mjs";
2
+ import { ConfigResourceKeys, ConfigSubResourceKeys, ParameterizedEntryPath, ParameterizedResourcePath, PathComponentKeys, PathResourceCandidates, PathSubResourceKeys, PathVariables, PathVariablesForResource, ResolveConfigNode, ResolveParameterizedPathValue, ResourceSubResourceKeys } from "./types.mjs";
@@ -0,0 +1,123 @@
1
+ const require_component_keys = require('../component-keys.cjs');
2
+ const require_variables = require('./variables.cjs');
3
+
4
+ //#region src/create-config/paths/introspect.ts
5
+ /** Every branch below `node`, at any depth within the path depth cap. */
6
+ function collectDescendantNodes(node, depth = 6) {
7
+ if (depth === 0) return [];
8
+ const nodes = [];
9
+ for (const key of require_component_keys.readSubResourceKeys(node)) {
10
+ const subNode = node[key];
11
+ if (!require_component_keys.isConfigNode(subNode)) continue;
12
+ nodes.push(subNode, ...collectDescendantNodes(subNode, depth - 1));
13
+ }
14
+ return nodes;
15
+ }
16
+ /** Sub-resource keys on `node` and below it, matching the values a nested param accepts. */
17
+ function collectSubResourceKeysBelow(node, depth = 6) {
18
+ const keys = new Set(require_component_keys.readSubResourceKeys(node));
19
+ for (const descendant of collectDescendantNodes(node, depth)) for (const key of require_component_keys.readSubResourceKeys(descendant)) keys.add(key);
20
+ return [...keys];
21
+ }
22
+ /**
23
+ * Whether the segments can be walked from `node`, treating variables as wildcards.
24
+ * Used to narrow which resources a `$resource` path can target.
25
+ */
26
+ function pathExistsOnNode(node, segments) {
27
+ if (segments.length === 0) return true;
28
+ if (!require_component_keys.isConfigNode(node)) return false;
29
+ const [segment, ...rest] = segments;
30
+ if (segment === void 0) return true;
31
+ const variableName = require_variables.readPathVariableName(segment);
32
+ if (variableName === "subResource") return collectDescendantNodes(node).some((descendant) => pathExistsOnNode(descendant, rest));
33
+ if (variableName === "component") return require_component_keys.readComponentKeys(node).some((key) => pathExistsOnNode(node[key], rest));
34
+ if (variableName !== void 0) return false;
35
+ const next = node[segment];
36
+ return next !== void 0 && pathExistsOnNode(next, rest);
37
+ }
38
+ /** Resource keys configured in a config map. */
39
+ function collectConfigResources(config) {
40
+ return Object.keys(config).filter((key) => require_component_keys.isConfigNode(config[key]));
41
+ }
42
+ /**
43
+ * Resources a path can target. A `$resource` path keeps only the resources whose config
44
+ * satisfies the path's fixed segments; a literal path keeps just that resource.
45
+ */
46
+ function collectPathResources(config, path) {
47
+ const [head, ...rest] = path.split(".");
48
+ const resources = collectConfigResources(config);
49
+ if (head === void 0) return [];
50
+ if (require_variables.readPathVariableName(head) === "resource") return resources.filter((resource) => pathExistsOnNode(config[resource], rest));
51
+ return resources.filter((resource) => resource === head && pathExistsOnNode(config[resource], rest));
52
+ }
53
+ /** Nodes a path's fixed prefix resolves to, stopping before `segments` runs out. */
54
+ function collectNodesForSegments(node, segments) {
55
+ let frontier = [node];
56
+ for (const segment of segments) {
57
+ const next = [];
58
+ const variableName = require_variables.readPathVariableName(segment);
59
+ for (const current of frontier) {
60
+ if (variableName === "subResource") {
61
+ next.push(...collectDescendantNodes(current));
62
+ continue;
63
+ }
64
+ if (variableName === "component") {
65
+ for (const key of require_component_keys.readComponentKeys(current)) {
66
+ const value = current[key];
67
+ if (require_component_keys.isConfigNode(value)) next.push(value);
68
+ }
69
+ continue;
70
+ }
71
+ if (variableName !== void 0) continue;
72
+ const value = current[segment];
73
+ if (require_component_keys.isConfigNode(value)) next.push(value);
74
+ }
75
+ frontier = next;
76
+ }
77
+ return frontier;
78
+ }
79
+ /** Sub-resource keys valid for the `$subResource` segment of a path. */
80
+ function collectPathSubResourceKeys(config, path) {
81
+ const segments = path.split(".");
82
+ const subResourceIndex = segments.findIndex((segment) => require_variables.readPathVariableName(segment) === "subResource");
83
+ const keys = /* @__PURE__ */ new Set();
84
+ for (const resource of collectPathResources(config, path)) {
85
+ const resourceNode = config[resource];
86
+ if (!require_component_keys.isConfigNode(resourceNode)) continue;
87
+ const prefix = subResourceIndex === -1 ? [] : segments.slice(1, subResourceIndex);
88
+ for (const node of collectNodesForSegments(resourceNode, prefix)) for (const key of collectSubResourceKeysBelow(node)) keys.add(key);
89
+ }
90
+ return [...keys];
91
+ }
92
+ /** Component slot keys configured on the node a path resolves to. */
93
+ function collectPathComponentKeys(config, path) {
94
+ const tail = path.split(".").slice(1);
95
+ const withoutTrailingComponent = require_variables.readPathVariableName(tail.at(-1) ?? "") === "component" ? tail.slice(0, -1) : tail;
96
+ const keys = /* @__PURE__ */ new Set();
97
+ for (const resource of collectPathResources(config, path)) {
98
+ const resourceNode = config[resource];
99
+ if (!require_component_keys.isConfigNode(resourceNode)) continue;
100
+ for (const node of collectNodesForSegments(resourceNode, withoutTrailingComponent)) for (const key of require_component_keys.readComponentKeys(node)) keys.add(key);
101
+ }
102
+ return [...keys];
103
+ }
104
+ /** Sub-resource keys configured for one resource, at any depth. */
105
+ function collectResourceSubResourceKeys(config, resource) {
106
+ const resourceNode = config[resource];
107
+ return require_component_keys.isConfigNode(resourceNode) ? collectSubResourceKeysBelow(resourceNode) : [];
108
+ }
109
+ /** All sub-resource keys configured anywhere in a config map. */
110
+ function collectAllSubResourceKeys(config) {
111
+ const keys = /* @__PURE__ */ new Set();
112
+ for (const resource of collectConfigResources(config)) for (const key of collectResourceSubResourceKeys(config, resource)) keys.add(key);
113
+ return [...keys];
114
+ }
115
+
116
+ //#endregion
117
+ exports.collectAllSubResourceKeys = collectAllSubResourceKeys;
118
+ exports.collectConfigResources = collectConfigResources;
119
+ exports.collectPathComponentKeys = collectPathComponentKeys;
120
+ exports.collectPathResources = collectPathResources;
121
+ exports.collectPathSubResourceKeys = collectPathSubResourceKeys;
122
+ exports.collectResourceSubResourceKeys = collectResourceSubResourceKeys;
123
+ //# sourceMappingURL=introspect.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspect.cjs","names":["readSubResourceKeys","isConfigNode","readPathVariableName","readComponentKeys"],"sources":["../../../src/create-config/paths/introspect.ts"],"sourcesContent":["import {\n isConfigNode,\n readComponentKeys,\n readSubResourceKeys,\n} from '../component-keys';\nimport { MAX_PATH_DEPTH } from './generate';\nimport { readPathVariableName } from './variables';\n\n/** Every branch below `node`, at any depth within the path depth cap. */\nexport function collectDescendantNodes(\n node: Record<string, unknown>,\n depth = MAX_PATH_DEPTH,\n): Record<string, unknown>[] {\n if (depth === 0) {\n return [];\n }\n\n const nodes: Record<string, unknown>[] = [];\n\n for (const key of readSubResourceKeys(node)) {\n const subNode = node[key];\n\n if (!isConfigNode(subNode)) {\n continue;\n }\n\n nodes.push(subNode, ...collectDescendantNodes(subNode, depth - 1));\n }\n\n return nodes;\n}\n\n/** Sub-resource keys on `node` and below it, matching the values a nested param accepts. */\nexport function collectSubResourceKeysBelow(\n node: Record<string, unknown>,\n depth = MAX_PATH_DEPTH,\n): string[] {\n const keys = new Set<string>(readSubResourceKeys(node));\n\n for (const descendant of collectDescendantNodes(node, depth)) {\n for (const key of readSubResourceKeys(descendant)) {\n keys.add(key);\n }\n }\n\n return [...keys];\n}\n\n/**\n * Whether the segments can be walked from `node`, treating variables as wildcards.\n * Used to narrow which resources a `$resource` path can target.\n */\nexport function pathExistsOnNode(\n node: unknown,\n segments: readonly string[],\n): boolean {\n if (segments.length === 0) {\n return true;\n }\n\n if (!isConfigNode(node)) {\n return false;\n }\n\n const [segment, ...rest] = segments;\n\n if (segment === undefined) {\n return true;\n }\n\n const variableName = readPathVariableName(segment);\n\n if (variableName === 'subResource') {\n return collectDescendantNodes(node).some((descendant) =>\n pathExistsOnNode(descendant, rest),\n );\n }\n\n if (variableName === 'component') {\n return readComponentKeys(node).some((key) =>\n pathExistsOnNode(node[key], rest),\n );\n }\n\n if (variableName !== undefined) {\n return false;\n }\n\n const next = node[segment];\n\n return next !== undefined && pathExistsOnNode(next, rest);\n}\n\n/** Resource keys configured in a config map. */\nexport function collectConfigResources(\n config: Record<string, unknown>,\n): string[] {\n return Object.keys(config).filter((key) => isConfigNode(config[key]));\n}\n\n/**\n * Resources a path can target. A `$resource` path keeps only the resources whose config\n * satisfies the path's fixed segments; a literal path keeps just that resource.\n */\nexport function collectPathResources(\n config: Record<string, unknown>,\n path: string,\n): string[] {\n const [head, ...rest] = path.split('.');\n const resources = collectConfigResources(config);\n\n if (head === undefined) {\n return [];\n }\n\n if (readPathVariableName(head) === 'resource') {\n return resources.filter((resource) =>\n pathExistsOnNode(config[resource], rest),\n );\n }\n\n return resources.filter(\n (resource) => resource === head && pathExistsOnNode(config[resource], rest),\n );\n}\n\n/** Nodes a path's fixed prefix resolves to, stopping before `segments` runs out. */\nfunction collectNodesForSegments(\n node: Record<string, unknown>,\n segments: readonly string[],\n): Record<string, unknown>[] {\n let frontier: Record<string, unknown>[] = [node];\n\n for (const segment of segments) {\n const next: Record<string, unknown>[] = [];\n const variableName = readPathVariableName(segment);\n\n for (const current of frontier) {\n if (variableName === 'subResource') {\n next.push(...collectDescendantNodes(current));\n continue;\n }\n\n if (variableName === 'component') {\n for (const key of readComponentKeys(current)) {\n const value = current[key];\n\n if (isConfigNode(value)) {\n next.push(value);\n }\n }\n continue;\n }\n\n if (variableName !== undefined) {\n continue;\n }\n\n const value = current[segment];\n\n if (isConfigNode(value)) {\n next.push(value);\n }\n }\n\n frontier = next;\n }\n\n return frontier;\n}\n\n/** Sub-resource keys valid for the `$subResource` segment of a path. */\nexport function collectPathSubResourceKeys(\n config: Record<string, unknown>,\n path: string,\n): string[] {\n const segments = path.split('.');\n const subResourceIndex = segments.findIndex(\n (segment) => readPathVariableName(segment) === 'subResource',\n );\n const keys = new Set<string>();\n\n for (const resource of collectPathResources(config, path)) {\n const resourceNode = config[resource];\n\n if (!isConfigNode(resourceNode)) {\n continue;\n }\n\n const prefix =\n subResourceIndex === -1 ? [] : segments.slice(1, subResourceIndex);\n\n for (const node of collectNodesForSegments(resourceNode, prefix)) {\n for (const key of collectSubResourceKeysBelow(node)) {\n keys.add(key);\n }\n }\n }\n\n return [...keys];\n}\n\n/** Component slot keys configured on the node a path resolves to. */\nexport function collectPathComponentKeys(\n config: Record<string, unknown>,\n path: string,\n): string[] {\n const segments = path.split('.');\n const tail = segments.slice(1);\n const withoutTrailingComponent =\n readPathVariableName(tail.at(-1) ?? '') === 'component'\n ? tail.slice(0, -1)\n : tail;\n const keys = new Set<string>();\n\n for (const resource of collectPathResources(config, path)) {\n const resourceNode = config[resource];\n\n if (!isConfigNode(resourceNode)) {\n continue;\n }\n\n for (const node of collectNodesForSegments(\n resourceNode,\n withoutTrailingComponent,\n )) {\n for (const key of readComponentKeys(node)) {\n keys.add(key);\n }\n }\n }\n\n return [...keys];\n}\n\n/** Sub-resource keys configured for one resource, at any depth. */\nexport function collectResourceSubResourceKeys(\n config: Record<string, unknown>,\n resource: string,\n): string[] {\n const resourceNode = config[resource];\n\n return isConfigNode(resourceNode)\n ? collectSubResourceKeysBelow(resourceNode)\n : [];\n}\n\n/** All sub-resource keys configured anywhere in a config map. */\nexport function collectAllSubResourceKeys(\n config: Record<string, unknown>,\n): string[] {\n const keys = new Set<string>();\n\n for (const resource of collectConfigResources(config)) {\n for (const key of collectResourceSubResourceKeys(config, resource)) {\n keys.add(key);\n }\n }\n\n return [...keys];\n}\n"],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ import { isConfigNode, readComponentKeys, readSubResourceKeys } from "../component-keys.mjs";
2
+ import { readPathVariableName } from "./variables.mjs";
3
+
4
+ //#region src/create-config/paths/introspect.ts
5
+ /** Every branch below `node`, at any depth within the path depth cap. */
6
+ function collectDescendantNodes(node, depth = 6) {
7
+ if (depth === 0) return [];
8
+ const nodes = [];
9
+ for (const key of readSubResourceKeys(node)) {
10
+ const subNode = node[key];
11
+ if (!isConfigNode(subNode)) continue;
12
+ nodes.push(subNode, ...collectDescendantNodes(subNode, depth - 1));
13
+ }
14
+ return nodes;
15
+ }
16
+ /** Sub-resource keys on `node` and below it, matching the values a nested param accepts. */
17
+ function collectSubResourceKeysBelow(node, depth = 6) {
18
+ const keys = new Set(readSubResourceKeys(node));
19
+ for (const descendant of collectDescendantNodes(node, depth)) for (const key of readSubResourceKeys(descendant)) keys.add(key);
20
+ return [...keys];
21
+ }
22
+ /**
23
+ * Whether the segments can be walked from `node`, treating variables as wildcards.
24
+ * Used to narrow which resources a `$resource` path can target.
25
+ */
26
+ function pathExistsOnNode(node, segments) {
27
+ if (segments.length === 0) return true;
28
+ if (!isConfigNode(node)) return false;
29
+ const [segment, ...rest] = segments;
30
+ if (segment === void 0) return true;
31
+ const variableName = readPathVariableName(segment);
32
+ if (variableName === "subResource") return collectDescendantNodes(node).some((descendant) => pathExistsOnNode(descendant, rest));
33
+ if (variableName === "component") return readComponentKeys(node).some((key) => pathExistsOnNode(node[key], rest));
34
+ if (variableName !== void 0) return false;
35
+ const next = node[segment];
36
+ return next !== void 0 && pathExistsOnNode(next, rest);
37
+ }
38
+ /** Resource keys configured in a config map. */
39
+ function collectConfigResources(config) {
40
+ return Object.keys(config).filter((key) => isConfigNode(config[key]));
41
+ }
42
+ /**
43
+ * Resources a path can target. A `$resource` path keeps only the resources whose config
44
+ * satisfies the path's fixed segments; a literal path keeps just that resource.
45
+ */
46
+ function collectPathResources(config, path) {
47
+ const [head, ...rest] = path.split(".");
48
+ const resources = collectConfigResources(config);
49
+ if (head === void 0) return [];
50
+ if (readPathVariableName(head) === "resource") return resources.filter((resource) => pathExistsOnNode(config[resource], rest));
51
+ return resources.filter((resource) => resource === head && pathExistsOnNode(config[resource], rest));
52
+ }
53
+ /** Nodes a path's fixed prefix resolves to, stopping before `segments` runs out. */
54
+ function collectNodesForSegments(node, segments) {
55
+ let frontier = [node];
56
+ for (const segment of segments) {
57
+ const next = [];
58
+ const variableName = readPathVariableName(segment);
59
+ for (const current of frontier) {
60
+ if (variableName === "subResource") {
61
+ next.push(...collectDescendantNodes(current));
62
+ continue;
63
+ }
64
+ if (variableName === "component") {
65
+ for (const key of readComponentKeys(current)) {
66
+ const value = current[key];
67
+ if (isConfigNode(value)) next.push(value);
68
+ }
69
+ continue;
70
+ }
71
+ if (variableName !== void 0) continue;
72
+ const value = current[segment];
73
+ if (isConfigNode(value)) next.push(value);
74
+ }
75
+ frontier = next;
76
+ }
77
+ return frontier;
78
+ }
79
+ /** Sub-resource keys valid for the `$subResource` segment of a path. */
80
+ function collectPathSubResourceKeys(config, path) {
81
+ const segments = path.split(".");
82
+ const subResourceIndex = segments.findIndex((segment) => readPathVariableName(segment) === "subResource");
83
+ const keys = /* @__PURE__ */ new Set();
84
+ for (const resource of collectPathResources(config, path)) {
85
+ const resourceNode = config[resource];
86
+ if (!isConfigNode(resourceNode)) continue;
87
+ const prefix = subResourceIndex === -1 ? [] : segments.slice(1, subResourceIndex);
88
+ for (const node of collectNodesForSegments(resourceNode, prefix)) for (const key of collectSubResourceKeysBelow(node)) keys.add(key);
89
+ }
90
+ return [...keys];
91
+ }
92
+ /** Component slot keys configured on the node a path resolves to. */
93
+ function collectPathComponentKeys(config, path) {
94
+ const tail = path.split(".").slice(1);
95
+ const withoutTrailingComponent = readPathVariableName(tail.at(-1) ?? "") === "component" ? tail.slice(0, -1) : tail;
96
+ const keys = /* @__PURE__ */ new Set();
97
+ for (const resource of collectPathResources(config, path)) {
98
+ const resourceNode = config[resource];
99
+ if (!isConfigNode(resourceNode)) continue;
100
+ for (const node of collectNodesForSegments(resourceNode, withoutTrailingComponent)) for (const key of readComponentKeys(node)) keys.add(key);
101
+ }
102
+ return [...keys];
103
+ }
104
+ /** Sub-resource keys configured for one resource, at any depth. */
105
+ function collectResourceSubResourceKeys(config, resource) {
106
+ const resourceNode = config[resource];
107
+ return isConfigNode(resourceNode) ? collectSubResourceKeysBelow(resourceNode) : [];
108
+ }
109
+ /** All sub-resource keys configured anywhere in a config map. */
110
+ function collectAllSubResourceKeys(config) {
111
+ const keys = /* @__PURE__ */ new Set();
112
+ for (const resource of collectConfigResources(config)) for (const key of collectResourceSubResourceKeys(config, resource)) keys.add(key);
113
+ return [...keys];
114
+ }
115
+
116
+ //#endregion
117
+ export { collectAllSubResourceKeys, collectConfigResources, collectPathComponentKeys, collectPathResources, collectPathSubResourceKeys, collectResourceSubResourceKeys };
118
+ //# sourceMappingURL=introspect.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspect.mjs","names":[],"sources":["../../../src/create-config/paths/introspect.ts"],"sourcesContent":["import {\n isConfigNode,\n readComponentKeys,\n readSubResourceKeys,\n} from '../component-keys';\nimport { MAX_PATH_DEPTH } from './generate';\nimport { readPathVariableName } from './variables';\n\n/** Every branch below `node`, at any depth within the path depth cap. */\nexport function collectDescendantNodes(\n node: Record<string, unknown>,\n depth = MAX_PATH_DEPTH,\n): Record<string, unknown>[] {\n if (depth === 0) {\n return [];\n }\n\n const nodes: Record<string, unknown>[] = [];\n\n for (const key of readSubResourceKeys(node)) {\n const subNode = node[key];\n\n if (!isConfigNode(subNode)) {\n continue;\n }\n\n nodes.push(subNode, ...collectDescendantNodes(subNode, depth - 1));\n }\n\n return nodes;\n}\n\n/** Sub-resource keys on `node` and below it, matching the values a nested param accepts. */\nexport function collectSubResourceKeysBelow(\n node: Record<string, unknown>,\n depth = MAX_PATH_DEPTH,\n): string[] {\n const keys = new Set<string>(readSubResourceKeys(node));\n\n for (const descendant of collectDescendantNodes(node, depth)) {\n for (const key of readSubResourceKeys(descendant)) {\n keys.add(key);\n }\n }\n\n return [...keys];\n}\n\n/**\n * Whether the segments can be walked from `node`, treating variables as wildcards.\n * Used to narrow which resources a `$resource` path can target.\n */\nexport function pathExistsOnNode(\n node: unknown,\n segments: readonly string[],\n): boolean {\n if (segments.length === 0) {\n return true;\n }\n\n if (!isConfigNode(node)) {\n return false;\n }\n\n const [segment, ...rest] = segments;\n\n if (segment === undefined) {\n return true;\n }\n\n const variableName = readPathVariableName(segment);\n\n if (variableName === 'subResource') {\n return collectDescendantNodes(node).some((descendant) =>\n pathExistsOnNode(descendant, rest),\n );\n }\n\n if (variableName === 'component') {\n return readComponentKeys(node).some((key) =>\n pathExistsOnNode(node[key], rest),\n );\n }\n\n if (variableName !== undefined) {\n return false;\n }\n\n const next = node[segment];\n\n return next !== undefined && pathExistsOnNode(next, rest);\n}\n\n/** Resource keys configured in a config map. */\nexport function collectConfigResources(\n config: Record<string, unknown>,\n): string[] {\n return Object.keys(config).filter((key) => isConfigNode(config[key]));\n}\n\n/**\n * Resources a path can target. A `$resource` path keeps only the resources whose config\n * satisfies the path's fixed segments; a literal path keeps just that resource.\n */\nexport function collectPathResources(\n config: Record<string, unknown>,\n path: string,\n): string[] {\n const [head, ...rest] = path.split('.');\n const resources = collectConfigResources(config);\n\n if (head === undefined) {\n return [];\n }\n\n if (readPathVariableName(head) === 'resource') {\n return resources.filter((resource) =>\n pathExistsOnNode(config[resource], rest),\n );\n }\n\n return resources.filter(\n (resource) => resource === head && pathExistsOnNode(config[resource], rest),\n );\n}\n\n/** Nodes a path's fixed prefix resolves to, stopping before `segments` runs out. */\nfunction collectNodesForSegments(\n node: Record<string, unknown>,\n segments: readonly string[],\n): Record<string, unknown>[] {\n let frontier: Record<string, unknown>[] = [node];\n\n for (const segment of segments) {\n const next: Record<string, unknown>[] = [];\n const variableName = readPathVariableName(segment);\n\n for (const current of frontier) {\n if (variableName === 'subResource') {\n next.push(...collectDescendantNodes(current));\n continue;\n }\n\n if (variableName === 'component') {\n for (const key of readComponentKeys(current)) {\n const value = current[key];\n\n if (isConfigNode(value)) {\n next.push(value);\n }\n }\n continue;\n }\n\n if (variableName !== undefined) {\n continue;\n }\n\n const value = current[segment];\n\n if (isConfigNode(value)) {\n next.push(value);\n }\n }\n\n frontier = next;\n }\n\n return frontier;\n}\n\n/** Sub-resource keys valid for the `$subResource` segment of a path. */\nexport function collectPathSubResourceKeys(\n config: Record<string, unknown>,\n path: string,\n): string[] {\n const segments = path.split('.');\n const subResourceIndex = segments.findIndex(\n (segment) => readPathVariableName(segment) === 'subResource',\n );\n const keys = new Set<string>();\n\n for (const resource of collectPathResources(config, path)) {\n const resourceNode = config[resource];\n\n if (!isConfigNode(resourceNode)) {\n continue;\n }\n\n const prefix =\n subResourceIndex === -1 ? [] : segments.slice(1, subResourceIndex);\n\n for (const node of collectNodesForSegments(resourceNode, prefix)) {\n for (const key of collectSubResourceKeysBelow(node)) {\n keys.add(key);\n }\n }\n }\n\n return [...keys];\n}\n\n/** Component slot keys configured on the node a path resolves to. */\nexport function collectPathComponentKeys(\n config: Record<string, unknown>,\n path: string,\n): string[] {\n const segments = path.split('.');\n const tail = segments.slice(1);\n const withoutTrailingComponent =\n readPathVariableName(tail.at(-1) ?? '') === 'component'\n ? tail.slice(0, -1)\n : tail;\n const keys = new Set<string>();\n\n for (const resource of collectPathResources(config, path)) {\n const resourceNode = config[resource];\n\n if (!isConfigNode(resourceNode)) {\n continue;\n }\n\n for (const node of collectNodesForSegments(\n resourceNode,\n withoutTrailingComponent,\n )) {\n for (const key of readComponentKeys(node)) {\n keys.add(key);\n }\n }\n }\n\n return [...keys];\n}\n\n/** Sub-resource keys configured for one resource, at any depth. */\nexport function collectResourceSubResourceKeys(\n config: Record<string, unknown>,\n resource: string,\n): string[] {\n const resourceNode = config[resource];\n\n return isConfigNode(resourceNode)\n ? collectSubResourceKeysBelow(resourceNode)\n : [];\n}\n\n/** All sub-resource keys configured anywhere in a config map. */\nexport function collectAllSubResourceKeys(\n config: Record<string, unknown>,\n): string[] {\n const keys = new Set<string>();\n\n for (const resource of collectConfigResources(config)) {\n for (const key of collectResourceSubResourceKeys(config, resource)) {\n keys.add(key);\n }\n }\n\n return [...keys];\n}\n"],"mappings":"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"}
@@ -0,0 +1,130 @@
1
+ const require_invalid_path = require('../../errors/invalid-path.cjs');
2
+ const require_invalid_resource = require('../../errors/invalid-resource.cjs');
3
+ const require_invalid_sub_resource = require('../../errors/invalid-sub-resource.cjs');
4
+ const require_invalid_component = require('../../errors/invalid-component.cjs');
5
+ const require_missing_path_variable = require('../../errors/missing-path-variable.cjs');
6
+ const require_component_keys = require('../component-keys.cjs');
7
+ const require_variables = require('./variables.cjs');
8
+
9
+ //#region src/create-config/paths/resolve.ts
10
+ function readSubResourceParamKey(param) {
11
+ return typeof param === "string" ? param : param.value;
12
+ }
13
+ /** Sub-resource slugs a param walks through, outermost first. */
14
+ function flattenSubResourceParam(param) {
15
+ if (typeof param === "string") return [param];
16
+ return [param.value, ...flattenSubResourceParam(param.subResource)];
17
+ }
18
+ function isSubResourceParamValue(value) {
19
+ if (typeof value === "string") return value.length > 0;
20
+ if (typeof value !== "object" || value === null) return false;
21
+ const { value: slug, subResource } = value;
22
+ return typeof slug === "string" && isSubResourceParamValue(subResource);
23
+ }
24
+ function resolveSubResourceParam(node, param, resource) {
25
+ const key = readSubResourceParamKey(param);
26
+ if (!require_component_keys.isConfigNode(node)) throw new require_invalid_sub_resource.InvalidSubResourceError({
27
+ subResource: key,
28
+ resource,
29
+ reason: `"${key}" cannot be read because its parent is not a configured resource`
30
+ });
31
+ const next = node[key];
32
+ if (!require_component_keys.isConfigNode(next)) throw new require_invalid_sub_resource.InvalidSubResourceError({
33
+ subResource: key,
34
+ resource,
35
+ validSubResources: require_component_keys.readSubResourceKeys(node)
36
+ });
37
+ if (typeof param === "string") return next;
38
+ return resolveSubResourceParam(next, param.subResource, resource);
39
+ }
40
+ function readVariable(variables, name, path) {
41
+ const value = variables[name];
42
+ if (value === void 0) throw new require_missing_path_variable.MissingPathVariableError({
43
+ variable: name,
44
+ path,
45
+ providedVariables: Object.keys(variables)
46
+ });
47
+ return value;
48
+ }
49
+ /**
50
+ * Reads the value a path points at, substituting variable segments from `variables`.
51
+ * Throws a typed error naming the segment that could not be resolved.
52
+ */
53
+ function resolveParameterizedPath(config, path, variables = {}) {
54
+ const segments = path.split(".");
55
+ let cursor = config;
56
+ let resource;
57
+ for (const [index, segment] of segments.entries()) {
58
+ const isRoot = index === 0;
59
+ const variableName = require_variables.readPathVariableName(segment);
60
+ if (variableName === void 0) {
61
+ if (!require_component_keys.isConfigNode(cursor)) throw new require_invalid_path.InvalidPathError({
62
+ path,
63
+ reason: `"${segment}" cannot be read because the value before it is not a config branch`
64
+ });
65
+ const next = cursor[segment];
66
+ if (next === void 0) {
67
+ if (isRoot) throw new require_invalid_resource.InvalidResourceError({
68
+ resource: segment,
69
+ validResources: Object.keys(config)
70
+ });
71
+ throw new require_invalid_path.InvalidPathError({
72
+ path,
73
+ reason: `"${segment}" is not configured`
74
+ });
75
+ }
76
+ if (isRoot) resource = segment;
77
+ cursor = next;
78
+ continue;
79
+ }
80
+ switch (variableName) {
81
+ case "resource": {
82
+ const value = readVariable(variables, "resource", path);
83
+ if (typeof value !== "string" || !require_component_keys.isConfigNode(config[value])) throw new require_invalid_resource.InvalidResourceError({
84
+ resource: value,
85
+ validResources: Object.keys(config)
86
+ });
87
+ resource = value;
88
+ cursor = config[value];
89
+ break;
90
+ }
91
+ case "subResource": {
92
+ const value = readVariable(variables, "subResource", path);
93
+ if (!isSubResourceParamValue(value)) throw new require_invalid_sub_resource.InvalidSubResourceError({
94
+ subResource: value,
95
+ resource,
96
+ reason: "A sub-resource must be a slug or an object with `value` and `subResource`"
97
+ });
98
+ cursor = resolveSubResourceParam(cursor, value, resource);
99
+ break;
100
+ }
101
+ case "component": {
102
+ const value = readVariable(variables, "component", path);
103
+ if (!require_component_keys.isConfigNode(cursor)) throw new require_invalid_component.InvalidComponentError({
104
+ component: value,
105
+ path,
106
+ resource,
107
+ reason: `"${String(value)}" cannot be read because the value before it is not a config branch`
108
+ });
109
+ if (typeof value !== "string" || cursor[value] === void 0) throw new require_invalid_component.InvalidComponentError({
110
+ component: value,
111
+ path,
112
+ resource,
113
+ validComponents: require_component_keys.readComponentKeys(cursor)
114
+ });
115
+ cursor = cursor[value];
116
+ break;
117
+ }
118
+ default: throw new require_invalid_path.InvalidPathError({
119
+ path,
120
+ reason: `"${segment}" is not a supported path variable`
121
+ });
122
+ }
123
+ }
124
+ return cursor;
125
+ }
126
+
127
+ //#endregion
128
+ exports.flattenSubResourceParam = flattenSubResourceParam;
129
+ exports.resolveParameterizedPath = resolveParameterizedPath;
130
+ //# sourceMappingURL=resolve.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.cjs","names":["isConfigNode","InvalidSubResourceError","readSubResourceKeys","MissingPathVariableError","readPathVariableName","InvalidPathError","InvalidResourceError","InvalidComponentError","readComponentKeys"],"sources":["../../../src/create-config/paths/resolve.ts"],"sourcesContent":["import {\n InvalidComponentError,\n InvalidPathError,\n InvalidResourceError,\n InvalidSubResourceError,\n MissingPathVariableError,\n} from '../../errors';\nimport {\n isConfigNode,\n readComponentKeys,\n readSubResourceKeys,\n} from '../component-keys';\nimport { readPathVariableName } from './variables';\n\n/** A `$subResource` value: a slug, or a slug plus the next `subResource` below it. */\nexport type SubResourceParamValue =\n | string\n | { value: string; subResource: SubResourceParamValue };\n\n/** Values supplied for the variables a path leaves open. */\nexport type PathVariableValues = {\n resource?: string;\n subResource?: SubResourceParamValue;\n component?: string;\n};\n\nfunction readSubResourceParamKey(param: SubResourceParamValue): string {\n return typeof param === 'string' ? param : param.value;\n}\n\n/** Sub-resource slugs a param walks through, outermost first. */\nexport function flattenSubResourceParam(\n param: SubResourceParamValue,\n): string[] {\n if (typeof param === 'string') {\n return [param];\n }\n\n return [param.value, ...flattenSubResourceParam(param.subResource)];\n}\n\nfunction isSubResourceParamValue(\n value: unknown,\n): value is SubResourceParamValue {\n if (typeof value === 'string') {\n return value.length > 0;\n }\n\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const { value: slug, subResource } = value as {\n value?: unknown;\n subResource?: unknown;\n };\n\n return typeof slug === 'string' && isSubResourceParamValue(subResource);\n}\n\nfunction resolveSubResourceParam(\n node: unknown,\n param: SubResourceParamValue,\n resource: string | undefined,\n): unknown {\n const key = readSubResourceParamKey(param);\n\n if (!isConfigNode(node)) {\n throw new InvalidSubResourceError({\n subResource: key,\n resource,\n reason: `\"${key}\" cannot be read because its parent is not a configured resource`,\n });\n }\n\n const next = node[key];\n\n if (!isConfigNode(next)) {\n throw new InvalidSubResourceError({\n subResource: key,\n resource,\n validSubResources: readSubResourceKeys(node),\n });\n }\n\n if (typeof param === 'string') {\n return next;\n }\n\n return resolveSubResourceParam(next, param.subResource, resource);\n}\n\nfunction readVariable<Name extends keyof PathVariableValues>(\n variables: PathVariableValues,\n name: Name,\n path: string,\n): NonNullable<PathVariableValues[Name]> {\n const value = variables[name];\n\n if (value === undefined) {\n throw new MissingPathVariableError({\n variable: name,\n path,\n providedVariables: Object.keys(variables),\n });\n }\n\n return value as NonNullable<PathVariableValues[Name]>;\n}\n\n/**\n * Reads the value a path points at, substituting variable segments from `variables`.\n * Throws a typed error naming the segment that could not be resolved.\n */\nexport function resolveParameterizedPath(\n config: Record<string, unknown>,\n path: string,\n variables: PathVariableValues = {},\n): unknown {\n const segments = path.split('.');\n let cursor: unknown = config;\n let resource: string | undefined;\n\n for (const [index, segment] of segments.entries()) {\n const isRoot = index === 0;\n const variableName = readPathVariableName(segment);\n\n if (variableName === undefined) {\n if (!isConfigNode(cursor)) {\n throw new InvalidPathError({\n path,\n reason: `\"${segment}\" cannot be read because the value before it is not a config branch`,\n });\n }\n\n const next = cursor[segment];\n\n if (next === undefined) {\n if (isRoot) {\n throw new InvalidResourceError({\n resource: segment,\n validResources: Object.keys(config),\n });\n }\n\n throw new InvalidPathError({\n path,\n reason: `\"${segment}\" is not configured`,\n });\n }\n\n if (isRoot) {\n resource = segment;\n }\n\n cursor = next;\n continue;\n }\n\n switch (variableName) {\n case 'resource': {\n const value = readVariable(variables, 'resource', path);\n\n if (typeof value !== 'string' || !isConfigNode(config[value])) {\n throw new InvalidResourceError({\n resource: value,\n validResources: Object.keys(config),\n });\n }\n\n resource = value;\n cursor = config[value];\n break;\n }\n case 'subResource': {\n const value = readVariable(variables, 'subResource', path);\n\n if (!isSubResourceParamValue(value)) {\n throw new InvalidSubResourceError({\n subResource: value,\n resource,\n reason:\n 'A sub-resource must be a slug or an object with `value` and `subResource`',\n });\n }\n\n cursor = resolveSubResourceParam(cursor, value, resource);\n break;\n }\n case 'component': {\n const value = readVariable(variables, 'component', path);\n\n if (!isConfigNode(cursor)) {\n throw new InvalidComponentError({\n component: value,\n path,\n resource,\n reason: `\"${String(value)}\" cannot be read because the value before it is not a config branch`,\n });\n }\n\n if (typeof value !== 'string' || cursor[value] === undefined) {\n throw new InvalidComponentError({\n component: value,\n path,\n resource,\n validComponents: readComponentKeys(cursor),\n });\n }\n\n cursor = cursor[value];\n break;\n }\n default: {\n throw new InvalidPathError({\n path,\n reason: `\"${segment}\" is not a supported path variable`,\n });\n }\n }\n }\n\n return cursor;\n}\n"],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ import { InvalidPathError } from "../../errors/invalid-path.mjs";
2
+ import { InvalidResourceError } from "../../errors/invalid-resource.mjs";
3
+ import { InvalidSubResourceError } from "../../errors/invalid-sub-resource.mjs";
4
+ import { InvalidComponentError } from "../../errors/invalid-component.mjs";
5
+ import { MissingPathVariableError } from "../../errors/missing-path-variable.mjs";
6
+ import { isConfigNode, readComponentKeys, readSubResourceKeys } from "../component-keys.mjs";
7
+ import { readPathVariableName } from "./variables.mjs";
8
+
9
+ //#region src/create-config/paths/resolve.ts
10
+ function readSubResourceParamKey(param) {
11
+ return typeof param === "string" ? param : param.value;
12
+ }
13
+ /** Sub-resource slugs a param walks through, outermost first. */
14
+ function flattenSubResourceParam(param) {
15
+ if (typeof param === "string") return [param];
16
+ return [param.value, ...flattenSubResourceParam(param.subResource)];
17
+ }
18
+ function isSubResourceParamValue(value) {
19
+ if (typeof value === "string") return value.length > 0;
20
+ if (typeof value !== "object" || value === null) return false;
21
+ const { value: slug, subResource } = value;
22
+ return typeof slug === "string" && isSubResourceParamValue(subResource);
23
+ }
24
+ function resolveSubResourceParam(node, param, resource) {
25
+ const key = readSubResourceParamKey(param);
26
+ if (!isConfigNode(node)) throw new InvalidSubResourceError({
27
+ subResource: key,
28
+ resource,
29
+ reason: `"${key}" cannot be read because its parent is not a configured resource`
30
+ });
31
+ const next = node[key];
32
+ if (!isConfigNode(next)) throw new InvalidSubResourceError({
33
+ subResource: key,
34
+ resource,
35
+ validSubResources: readSubResourceKeys(node)
36
+ });
37
+ if (typeof param === "string") return next;
38
+ return resolveSubResourceParam(next, param.subResource, resource);
39
+ }
40
+ function readVariable(variables, name, path) {
41
+ const value = variables[name];
42
+ if (value === void 0) throw new MissingPathVariableError({
43
+ variable: name,
44
+ path,
45
+ providedVariables: Object.keys(variables)
46
+ });
47
+ return value;
48
+ }
49
+ /**
50
+ * Reads the value a path points at, substituting variable segments from `variables`.
51
+ * Throws a typed error naming the segment that could not be resolved.
52
+ */
53
+ function resolveParameterizedPath(config, path, variables = {}) {
54
+ const segments = path.split(".");
55
+ let cursor = config;
56
+ let resource;
57
+ for (const [index, segment] of segments.entries()) {
58
+ const isRoot = index === 0;
59
+ const variableName = readPathVariableName(segment);
60
+ if (variableName === void 0) {
61
+ if (!isConfigNode(cursor)) throw new InvalidPathError({
62
+ path,
63
+ reason: `"${segment}" cannot be read because the value before it is not a config branch`
64
+ });
65
+ const next = cursor[segment];
66
+ if (next === void 0) {
67
+ if (isRoot) throw new InvalidResourceError({
68
+ resource: segment,
69
+ validResources: Object.keys(config)
70
+ });
71
+ throw new InvalidPathError({
72
+ path,
73
+ reason: `"${segment}" is not configured`
74
+ });
75
+ }
76
+ if (isRoot) resource = segment;
77
+ cursor = next;
78
+ continue;
79
+ }
80
+ switch (variableName) {
81
+ case "resource": {
82
+ const value = readVariable(variables, "resource", path);
83
+ if (typeof value !== "string" || !isConfigNode(config[value])) throw new InvalidResourceError({
84
+ resource: value,
85
+ validResources: Object.keys(config)
86
+ });
87
+ resource = value;
88
+ cursor = config[value];
89
+ break;
90
+ }
91
+ case "subResource": {
92
+ const value = readVariable(variables, "subResource", path);
93
+ if (!isSubResourceParamValue(value)) throw new InvalidSubResourceError({
94
+ subResource: value,
95
+ resource,
96
+ reason: "A sub-resource must be a slug or an object with `value` and `subResource`"
97
+ });
98
+ cursor = resolveSubResourceParam(cursor, value, resource);
99
+ break;
100
+ }
101
+ case "component": {
102
+ const value = readVariable(variables, "component", path);
103
+ if (!isConfigNode(cursor)) throw new InvalidComponentError({
104
+ component: value,
105
+ path,
106
+ resource,
107
+ reason: `"${String(value)}" cannot be read because the value before it is not a config branch`
108
+ });
109
+ if (typeof value !== "string" || cursor[value] === void 0) throw new InvalidComponentError({
110
+ component: value,
111
+ path,
112
+ resource,
113
+ validComponents: readComponentKeys(cursor)
114
+ });
115
+ cursor = cursor[value];
116
+ break;
117
+ }
118
+ default: throw new InvalidPathError({
119
+ path,
120
+ reason: `"${segment}" is not a supported path variable`
121
+ });
122
+ }
123
+ }
124
+ return cursor;
125
+ }
126
+
127
+ //#endregion
128
+ export { flattenSubResourceParam, resolveParameterizedPath };
129
+ //# sourceMappingURL=resolve.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.mjs","names":[],"sources":["../../../src/create-config/paths/resolve.ts"],"sourcesContent":["import {\n InvalidComponentError,\n InvalidPathError,\n InvalidResourceError,\n InvalidSubResourceError,\n MissingPathVariableError,\n} from '../../errors';\nimport {\n isConfigNode,\n readComponentKeys,\n readSubResourceKeys,\n} from '../component-keys';\nimport { readPathVariableName } from './variables';\n\n/** A `$subResource` value: a slug, or a slug plus the next `subResource` below it. */\nexport type SubResourceParamValue =\n | string\n | { value: string; subResource: SubResourceParamValue };\n\n/** Values supplied for the variables a path leaves open. */\nexport type PathVariableValues = {\n resource?: string;\n subResource?: SubResourceParamValue;\n component?: string;\n};\n\nfunction readSubResourceParamKey(param: SubResourceParamValue): string {\n return typeof param === 'string' ? param : param.value;\n}\n\n/** Sub-resource slugs a param walks through, outermost first. */\nexport function flattenSubResourceParam(\n param: SubResourceParamValue,\n): string[] {\n if (typeof param === 'string') {\n return [param];\n }\n\n return [param.value, ...flattenSubResourceParam(param.subResource)];\n}\n\nfunction isSubResourceParamValue(\n value: unknown,\n): value is SubResourceParamValue {\n if (typeof value === 'string') {\n return value.length > 0;\n }\n\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const { value: slug, subResource } = value as {\n value?: unknown;\n subResource?: unknown;\n };\n\n return typeof slug === 'string' && isSubResourceParamValue(subResource);\n}\n\nfunction resolveSubResourceParam(\n node: unknown,\n param: SubResourceParamValue,\n resource: string | undefined,\n): unknown {\n const key = readSubResourceParamKey(param);\n\n if (!isConfigNode(node)) {\n throw new InvalidSubResourceError({\n subResource: key,\n resource,\n reason: `\"${key}\" cannot be read because its parent is not a configured resource`,\n });\n }\n\n const next = node[key];\n\n if (!isConfigNode(next)) {\n throw new InvalidSubResourceError({\n subResource: key,\n resource,\n validSubResources: readSubResourceKeys(node),\n });\n }\n\n if (typeof param === 'string') {\n return next;\n }\n\n return resolveSubResourceParam(next, param.subResource, resource);\n}\n\nfunction readVariable<Name extends keyof PathVariableValues>(\n variables: PathVariableValues,\n name: Name,\n path: string,\n): NonNullable<PathVariableValues[Name]> {\n const value = variables[name];\n\n if (value === undefined) {\n throw new MissingPathVariableError({\n variable: name,\n path,\n providedVariables: Object.keys(variables),\n });\n }\n\n return value as NonNullable<PathVariableValues[Name]>;\n}\n\n/**\n * Reads the value a path points at, substituting variable segments from `variables`.\n * Throws a typed error naming the segment that could not be resolved.\n */\nexport function resolveParameterizedPath(\n config: Record<string, unknown>,\n path: string,\n variables: PathVariableValues = {},\n): unknown {\n const segments = path.split('.');\n let cursor: unknown = config;\n let resource: string | undefined;\n\n for (const [index, segment] of segments.entries()) {\n const isRoot = index === 0;\n const variableName = readPathVariableName(segment);\n\n if (variableName === undefined) {\n if (!isConfigNode(cursor)) {\n throw new InvalidPathError({\n path,\n reason: `\"${segment}\" cannot be read because the value before it is not a config branch`,\n });\n }\n\n const next = cursor[segment];\n\n if (next === undefined) {\n if (isRoot) {\n throw new InvalidResourceError({\n resource: segment,\n validResources: Object.keys(config),\n });\n }\n\n throw new InvalidPathError({\n path,\n reason: `\"${segment}\" is not configured`,\n });\n }\n\n if (isRoot) {\n resource = segment;\n }\n\n cursor = next;\n continue;\n }\n\n switch (variableName) {\n case 'resource': {\n const value = readVariable(variables, 'resource', path);\n\n if (typeof value !== 'string' || !isConfigNode(config[value])) {\n throw new InvalidResourceError({\n resource: value,\n validResources: Object.keys(config),\n });\n }\n\n resource = value;\n cursor = config[value];\n break;\n }\n case 'subResource': {\n const value = readVariable(variables, 'subResource', path);\n\n if (!isSubResourceParamValue(value)) {\n throw new InvalidSubResourceError({\n subResource: value,\n resource,\n reason:\n 'A sub-resource must be a slug or an object with `value` and `subResource`',\n });\n }\n\n cursor = resolveSubResourceParam(cursor, value, resource);\n break;\n }\n case 'component': {\n const value = readVariable(variables, 'component', path);\n\n if (!isConfigNode(cursor)) {\n throw new InvalidComponentError({\n component: value,\n path,\n resource,\n reason: `\"${String(value)}\" cannot be read because the value before it is not a config branch`,\n });\n }\n\n if (typeof value !== 'string' || cursor[value] === undefined) {\n throw new InvalidComponentError({\n component: value,\n path,\n resource,\n validComponents: readComponentKeys(cursor),\n });\n }\n\n cursor = cursor[value];\n break;\n }\n default: {\n throw new InvalidPathError({\n path,\n reason: `\"${segment}\" is not a supported path variable`,\n });\n }\n }\n }\n\n return cursor;\n}\n"],"mappings":"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"}