@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.
- package/LICENSE +21 -0
- package/README.md +899 -0
- package/dist/adapters-revalidate.d.ts +44 -0
- package/dist/adapters-revalidate.d.ts.map +1 -0
- package/dist/adapters-revalidate.js +68 -0
- package/dist/adapters-revalidate.js.map +1 -0
- package/dist/alpha.d.ts +21 -0
- package/dist/alpha.d.ts.map +1 -0
- package/dist/alpha.js +21 -0
- package/dist/alpha.js.map +1 -0
- package/dist/analytics-core.d.ts +34 -0
- package/dist/analytics-core.d.ts.map +1 -0
- package/dist/analytics-core.js +138 -0
- package/dist/analytics-core.js.map +1 -0
- package/dist/analytics-server.d.ts +54 -0
- package/dist/analytics-server.d.ts.map +1 -0
- package/dist/analytics-server.js +78 -0
- package/dist/analytics-server.js.map +1 -0
- package/dist/attribution-logo.d.ts +2 -0
- package/dist/attribution-logo.d.ts.map +1 -0
- package/dist/attribution-logo.js +5 -0
- package/dist/attribution-logo.js.map +1 -0
- package/dist/auth-handler.d.ts +92 -0
- package/dist/auth-handler.d.ts.map +1 -0
- package/dist/auth-handler.js +277 -0
- package/dist/auth-handler.js.map +1 -0
- package/dist/biab-forms.css +484 -0
- package/dist/cli.d.ts +46 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1824 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +1681 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +2845 -0
- package/dist/client.js.map +1 -0
- package/dist/collections.d.ts +13 -0
- package/dist/collections.d.ts.map +1 -0
- package/dist/collections.js +13 -0
- package/dist/collections.js.map +1 -0
- package/dist/contracts.d.ts +14609 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +2428 -0
- package/dist/contracts.js.map +1 -0
- package/dist/data-model-schema.d.ts +962 -0
- package/dist/data-model-schema.d.ts.map +1 -0
- package/dist/data-model-schema.js +2295 -0
- package/dist/data-model-schema.js.map +1 -0
- package/dist/distribution.d.ts +38 -0
- package/dist/distribution.d.ts.map +1 -0
- package/dist/distribution.js +61 -0
- package/dist/distribution.js.map +1 -0
- package/dist/element.d.ts +121 -0
- package/dist/element.d.ts.map +1 -0
- package/dist/element.js +276 -0
- package/dist/element.js.map +1 -0
- package/dist/enums.d.ts +25 -0
- package/dist/enums.d.ts.map +1 -0
- package/dist/enums.js +38 -0
- package/dist/enums.js.map +1 -0
- package/dist/errors.d.ts +195 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +188 -0
- package/dist/errors.js.map +1 -0
- package/dist/forms-core/autocomplete.d.ts +37 -0
- package/dist/forms-core/autocomplete.d.ts.map +1 -0
- package/dist/forms-core/autocomplete.js +82 -0
- package/dist/forms-core/autocomplete.js.map +1 -0
- package/dist/forms-core/business-function.d.ts +61 -0
- package/dist/forms-core/business-function.d.ts.map +1 -0
- package/dist/forms-core/business-function.js +149 -0
- package/dist/forms-core/business-function.js.map +1 -0
- package/dist/forms-core/calculation.d.ts +72 -0
- package/dist/forms-core/calculation.d.ts.map +1 -0
- package/dist/forms-core/calculation.js +103 -0
- package/dist/forms-core/calculation.js.map +1 -0
- package/dist/forms-core/controller.d.ts +249 -0
- package/dist/forms-core/controller.d.ts.map +1 -0
- package/dist/forms-core/controller.js +590 -0
- package/dist/forms-core/controller.js.map +1 -0
- package/dist/forms-core/elements.d.ts +44 -0
- package/dist/forms-core/elements.d.ts.map +1 -0
- package/dist/forms-core/elements.js +68 -0
- package/dist/forms-core/elements.js.map +1 -0
- package/dist/forms-core/email-domains.d.ts +10 -0
- package/dist/forms-core/email-domains.d.ts.map +1 -0
- package/dist/forms-core/email-domains.js +69 -0
- package/dist/forms-core/email-domains.js.map +1 -0
- package/dist/forms-core/file-rules.d.ts +51 -0
- package/dist/forms-core/file-rules.d.ts.map +1 -0
- package/dist/forms-core/file-rules.js +105 -0
- package/dist/forms-core/file-rules.js.map +1 -0
- package/dist/forms-core/formula.d.ts +114 -0
- package/dist/forms-core/formula.d.ts.map +1 -0
- package/dist/forms-core/formula.js +1170 -0
- package/dist/forms-core/formula.js.map +1 -0
- package/dist/forms-core/index.d.ts +24 -0
- package/dist/forms-core/index.d.ts.map +1 -0
- package/dist/forms-core/index.js +28 -0
- package/dist/forms-core/index.js.map +1 -0
- package/dist/forms-core/units.d.ts +49 -0
- package/dist/forms-core/units.d.ts.map +1 -0
- package/dist/forms-core/units.js +255 -0
- package/dist/forms-core/units.js.map +1 -0
- package/dist/forms-core/us-states.d.ts +14 -0
- package/dist/forms-core/us-states.d.ts.map +1 -0
- package/dist/forms-core/us-states.js +66 -0
- package/dist/forms-core/us-states.js.map +1 -0
- package/dist/forms-core/validation.d.ts +266 -0
- package/dist/forms-core/validation.d.ts.map +1 -0
- package/dist/forms-core/validation.js +850 -0
- package/dist/forms-core/validation.js.map +1 -0
- package/dist/forms.d.ts +19 -0
- package/dist/forms.d.ts.map +1 -0
- package/dist/forms.js +29 -0
- package/dist/forms.js.map +1 -0
- package/dist/gallery-types.d.ts +32 -0
- package/dist/gallery-types.d.ts.map +1 -0
- package/dist/gallery-types.js +2 -0
- package/dist/gallery-types.js.map +1 -0
- package/dist/graphql.d.ts +97 -0
- package/dist/graphql.d.ts.map +1 -0
- package/dist/graphql.js +212 -0
- package/dist/graphql.js.map +1 -0
- package/dist/index.cjs +19448 -0
- package/dist/index.d.cts +19 -0
- package/dist/index.d.mts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/marketing-schema.d.ts +216 -0
- package/dist/marketing-schema.d.ts.map +1 -0
- package/dist/marketing-schema.js +200 -0
- package/dist/marketing-schema.js.map +1 -0
- package/dist/mcp.d.ts +42 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +87 -0
- package/dist/mcp.js.map +1 -0
- package/dist/native.d.ts +45 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +43 -0
- package/dist/native.js.map +1 -0
- package/dist/next-revalidate.d.ts +43 -0
- package/dist/next-revalidate.d.ts.map +1 -0
- package/dist/next-revalidate.js +84 -0
- package/dist/next-revalidate.js.map +1 -0
- package/dist/parallel-pages.d.ts +102 -0
- package/dist/parallel-pages.d.ts.map +1 -0
- package/dist/parallel-pages.js +108 -0
- package/dist/parallel-pages.js.map +1 -0
- package/dist/payment-icons.d.ts +66 -0
- package/dist/payment-icons.d.ts.map +1 -0
- package/dist/payment-icons.js +160 -0
- package/dist/payment-icons.js.map +1 -0
- package/dist/proxy.d.ts +67 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +97 -0
- package/dist/proxy.js.map +1 -0
- package/dist/react-analytics.d.ts +39 -0
- package/dist/react-analytics.d.ts.map +1 -0
- package/dist/react-analytics.js +59 -0
- package/dist/react-analytics.js.map +1 -0
- package/dist/react-attribution.d.ts +99 -0
- package/dist/react-attribution.d.ts.map +1 -0
- package/dist/react-attribution.js +52 -0
- package/dist/react-attribution.js.map +1 -0
- package/dist/react-marketing.d.ts +181 -0
- package/dist/react-marketing.d.ts.map +1 -0
- package/dist/react-marketing.js +215 -0
- package/dist/react-marketing.js.map +1 -0
- package/dist/react.d.ts +727 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +2418 -0
- package/dist/react.js.map +1 -0
- package/dist/reserved-paths.d.ts +24 -0
- package/dist/reserved-paths.d.ts.map +1 -0
- package/dist/reserved-paths.js +60 -0
- package/dist/reserved-paths.js.map +1 -0
- package/dist/revalidate-verify.d.ts +30 -0
- package/dist/revalidate-verify.d.ts.map +1 -0
- package/dist/revalidate-verify.js +74 -0
- package/dist/revalidate-verify.js.map +1 -0
- package/dist/sdk.d.ts +678 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +801 -0
- package/dist/sdk.js.map +1 -0
- package/dist/seo.d.ts +218 -0
- package/dist/seo.d.ts.map +1 -0
- package/dist/seo.js +309 -0
- package/dist/seo.js.map +1 -0
- package/dist/socials.d.ts +47 -0
- package/dist/socials.d.ts.map +1 -0
- package/dist/socials.js +172 -0
- package/dist/socials.js.map +1 -0
- package/dist/static.d.ts +14 -0
- package/dist/static.d.ts.map +1 -0
- package/dist/static.js +14 -0
- package/dist/static.js.map +1 -0
- package/dist/url-pattern.d.ts +47 -0
- package/dist/url-pattern.d.ts.map +1 -0
- package/dist/url-pattern.js +124 -0
- package/dist/url-pattern.js.map +1 -0
- package/dist/vanilla.d.ts +77 -0
- package/dist/vanilla.d.ts.map +1 -0
- package/dist/vanilla.js +903 -0
- package/dist/vanilla.js.map +1 -0
- package/openapi.json +13779 -0
- package/package.json +204 -0
- package/src/adapters-revalidate.ts +88 -0
- package/src/alpha.ts +89 -0
- package/src/analytics-core.ts +175 -0
- package/src/analytics-server.ts +99 -0
- package/src/angular/biab-form.component.ts +1443 -0
- package/src/angular/index.ts +26 -0
- package/src/attribution-logo.ts +5 -0
- package/src/auth-handler.ts +352 -0
- package/src/cli.ts +2517 -0
- package/src/client.ts +3578 -0
- package/src/collections.ts +101 -0
- package/src/contracts.ts +3390 -0
- package/src/data-model-schema.ts +4042 -0
- package/src/distribution.ts +73 -0
- package/src/element.ts +321 -0
- package/src/enums.ts +65 -0
- package/src/errors.ts +338 -0
- package/src/forms-core/autocomplete.ts +97 -0
- package/src/forms-core/business-function.ts +209 -0
- package/src/forms-core/calculation.ts +167 -0
- package/src/forms-core/controller.ts +922 -0
- package/src/forms-core/elements.ts +86 -0
- package/src/forms-core/email-domains.ts +68 -0
- package/src/forms-core/file-rules.ts +123 -0
- package/src/forms-core/formula.ts +1339 -0
- package/src/forms-core/index.ts +122 -0
- package/src/forms-core/units.ts +308 -0
- package/src/forms-core/us-states.ts +68 -0
- package/src/forms-core/validation.ts +1037 -0
- package/src/forms.ts +63 -0
- package/src/gallery-types.ts +31 -0
- package/src/graphql.ts +295 -0
- package/src/index.ts +505 -0
- package/src/marketing-schema.ts +395 -0
- package/src/mcp.ts +108 -0
- package/src/native.ts +73 -0
- package/src/next-revalidate.ts +124 -0
- package/src/parallel-pages.ts +192 -0
- package/src/payment-icons.ts +205 -0
- package/src/proxy.ts +173 -0
- package/src/qwik.tsx +1234 -0
- package/src/react-analytics.tsx +76 -0
- package/src/react-attribution.tsx +192 -0
- package/src/react-marketing.tsx +338 -0
- package/src/react.tsx +5495 -0
- package/src/reserved-paths.ts +65 -0
- package/src/revalidate-verify.ts +98 -0
- package/src/sdk.ts +1671 -0
- package/src/seo.ts +488 -0
- package/src/socials.ts +230 -0
- package/src/solid.tsx +1466 -0
- package/src/static.ts +21 -0
- package/src/styles/biab-forms.css +484 -0
- package/src/svelte/BiabForm.svelte +1069 -0
- package/src/svelte/context.ts +57 -0
- package/src/svelte/index.ts +79 -0
- package/src/url-pattern.ts +153 -0
- package/src/vanilla.ts +1290 -0
- package/src/vue/BiabForm.vue +1538 -0
- 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
|
+
}
|