@businessdash/sdk 0.9.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +899 -0
  3. package/dist/adapters-revalidate.d.ts +44 -0
  4. package/dist/adapters-revalidate.d.ts.map +1 -0
  5. package/dist/adapters-revalidate.js +68 -0
  6. package/dist/adapters-revalidate.js.map +1 -0
  7. package/dist/alpha.d.ts +21 -0
  8. package/dist/alpha.d.ts.map +1 -0
  9. package/dist/alpha.js +21 -0
  10. package/dist/alpha.js.map +1 -0
  11. package/dist/analytics-core.d.ts +34 -0
  12. package/dist/analytics-core.d.ts.map +1 -0
  13. package/dist/analytics-core.js +138 -0
  14. package/dist/analytics-core.js.map +1 -0
  15. package/dist/analytics-server.d.ts +54 -0
  16. package/dist/analytics-server.d.ts.map +1 -0
  17. package/dist/analytics-server.js +78 -0
  18. package/dist/analytics-server.js.map +1 -0
  19. package/dist/attribution-logo.d.ts +2 -0
  20. package/dist/attribution-logo.d.ts.map +1 -0
  21. package/dist/attribution-logo.js +5 -0
  22. package/dist/attribution-logo.js.map +1 -0
  23. package/dist/auth-handler.d.ts +92 -0
  24. package/dist/auth-handler.d.ts.map +1 -0
  25. package/dist/auth-handler.js +277 -0
  26. package/dist/auth-handler.js.map +1 -0
  27. package/dist/biab-forms.css +484 -0
  28. package/dist/cli.d.ts +46 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +1824 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/client.d.ts +1681 -0
  33. package/dist/client.d.ts.map +1 -0
  34. package/dist/client.js +2845 -0
  35. package/dist/client.js.map +1 -0
  36. package/dist/collections.d.ts +13 -0
  37. package/dist/collections.d.ts.map +1 -0
  38. package/dist/collections.js +13 -0
  39. package/dist/collections.js.map +1 -0
  40. package/dist/contracts.d.ts +14609 -0
  41. package/dist/contracts.d.ts.map +1 -0
  42. package/dist/contracts.js +2428 -0
  43. package/dist/contracts.js.map +1 -0
  44. package/dist/data-model-schema.d.ts +962 -0
  45. package/dist/data-model-schema.d.ts.map +1 -0
  46. package/dist/data-model-schema.js +2295 -0
  47. package/dist/data-model-schema.js.map +1 -0
  48. package/dist/distribution.d.ts +38 -0
  49. package/dist/distribution.d.ts.map +1 -0
  50. package/dist/distribution.js +61 -0
  51. package/dist/distribution.js.map +1 -0
  52. package/dist/element.d.ts +121 -0
  53. package/dist/element.d.ts.map +1 -0
  54. package/dist/element.js +276 -0
  55. package/dist/element.js.map +1 -0
  56. package/dist/enums.d.ts +25 -0
  57. package/dist/enums.d.ts.map +1 -0
  58. package/dist/enums.js +38 -0
  59. package/dist/enums.js.map +1 -0
  60. package/dist/errors.d.ts +195 -0
  61. package/dist/errors.d.ts.map +1 -0
  62. package/dist/errors.js +188 -0
  63. package/dist/errors.js.map +1 -0
  64. package/dist/forms-core/autocomplete.d.ts +37 -0
  65. package/dist/forms-core/autocomplete.d.ts.map +1 -0
  66. package/dist/forms-core/autocomplete.js +82 -0
  67. package/dist/forms-core/autocomplete.js.map +1 -0
  68. package/dist/forms-core/business-function.d.ts +61 -0
  69. package/dist/forms-core/business-function.d.ts.map +1 -0
  70. package/dist/forms-core/business-function.js +149 -0
  71. package/dist/forms-core/business-function.js.map +1 -0
  72. package/dist/forms-core/calculation.d.ts +72 -0
  73. package/dist/forms-core/calculation.d.ts.map +1 -0
  74. package/dist/forms-core/calculation.js +103 -0
  75. package/dist/forms-core/calculation.js.map +1 -0
  76. package/dist/forms-core/controller.d.ts +249 -0
  77. package/dist/forms-core/controller.d.ts.map +1 -0
  78. package/dist/forms-core/controller.js +590 -0
  79. package/dist/forms-core/controller.js.map +1 -0
  80. package/dist/forms-core/elements.d.ts +44 -0
  81. package/dist/forms-core/elements.d.ts.map +1 -0
  82. package/dist/forms-core/elements.js +68 -0
  83. package/dist/forms-core/elements.js.map +1 -0
  84. package/dist/forms-core/email-domains.d.ts +10 -0
  85. package/dist/forms-core/email-domains.d.ts.map +1 -0
  86. package/dist/forms-core/email-domains.js +69 -0
  87. package/dist/forms-core/email-domains.js.map +1 -0
  88. package/dist/forms-core/file-rules.d.ts +51 -0
  89. package/dist/forms-core/file-rules.d.ts.map +1 -0
  90. package/dist/forms-core/file-rules.js +105 -0
  91. package/dist/forms-core/file-rules.js.map +1 -0
  92. package/dist/forms-core/formula.d.ts +114 -0
  93. package/dist/forms-core/formula.d.ts.map +1 -0
  94. package/dist/forms-core/formula.js +1170 -0
  95. package/dist/forms-core/formula.js.map +1 -0
  96. package/dist/forms-core/index.d.ts +24 -0
  97. package/dist/forms-core/index.d.ts.map +1 -0
  98. package/dist/forms-core/index.js +28 -0
  99. package/dist/forms-core/index.js.map +1 -0
  100. package/dist/forms-core/units.d.ts +49 -0
  101. package/dist/forms-core/units.d.ts.map +1 -0
  102. package/dist/forms-core/units.js +255 -0
  103. package/dist/forms-core/units.js.map +1 -0
  104. package/dist/forms-core/us-states.d.ts +14 -0
  105. package/dist/forms-core/us-states.d.ts.map +1 -0
  106. package/dist/forms-core/us-states.js +66 -0
  107. package/dist/forms-core/us-states.js.map +1 -0
  108. package/dist/forms-core/validation.d.ts +266 -0
  109. package/dist/forms-core/validation.d.ts.map +1 -0
  110. package/dist/forms-core/validation.js +850 -0
  111. package/dist/forms-core/validation.js.map +1 -0
  112. package/dist/forms.d.ts +19 -0
  113. package/dist/forms.d.ts.map +1 -0
  114. package/dist/forms.js +29 -0
  115. package/dist/forms.js.map +1 -0
  116. package/dist/gallery-types.d.ts +32 -0
  117. package/dist/gallery-types.d.ts.map +1 -0
  118. package/dist/gallery-types.js +2 -0
  119. package/dist/gallery-types.js.map +1 -0
  120. package/dist/graphql.d.ts +97 -0
  121. package/dist/graphql.d.ts.map +1 -0
  122. package/dist/graphql.js +212 -0
  123. package/dist/graphql.js.map +1 -0
  124. package/dist/index.cjs +19448 -0
  125. package/dist/index.d.cts +19 -0
  126. package/dist/index.d.mts +19 -0
  127. package/dist/index.d.ts +19 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +20 -0
  130. package/dist/index.js.map +1 -0
  131. package/dist/marketing-schema.d.ts +216 -0
  132. package/dist/marketing-schema.d.ts.map +1 -0
  133. package/dist/marketing-schema.js +200 -0
  134. package/dist/marketing-schema.js.map +1 -0
  135. package/dist/mcp.d.ts +42 -0
  136. package/dist/mcp.d.ts.map +1 -0
  137. package/dist/mcp.js +87 -0
  138. package/dist/mcp.js.map +1 -0
  139. package/dist/native.d.ts +45 -0
  140. package/dist/native.d.ts.map +1 -0
  141. package/dist/native.js +43 -0
  142. package/dist/native.js.map +1 -0
  143. package/dist/next-revalidate.d.ts +43 -0
  144. package/dist/next-revalidate.d.ts.map +1 -0
  145. package/dist/next-revalidate.js +84 -0
  146. package/dist/next-revalidate.js.map +1 -0
  147. package/dist/parallel-pages.d.ts +102 -0
  148. package/dist/parallel-pages.d.ts.map +1 -0
  149. package/dist/parallel-pages.js +108 -0
  150. package/dist/parallel-pages.js.map +1 -0
  151. package/dist/payment-icons.d.ts +66 -0
  152. package/dist/payment-icons.d.ts.map +1 -0
  153. package/dist/payment-icons.js +160 -0
  154. package/dist/payment-icons.js.map +1 -0
  155. package/dist/proxy.d.ts +67 -0
  156. package/dist/proxy.d.ts.map +1 -0
  157. package/dist/proxy.js +97 -0
  158. package/dist/proxy.js.map +1 -0
  159. package/dist/react-analytics.d.ts +39 -0
  160. package/dist/react-analytics.d.ts.map +1 -0
  161. package/dist/react-analytics.js +59 -0
  162. package/dist/react-analytics.js.map +1 -0
  163. package/dist/react-attribution.d.ts +99 -0
  164. package/dist/react-attribution.d.ts.map +1 -0
  165. package/dist/react-attribution.js +52 -0
  166. package/dist/react-attribution.js.map +1 -0
  167. package/dist/react-marketing.d.ts +181 -0
  168. package/dist/react-marketing.d.ts.map +1 -0
  169. package/dist/react-marketing.js +215 -0
  170. package/dist/react-marketing.js.map +1 -0
  171. package/dist/react.d.ts +727 -0
  172. package/dist/react.d.ts.map +1 -0
  173. package/dist/react.js +2418 -0
  174. package/dist/react.js.map +1 -0
  175. package/dist/reserved-paths.d.ts +24 -0
  176. package/dist/reserved-paths.d.ts.map +1 -0
  177. package/dist/reserved-paths.js +60 -0
  178. package/dist/reserved-paths.js.map +1 -0
  179. package/dist/revalidate-verify.d.ts +30 -0
  180. package/dist/revalidate-verify.d.ts.map +1 -0
  181. package/dist/revalidate-verify.js +74 -0
  182. package/dist/revalidate-verify.js.map +1 -0
  183. package/dist/sdk.d.ts +678 -0
  184. package/dist/sdk.d.ts.map +1 -0
  185. package/dist/sdk.js +801 -0
  186. package/dist/sdk.js.map +1 -0
  187. package/dist/seo.d.ts +218 -0
  188. package/dist/seo.d.ts.map +1 -0
  189. package/dist/seo.js +309 -0
  190. package/dist/seo.js.map +1 -0
  191. package/dist/socials.d.ts +47 -0
  192. package/dist/socials.d.ts.map +1 -0
  193. package/dist/socials.js +172 -0
  194. package/dist/socials.js.map +1 -0
  195. package/dist/static.d.ts +14 -0
  196. package/dist/static.d.ts.map +1 -0
  197. package/dist/static.js +14 -0
  198. package/dist/static.js.map +1 -0
  199. package/dist/url-pattern.d.ts +47 -0
  200. package/dist/url-pattern.d.ts.map +1 -0
  201. package/dist/url-pattern.js +124 -0
  202. package/dist/url-pattern.js.map +1 -0
  203. package/dist/vanilla.d.ts +77 -0
  204. package/dist/vanilla.d.ts.map +1 -0
  205. package/dist/vanilla.js +903 -0
  206. package/dist/vanilla.js.map +1 -0
  207. package/openapi.json +13779 -0
  208. package/package.json +204 -0
  209. package/src/adapters-revalidate.ts +88 -0
  210. package/src/alpha.ts +89 -0
  211. package/src/analytics-core.ts +175 -0
  212. package/src/analytics-server.ts +99 -0
  213. package/src/angular/biab-form.component.ts +1443 -0
  214. package/src/angular/index.ts +26 -0
  215. package/src/attribution-logo.ts +5 -0
  216. package/src/auth-handler.ts +352 -0
  217. package/src/cli.ts +2517 -0
  218. package/src/client.ts +3578 -0
  219. package/src/collections.ts +101 -0
  220. package/src/contracts.ts +3390 -0
  221. package/src/data-model-schema.ts +4042 -0
  222. package/src/distribution.ts +73 -0
  223. package/src/element.ts +321 -0
  224. package/src/enums.ts +65 -0
  225. package/src/errors.ts +338 -0
  226. package/src/forms-core/autocomplete.ts +97 -0
  227. package/src/forms-core/business-function.ts +209 -0
  228. package/src/forms-core/calculation.ts +167 -0
  229. package/src/forms-core/controller.ts +922 -0
  230. package/src/forms-core/elements.ts +86 -0
  231. package/src/forms-core/email-domains.ts +68 -0
  232. package/src/forms-core/file-rules.ts +123 -0
  233. package/src/forms-core/formula.ts +1339 -0
  234. package/src/forms-core/index.ts +122 -0
  235. package/src/forms-core/units.ts +308 -0
  236. package/src/forms-core/us-states.ts +68 -0
  237. package/src/forms-core/validation.ts +1037 -0
  238. package/src/forms.ts +63 -0
  239. package/src/gallery-types.ts +31 -0
  240. package/src/graphql.ts +295 -0
  241. package/src/index.ts +505 -0
  242. package/src/marketing-schema.ts +395 -0
  243. package/src/mcp.ts +108 -0
  244. package/src/native.ts +73 -0
  245. package/src/next-revalidate.ts +124 -0
  246. package/src/parallel-pages.ts +192 -0
  247. package/src/payment-icons.ts +205 -0
  248. package/src/proxy.ts +173 -0
  249. package/src/qwik.tsx +1234 -0
  250. package/src/react-analytics.tsx +76 -0
  251. package/src/react-attribution.tsx +192 -0
  252. package/src/react-marketing.tsx +338 -0
  253. package/src/react.tsx +5495 -0
  254. package/src/reserved-paths.ts +65 -0
  255. package/src/revalidate-verify.ts +98 -0
  256. package/src/sdk.ts +1671 -0
  257. package/src/seo.ts +488 -0
  258. package/src/socials.ts +230 -0
  259. package/src/solid.tsx +1466 -0
  260. package/src/static.ts +21 -0
  261. package/src/styles/biab-forms.css +484 -0
  262. package/src/svelte/BiabForm.svelte +1069 -0
  263. package/src/svelte/context.ts +57 -0
  264. package/src/svelte/index.ts +79 -0
  265. package/src/url-pattern.ts +153 -0
  266. package/src/vanilla.ts +1290 -0
  267. package/src/vue/BiabForm.vue +1538 -0
  268. package/src/vue/index.ts +119 -0
package/src/forms.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `@businessdash/sdk/forms` — public entry for the headless `<BiabForm>` core.
3
+ *
4
+ * This is the compiled (tsc → dist) entry the framework bindings depend on. It
5
+ * re-exports the framework-agnostic controller + validation primitives from
6
+ * `forms-core`. Bindings shipped as SOURCE (Vue / Svelte / Solid / Qwik /
7
+ * Angular) import from `@businessdash/sdk/forms`; bindings compiled to dist (React /
8
+ * web-component / vanilla) do the same.
9
+ */
10
+
11
+ export * from "./forms-core/index.js";
12
+
13
+ // ── Form Schema Design Layer ─────────────────────────────────────────
14
+ //
15
+ // These exports let you define the *structure* of a form (layout / steps /
16
+ // conditional groups) that is compiled by createFormSchemaOrchestrator()
17
+ // alongside your collections. They live here because platform forms are
18
+ // assembled from both schema layout + runtime controller.
19
+ //
20
+ // ```ts
21
+ // import { layout, createFormSchemaOrchestrator } from "@businessdash/sdk/forms";
22
+ // ```
23
+
24
+ export {
25
+ type BoundaryMetadata,
26
+ type CollectionRefNode,
27
+ type ConditionalBlockNode,
28
+ createFormSchemaOrchestrator,
29
+ createSingleResourceForm,
30
+ type EvaluationRule,
31
+ type FormAndSchemaConfig,
32
+ type FormNode,
33
+ type FormOrchestrationOptions,
34
+ type FormStepNode,
35
+ type InferCollectionValues,
36
+ type InferFieldsValues,
37
+ type InferFieldValue,
38
+ type InferFormValues,
39
+ type InferSchemaValues,
40
+ type LayoutNode,
41
+ type LogicalOperator,
42
+ layout,
43
+ layoutContainers,
44
+ type MultiPopulatorConfig,
45
+ type MultiPopulatorNode,
46
+ type MultiPopulatorOutput,
47
+ type OptionGroup,
48
+ type OrConditionNode,
49
+ type ProgressStyle,
50
+ type RuleMatchOperator,
51
+ SchemaBoundary,
52
+ type SchemaBoundaryNode,
53
+ type SingleResourceFormOptions,
54
+ type SingleResourceForms,
55
+ } from "./data-model-schema.js";
56
+
57
+ /**
58
+ * Slug of the platform-provided default contact form ("General Inquiry"). Any
59
+ * org that hasn't authored its own `general-inquiry` form falls back to this
60
+ * platform default, so a starter renders a working contact form with zero
61
+ * dashboard setup. Swap in your own org's form slug once you've built one.
62
+ */
63
+ export const BIAB_DEFAULT_FORM_SLUG = "general-inquiry";
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Shape of one gallery item returned by `GET /sites/{siteId}/gallery`.
3
+ *
4
+ * Mirrors the platform-side `BundleGalleryItem` so the SDK can offer
5
+ * typed field selection (`fields: ["src", "title"]` narrows the
6
+ * return type via const generics in `sdk.ts`).
7
+ *
8
+ * Kept as a flat type — no SDK dependency on zod or runtime
9
+ * validation here, since `createBiabClient` trusts the platform
10
+ * response (the platform owns the schema; the SDK is a typed
11
+ * facade).
12
+ */
13
+ export interface BundleGalleryItem {
14
+ id: string;
15
+ type: "image" | "video";
16
+ src: string;
17
+ width: number | null;
18
+ height: number | null;
19
+ blurDataURL: string | null;
20
+ alt: string | null;
21
+ title: string | null;
22
+ category: string | null;
23
+ description: string | null;
24
+ jobId: string | null;
25
+ jobName: string | null;
26
+ takenAt: string | null;
27
+ location: string | null;
28
+ system: string | null;
29
+ year: number | null;
30
+ aboutHtml: string | null;
31
+ }
package/src/graphql.ts ADDED
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Generate a GraphQL SDL schema from a BIAB data model.
3
+ *
4
+ * "GraphQL layer compatibility" for a data-model-as-code platform starts here:
5
+ * a real, typed GraphQL schema of the org's custom database that any GraphQL
6
+ * codegen / tooling can consume, and that an AI agent can read alongside a
7
+ * `export-data-model` snapshot — the schema of the data next to the data.
8
+ *
9
+ * Pure and dependency-free: string generation over the live-model artifact
10
+ * (the same JSON `view-data-model --json` returns), exactly how
11
+ * `view-data-model --out` renders the TS config client-side. It does NOT stand
12
+ * up a GraphQL server — execution (resolvers over this schema) is a separate,
13
+ * heavier layer that needs a runtime.
14
+ *
15
+ * Shape decisions:
16
+ * - Every relation is a LIST. BIAB relations are link rows (`sync-records`
17
+ * converges an array of target ids), and the records API always returns
18
+ * `relations` as arrays — so `[Target!]!` is faithful, one-to-one included.
19
+ * - SELECT/MULTI_SELECT become a GraphQL enum WHEN every option value is a
20
+ * legal, unique enum name; otherwise a `String` with the allowed values in
21
+ * a comment. Keeps the SDL valid against arbitrary option text.
22
+ * - CURRENCY is `Int` — money is integer cents platform-wide.
23
+ */
24
+
25
+ export type GraphqlSdlOption = { value: string; label?: string };
26
+
27
+ export type GraphqlSdlField = {
28
+ name: string;
29
+ type: string;
30
+ label?: string;
31
+ description?: string;
32
+ /** Absent OR true ⇒ nullable; only an explicit `false` marks a required field. */
33
+ isNullable?: boolean;
34
+ options?: GraphqlSdlOption[];
35
+ /** `universalIdentifier` of the target object, for RELATION/MORPH_RELATION. */
36
+ relationTarget?: string;
37
+ };
38
+
39
+ export type GraphqlSdlObject = {
40
+ universalIdentifier: string;
41
+ kind?: string;
42
+ nameSingular: string;
43
+ namePlural: string;
44
+ labelSingular?: string;
45
+ labelPlural?: string;
46
+ description?: string;
47
+ fields: GraphqlSdlField[];
48
+ };
49
+
50
+ export type GraphqlSdlModel = { objects: GraphqlSdlObject[] };
51
+
52
+ const GRAPHQL_NAME = /^[_A-Za-z][_0-9A-Za-z]*$/;
53
+
54
+ function pascal(input: string): string {
55
+ const parts = input.split(/[^0-9A-Za-z]+/).filter(Boolean);
56
+ const joined = parts
57
+ .map((p) => p.charAt(0).toUpperCase() + p.slice(1))
58
+ .join("");
59
+ return /^[A-Za-z_]/.test(joined) ? joined : `T${joined}`;
60
+ }
61
+
62
+ function camel(input: string): string {
63
+ const p = pascal(input);
64
+ return p.charAt(0).toLowerCase() + p.slice(1);
65
+ }
66
+
67
+ /** GraphQL descriptions are triple-quoted strings; a stray `"""` would break
68
+ * the block, so collapse them. */
69
+ function describe(text: string | undefined, indent: string): string {
70
+ if (!text) return "";
71
+ const clean = text.replace(/"""/g, '"').replace(/\r?\n/g, " ").trim();
72
+ return clean ? `${indent}"""${clean}"""\n` : "";
73
+ }
74
+
75
+ type EnumPlan = { name: string; values: string[] } | null;
76
+
77
+ /** An enum only if every option value is already a legal, unique GraphQL enum
78
+ * member — otherwise the field falls back to String and stays valid. */
79
+ function planEnum(typeName: string, field: GraphqlSdlField): EnumPlan {
80
+ const values = (field.options ?? []).map((o) => o.value);
81
+ if (values.length === 0) return null;
82
+ const seen = new Set<string>();
83
+ for (const v of values) {
84
+ if (!GRAPHQL_NAME.test(v) || seen.has(v)) return null;
85
+ seen.add(v);
86
+ }
87
+ return { name: `${typeName}${pascal(field.name)}`, values };
88
+ }
89
+
90
+ function scalarFor(field: GraphqlSdlField): string {
91
+ switch (field.type) {
92
+ case "NUMBER":
93
+ return "Float";
94
+ case "RATING":
95
+ return "Int";
96
+ case "CURRENCY":
97
+ return "Int";
98
+ case "BOOLEAN":
99
+ return "Boolean";
100
+ case "DATE_TIME":
101
+ return "DateTime";
102
+ default:
103
+ // TEXT, UUID, RICH_TEXT, EMAILS, PHONES, DATE, and anything unmapped.
104
+ return "String";
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Render the model as a GraphQL SDL document (string).
110
+ *
111
+ * By default only `kind === "custom"` objects — the custom collections — are
112
+ * emitted; standard CRM objects are the platform's, not the org's schema. Pass
113
+ * `includeStandard` to emit them too.
114
+ */
115
+ export function dataModelToGraphqlSdl(
116
+ model: GraphqlSdlModel,
117
+ opts: { includeStandard?: boolean; header?: string } = {},
118
+ ): string {
119
+ const all = model.objects ?? [];
120
+ const objects = opts.includeStandard
121
+ ? all
122
+ : all.filter((o) => o.kind !== "standard");
123
+
124
+ // Target-type resolution spans the WHOLE model — a custom object can relate
125
+ // to a standard one even when standard types aren't emitted.
126
+ const typeNameByUid = new Map(
127
+ all.map((o) => [o.universalIdentifier, pascal(o.nameSingular)]),
128
+ );
129
+
130
+ const enums: string[] = [];
131
+ const types: string[] = [];
132
+ const connections: string[] = [];
133
+ const queryFields: string[] = [];
134
+
135
+ for (const obj of objects) {
136
+ const typeName = pascal(obj.nameSingular);
137
+ const lines: string[] = [];
138
+ lines.push(describe(obj.description ?? obj.labelSingular, ""));
139
+ lines.push(`type ${typeName} {`);
140
+ lines.push("\tid: ID!");
141
+ lines.push("\tuniversalIdentifier: ID");
142
+
143
+ for (const field of obj.fields) {
144
+ if (field.name === "id" || field.name === "universalIdentifier") continue;
145
+ const fieldName = camel(field.name);
146
+ if (!GRAPHQL_NAME.test(fieldName)) continue;
147
+ const required = field.isNullable === false;
148
+
149
+ let gqlType: string;
150
+ if (field.type === "RELATION" || field.type === "MORPH_RELATION") {
151
+ const target = field.relationTarget
152
+ ? typeNameByUid.get(field.relationTarget)
153
+ : undefined;
154
+ // A relation always resolves to a list (link rows); a morph with no
155
+ // single target degrades to JSON rather than an invalid ref.
156
+ gqlType = target ? `[${target}!]!` : "JSON";
157
+ } else if (field.type === "SELECT" || field.type === "MULTI_SELECT") {
158
+ const plan = planEnum(typeName, field);
159
+ if (plan) {
160
+ enums.push(
161
+ `enum ${plan.name} {\n${plan.values.map((v) => `\t${v}`).join("\n")}\n}`,
162
+ );
163
+ gqlType =
164
+ field.type === "MULTI_SELECT" ? `[${plan.name}!]` : plan.name;
165
+ } else {
166
+ gqlType = field.type === "MULTI_SELECT" ? "[String!]" : "String";
167
+ }
168
+ } else {
169
+ gqlType = scalarFor(field);
170
+ }
171
+
172
+ if (required && !gqlType.endsWith("!")) gqlType = `${gqlType}!`;
173
+ lines.push(describe(field.description ?? field.label, "\t"));
174
+ lines.push(`\t${fieldName}: ${gqlType}`);
175
+ }
176
+
177
+ lines.push("\tcreatedAt: DateTime!");
178
+ lines.push("\tupdatedAt: DateTime!");
179
+ lines.push("}");
180
+ types.push(lines.filter(Boolean).join("\n"));
181
+
182
+ connections.push(
183
+ `type ${typeName}Connection {\n\tnodes: [${typeName}!]!\n\t"""Pass back to \`cursor\` for the next page; null on the last page."""\n\tnextCursor: String\n}`,
184
+ );
185
+ queryFields.push(
186
+ `\t${camel(obj.namePlural)}(limit: Int, cursor: String): ${typeName}Connection!`,
187
+ );
188
+ queryFields.push(`\t${camel(obj.nameSingular)}(id: ID!): ${typeName}`);
189
+ }
190
+
191
+ const header =
192
+ opts.header ??
193
+ "# GraphQL schema generated from your BIAB data model by `biab-dev export-graphql-schema`.\n# Regenerate on schema change; commit it for a git-diffable, AI-readable contract.";
194
+
195
+ const scalars =
196
+ "scalar DateTime\nscalar JSON\n\n# Cents; money is integer cents platform-wide.\n";
197
+
198
+ const query =
199
+ queryFields.length > 0
200
+ ? `type Query {\n${queryFields.join("\n")}\n}`
201
+ : "type Query {\n\t_empty: Boolean\n}";
202
+
203
+ return [
204
+ header,
205
+ scalars.trim(),
206
+ enums.join("\n\n"),
207
+ types.join("\n\n"),
208
+ connections.join("\n\n"),
209
+ query,
210
+ ]
211
+ .filter((s) => s.trim().length > 0)
212
+ .join("\n\n")
213
+ .concat("\n");
214
+ }
215
+
216
+ // ── Client ───────────────────────────────────────────────────────────
217
+
218
+ export type GraphqlResult<T = unknown> = {
219
+ data?: T;
220
+ errors?: Array<{
221
+ message: string;
222
+ locations?: Array<{ line: number; column: number }>;
223
+ path?: Array<string | number>;
224
+ }>;
225
+ };
226
+
227
+ export type GraphqlClient = {
228
+ query<T = unknown>(
229
+ query: string,
230
+ variables?: Record<string, unknown>,
231
+ operationName?: string,
232
+ ): Promise<GraphqlResult<T>>;
233
+ };
234
+
235
+ /**
236
+ * Minimal client for the executable GraphQL endpoint
237
+ * (`POST /sites/{siteId}/graphql`). Dependency-free — a typed fetch wrapper, so
238
+ * it drops into any of the SDK's framework starters without pulling in a
239
+ * GraphQL client library. Pair the returned shape with types generated from
240
+ * `export-graphql-schema` for end-to-end typing.
241
+ *
242
+ * Requires a SECRET key with the `graphql:query` scope (the endpoint is not
243
+ * browser-callable). Enterprise entitlement + rate limits are enforced
244
+ * server-side; a plan-gated caller gets `{ available: false }` rather than
245
+ * `data`/`errors`, so check for that if you gate the UI on it.
246
+ */
247
+ export function createGraphqlClient(input: {
248
+ /** Package API base, e.g. `https://biab.app/api/package/v1`. */
249
+ baseUrl: string;
250
+ siteId: string;
251
+ /** A secret (`biab_…`) key holding `graphql:query`. */
252
+ apiKey: string;
253
+ fetch?: typeof fetch;
254
+ }): GraphqlClient {
255
+ const doFetch = input.fetch ?? globalThis.fetch;
256
+ const base = input.baseUrl.replace(/\/+$/, "");
257
+ const url = `${base}/sites/${encodeURIComponent(input.siteId)}/graphql`;
258
+ return {
259
+ async query<T = unknown>(
260
+ query: string,
261
+ variables?: Record<string, unknown>,
262
+ operationName?: string,
263
+ ): Promise<GraphqlResult<T>> {
264
+ const res = await doFetch(url, {
265
+ method: "POST",
266
+ headers: {
267
+ "content-type": "application/json",
268
+ authorization: `Bearer ${input.apiKey}`,
269
+ },
270
+ body: JSON.stringify({
271
+ query,
272
+ ...(variables ? { variables } : {}),
273
+ ...(operationName ? { operationName } : {}),
274
+ }),
275
+ });
276
+ const text = await res.text();
277
+ let parsed: unknown = null;
278
+ try {
279
+ parsed = text ? JSON.parse(text) : null;
280
+ } catch {
281
+ parsed = null;
282
+ }
283
+ if (!res.ok) {
284
+ const message =
285
+ parsed &&
286
+ typeof parsed === "object" &&
287
+ typeof (parsed as { message?: unknown }).message === "string"
288
+ ? (parsed as { message: string }).message
289
+ : `HTTP ${res.status} ${res.statusText}`;
290
+ return { errors: [{ message }] } as GraphqlResult<T>;
291
+ }
292
+ return (parsed ?? {}) as GraphqlResult<T>;
293
+ },
294
+ };
295
+ }