@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,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Distribution helpers — serve your curated answer-engine artifacts from
|
|
3
|
+
* YOUR domain.
|
|
4
|
+
*
|
|
5
|
+
* The llms.txt convention only works at the site's own root (an AI crawler
|
|
6
|
+
* asks for https://yoursite.com/llms.txt), while the source of truth lives
|
|
7
|
+
* on BIAB where the org curates it. This proxies the two together.
|
|
8
|
+
*
|
|
9
|
+
* Next.js wiring (app/llms.txt/route.ts):
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { llmsTxtHandler } from '@businessdash/sdk/distribution'
|
|
13
|
+
*
|
|
14
|
+
* export const GET = llmsTxtHandler({
|
|
15
|
+
* siteId: process.env.BIAB_SITE_ID!,
|
|
16
|
+
* baseUrl: process.env.NEXT_PUBLIC_BIAB_APP_URL!, // e.g. https://www.biab.app
|
|
17
|
+
* })
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* The product feed needs no proxy — you submit its BIAB URL directly to a
|
|
21
|
+
* merchant program — but `productFeedUrl` builds it for display/submission.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export type DistributionSourceOptions = {
|
|
25
|
+
siteId: string;
|
|
26
|
+
/** BIAB app origin, e.g. "https://www.biab.app". */
|
|
27
|
+
baseUrl: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function feedBase(options: DistributionSourceOptions): string {
|
|
31
|
+
return `${options.baseUrl.replace(/\/$/, "")}/api/public/ai-feed/${encodeURIComponent(
|
|
32
|
+
options.siteId,
|
|
33
|
+
)}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The public product-feed URL — submit this to merchant/feed programs. */
|
|
37
|
+
export function productFeedUrl(options: DistributionSourceOptions): string {
|
|
38
|
+
return `${feedBase(options)}/products`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The public llms.txt source URL (what {@link llmsTxtHandler} proxies). */
|
|
42
|
+
export function llmsTxtUrl(options: DistributionSourceOptions): string {
|
|
43
|
+
return `${feedBase(options)}/llms.txt`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A GET handler serving the site's llms.txt from its own domain root.
|
|
48
|
+
* Returns 404 untouched (feed disabled or entitlement lapsed) and a plain
|
|
49
|
+
* 503 on network failure — never throws into the framework.
|
|
50
|
+
*/
|
|
51
|
+
export function llmsTxtHandler(
|
|
52
|
+
options: DistributionSourceOptions,
|
|
53
|
+
): () => Promise<Response> {
|
|
54
|
+
const source = llmsTxtUrl(options);
|
|
55
|
+
return async () => {
|
|
56
|
+
try {
|
|
57
|
+
const res = await fetch(source, {
|
|
58
|
+
next: { revalidate: 300 },
|
|
59
|
+
} as RequestInit);
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
return new Response("Not found", { status: res.status });
|
|
62
|
+
}
|
|
63
|
+
return new Response(await res.text(), {
|
|
64
|
+
headers: {
|
|
65
|
+
"Cache-Control": "public, s-maxage=300, stale-while-revalidate=600",
|
|
66
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
} catch {
|
|
70
|
+
return new Response("Temporarily unavailable", { status: 503 });
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
package/src/element.ts
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@businessdash/sdk/element` — the `<biab-form>` custom element binding.
|
|
3
|
+
*
|
|
4
|
+
* A drop-in web component that renders a BIAB form anywhere a tag can go:
|
|
5
|
+
*
|
|
6
|
+
* <biab-form slug="contact" submit-label="Send"></biab-form>
|
|
7
|
+
*
|
|
8
|
+
* It is a thin custom-element shell around the framework-free `mountBiabForm`
|
|
9
|
+
* (`@businessdash/sdk/vanilla`): the element owns the lifecycle (connect / attribute
|
|
10
|
+
* changes / disconnect) and `mountBiabForm` owns all rendering + the headless
|
|
11
|
+
* controller. It renders into LIGHT DOM (no shadow root) so the consumer's
|
|
12
|
+
* `biab-*` CSS cascades straight in — identical to every other binding.
|
|
13
|
+
*
|
|
14
|
+
* ── Passing the SDK client ───────────────────────────────────────────────────
|
|
15
|
+
* A `BiabClient` can't travel through an HTML attribute, so there are three ways
|
|
16
|
+
* to give the element one (checked in this order):
|
|
17
|
+
* 1. The `.client` JS property: el.client = createBiabClient(...)
|
|
18
|
+
* 2. A DI default set once at startup: setBiabFormClient(createBiabClient(...))
|
|
19
|
+
* (the "Provider equivalent" — every <biab-form> on the page uses it)
|
|
20
|
+
* 3. A pre-fetched schema (SSR), via the `.schema` property — no client needed
|
|
21
|
+
* to render; a client is still required to actually submit.
|
|
22
|
+
*
|
|
23
|
+
* ── Attributes (reflected, kebab-case) ───────────────────────────────────────
|
|
24
|
+
* slug — form slug to load via the client
|
|
25
|
+
* submit-label — label for the final submit button (default "Submit")
|
|
26
|
+
* dry-run — present ⇒ validate server-side but store nothing
|
|
27
|
+
* class — extra class(es) added to the inner `.biab-form`
|
|
28
|
+
* auto-complete — global HTML autofill switch: "false" ⇒ off, "true" or
|
|
29
|
+
* absent ⇒ infer tokens per field
|
|
30
|
+
* field-auto-complete — JSON object of per-field overrides keyed by field id
|
|
31
|
+
* ({ "<id>": token | true | false })
|
|
32
|
+
*
|
|
33
|
+
* ── Properties (JS, richer types) ────────────────────────────────────────────
|
|
34
|
+
* .client : BiabClient — the SDK client
|
|
35
|
+
* .schema : FormSchema — pre-fetched schema (SSR / adopt)
|
|
36
|
+
* .submitOptions : FormSubmitOptions
|
|
37
|
+
* .autoComplete : boolean | undefined — mirrors `auto-complete`
|
|
38
|
+
* .fieldAutoComplete : Record<string, string | boolean> | undefined
|
|
39
|
+
*
|
|
40
|
+
* ── Events (bubbling, composed) ──────────────────────────────────────────────
|
|
41
|
+
* biab-form-success — detail: the ok FormSubmitResult
|
|
42
|
+
* biab-form-error — detail: the failed FormSubmitResult
|
|
43
|
+
* biab-form-submit — detail: { data } working values at submit time
|
|
44
|
+
*
|
|
45
|
+
* Compiled to dist (plain TS, no JSX). Importing this module registers the
|
|
46
|
+
* element once (idempotent).
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
import type { BiabClient, FormSchema, FormSubmitResult } from "./sdk.js";
|
|
50
|
+
import {
|
|
51
|
+
type BiabFormInstance,
|
|
52
|
+
type FormSubmitOptions,
|
|
53
|
+
mountBiabForm,
|
|
54
|
+
} from "./vanilla.js";
|
|
55
|
+
|
|
56
|
+
// Re-export the core + the vanilla mount so this subpath is a complete entry.
|
|
57
|
+
export * from "./forms-core/index.js";
|
|
58
|
+
export type {
|
|
59
|
+
BiabFormInstance,
|
|
60
|
+
MountBiabFormOptions,
|
|
61
|
+
} from "./vanilla.js";
|
|
62
|
+
export { mountBiabForm } from "./vanilla.js";
|
|
63
|
+
|
|
64
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
65
|
+
// Provider equivalent — a process-wide default client for every <biab-form>.
|
|
66
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
let defaultClient: BiabClient | null = null;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Set (or clear) the default SDK client used by every `<biab-form>` that doesn't
|
|
72
|
+
* have its own `.client` property. The web-component analogue of a React
|
|
73
|
+
* `<BiabFormProvider client={…}>`. Call once at startup:
|
|
74
|
+
*
|
|
75
|
+
* import { setBiabFormClient } from "@businessdash/sdk/element";
|
|
76
|
+
* setBiabFormClient(createBiabClient({ apiKey, siteId, baseUrl }));
|
|
77
|
+
*/
|
|
78
|
+
export function setBiabFormClient(client: BiabClient | null): void {
|
|
79
|
+
defaultClient = client;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Read the current default client (mostly for tests / introspection). */
|
|
83
|
+
export function getBiabFormClient(): BiabClient | null {
|
|
84
|
+
return defaultClient;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
88
|
+
// The custom element.
|
|
89
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
export class BiabFormElement extends HTMLElement {
|
|
92
|
+
static get observedAttributes(): string[] {
|
|
93
|
+
return [
|
|
94
|
+
"slug",
|
|
95
|
+
"submit-label",
|
|
96
|
+
"dry-run",
|
|
97
|
+
"class",
|
|
98
|
+
"auto-complete",
|
|
99
|
+
"field-auto-complete",
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The mounted instance (null until connected with enough config). */
|
|
104
|
+
private instance: BiabFormInstance | null = null;
|
|
105
|
+
|
|
106
|
+
/** Property-only backing fields (can't ride on attributes). */
|
|
107
|
+
private _client: BiabClient | undefined;
|
|
108
|
+
private _schema: FormSchema | undefined;
|
|
109
|
+
private _submitOptions: FormSubmitOptions | undefined;
|
|
110
|
+
/** Per-field autofill overrides set via the JS property; the
|
|
111
|
+
* `field-auto-complete` JSON attribute is parsed at mount as a fallback. */
|
|
112
|
+
private _fieldAutoComplete: Record<string, string | boolean> | undefined;
|
|
113
|
+
|
|
114
|
+
// ── Property accessors ─────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
get client(): BiabClient | undefined {
|
|
117
|
+
return this._client;
|
|
118
|
+
}
|
|
119
|
+
set client(value: BiabClient | undefined) {
|
|
120
|
+
this._client = value;
|
|
121
|
+
this.remount();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get schema(): FormSchema | undefined {
|
|
125
|
+
return this._schema;
|
|
126
|
+
}
|
|
127
|
+
set schema(value: FormSchema | undefined) {
|
|
128
|
+
this._schema = value;
|
|
129
|
+
this.remount();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
get submitOptions(): FormSubmitOptions | undefined {
|
|
133
|
+
return this._submitOptions;
|
|
134
|
+
}
|
|
135
|
+
set submitOptions(value: FormSubmitOptions | undefined) {
|
|
136
|
+
this._submitOptions = value;
|
|
137
|
+
this.remount();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
get slug(): string | null {
|
|
141
|
+
return this.getAttribute("slug");
|
|
142
|
+
}
|
|
143
|
+
set slug(value: string | null) {
|
|
144
|
+
if (value == null) this.removeAttribute("slug");
|
|
145
|
+
else this.setAttribute("slug", value);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
get submitLabel(): string | null {
|
|
149
|
+
return this.getAttribute("submit-label");
|
|
150
|
+
}
|
|
151
|
+
set submitLabel(value: string | null) {
|
|
152
|
+
if (value == null) this.removeAttribute("submit-label");
|
|
153
|
+
else this.setAttribute("submit-label", value);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
get dryRun(): boolean {
|
|
157
|
+
return this.hasAttribute("dry-run");
|
|
158
|
+
}
|
|
159
|
+
set dryRun(value: boolean) {
|
|
160
|
+
if (value) this.setAttribute("dry-run", "");
|
|
161
|
+
else this.removeAttribute("dry-run");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Global autofill switch. `undefined` (attribute absent) infers tokens;
|
|
165
|
+
* `false` (`auto-complete="false"`) emits `autocomplete="off"` everywhere. */
|
|
166
|
+
get autoComplete(): boolean | undefined {
|
|
167
|
+
const attr = this.getAttribute("auto-complete");
|
|
168
|
+
if (attr == null) return undefined;
|
|
169
|
+
return attr !== "false";
|
|
170
|
+
}
|
|
171
|
+
set autoComplete(value: boolean | undefined) {
|
|
172
|
+
if (value == null) this.removeAttribute("auto-complete");
|
|
173
|
+
else this.setAttribute("auto-complete", value ? "true" : "false");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Per-field autofill overrides keyed by field id. Settable as a JS object
|
|
177
|
+
* property or as the `field-auto-complete` JSON attribute. */
|
|
178
|
+
get fieldAutoComplete(): Record<string, string | boolean> | undefined {
|
|
179
|
+
if (this._fieldAutoComplete !== undefined) return this._fieldAutoComplete;
|
|
180
|
+
return parseFieldAutoComplete(this.getAttribute("field-auto-complete"));
|
|
181
|
+
}
|
|
182
|
+
set fieldAutoComplete(value: Record<string, string | boolean> | undefined) {
|
|
183
|
+
this._fieldAutoComplete = value;
|
|
184
|
+
this.remount();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ── Lifecycle ───────────────────────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
connectedCallback(): void {
|
|
190
|
+
this.mount();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
disconnectedCallback(): void {
|
|
194
|
+
this.teardown();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
attributeChangedCallback(
|
|
198
|
+
_name: string,
|
|
199
|
+
oldValue: string | null,
|
|
200
|
+
newValue: string | null,
|
|
201
|
+
): void {
|
|
202
|
+
if (oldValue === newValue) return;
|
|
203
|
+
// Any observed attribute changing re-derives the mount options.
|
|
204
|
+
this.remount();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Imperatively submit the form (parity with `mountBiabForm`'s handle). */
|
|
208
|
+
submit(options?: FormSubmitOptions): Promise<FormSubmitResult> | undefined {
|
|
209
|
+
return this.instance?.submit(options);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Reset values / errors / step. */
|
|
213
|
+
reset(): void {
|
|
214
|
+
this.instance?.reset();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Re-fetch (or re-adopt) the schema. */
|
|
218
|
+
reload(): Promise<void> | undefined {
|
|
219
|
+
return this.instance?.reload();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── Internals ────────────────────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
private mount(): void {
|
|
225
|
+
if (this.instance) return;
|
|
226
|
+
|
|
227
|
+
const client = this._client ?? defaultClient ?? undefined;
|
|
228
|
+
const slug = this.getAttribute("slug") ?? undefined;
|
|
229
|
+
|
|
230
|
+
// Need either a client+slug or a pre-fetched schema to render.
|
|
231
|
+
if (!this._schema && (!client || !slug)) {
|
|
232
|
+
this.replaceChildren();
|
|
233
|
+
const hint = document.createElement("div");
|
|
234
|
+
hint.className = "biab-form biab-form__error";
|
|
235
|
+
hint.textContent =
|
|
236
|
+
"<biab-form> needs a `slug` + a client (set `.client` or call setBiabFormClient), or a pre-fetched `.schema`.";
|
|
237
|
+
this.append(hint);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const extraClass = this.getAttribute("class") ?? undefined;
|
|
242
|
+
const submitLabel = this.getAttribute("submit-label") ?? undefined;
|
|
243
|
+
const autoComplete = this.autoComplete;
|
|
244
|
+
const fieldAutoComplete = this.fieldAutoComplete;
|
|
245
|
+
|
|
246
|
+
this.instance = mountBiabForm(this, {
|
|
247
|
+
...(client ? { client } : {}),
|
|
248
|
+
...(slug ? { slug } : {}),
|
|
249
|
+
...(this._schema ? { schema: this._schema } : {}),
|
|
250
|
+
...(submitLabel ? { submitLabel } : {}),
|
|
251
|
+
...(this.hasAttribute("dry-run") ? { dryRun: true } : {}),
|
|
252
|
+
...(extraClass ? { class: extraClass } : {}),
|
|
253
|
+
...(this._submitOptions ? { submitOptions: this._submitOptions } : {}),
|
|
254
|
+
...(autoComplete !== undefined ? { autoComplete } : {}),
|
|
255
|
+
...(fieldAutoComplete ? { fieldAutoComplete } : {}),
|
|
256
|
+
onSubmit: (data) => this.emit("biab-form-submit", { data }),
|
|
257
|
+
onSuccess: (result) => this.emit("biab-form-success", result),
|
|
258
|
+
onError: (result) => this.emit("biab-form-error", result),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private teardown(): void {
|
|
263
|
+
this.instance?.destroy();
|
|
264
|
+
this.instance = null;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Tear down + re-mount with current attrs/props. Cheap; ignored pre-connect. */
|
|
268
|
+
private remount(): void {
|
|
269
|
+
if (!this.isConnected) return;
|
|
270
|
+
this.teardown();
|
|
271
|
+
this.mount();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private emit(type: string, detail: unknown): void {
|
|
275
|
+
this.dispatchEvent(
|
|
276
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true }),
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Parse the `field-auto-complete` JSON attribute into a per-field overrides
|
|
283
|
+
* record. Returns `undefined` on absent/blank/invalid JSON or a non-object so a
|
|
284
|
+
* malformed attribute silently falls back to inferred tokens.
|
|
285
|
+
*/
|
|
286
|
+
function parseFieldAutoComplete(
|
|
287
|
+
raw: string | null,
|
|
288
|
+
): Record<string, string | boolean> | undefined {
|
|
289
|
+
if (raw == null || raw.trim() === "") return undefined;
|
|
290
|
+
try {
|
|
291
|
+
const parsed: unknown = JSON.parse(raw);
|
|
292
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
293
|
+
return parsed as Record<string, string | boolean>;
|
|
294
|
+
}
|
|
295
|
+
} catch {
|
|
296
|
+
// Malformed JSON — ignore and fall back to inferred tokens.
|
|
297
|
+
}
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Register `<biab-form>` once. Idempotent and SSR-safe (no-op when
|
|
303
|
+
* `customElements` is unavailable, e.g. on the server). Called automatically on
|
|
304
|
+
* import; also exported so a consumer can register under a custom tag name.
|
|
305
|
+
*/
|
|
306
|
+
export function defineBiabForm(tagName = "biab-form"): void {
|
|
307
|
+
if (typeof customElements === "undefined") return;
|
|
308
|
+
if (customElements.get(tagName)) return;
|
|
309
|
+
customElements.define(tagName, BiabFormElement);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Auto-register on import (the common case: `import "@businessdash/sdk/element"`).
|
|
313
|
+
defineBiabForm();
|
|
314
|
+
|
|
315
|
+
// Ambient typing so `document.createElement("biab-form")` is typed in TS apps
|
|
316
|
+
// that pull this module in.
|
|
317
|
+
declare global {
|
|
318
|
+
interface HTMLElementTagNameMap {
|
|
319
|
+
"biab-form": BiabFormElement;
|
|
320
|
+
}
|
|
321
|
+
}
|
package/src/enums.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Core utility to extract and humanize strings into clean labels
|
|
2
|
+
function formatLabel(value: string): string {
|
|
3
|
+
// 1. Separate camelCase/PascalCase boundaries (e.g., "inProgress" -> "in Progress")
|
|
4
|
+
const spaced = value.replace(
|
|
5
|
+
/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/g,
|
|
6
|
+
" ",
|
|
7
|
+
);
|
|
8
|
+
// 2. Normalize snake_case (_) and kebab-case (-) into clear spaces
|
|
9
|
+
const normalized = spaced.replace(/[_-]+/g, " ").trim();
|
|
10
|
+
// 3. Capitalize the first letter of each discrete word
|
|
11
|
+
return normalized
|
|
12
|
+
.toLowerCase()
|
|
13
|
+
.split(" ")
|
|
14
|
+
.filter((word) => word.length > 0) // Clean up any duplicate spacing artifacts
|
|
15
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
16
|
+
.join(" ");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface EnumEntry {
|
|
20
|
+
value: string;
|
|
21
|
+
label: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// The core runtime handler implementation for the Pull Request
|
|
25
|
+
export function Enums<
|
|
26
|
+
const T extends readonly (string | { value: string; label?: string })[],
|
|
27
|
+
>(definitions: T) {
|
|
28
|
+
// Normalize mixed inputs to a standard array layout
|
|
29
|
+
const entries: EnumEntry[] = definitions.map((item) => {
|
|
30
|
+
if (typeof item === "string") {
|
|
31
|
+
return {
|
|
32
|
+
value: item,
|
|
33
|
+
label: formatLabel(item),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
value: item.value,
|
|
38
|
+
label: item.label ?? formatLabel(item.value),
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
type: "enum" as const,
|
|
44
|
+
entries,
|
|
45
|
+
values: entries.map((e) => e.value) as unknown as {
|
|
46
|
+
[K in keyof T]: T[K] extends string
|
|
47
|
+
? T[K]
|
|
48
|
+
: T[K] extends { value: infer V }
|
|
49
|
+
? V
|
|
50
|
+
: never;
|
|
51
|
+
},
|
|
52
|
+
isValid: (val: unknown): boolean => entries.some((e) => e.value === val),
|
|
53
|
+
getLabel: (val: string): string | undefined =>
|
|
54
|
+
entries.find((e) => e.value === val)?.label,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Utility helper to extract exact value types from the collection
|
|
59
|
+
export type InferEnumValues<T extends { values: readonly string[] }> =
|
|
60
|
+
T["values"][number];
|
|
61
|
+
|
|
62
|
+
export type Enum = EnumEntry;
|
|
63
|
+
export type DefineEnums = EnumEntry;
|
|
64
|
+
export type InferEnum<T extends { values: readonly string[] }> =
|
|
65
|
+
InferEnumValues<T>;
|