@businessdash/sdk 0.9.81 → 0.9.90
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/README.md +115 -902
- package/assets/{biab-sw.js → bd-sw.js} +2 -2
- package/dist/account-core/index.d.ts +1 -1
- package/dist/account-core/index.d.ts.map +1 -1
- package/dist/adapters-revalidate.d.ts +2 -2
- package/dist/adapters-revalidate.js +2 -2
- package/dist/adapters-revalidate.js.map +1 -1
- package/dist/analytics-core.d.ts +6 -6
- package/dist/analytics-core.d.ts.map +1 -1
- package/dist/analytics-core.js +4 -4
- package/dist/analytics-core.js.map +1 -1
- package/dist/analytics-server.d.ts +6 -6
- package/dist/analytics-server.d.ts.map +1 -1
- package/dist/analytics-server.js +5 -5
- package/dist/auth-handler.d.ts +14 -14
- package/dist/auth-handler.d.ts.map +1 -1
- package/dist/auth-handler.js +16 -16
- package/dist/auth-handler.js.map +1 -1
- package/dist/auth-links.d.ts +2 -2
- package/dist/auth-links.d.ts.map +1 -1
- package/dist/auth-links.js +2 -2
- package/dist/auth-links.js.map +1 -1
- package/dist/{biab-forms.css → bd-forms.css} +158 -158
- package/dist/cli.d.ts +4 -4
- package/dist/cli.js +78 -78
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +158 -147
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +156 -137
- package/dist/client.js.map +1 -1
- package/dist/consent-core/index.cjs +2 -2
- package/dist/consent-core/index.d.ts +2 -2
- package/dist/consent-core/index.d.ts.map +1 -1
- package/dist/consent-core/index.js +10 -8
- package/dist/consent-core/index.js.map +1 -1
- package/dist/contracts.d.ts +411 -52
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +89 -15
- package/dist/contracts.js.map +1 -1
- package/dist/data-model-schema.d.ts +15 -15
- package/dist/data-model-schema.d.ts.map +1 -1
- package/dist/data-model-schema.js +19 -17
- package/dist/data-model-schema.js.map +1 -1
- package/dist/distribution.d.ts +5 -5
- package/dist/distribution.d.ts.map +1 -1
- package/dist/distribution.js +4 -4
- package/dist/element.d.ts +36 -36
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +43 -43
- package/dist/element.js.map +1 -1
- package/dist/embed-protocol.d.ts +11 -11
- package/dist/embed-protocol.d.ts.map +1 -1
- package/dist/embed-protocol.js +1 -1
- package/dist/embed-protocol.js.map +1 -1
- package/dist/errors.d.ts +15 -15
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +18 -18
- package/dist/errors.js.map +1 -1
- package/dist/forms-core/autocomplete.d.ts +3 -3
- package/dist/forms-core/autocomplete.d.ts.map +1 -1
- package/dist/forms-core/autocomplete.js +2 -2
- package/dist/forms-core/controller.d.ts +10 -10
- package/dist/forms-core/controller.d.ts.map +1 -1
- package/dist/forms-core/controller.js +4 -4
- package/dist/forms-core/controller.js.map +1 -1
- package/dist/forms-core/elements.d.ts +4 -4
- package/dist/forms-core/elements.d.ts.map +1 -1
- package/dist/forms-core/elements.js +7 -7
- package/dist/forms-core/elements.js.map +1 -1
- package/dist/forms-core/formula.d.ts +1 -1
- package/dist/forms-core/formula.js +1 -1
- package/dist/forms-core/index.d.ts +6 -6
- package/dist/forms-core/index.d.ts.map +1 -1
- package/dist/forms-core/index.js +5 -5
- package/dist/forms-core/index.js.map +1 -1
- package/dist/forms-core/units.d.ts +1 -1
- package/dist/forms-core/units.js +1 -1
- package/dist/forms-core/us-states.d.ts +1 -1
- package/dist/forms-core/us-states.js +1 -1
- package/dist/forms-core/validation.d.ts +12 -3
- package/dist/forms-core/validation.d.ts.map +1 -1
- package/dist/forms-core/validation.js +152 -17
- package/dist/forms-core/validation.js.map +1 -1
- package/dist/forms.d.ts +2 -2
- package/dist/forms.d.ts.map +1 -1
- package/dist/forms.js +2 -2
- package/dist/forms.js.map +1 -1
- package/dist/framework-support.js +1 -1
- package/dist/framework-support.js.map +1 -1
- package/dist/gallery-types.d.ts +1 -1
- package/dist/graphql.d.ts +3 -3
- package/dist/graphql.d.ts.map +1 -1
- package/dist/graphql.js +3 -3
- package/dist/graphql.js.map +1 -1
- package/dist/index.cjs +350 -273
- package/dist/index.d.cts +5 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/legal-core/index.cjs +8 -8
- package/dist/legal-core/index.d.ts +3 -3
- package/dist/legal-core/index.d.ts.map +1 -1
- package/dist/legal-core/index.js +11 -11
- package/dist/legal-core/index.js.map +1 -1
- package/dist/marketing-core/index.d.ts +51 -1
- package/dist/marketing-core/index.d.ts.map +1 -1
- package/dist/marketing-core/index.js +35 -0
- package/dist/marketing-core/index.js.map +1 -1
- package/dist/marketing-schema.d.ts +9 -9
- package/dist/marketing-schema.d.ts.map +1 -1
- package/dist/marketing-schema.js +1 -1
- package/dist/mcp.d.ts +4 -4
- package/dist/mcp.js +4 -4
- package/dist/native.d.ts +6 -6
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +4 -4
- package/dist/native.js.map +1 -1
- package/dist/next-revalidate.d.ts +8 -8
- package/dist/next-revalidate.d.ts.map +1 -1
- package/dist/next-revalidate.js +7 -7
- package/dist/next-revalidate.js.map +1 -1
- package/dist/notifications-core/index.d.ts +1 -1
- package/dist/notifications-core/index.js +1 -1
- package/dist/parallel-pages.d.ts +4 -4
- package/dist/parallel-pages.d.ts.map +1 -1
- package/dist/parallel-pages.js +5 -5
- package/dist/parallel-pages.js.map +1 -1
- package/dist/portal-core/index.d.ts +1 -1
- package/dist/portal-core/index.js +1 -1
- package/dist/proxy.d.ts +18 -18
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +10 -10
- package/dist/proxy.js.map +1 -1
- package/dist/push-core/index.cjs +1 -1
- package/dist/push-core/index.d.ts +1 -1
- package/dist/push-core/index.js +1 -1
- package/dist/push-core/index.js.map +1 -1
- package/dist/react-analytics.d.ts +14 -14
- package/dist/react-analytics.d.ts.map +1 -1
- package/dist/react-analytics.js +15 -15
- package/dist/react-analytics.js.map +1 -1
- package/dist/react-attribution.d.ts +2 -2
- package/dist/react-attribution.d.ts.map +1 -1
- package/dist/react-attribution.js +1 -1
- package/dist/react-marketing.js +5 -5
- package/dist/react-marketing.js.map +1 -1
- package/dist/react.d.ts +38 -61
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +247 -261
- package/dist/react.js.map +1 -1
- package/dist/reserved-paths.d.ts +2 -2
- package/dist/reserved-paths.js +3 -3
- package/dist/reserved-paths.js.map +1 -1
- package/dist/revalidate-verify.d.ts +3 -3
- package/dist/revalidate-verify.d.ts.map +1 -1
- package/dist/revalidate-verify.js +1 -1
- package/dist/revalidate-verify.js.map +1 -1
- package/dist/sdk.d.ts +49 -49
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +47 -51
- package/dist/sdk.js.map +1 -1
- package/dist/seed-core/index.js +8 -8
- package/dist/seed-core/index.js.map +1 -1
- package/dist/seo-core/blog.d.ts +1 -1
- package/dist/seo-core/blog.js +1 -1
- package/dist/seo-core/blog.js.map +1 -1
- package/dist/seo-core/index.cjs +2 -2
- package/dist/seo-core/index.js +1 -1
- package/dist/seo-core/index.js.map +1 -1
- package/dist/seo.d.ts +1 -1
- package/dist/seo.d.ts.map +1 -1
- package/dist/seo.js +1 -1
- package/dist/seo.js.map +1 -1
- package/dist/sitemap-core/index.cjs +3 -3
- package/dist/sitemap-core/index.js +2 -2
- package/dist/sitemap-core/index.js.map +1 -1
- package/dist/socials.d.ts +6 -6
- package/dist/socials.d.ts.map +1 -1
- package/dist/socials.js +5 -5
- package/dist/socials.js.map +1 -1
- package/dist/solid-compat.d.ts +1 -1
- package/dist/solid-compat.js +4 -4
- package/dist/solid-compat.js.map +1 -1
- package/dist/store-core/index.d.ts +1 -1
- package/dist/vanilla.d.ts +12 -12
- package/dist/vanilla.d.ts.map +1 -1
- package/dist/vanilla.js +192 -145
- package/dist/vanilla.js.map +1 -1
- package/openapi.json +264 -1
- package/package.json +8 -8
- package/src/account-core/index.ts +1 -1
- package/src/adapters-revalidate.ts +3 -3
- package/src/analytics-core.ts +8 -8
- package/src/analytics-server.ts +6 -6
- package/src/angular/{biab-form.component.ts → bd-form.component.ts} +269 -161
- package/src/angular/headless.ts +12 -12
- package/src/angular/index.ts +9 -9
- package/src/auth-handler.ts +22 -22
- package/src/auth-links.ts +2 -2
- package/src/cli.ts +81 -81
- package/src/client.ts +218 -193
- package/src/consent-core/index.ts +10 -8
- package/src/contracts.ts +99 -15
- package/src/data-model-schema.ts +29 -26
- package/src/distribution.ts +5 -5
- package/src/element.ts +54 -54
- package/src/embed-protocol.ts +14 -14
- package/src/errors.ts +27 -27
- package/src/forms-core/autocomplete.ts +3 -3
- package/src/forms-core/controller.ts +12 -12
- package/src/forms-core/elements.ts +7 -7
- package/src/forms-core/formula.ts +1 -1
- package/src/forms-core/index.ts +8 -8
- package/src/forms-core/units.ts +1 -1
- package/src/forms-core/us-states.ts +1 -1
- package/src/forms-core/validation.ts +158 -14
- package/src/forms.ts +2 -2
- package/src/framework-support.ts +1 -1
- package/src/gallery-types.ts +1 -1
- package/src/graphql.ts +4 -4
- package/src/index.ts +38 -38
- package/src/legal-core/index.ts +12 -12
- package/src/marketing-core/index.ts +62 -0
- package/src/marketing-schema.ts +9 -9
- package/src/mcp.ts +4 -4
- package/src/native.ts +14 -14
- package/src/next-revalidate.ts +10 -10
- package/src/notifications-core/index.ts +1 -1
- package/src/parallel-pages.ts +6 -6
- package/src/portal-core/index.ts +1 -1
- package/src/proxy.ts +28 -28
- package/src/push-core/index.ts +2 -2
- package/src/qwik.tsx +262 -166
- package/src/react-analytics.tsx +17 -17
- package/src/react-attribution.tsx +2 -2
- package/src/react-marketing.tsx +5 -5
- package/src/react.tsx +541 -500
- package/src/reserved-paths.ts +3 -3
- package/src/revalidate-verify.ts +5 -5
- package/src/sdk.ts +106 -110
- package/src/seed-core/index.ts +8 -8
- package/src/seo-core/blog.ts +2 -2
- package/src/seo-core/index.ts +1 -1
- package/src/seo.ts +1 -1
- package/src/sitemap-core/index.ts +2 -2
- package/src/socials.ts +6 -6
- package/src/solid-compat.tsx +5 -5
- package/src/solid-surfaces.tsx +28 -28
- package/src/solid.tsx +264 -175
- package/src/store-core/index.ts +1 -1
- package/src/styles/{biab-forms.css → bd-forms.css} +158 -158
- package/src/svelte/{BiabForm.svelte → BdForm.svelte} +240 -173
- package/src/svelte/context.ts +16 -16
- package/src/svelte/headless.ts +10 -10
- package/src/svelte/index.ts +16 -16
- package/src/vanilla.ts +250 -169
- package/src/vue/{BiabForm.vue → BdForm.vue} +270 -157
- package/src/vue/headless.ts +11 -11
- package/src/vue/index.ts +26 -26
|
@@ -25,7 +25,7 @@ export function shouldSearchOptions(
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* Where
|
|
28
|
+
* Where BD serves icon art.
|
|
29
29
|
*
|
|
30
30
|
* The `icon_select` element stores an icon NAME, and rendering it back needs the
|
|
31
31
|
* icon set. The SDK deliberately doesn't bundle one: it's ~1,600 icons, and
|
|
@@ -33,10 +33,10 @@ export function shouldSearchOptions(
|
|
|
33
33
|
* `<img src="…/api/icons/<name>.svg">`, so the platform stays the single source
|
|
34
34
|
* of truth for what a name means.
|
|
35
35
|
*
|
|
36
|
-
* Override with `
|
|
37
|
-
* non-production
|
|
36
|
+
* Override with `NEXT_PUBLIC_BD_ORIGIN` when pointing a consumer app at a
|
|
37
|
+
* non-production BD.
|
|
38
38
|
*/
|
|
39
|
-
export const
|
|
39
|
+
export const BD_ICON_BASE: string = (() => {
|
|
40
40
|
// Read through `globalThis` rather than the bare `process` identifier: the
|
|
41
41
|
// SDK is consumed by browser bundlers that have no `@types/node`, where
|
|
42
42
|
// naming `process` directly fails to compile (and can throw at runtime).
|
|
@@ -45,17 +45,17 @@ export const BIAB_ICON_BASE: string = (() => {
|
|
|
45
45
|
process?: { env?: Record<string, string | undefined> };
|
|
46
46
|
}
|
|
47
47
|
).process?.env;
|
|
48
|
-
return env?.["
|
|
48
|
+
return env?.["NEXT_PUBLIC_BD_ORIGIN"] || "https://www.biab.app";
|
|
49
49
|
})();
|
|
50
50
|
|
|
51
51
|
/** SVG URL for one icon name. */
|
|
52
52
|
export function iconUrl(name: string): string {
|
|
53
|
-
return `${
|
|
53
|
+
return `${BD_ICON_BASE}/api/icons/${encodeURIComponent(name)}.svg`;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/** The lazily-fetched icon-name index (only needed when an author hasn't
|
|
57
57
|
* curated `iconConfig.allowedIcons`). */
|
|
58
|
-
export const ICON_INDEX_URL = `${
|
|
58
|
+
export const ICON_INDEX_URL = `${BD_ICON_BASE}/api/icons`;
|
|
59
59
|
|
|
60
60
|
/** Narrow an icon list by a search query, capped so the grid never mounts
|
|
61
61
|
* thousands of nodes at once. */
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* identifiers (that aren't a provided variable) and unknown functions are hard
|
|
8
8
|
* errors. The same module runs on the client (non-sensitive functions, live) and
|
|
9
9
|
* the server (all functions) so results never disagree. The SDK keeps a mirror at
|
|
10
|
-
* `
|
|
10
|
+
* `sdk/src/forms-core/formula.ts`.
|
|
11
11
|
*
|
|
12
12
|
* Values are scalars: number | string | boolean. (Tables / arrays are a
|
|
13
13
|
* fast-follow.) Function reference: see FORMULA_FUNCTIONS.
|
package/src/forms-core/index.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@businessdash/sdk/forms` headless core — framework-agnostic form runtime.
|
|
3
3
|
*
|
|
4
|
-
* Everything a `<
|
|
4
|
+
* Everything a `<BdForm>` binding needs that ISN'T framework-specific:
|
|
5
5
|
* - `createFormController` — the observable store (values, errors, steps,
|
|
6
6
|
* visibility, submit lifecycle, file uploads).
|
|
7
|
-
* - validation + condition primitives mirrored 1:1 from the
|
|
7
|
+
* - validation + condition primitives mirrored 1:1 from the BD server so the
|
|
8
8
|
* client never disagrees with what the platform enforces.
|
|
9
9
|
*
|
|
10
10
|
* Bindings (React / Vue / Svelte / Solid / Qwik / Angular / web-component /
|
|
11
11
|
* vanilla) import from here, subscribe to `snapshot()`, and render by field
|
|
12
|
-
* `type` using the `
|
|
12
|
+
* `type` using the `bd-*` CSS class convention.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
// Re-export the SDK form types so a binding can import schema/result/field types
|
|
16
16
|
// from the single `/forms` entry without reaching into the root export.
|
|
17
|
-
// `
|
|
17
|
+
// `BdClient` is included because `CreateFormControllerOptions.client` is typed
|
|
18
18
|
// to it — a binding that accepts/injects a client needs the type from here.
|
|
19
19
|
export type {
|
|
20
|
-
|
|
20
|
+
BdClient,
|
|
21
21
|
FormActionPreview,
|
|
22
22
|
FormActionType,
|
|
23
23
|
FormFieldDef,
|
|
@@ -60,7 +60,7 @@ export {
|
|
|
60
60
|
formatCalcValue,
|
|
61
61
|
} from "./calculation.js";
|
|
62
62
|
export {
|
|
63
|
-
type
|
|
63
|
+
type BdFormsClient,
|
|
64
64
|
type CreateFormControllerOptions,
|
|
65
65
|
concurrentRevealCount,
|
|
66
66
|
createFormController,
|
|
@@ -78,7 +78,7 @@ export {
|
|
|
78
78
|
seedInitialData,
|
|
79
79
|
} from "./controller.js";
|
|
80
80
|
export {
|
|
81
|
-
|
|
81
|
+
BD_ICON_BASE,
|
|
82
82
|
fetchIconNames,
|
|
83
83
|
filterIconNames,
|
|
84
84
|
ICON_INDEX_URL,
|
|
@@ -88,7 +88,7 @@ export {
|
|
|
88
88
|
} from "./elements.js";
|
|
89
89
|
|
|
90
90
|
// File-upload rules — accept-attribute building (incl. iOS HEIC/HEVC), per-file
|
|
91
|
-
// validation, and the form-wide file cap. Shared 1:1 with the
|
|
91
|
+
// validation, and the form-wide file cap. Shared 1:1 with the BD server.
|
|
92
92
|
export {
|
|
93
93
|
buildAcceptAttr,
|
|
94
94
|
expandAllowedTypes,
|
package/src/forms-core/units.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Each linear category defines a `baseUnit` and a `factor` per unit such that
|
|
6
6
|
* `valueInBase = value * factor`. Temperature is affine (offset + scale), so it
|
|
7
|
-
* is special-cased. `<
|
|
7
|
+
* is special-cased. `<BdForm/>` uses these to render the unit picker, show a
|
|
8
8
|
* live conversion hint, and compute the value's base — the server recomputes the
|
|
9
9
|
* base authoritatively on submit, so this is convenience only.
|
|
10
10
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* US states (+ DC) for the Address element's State dropdown, and the US ZIP code
|
|
3
3
|
* format. Shared by the server validator, the FormPreview/editor renders, and the
|
|
4
|
-
* SDK (mirrored at `
|
|
4
|
+
* SDK (mirrored at `sdk/src/forms-core/us-states.ts`). Keep the two in sync.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export const US_STATES: ReadonlyArray<{ name: string; abbr: string }> = [
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* Framework-agnostic, isomorphic form validation + condition evaluation.
|
|
3
3
|
*
|
|
4
4
|
* Zero UI deps, zero React, zero DOM. This is the single source of truth every
|
|
5
|
-
* `<
|
|
6
|
-
* vanilla) shares. It is a faithful port of three existing
|
|
5
|
+
* `<BdForm>` binding (React / Vue / Svelte / Solid / Qwik / Angular / Element /
|
|
6
|
+
* vanilla) shares. It is a faithful port of three existing BD modules so the
|
|
7
7
|
* client never disagrees with the server:
|
|
8
8
|
*
|
|
9
9
|
* - `src/server/forms/validate.ts` — isEmpty / validateValue /
|
|
10
10
|
* evalCondition / findMissingRequired
|
|
11
11
|
* - `src/app/.../builder/FormPreview.tsx` — seedInitialData / concurrentRevealCount /
|
|
12
12
|
* the or_condition + condition_block walk
|
|
13
|
-
* - `
|
|
13
|
+
* - `sdk/src/sdk.ts` validateFormSubmission — the wire-level submission check
|
|
14
14
|
*
|
|
15
15
|
* The schema published by `client.forms.schema(slug)` carries the FULL nested
|
|
16
16
|
* field tree (layout nodes, `condition_block`, `or_condition`, `input_group`,
|
|
@@ -117,6 +117,15 @@ export interface FormCoreField {
|
|
|
117
117
|
sources?: "both" | "upload" | "url";
|
|
118
118
|
};
|
|
119
119
|
phoneConfig?: { international?: boolean; showFlags?: boolean };
|
|
120
|
+
/** `name` composite: which optional parts render. First/last are always on.
|
|
121
|
+
* Value is { prefix?, first, middle?, last?, suffix?, nicknames?, full, short }
|
|
122
|
+
* — `full`/`short` are recomputed server-side at submission validation. */
|
|
123
|
+
nameConfig?: {
|
|
124
|
+
prefix?: boolean;
|
|
125
|
+
middle?: boolean;
|
|
126
|
+
suffix?: boolean;
|
|
127
|
+
nicknames?: boolean;
|
|
128
|
+
};
|
|
120
129
|
timeConfig?: {
|
|
121
130
|
mode?: "simple" | "time_block" | "appointment";
|
|
122
131
|
format?: "12h" | "24h";
|
|
@@ -217,6 +226,10 @@ const MAX_ARRAY_LEN = 500;
|
|
|
217
226
|
const MAX_PHONE_LEN = 64;
|
|
218
227
|
const MAX_DATE_LEN = 64;
|
|
219
228
|
const MAX_ADDRESS_FIELD_LEN = 512;
|
|
229
|
+
const MAX_NAME_PART_LEN = 128;
|
|
230
|
+
// Computed `full`/`short` can span several parts; bounded but roomier.
|
|
231
|
+
const MAX_NAME_STRING_LEN = 1024;
|
|
232
|
+
const MAX_NICKNAMES = 20;
|
|
220
233
|
const MAX_FILE_FIELD_LEN = 4096;
|
|
221
234
|
|
|
222
235
|
/**
|
|
@@ -264,6 +277,77 @@ const NO_SEED_STATIC = new Set([
|
|
|
264
277
|
"calculation",
|
|
265
278
|
]);
|
|
266
279
|
|
|
280
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
281
|
+
// Name helpers — the `name` composite's parts + derived strings. Mirrors
|
|
282
|
+
// validate.ts / the server's name-format module: the server recomputes
|
|
283
|
+
// `full`/`short` authoritatively at submission validation; the client
|
|
284
|
+
// normalizes too so payloads leave here canonical.
|
|
285
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
286
|
+
|
|
287
|
+
/** The user-entered parts, in display order. */
|
|
288
|
+
const NAME_PART_ORDER = [
|
|
289
|
+
"prefix",
|
|
290
|
+
"first",
|
|
291
|
+
"middle",
|
|
292
|
+
"last",
|
|
293
|
+
"suffix",
|
|
294
|
+
] as const;
|
|
295
|
+
|
|
296
|
+
const NAME_VALUE_KEYS = new Set<string>([
|
|
297
|
+
...NAME_PART_ORDER,
|
|
298
|
+
"nicknames",
|
|
299
|
+
"full",
|
|
300
|
+
"short",
|
|
301
|
+
]);
|
|
302
|
+
|
|
303
|
+
function namePart(v: unknown): string {
|
|
304
|
+
return typeof v === "string" ? v.trim() : "";
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Every provided part joined with single spaces ("Dr. Jane Quinn Doe Jr."). */
|
|
308
|
+
function composeFullName(v: Record<string, unknown>): string {
|
|
309
|
+
return NAME_PART_ORDER.map((k) => namePart(v[k]))
|
|
310
|
+
.filter(Boolean)
|
|
311
|
+
.join(" ");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** First + last (whichever of the two exist) — "Jane Doe". */
|
|
315
|
+
function composeShortName(v: Record<string, unknown>): string {
|
|
316
|
+
return [namePart(v["first"]), namePart(v["last"])].filter(Boolean).join(" ");
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Nicknames: the input holds a raw comma-separated string while the visitor
|
|
320
|
+
* types; a string and an array both become trimmed, non-empty items. */
|
|
321
|
+
function normalizeNicknames(raw: unknown): string[] {
|
|
322
|
+
const items =
|
|
323
|
+
typeof raw === "string" ? raw.split(",") : Array.isArray(raw) ? raw : [];
|
|
324
|
+
return items
|
|
325
|
+
.filter((n): n is string => typeof n === "string")
|
|
326
|
+
.map((n) => n.trim())
|
|
327
|
+
.filter(Boolean);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Canonicalize a `name` value: trim every part, drop empty ones, normalize
|
|
332
|
+
* nicknames into a string[], and RECOMPUTE `full`/`short` — whatever the
|
|
333
|
+
* working data held for those is discarded (the server does the same again,
|
|
334
|
+
* authoritatively).
|
|
335
|
+
*/
|
|
336
|
+
function normalizeNameValue(
|
|
337
|
+
raw: Record<string, unknown>,
|
|
338
|
+
): Record<string, unknown> {
|
|
339
|
+
const out: Record<string, unknown> = {};
|
|
340
|
+
for (const key of NAME_PART_ORDER) {
|
|
341
|
+
const v = namePart(raw[key]);
|
|
342
|
+
if (v) out[key] = v;
|
|
343
|
+
}
|
|
344
|
+
const nicknames = normalizeNicknames(raw["nicknames"]);
|
|
345
|
+
if (nicknames.length > 0) out["nicknames"] = nicknames;
|
|
346
|
+
out["full"] = composeFullName(out);
|
|
347
|
+
out["short"] = composeShortName(out);
|
|
348
|
+
return out;
|
|
349
|
+
}
|
|
350
|
+
|
|
267
351
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
268
352
|
// isEmpty — per-type "is this answered?" rule. Mirrors validate.ts isEmpty.
|
|
269
353
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -334,6 +418,12 @@ export function isEmpty(field: FormCoreField, value: unknown): boolean {
|
|
|
334
418
|
| undefined;
|
|
335
419
|
return !v?.line1 || !v.city || !v.state || !v.zip;
|
|
336
420
|
}
|
|
421
|
+
case "name": {
|
|
422
|
+
// Answered when both always-on parts are present. Optional parts
|
|
423
|
+
// (prefix, middle, suffix, nicknames) never satisfy a required name.
|
|
424
|
+
const v = value as { first?: string; last?: string } | undefined;
|
|
425
|
+
return !v?.first?.trim() || !v.last?.trim();
|
|
426
|
+
}
|
|
337
427
|
case "time_picker": {
|
|
338
428
|
const mode = field.timeConfig?.mode ?? "simple";
|
|
339
429
|
if (mode === "time_block") {
|
|
@@ -360,14 +450,32 @@ export function isEmpty(field: FormCoreField, value: unknown): boolean {
|
|
|
360
450
|
// pass here (required-ness is checked separately).
|
|
361
451
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
362
452
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
453
|
+
/**
|
|
454
|
+
* Compile an author-supplied pattern: a bare source (`^foo$`) or a
|
|
455
|
+
* slash-delimited literal with flags (`/^foo$/i`) — the way authors actually
|
|
456
|
+
* write them. Returns null when nothing compiles; a broken stored pattern
|
|
457
|
+
* must never block a submission. Mirrors the platform's shared helper.
|
|
458
|
+
*/
|
|
459
|
+
function compileFieldRegex(raw: string): RegExp | null {
|
|
460
|
+
const m = /^\/(.*)\/([dgimsuvy]*)$/s.exec(raw.trim());
|
|
461
|
+
const source = m ? (m[1] as string) : raw.trim();
|
|
462
|
+
const flags = m ? (m[2] as string) : "";
|
|
366
463
|
try {
|
|
367
|
-
|
|
464
|
+
return new RegExp(source, flags);
|
|
368
465
|
} catch {
|
|
369
|
-
|
|
466
|
+
try {
|
|
467
|
+
return new RegExp(raw);
|
|
468
|
+
} catch {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
370
471
|
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function regexFail(field: FormCoreField, value: string): string | null {
|
|
475
|
+
const pattern = field.regexPattern;
|
|
476
|
+
if (!pattern) return null;
|
|
477
|
+
const re = compileFieldRegex(pattern);
|
|
478
|
+
if (re && !re.test(value)) return "regex";
|
|
371
479
|
return null;
|
|
372
480
|
}
|
|
373
481
|
|
|
@@ -635,6 +743,38 @@ export function validateValue(
|
|
|
635
743
|
if (typeof zip === "string" && !isValidUsZip(zip)) return "format";
|
|
636
744
|
return null;
|
|
637
745
|
}
|
|
746
|
+
case "name": {
|
|
747
|
+
// { prefix?, first, middle?, last?, suffix?, nicknames?, full?, short? }.
|
|
748
|
+
// Client-sent `full`/`short` are accepted here but recomputed at the
|
|
749
|
+
// pre-submit normalization step; `nicknames` may still be the raw comma
|
|
750
|
+
// string. Mirrors validate.ts.
|
|
751
|
+
if (!isPlainObject(value)) return "type";
|
|
752
|
+
for (const [key, v] of Object.entries(value)) {
|
|
753
|
+
if (!NAME_VALUE_KEYS.has(key)) return "options";
|
|
754
|
+
if (v === undefined || key === "nicknames") continue;
|
|
755
|
+
if (typeof v !== "string") return "type";
|
|
756
|
+
const cap =
|
|
757
|
+
key === "full" || key === "short"
|
|
758
|
+
? MAX_NAME_STRING_LEN
|
|
759
|
+
: MAX_NAME_PART_LEN;
|
|
760
|
+
if (v.length > cap) return "max";
|
|
761
|
+
}
|
|
762
|
+
const nn = (value as { nicknames?: unknown }).nicknames;
|
|
763
|
+
if (nn !== undefined) {
|
|
764
|
+
if (typeof nn === "string") {
|
|
765
|
+
if (nn.length > MAX_NAME_STRING_LEN) return "max";
|
|
766
|
+
} else if (Array.isArray(nn)) {
|
|
767
|
+
if (nn.length > MAX_NICKNAMES) return "max";
|
|
768
|
+
for (const item of nn) {
|
|
769
|
+
if (typeof item !== "string") return "type";
|
|
770
|
+
if (item.length > MAX_NAME_PART_LEN) return "max";
|
|
771
|
+
}
|
|
772
|
+
} else {
|
|
773
|
+
return "type";
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
638
778
|
case "schedule": {
|
|
639
779
|
// Availability value: { asap?, days?: string[], times?: string[], notes? }.
|
|
640
780
|
// Auto value: { slotStartAt: <ISO> } (the chosen instant-booking slot).
|
|
@@ -860,12 +1000,10 @@ export function findMissingRequired(
|
|
|
860
1000
|
const pattern = f.regexPattern;
|
|
861
1001
|
const v = String(scope[f.id] ?? "");
|
|
862
1002
|
if (pattern && v) {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
}
|
|
867
|
-
} catch {
|
|
868
|
-
/* ignore an invalid pattern */
|
|
1003
|
+
// Shared delimiter-aware compile; invalid patterns never block.
|
|
1004
|
+
const re = compileFieldRegex(pattern);
|
|
1005
|
+
if (re && !re.test(v)) {
|
|
1006
|
+
missing.push({ id: f.id, label: f.label || "Invalid format" });
|
|
869
1007
|
}
|
|
870
1008
|
}
|
|
871
1009
|
}
|
|
@@ -1027,6 +1165,12 @@ export function toSubmissionPayload(
|
|
|
1027
1165
|
: instance,
|
|
1028
1166
|
);
|
|
1029
1167
|
}
|
|
1168
|
+
// A name submits canonically: trimmed parts, nicknames comma string →
|
|
1169
|
+
// string[], freshly computed `full`/`short` (the server recomputes them
|
|
1170
|
+
// again, authoritatively).
|
|
1171
|
+
if (field.type === "name" && isPlainObject(inputValue)) {
|
|
1172
|
+
value = normalizeNameValue(inputValue);
|
|
1173
|
+
}
|
|
1030
1174
|
out[field.key ?? id] = value;
|
|
1031
1175
|
}
|
|
1032
1176
|
|
package/src/forms.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@businessdash/sdk/forms` — public entry for the headless `<
|
|
2
|
+
* `@businessdash/sdk/forms` — public entry for the headless `<BdForm>` core.
|
|
3
3
|
*
|
|
4
4
|
* This is the compiled (tsc → dist) entry the framework bindings depend on. It
|
|
5
5
|
* re-exports the framework-agnostic controller + validation primitives from
|
|
@@ -60,4 +60,4 @@ export {
|
|
|
60
60
|
* platform default, so a starter renders a working contact form with zero
|
|
61
61
|
* dashboard setup. Swap in your own org's form slug once you've built one.
|
|
62
62
|
*/
|
|
63
|
-
export const
|
|
63
|
+
export const BD_DEFAULT_FORM_SLUG = "general-inquiry";
|
package/src/framework-support.ts
CHANGED
|
@@ -419,7 +419,7 @@ export const REACT_TARGETS: readonly ReactTarget[] = [
|
|
|
419
419
|
name: "TanStack Start",
|
|
420
420
|
entry: "@businessdash/sdk/react",
|
|
421
421
|
requires: "Nothing — no RSC boundary to cross.",
|
|
422
|
-
starter: "Tanstack-Start",
|
|
422
|
+
starter: "Tanstack-Start-Solid",
|
|
423
423
|
status: "supported",
|
|
424
424
|
},
|
|
425
425
|
{
|
package/src/gallery-types.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* return type via const generics in `sdk.ts`).
|
|
7
7
|
*
|
|
8
8
|
* Kept as a flat type — no SDK dependency on zod or runtime
|
|
9
|
-
* validation here, since `
|
|
9
|
+
* validation here, since `createBdClient` trusts the platform
|
|
10
10
|
* response (the platform owns the schema; the SDK is a typed
|
|
11
11
|
* facade).
|
|
12
12
|
*/
|
package/src/graphql.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generate a GraphQL SDL schema from a
|
|
2
|
+
* Generate a GraphQL SDL schema from a BD data model.
|
|
3
3
|
*
|
|
4
4
|
* "GraphQL layer compatibility" for a data-model-as-code platform starts here:
|
|
5
5
|
* a real, typed GraphQL schema of the org's custom database that any GraphQL
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* heavier layer that needs a runtime.
|
|
14
14
|
*
|
|
15
15
|
* Shape decisions:
|
|
16
|
-
* - Every relation is a LIST.
|
|
16
|
+
* - Every relation is a LIST. BD relations are link rows (`sync-records`
|
|
17
17
|
* converges an array of target ids), and the records API always returns
|
|
18
18
|
* `relations` as arrays — so `[Target!]!` is faithful, one-to-one included.
|
|
19
19
|
* - SELECT/MULTI_SELECT become a GraphQL enum WHEN every option value is a
|
|
@@ -190,7 +190,7 @@ export function dataModelToGraphqlSdl(
|
|
|
190
190
|
|
|
191
191
|
const header =
|
|
192
192
|
opts.header ??
|
|
193
|
-
"# GraphQL schema generated from your
|
|
193
|
+
"# GraphQL schema generated from your BD data model by `bd export-graphql-schema`.\n# Regenerate on schema change; commit it for a git-diffable, AI-readable contract.";
|
|
194
194
|
|
|
195
195
|
const scalars =
|
|
196
196
|
"scalar DateTime\nscalar JSON\n\n# Cents; money is integer cents platform-wide.\n";
|
|
@@ -248,7 +248,7 @@ export function createGraphqlClient(input: {
|
|
|
248
248
|
/** Package API base, e.g. `https://biab.app/api/package/v1`. */
|
|
249
249
|
baseUrl: string;
|
|
250
250
|
siteId: string;
|
|
251
|
-
/** A secret (`
|
|
251
|
+
/** A secret (`bd_…`) key holding `graphql:query`. */
|
|
252
252
|
apiKey: string;
|
|
253
253
|
fetch?: typeof fetch;
|
|
254
254
|
}): GraphqlClient {
|
package/src/index.ts
CHANGED
|
@@ -6,35 +6,35 @@ export {
|
|
|
6
6
|
DEFAULT_AUTH_COOKIE_NAME,
|
|
7
7
|
getTenantSession,
|
|
8
8
|
} from "./auth-handler.js";
|
|
9
|
-
export type {
|
|
9
|
+
export type { BdApiClientOptions, FetchLike } from "./client.js";
|
|
10
10
|
export {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
11
|
+
BdActionsClient,
|
|
12
|
+
BdAnalyticsClient,
|
|
13
|
+
BdApiClient,
|
|
14
|
+
BdAssetsClient,
|
|
15
|
+
BdAttributionClient,
|
|
16
|
+
BdAuthClient,
|
|
17
|
+
BdBlogClient,
|
|
18
|
+
BdCartClient,
|
|
19
|
+
BdChatbotClient,
|
|
20
|
+
BdCheckoutClient,
|
|
21
|
+
BdCollectionsClient,
|
|
22
|
+
BdCustomerPortalClient,
|
|
23
|
+
BdDashboardClient,
|
|
24
|
+
BdDataModelClient,
|
|
25
|
+
BdEmailTemplatesClient,
|
|
26
|
+
BdFollowersClient,
|
|
27
|
+
BdHiringClient,
|
|
28
|
+
BdMarketingClient,
|
|
29
|
+
BdMarketingPagesClient,
|
|
30
|
+
BdParallelPagesClient,
|
|
31
|
+
BdReviewsClient,
|
|
32
|
+
BdRowsClient,
|
|
33
|
+
BdSiteClient,
|
|
34
|
+
BdStorefrontClient,
|
|
35
35
|
clearMarketingCache,
|
|
36
36
|
clearMarketingPageCache,
|
|
37
|
-
|
|
37
|
+
createBdApiClient,
|
|
38
38
|
type ParallelPagesListResponse,
|
|
39
39
|
type ParallelPagesRenderResponse,
|
|
40
40
|
type ParallelPagesVariantsResponse,
|
|
@@ -371,13 +371,13 @@ export {
|
|
|
371
371
|
type InferEnumValues,
|
|
372
372
|
} from "./enums.js";
|
|
373
373
|
export {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
type
|
|
379
|
-
|
|
380
|
-
|
|
374
|
+
BdAccessRejectedError,
|
|
375
|
+
BdApiError,
|
|
376
|
+
BdFeatureNotEntitledError,
|
|
377
|
+
BdPaymentLapsedError,
|
|
378
|
+
type BdPlanGateBody,
|
|
379
|
+
BdServiceSuspendedError,
|
|
380
|
+
isBdPlanGateBody,
|
|
381
381
|
type UnwrappedAccess,
|
|
382
382
|
type UnwrappedEntitlement,
|
|
383
383
|
unwrapAccess,
|
|
@@ -432,11 +432,11 @@ export {
|
|
|
432
432
|
// High-level SDK surface — the consumer DX entry point.
|
|
433
433
|
// (See sdk.ts for the design principle: consumer writes design +
|
|
434
434
|
// granular data requests; auth, caching, revalidation, tagging all
|
|
435
|
-
// live inside
|
|
435
|
+
// live inside bd.)
|
|
436
436
|
export {
|
|
437
|
-
type
|
|
438
|
-
type
|
|
439
|
-
|
|
437
|
+
type BdClient,
|
|
438
|
+
type CreateBdClientOptions,
|
|
439
|
+
createBdClient,
|
|
440
440
|
type FormFieldDef,
|
|
441
441
|
type FormFileValue,
|
|
442
442
|
type FormSchema,
|
|
@@ -464,7 +464,7 @@ export {
|
|
|
464
464
|
type ArticleInput,
|
|
465
465
|
article,
|
|
466
466
|
type BreadcrumbInput,
|
|
467
|
-
|
|
467
|
+
bdSchemas,
|
|
468
468
|
breadcrumb,
|
|
469
469
|
type FaqInput,
|
|
470
470
|
faq,
|
package/src/legal-core/index.ts
CHANGED
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
* to inject. The page is deliberately unstyled markup: legal pages should look
|
|
43
43
|
* like the rest of the site, and an SDK that shipped its own typography would
|
|
44
44
|
* be one more thing to override. `renderLegalPageHtml()` produces semantic HTML
|
|
45
|
-
* with stable `data-
|
|
45
|
+
* with stable `data-bd-legal-*` hooks you can style, or ignore entirely and
|
|
46
46
|
* build your own from the same data.
|
|
47
47
|
*
|
|
48
48
|
* @module
|
|
@@ -170,7 +170,7 @@ export function legalLastUpdated(
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
export type RenderLegalPageOptions = {
|
|
173
|
-
/** Wrapped around everything. Defaults to `
|
|
173
|
+
/** Wrapped around everything. Defaults to `bd-legal`. */
|
|
174
174
|
className?: string;
|
|
175
175
|
/** Locale for the "Last updated" line. */
|
|
176
176
|
locale?: string;
|
|
@@ -181,7 +181,7 @@ export type RenderLegalPageOptions = {
|
|
|
181
181
|
/**
|
|
182
182
|
* Semantic markup for one document: header, title, body, footer.
|
|
183
183
|
*
|
|
184
|
-
* Unstyled by design. Every element carries a stable `data-
|
|
184
|
+
* Unstyled by design. Every element carries a stable `data-bd-legal-*`
|
|
185
185
|
* attribute so a stylesheet can reach it, and none carries a class you would
|
|
186
186
|
* have to fight. If you want a different structure, read the same fields off
|
|
187
187
|
* the document and build your own — this is a convenience, not a requirement.
|
|
@@ -195,7 +195,7 @@ export function renderLegalPageHtml(
|
|
|
195
195
|
options: RenderLegalPageOptions = {},
|
|
196
196
|
): string {
|
|
197
197
|
const presentation = document.presentation;
|
|
198
|
-
const className = options.className ?? "
|
|
198
|
+
const className = options.className ?? "bd-legal";
|
|
199
199
|
const homeHref = options.homeHref ?? "/";
|
|
200
200
|
const updated = legalLastUpdated(document, options.locale);
|
|
201
201
|
|
|
@@ -206,24 +206,24 @@ export function renderLegalPageHtml(
|
|
|
206
206
|
: presentation.showLogoInFooter;
|
|
207
207
|
if (!show || !presentation.logoUrl) return "";
|
|
208
208
|
return (
|
|
209
|
-
`<a href="${escapeHtml(homeHref)}" data-
|
|
210
|
-
`<img src="${escapeHtml(presentation.logoUrl)}" alt="" data-
|
|
209
|
+
`<a href="${escapeHtml(homeHref)}" data-bd-legal-logo-link>` +
|
|
210
|
+
`<img src="${escapeHtml(presentation.logoUrl)}" alt="" data-bd-legal-logo="${where}" />` +
|
|
211
211
|
"</a>"
|
|
212
212
|
);
|
|
213
213
|
};
|
|
214
214
|
|
|
215
215
|
const parts = [
|
|
216
|
-
`<article class="${escapeHtml(className)}" data-
|
|
217
|
-
"<header data-
|
|
216
|
+
`<article class="${escapeHtml(className)}" data-bd-legal>`,
|
|
217
|
+
"<header data-bd-legal-header>",
|
|
218
218
|
logo("header"),
|
|
219
219
|
presentation.headerHtml ?? "",
|
|
220
|
-
`<h1 data-
|
|
220
|
+
`<h1 data-bd-legal-title>${escapeHtml(document.title)}</h1>`,
|
|
221
221
|
updated
|
|
222
|
-
? `<p data-
|
|
222
|
+
? `<p data-bd-legal-updated>Last updated: ${escapeHtml(updated)}</p>`
|
|
223
223
|
: "",
|
|
224
224
|
"</header>",
|
|
225
|
-
`<div data-
|
|
226
|
-
"<footer data-
|
|
225
|
+
`<div data-bd-legal-body>${document.contentHtml}</div>`,
|
|
226
|
+
"<footer data-bd-legal-footer>",
|
|
227
227
|
presentation.footerHtml ?? "",
|
|
228
228
|
logo("footer"),
|
|
229
229
|
"</footer>",
|