@businessdash/sdk 0.9.60

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 (268) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +899 -0
  3. package/dist/adapters-revalidate.d.ts +44 -0
  4. package/dist/adapters-revalidate.d.ts.map +1 -0
  5. package/dist/adapters-revalidate.js +68 -0
  6. package/dist/adapters-revalidate.js.map +1 -0
  7. package/dist/alpha.d.ts +21 -0
  8. package/dist/alpha.d.ts.map +1 -0
  9. package/dist/alpha.js +21 -0
  10. package/dist/alpha.js.map +1 -0
  11. package/dist/analytics-core.d.ts +34 -0
  12. package/dist/analytics-core.d.ts.map +1 -0
  13. package/dist/analytics-core.js +138 -0
  14. package/dist/analytics-core.js.map +1 -0
  15. package/dist/analytics-server.d.ts +54 -0
  16. package/dist/analytics-server.d.ts.map +1 -0
  17. package/dist/analytics-server.js +78 -0
  18. package/dist/analytics-server.js.map +1 -0
  19. package/dist/attribution-logo.d.ts +2 -0
  20. package/dist/attribution-logo.d.ts.map +1 -0
  21. package/dist/attribution-logo.js +5 -0
  22. package/dist/attribution-logo.js.map +1 -0
  23. package/dist/auth-handler.d.ts +92 -0
  24. package/dist/auth-handler.d.ts.map +1 -0
  25. package/dist/auth-handler.js +277 -0
  26. package/dist/auth-handler.js.map +1 -0
  27. package/dist/biab-forms.css +484 -0
  28. package/dist/cli.d.ts +46 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +1824 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/client.d.ts +1681 -0
  33. package/dist/client.d.ts.map +1 -0
  34. package/dist/client.js +2845 -0
  35. package/dist/client.js.map +1 -0
  36. package/dist/collections.d.ts +13 -0
  37. package/dist/collections.d.ts.map +1 -0
  38. package/dist/collections.js +13 -0
  39. package/dist/collections.js.map +1 -0
  40. package/dist/contracts.d.ts +14609 -0
  41. package/dist/contracts.d.ts.map +1 -0
  42. package/dist/contracts.js +2428 -0
  43. package/dist/contracts.js.map +1 -0
  44. package/dist/data-model-schema.d.ts +962 -0
  45. package/dist/data-model-schema.d.ts.map +1 -0
  46. package/dist/data-model-schema.js +2295 -0
  47. package/dist/data-model-schema.js.map +1 -0
  48. package/dist/distribution.d.ts +38 -0
  49. package/dist/distribution.d.ts.map +1 -0
  50. package/dist/distribution.js +61 -0
  51. package/dist/distribution.js.map +1 -0
  52. package/dist/element.d.ts +121 -0
  53. package/dist/element.d.ts.map +1 -0
  54. package/dist/element.js +276 -0
  55. package/dist/element.js.map +1 -0
  56. package/dist/enums.d.ts +25 -0
  57. package/dist/enums.d.ts.map +1 -0
  58. package/dist/enums.js +38 -0
  59. package/dist/enums.js.map +1 -0
  60. package/dist/errors.d.ts +195 -0
  61. package/dist/errors.d.ts.map +1 -0
  62. package/dist/errors.js +188 -0
  63. package/dist/errors.js.map +1 -0
  64. package/dist/forms-core/autocomplete.d.ts +37 -0
  65. package/dist/forms-core/autocomplete.d.ts.map +1 -0
  66. package/dist/forms-core/autocomplete.js +82 -0
  67. package/dist/forms-core/autocomplete.js.map +1 -0
  68. package/dist/forms-core/business-function.d.ts +61 -0
  69. package/dist/forms-core/business-function.d.ts.map +1 -0
  70. package/dist/forms-core/business-function.js +149 -0
  71. package/dist/forms-core/business-function.js.map +1 -0
  72. package/dist/forms-core/calculation.d.ts +72 -0
  73. package/dist/forms-core/calculation.d.ts.map +1 -0
  74. package/dist/forms-core/calculation.js +103 -0
  75. package/dist/forms-core/calculation.js.map +1 -0
  76. package/dist/forms-core/controller.d.ts +249 -0
  77. package/dist/forms-core/controller.d.ts.map +1 -0
  78. package/dist/forms-core/controller.js +590 -0
  79. package/dist/forms-core/controller.js.map +1 -0
  80. package/dist/forms-core/elements.d.ts +44 -0
  81. package/dist/forms-core/elements.d.ts.map +1 -0
  82. package/dist/forms-core/elements.js +68 -0
  83. package/dist/forms-core/elements.js.map +1 -0
  84. package/dist/forms-core/email-domains.d.ts +10 -0
  85. package/dist/forms-core/email-domains.d.ts.map +1 -0
  86. package/dist/forms-core/email-domains.js +69 -0
  87. package/dist/forms-core/email-domains.js.map +1 -0
  88. package/dist/forms-core/file-rules.d.ts +51 -0
  89. package/dist/forms-core/file-rules.d.ts.map +1 -0
  90. package/dist/forms-core/file-rules.js +105 -0
  91. package/dist/forms-core/file-rules.js.map +1 -0
  92. package/dist/forms-core/formula.d.ts +114 -0
  93. package/dist/forms-core/formula.d.ts.map +1 -0
  94. package/dist/forms-core/formula.js +1170 -0
  95. package/dist/forms-core/formula.js.map +1 -0
  96. package/dist/forms-core/index.d.ts +24 -0
  97. package/dist/forms-core/index.d.ts.map +1 -0
  98. package/dist/forms-core/index.js +28 -0
  99. package/dist/forms-core/index.js.map +1 -0
  100. package/dist/forms-core/units.d.ts +49 -0
  101. package/dist/forms-core/units.d.ts.map +1 -0
  102. package/dist/forms-core/units.js +255 -0
  103. package/dist/forms-core/units.js.map +1 -0
  104. package/dist/forms-core/us-states.d.ts +14 -0
  105. package/dist/forms-core/us-states.d.ts.map +1 -0
  106. package/dist/forms-core/us-states.js +66 -0
  107. package/dist/forms-core/us-states.js.map +1 -0
  108. package/dist/forms-core/validation.d.ts +266 -0
  109. package/dist/forms-core/validation.d.ts.map +1 -0
  110. package/dist/forms-core/validation.js +850 -0
  111. package/dist/forms-core/validation.js.map +1 -0
  112. package/dist/forms.d.ts +19 -0
  113. package/dist/forms.d.ts.map +1 -0
  114. package/dist/forms.js +29 -0
  115. package/dist/forms.js.map +1 -0
  116. package/dist/gallery-types.d.ts +32 -0
  117. package/dist/gallery-types.d.ts.map +1 -0
  118. package/dist/gallery-types.js +2 -0
  119. package/dist/gallery-types.js.map +1 -0
  120. package/dist/graphql.d.ts +97 -0
  121. package/dist/graphql.d.ts.map +1 -0
  122. package/dist/graphql.js +212 -0
  123. package/dist/graphql.js.map +1 -0
  124. package/dist/index.cjs +19448 -0
  125. package/dist/index.d.cts +19 -0
  126. package/dist/index.d.mts +19 -0
  127. package/dist/index.d.ts +19 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +20 -0
  130. package/dist/index.js.map +1 -0
  131. package/dist/marketing-schema.d.ts +216 -0
  132. package/dist/marketing-schema.d.ts.map +1 -0
  133. package/dist/marketing-schema.js +200 -0
  134. package/dist/marketing-schema.js.map +1 -0
  135. package/dist/mcp.d.ts +42 -0
  136. package/dist/mcp.d.ts.map +1 -0
  137. package/dist/mcp.js +87 -0
  138. package/dist/mcp.js.map +1 -0
  139. package/dist/native.d.ts +45 -0
  140. package/dist/native.d.ts.map +1 -0
  141. package/dist/native.js +43 -0
  142. package/dist/native.js.map +1 -0
  143. package/dist/next-revalidate.d.ts +43 -0
  144. package/dist/next-revalidate.d.ts.map +1 -0
  145. package/dist/next-revalidate.js +84 -0
  146. package/dist/next-revalidate.js.map +1 -0
  147. package/dist/parallel-pages.d.ts +102 -0
  148. package/dist/parallel-pages.d.ts.map +1 -0
  149. package/dist/parallel-pages.js +108 -0
  150. package/dist/parallel-pages.js.map +1 -0
  151. package/dist/payment-icons.d.ts +66 -0
  152. package/dist/payment-icons.d.ts.map +1 -0
  153. package/dist/payment-icons.js +160 -0
  154. package/dist/payment-icons.js.map +1 -0
  155. package/dist/proxy.d.ts +67 -0
  156. package/dist/proxy.d.ts.map +1 -0
  157. package/dist/proxy.js +97 -0
  158. package/dist/proxy.js.map +1 -0
  159. package/dist/react-analytics.d.ts +39 -0
  160. package/dist/react-analytics.d.ts.map +1 -0
  161. package/dist/react-analytics.js +59 -0
  162. package/dist/react-analytics.js.map +1 -0
  163. package/dist/react-attribution.d.ts +99 -0
  164. package/dist/react-attribution.d.ts.map +1 -0
  165. package/dist/react-attribution.js +52 -0
  166. package/dist/react-attribution.js.map +1 -0
  167. package/dist/react-marketing.d.ts +181 -0
  168. package/dist/react-marketing.d.ts.map +1 -0
  169. package/dist/react-marketing.js +215 -0
  170. package/dist/react-marketing.js.map +1 -0
  171. package/dist/react.d.ts +727 -0
  172. package/dist/react.d.ts.map +1 -0
  173. package/dist/react.js +2418 -0
  174. package/dist/react.js.map +1 -0
  175. package/dist/reserved-paths.d.ts +24 -0
  176. package/dist/reserved-paths.d.ts.map +1 -0
  177. package/dist/reserved-paths.js +60 -0
  178. package/dist/reserved-paths.js.map +1 -0
  179. package/dist/revalidate-verify.d.ts +30 -0
  180. package/dist/revalidate-verify.d.ts.map +1 -0
  181. package/dist/revalidate-verify.js +74 -0
  182. package/dist/revalidate-verify.js.map +1 -0
  183. package/dist/sdk.d.ts +678 -0
  184. package/dist/sdk.d.ts.map +1 -0
  185. package/dist/sdk.js +801 -0
  186. package/dist/sdk.js.map +1 -0
  187. package/dist/seo.d.ts +218 -0
  188. package/dist/seo.d.ts.map +1 -0
  189. package/dist/seo.js +309 -0
  190. package/dist/seo.js.map +1 -0
  191. package/dist/socials.d.ts +47 -0
  192. package/dist/socials.d.ts.map +1 -0
  193. package/dist/socials.js +172 -0
  194. package/dist/socials.js.map +1 -0
  195. package/dist/static.d.ts +14 -0
  196. package/dist/static.d.ts.map +1 -0
  197. package/dist/static.js +14 -0
  198. package/dist/static.js.map +1 -0
  199. package/dist/url-pattern.d.ts +47 -0
  200. package/dist/url-pattern.d.ts.map +1 -0
  201. package/dist/url-pattern.js +124 -0
  202. package/dist/url-pattern.js.map +1 -0
  203. package/dist/vanilla.d.ts +77 -0
  204. package/dist/vanilla.d.ts.map +1 -0
  205. package/dist/vanilla.js +903 -0
  206. package/dist/vanilla.js.map +1 -0
  207. package/openapi.json +13779 -0
  208. package/package.json +204 -0
  209. package/src/adapters-revalidate.ts +88 -0
  210. package/src/alpha.ts +89 -0
  211. package/src/analytics-core.ts +175 -0
  212. package/src/analytics-server.ts +99 -0
  213. package/src/angular/biab-form.component.ts +1443 -0
  214. package/src/angular/index.ts +26 -0
  215. package/src/attribution-logo.ts +5 -0
  216. package/src/auth-handler.ts +352 -0
  217. package/src/cli.ts +2517 -0
  218. package/src/client.ts +3578 -0
  219. package/src/collections.ts +101 -0
  220. package/src/contracts.ts +3390 -0
  221. package/src/data-model-schema.ts +4042 -0
  222. package/src/distribution.ts +73 -0
  223. package/src/element.ts +321 -0
  224. package/src/enums.ts +65 -0
  225. package/src/errors.ts +338 -0
  226. package/src/forms-core/autocomplete.ts +97 -0
  227. package/src/forms-core/business-function.ts +209 -0
  228. package/src/forms-core/calculation.ts +167 -0
  229. package/src/forms-core/controller.ts +922 -0
  230. package/src/forms-core/elements.ts +86 -0
  231. package/src/forms-core/email-domains.ts +68 -0
  232. package/src/forms-core/file-rules.ts +123 -0
  233. package/src/forms-core/formula.ts +1339 -0
  234. package/src/forms-core/index.ts +122 -0
  235. package/src/forms-core/units.ts +308 -0
  236. package/src/forms-core/us-states.ts +68 -0
  237. package/src/forms-core/validation.ts +1037 -0
  238. package/src/forms.ts +63 -0
  239. package/src/gallery-types.ts +31 -0
  240. package/src/graphql.ts +295 -0
  241. package/src/index.ts +505 -0
  242. package/src/marketing-schema.ts +395 -0
  243. package/src/mcp.ts +108 -0
  244. package/src/native.ts +73 -0
  245. package/src/next-revalidate.ts +124 -0
  246. package/src/parallel-pages.ts +192 -0
  247. package/src/payment-icons.ts +205 -0
  248. package/src/proxy.ts +173 -0
  249. package/src/qwik.tsx +1234 -0
  250. package/src/react-analytics.tsx +76 -0
  251. package/src/react-attribution.tsx +192 -0
  252. package/src/react-marketing.tsx +338 -0
  253. package/src/react.tsx +5495 -0
  254. package/src/reserved-paths.ts +65 -0
  255. package/src/revalidate-verify.ts +98 -0
  256. package/src/sdk.ts +1671 -0
  257. package/src/seo.ts +488 -0
  258. package/src/socials.ts +230 -0
  259. package/src/solid.tsx +1466 -0
  260. package/src/static.ts +21 -0
  261. package/src/styles/biab-forms.css +484 -0
  262. package/src/svelte/BiabForm.svelte +1069 -0
  263. package/src/svelte/context.ts +57 -0
  264. package/src/svelte/index.ts +79 -0
  265. package/src/url-pattern.ts +153 -0
  266. package/src/vanilla.ts +1290 -0
  267. package/src/vue/BiabForm.vue +1538 -0
  268. package/src/vue/index.ts +119 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Svelte-native glue for `<BiabForm>` — kept in its own module (no `.svelte`
3
+ * import) so both `index.ts` and `BiabForm.svelte` can import it without a
4
+ * circular dependency through the component.
5
+ *
6
+ * • `formStore(controller)` — a Svelte readable store over the controller's
7
+ * immutable `snapshot()`. The idiomatic reactive primitive for this binding.
8
+ * • `setBiabClient` / `getBiabClient` — Svelte context DI for the
9
+ * `BiabClient` (the `<BiabProvider>` equivalent).
10
+ */
11
+
12
+ import { type Readable, readable } from "svelte/store";
13
+ import { getContext, setContext } from "svelte";
14
+
15
+ import type { BiabClient } from "../sdk.js";
16
+ import type { FormController, FormSnapshot } from "../forms-core/index.js";
17
+
18
+ /**
19
+ * Wrap a {@link FormController} in a Svelte {@link Readable} of its
20
+ * {@link FormSnapshot}. Use `$store` in markup for live, fine-grained
21
+ * reactivity. The store seeds synchronously from `controller.snapshot()` (the
22
+ * controller does not notify on subscribe) and updates on every mutation; the
23
+ * underlying subscription is torn down automatically when the last Svelte
24
+ * subscriber leaves.
25
+ */
26
+ export function formStore(controller: FormController): Readable<FormSnapshot> {
27
+ return readable(controller.snapshot(), (set) => {
28
+ // Re-sync immediately in case a mutation landed between `snapshot()` and
29
+ // subscribe (e.g. an in-flight `load()` resolving).
30
+ set(controller.snapshot());
31
+ return controller.subscribe((snapshot) => set(snapshot));
32
+ });
33
+ }
34
+
35
+ const BIAB_CLIENT_KEY = Symbol("biab.client");
36
+
37
+ /**
38
+ * Provide a {@link BiabClient} to every descendant `<BiabForm>` via Svelte
39
+ * context. Call this in a layout/parent `<script>` so nested forms can omit the
40
+ * `client` prop. Mirrors React's `<BiabProvider>` DI.
41
+ *
42
+ * NOTE: a `BiabClient` carries the org's bearer key, so only set it in a context
43
+ * that never reaches the browser bundle (e.g. a fully server-rendered tree). For
44
+ * the common SvelteKit pattern, prefer the `schema` + `submitAction` props on
45
+ * `<BiabForm>` so the key stays in `$lib/server`.
46
+ */
47
+ export function setBiabClient(client: BiabClient): void {
48
+ setContext(BIAB_CLIENT_KEY, client);
49
+ }
50
+
51
+ /** Read the {@link BiabClient} provided by an ancestor `setBiabClient`, if any. */
52
+ export function getBiabClient(): BiabClient | undefined {
53
+ return getContext<BiabClient | undefined>(BIAB_CLIENT_KEY);
54
+ }
55
+
56
+ /** Context key, exported so advanced consumers can read the same slot. */
57
+ export const BIAB_CLIENT_CONTEXT = BIAB_CLIENT_KEY;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * `@businessdash/sdk/svelte` — the Svelte 5 `<BiabForm>` drop-in binding.
3
+ *
4
+ * Ship mode: SOURCE. This subpath resolves straight to `.svelte` + `.ts`
5
+ * source (see `biab-dev/package.json` `"./svelte"`), so the CONSUMER's
6
+ * `svelte` / `vite-plugin-svelte` toolchain compiles it. Nothing here is
7
+ * pre-compiled to `dist`.
8
+ *
9
+ * It wraps the framework-agnostic headless core from `@businessdash/sdk/forms`
10
+ * (`createFormController`) in two idiomatic Svelte primitives (both in
11
+ * `./context.ts`, kept out of this barrel so there's no cycle through the
12
+ * component):
13
+ *
14
+ * 1. `formStore(controller)` — a Svelte **readable store** over the
15
+ * controller's `snapshot()`. `$store` gives the live `FormSnapshot` with
16
+ * fine-grained reactivity.
17
+ * 2. A Svelte **context** provider (`setBiabClient` / `getBiabClient`) for
18
+ * DI-ing the `BiabClient` — the idiomatic `<BiabProvider>` equivalent.
19
+ *
20
+ * `<BiabForm>` renders the full field tree (`visibleFields` + nested
21
+ * `children`), applies `schema.settings` (animation, multi-step + progress,
22
+ * concurrent reveal, org icon), and uses the `biab-*` light-DOM class
23
+ * convention so the org's CSS cascades — a 1:1 match with the dashboard
24
+ * `FormPreview` renderer.
25
+ */
26
+
27
+ export { default as BiabForm } from "./BiabForm.svelte";
28
+ export type {
29
+ BiabFormProps,
30
+ BiabFormSubmitAction,
31
+ BiabFormSuccess,
32
+ BiabFormFailure,
33
+ } from "./BiabForm.svelte";
34
+
35
+ export {
36
+ BIAB_CLIENT_CONTEXT,
37
+ formStore,
38
+ getBiabClient,
39
+ setBiabClient,
40
+ } from "./context.js";
41
+
42
+ // Re-export the headless core surface so a Svelte consumer can reach the
43
+ // controller, validation helpers, and SDK form types from this single subpath.
44
+ export {
45
+ type CreateFormControllerOptions,
46
+ type FieldError,
47
+ type FormCoreField,
48
+ type FormController,
49
+ type FormData,
50
+ type FormSnapshot,
51
+ type FormStatus,
52
+ type FormSubmitOptions,
53
+ type MissingField,
54
+ type ResolvedFormSettings,
55
+ concurrentRevealCount,
56
+ createFormController,
57
+ evalCondition,
58
+ findMissingRequired,
59
+ flattenValueFields,
60
+ isEmpty,
61
+ seedInitialData,
62
+ toSubmissionPayload,
63
+ validateValue,
64
+ } from "../forms-core/index.js";
65
+ export type {
66
+ FormActionPreview,
67
+ FormActionType,
68
+ FormFieldDef,
69
+ FormFileValue,
70
+ FormRenderSettings,
71
+ FormSchema,
72
+ FormSubmissionData,
73
+ FormSubmitFailureReason,
74
+ FormSubmitResult,
75
+ FormValidationIssue,
76
+ FormValidationResult,
77
+ FormsResource,
78
+ } from "../forms-core/index.js";
79
+ export type { BiabClient } from "../sdk.js";
@@ -0,0 +1,153 @@
1
+ /**
2
+ * URL pattern parser + variant enumeration for parallel pages.
3
+ *
4
+ * A pattern is a string like `"/repair/[service]/[area]"`. Each
5
+ * `[name]` segment is a variable slot. The parser extracts the
6
+ * variable names + validates them against a binding map; the
7
+ * enumerator computes the cartesian product of variable values into
8
+ * concrete URLs.
9
+ *
10
+ * Pure functions — no I/O, no React, no DB. Safe to import from
11
+ * both server and client bundles.
12
+ */
13
+
14
+ export type ParallelPagePatternSegment =
15
+ | { kind: "literal"; value: string }
16
+ | { kind: "variable"; name: string };
17
+
18
+ export type ParsedParallelPagePattern = {
19
+ pattern: string;
20
+ segments: ParallelPagePatternSegment[];
21
+ variableNames: string[];
22
+ };
23
+
24
+ /**
25
+ * Allowed variable-name characters — lowercase letters, digits, and
26
+ * hyphens / underscores so `[snow-removal]` and `[event_kind]` work.
27
+ * Matches the regex used by Next.js dynamic-route segments.
28
+ */
29
+ const VARIABLE_NAME = /^[a-z][a-z0-9_-]*$/;
30
+
31
+ /**
32
+ * Parse a `routePattern` into ordered segments. Throws on malformed
33
+ * input — the SDK + tRPC router both call this at write time so the
34
+ * row never persists an unparseable pattern.
35
+ */
36
+ export function parseParallelPagePattern(
37
+ pattern: string,
38
+ ): ParsedParallelPagePattern {
39
+ if (typeof pattern !== "string" || pattern.length === 0) {
40
+ throw new Error("Pattern must be a non-empty string.");
41
+ }
42
+ if (!pattern.startsWith("/")) {
43
+ throw new Error(
44
+ `Pattern must start with "/" — got ${JSON.stringify(pattern)}.`,
45
+ );
46
+ }
47
+ const parts = pattern.slice(1).split("/");
48
+ const segments: ParallelPagePatternSegment[] = [];
49
+ const variableNames: string[] = [];
50
+ const seen = new Set<string>();
51
+ for (const part of parts) {
52
+ if (part.length === 0) {
53
+ throw new Error(
54
+ `Pattern contains an empty segment (double slash?) — ${JSON.stringify(pattern)}.`,
55
+ );
56
+ }
57
+ const variableMatch = /^\[([^\]]+)\]$/.exec(part);
58
+ if (!variableMatch) {
59
+ segments.push({ kind: "literal", value: part });
60
+ continue;
61
+ }
62
+ const name = variableMatch[1] ?? "";
63
+ if (!VARIABLE_NAME.test(name)) {
64
+ throw new Error(
65
+ `Variable segment "[${name}]" is not a valid identifier ` +
66
+ "(use lowercase letters, digits, hyphens, underscores).",
67
+ );
68
+ }
69
+ if (seen.has(name)) {
70
+ throw new Error(
71
+ `Variable "[${name}]" appears more than once in ${JSON.stringify(pattern)}.`,
72
+ );
73
+ }
74
+ seen.add(name);
75
+ variableNames.push(name);
76
+ segments.push({ kind: "variable", name });
77
+ }
78
+ return { pattern, segments, variableNames };
79
+ }
80
+
81
+ /**
82
+ * Materialise a single variant URL given the resolved slug values
83
+ * (`{ service: "garage-door", area: "saint-paul" }` → "/repair/garage-door/saint-paul").
84
+ */
85
+ export function materialiseParallelPagePath(
86
+ parsed: ParsedParallelPagePattern,
87
+ values: Readonly<Record<string, string>>,
88
+ ): string {
89
+ const out: string[] = [];
90
+ for (const seg of parsed.segments) {
91
+ if (seg.kind === "literal") {
92
+ out.push(seg.value);
93
+ continue;
94
+ }
95
+ const v = values[seg.name];
96
+ if (v === undefined || v === null || v === "") {
97
+ throw new Error(
98
+ `Missing value for "[${seg.name}]" when materialising ${parsed.pattern}.`,
99
+ );
100
+ }
101
+ out.push(encodeURIComponent(v));
102
+ }
103
+ return `/${out.join("/")}`;
104
+ }
105
+
106
+ /**
107
+ * Enumerate the cartesian product of variable values into a flat
108
+ * array of variant maps. Order: each input value array is iterated
109
+ * in turn, with the LAST variable's values cycling fastest — so the
110
+ * result is stable + diffable across reads (useful for sitemaps and
111
+ * `generateStaticParams`).
112
+ *
113
+ * Returns an empty array when ANY variable has zero values (the
114
+ * cartesian product of a zero-length axis is empty).
115
+ */
116
+ export function enumerateParallelPageVariants(
117
+ variableNames: readonly string[],
118
+ valuesByVariable: Readonly<Record<string, ReadonlyArray<string>>>,
119
+ ): Array<Record<string, string>> {
120
+ if (variableNames.length === 0) return [{}];
121
+ for (const name of variableNames) {
122
+ const list = valuesByVariable[name];
123
+ if (!list) {
124
+ throw new Error(
125
+ `Missing value array for variable "${name}". ` +
126
+ `Provide one entry in valuesByVariable for every variable in the pattern.`,
127
+ );
128
+ }
129
+ if (list.length === 0) return [];
130
+ }
131
+ const out: Array<Record<string, string>> = [];
132
+ const indices: number[] = variableNames.map(() => 0);
133
+ while (true) {
134
+ const variant: Record<string, string> = {};
135
+ for (let i = 0; i < variableNames.length; i++) {
136
+ const name = variableNames[i]!;
137
+ const idx = indices[i]!;
138
+ variant[name] = valuesByVariable[name]![idx]!;
139
+ }
140
+ out.push(variant);
141
+ // Increment indices like an odometer (last variable cycles
142
+ // fastest). When the first variable rolls over, we're done.
143
+ let i = variableNames.length - 1;
144
+ while (i >= 0) {
145
+ indices[i] = indices[i]! + 1;
146
+ if (indices[i]! < valuesByVariable[variableNames[i]!]!.length) break;
147
+ indices[i] = 0;
148
+ i--;
149
+ }
150
+ if (i < 0) break;
151
+ }
152
+ return out;
153
+ }