@businessdash/sdk 0.9.60 → 0.9.80

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 (239) hide show
  1. package/README.md +991 -295
  2. package/assets/biab-sw.js +92 -0
  3. package/dist/account-core/index.d.ts +126 -0
  4. package/dist/account-core/index.d.ts.map +1 -0
  5. package/dist/account-core/index.js +191 -0
  6. package/dist/account-core/index.js.map +1 -0
  7. package/dist/account.d.ts +11 -0
  8. package/dist/account.d.ts.map +1 -0
  9. package/dist/account.js +11 -0
  10. package/dist/account.js.map +1 -0
  11. package/dist/auth-handler.d.ts +1 -1
  12. package/dist/auth-links.d.ts +33 -0
  13. package/dist/auth-links.d.ts.map +1 -0
  14. package/dist/auth-links.js +64 -0
  15. package/dist/auth-links.js.map +1 -0
  16. package/dist/blog-core/index.d.ts +112 -0
  17. package/dist/blog-core/index.d.ts.map +1 -0
  18. package/dist/blog-core/index.js +330 -0
  19. package/dist/blog-core/index.js.map +1 -0
  20. package/dist/blog.d.ts +11 -0
  21. package/dist/blog.d.ts.map +1 -0
  22. package/dist/blog.js +11 -0
  23. package/dist/blog.js.map +1 -0
  24. package/dist/chat-core/index.d.ts +97 -0
  25. package/dist/chat-core/index.d.ts.map +1 -0
  26. package/dist/chat-core/index.js +237 -0
  27. package/dist/chat-core/index.js.map +1 -0
  28. package/dist/chat.d.ts +11 -0
  29. package/dist/chat.d.ts.map +1 -0
  30. package/dist/chat.js +11 -0
  31. package/dist/chat.js.map +1 -0
  32. package/dist/checkout-core/index.d.ts +65 -0
  33. package/dist/checkout-core/index.d.ts.map +1 -0
  34. package/dist/checkout-core/index.js +159 -0
  35. package/dist/checkout-core/index.js.map +1 -0
  36. package/dist/checkout.d.ts +10 -0
  37. package/dist/checkout.d.ts.map +1 -0
  38. package/dist/checkout.js +10 -0
  39. package/dist/checkout.js.map +1 -0
  40. package/dist/cli.js +133 -25
  41. package/dist/cli.js.map +1 -1
  42. package/dist/client.d.ts +622 -1
  43. package/dist/client.d.ts.map +1 -1
  44. package/dist/client.js +888 -1
  45. package/dist/client.js.map +1 -1
  46. package/dist/consent-core/index.cjs +107 -0
  47. package/dist/consent-core/index.d.ts +103 -0
  48. package/dist/consent-core/index.d.ts.map +1 -0
  49. package/dist/consent-core/index.js +157 -0
  50. package/dist/consent-core/index.js.map +1 -0
  51. package/dist/contracts.d.ts +3526 -767
  52. package/dist/contracts.d.ts.map +1 -1
  53. package/dist/contracts.js +571 -4
  54. package/dist/contracts.js.map +1 -1
  55. package/dist/data-model-schema.d.ts +1 -1
  56. package/dist/data-model-schema.d.ts.map +1 -1
  57. package/dist/element.d.ts +30 -0
  58. package/dist/element.d.ts.map +1 -1
  59. package/dist/element.js +56 -0
  60. package/dist/element.js.map +1 -1
  61. package/dist/embed-protocol.d.ts +86 -0
  62. package/dist/embed-protocol.d.ts.map +1 -0
  63. package/dist/embed-protocol.js +60 -0
  64. package/dist/embed-protocol.js.map +1 -0
  65. package/dist/framework-support.d.ts +122 -0
  66. package/dist/framework-support.d.ts.map +1 -0
  67. package/dist/framework-support.js +323 -0
  68. package/dist/framework-support.js.map +1 -0
  69. package/dist/index.cjs +1355 -24
  70. package/dist/legal-core/index.cjs +115 -0
  71. package/dist/legal-core/index.d.ts +160 -0
  72. package/dist/legal-core/index.d.ts.map +1 -0
  73. package/dist/legal-core/index.js +220 -0
  74. package/dist/legal-core/index.js.map +1 -0
  75. package/dist/marketing-core/index.d.ts +66 -0
  76. package/dist/marketing-core/index.d.ts.map +1 -0
  77. package/dist/marketing-core/index.js +154 -0
  78. package/dist/marketing-core/index.js.map +1 -0
  79. package/dist/marketing.d.ts +7 -0
  80. package/dist/marketing.d.ts.map +1 -0
  81. package/dist/marketing.js +7 -0
  82. package/dist/marketing.js.map +1 -0
  83. package/dist/mcp-adapters/index.d.ts +135 -0
  84. package/dist/mcp-adapters/index.d.ts.map +1 -0
  85. package/dist/mcp-adapters/index.js +187 -0
  86. package/dist/mcp-adapters/index.js.map +1 -0
  87. package/dist/mcp-adapters.d.ts +7 -0
  88. package/dist/mcp-adapters.d.ts.map +1 -0
  89. package/dist/mcp-adapters.js +7 -0
  90. package/dist/mcp-adapters.js.map +1 -0
  91. package/dist/model3d-core/index.cjs +95 -0
  92. package/dist/model3d-core/index.d.ts +167 -0
  93. package/dist/model3d-core/index.d.ts.map +1 -0
  94. package/dist/model3d-core/index.js +158 -0
  95. package/dist/model3d-core/index.js.map +1 -0
  96. package/dist/notifications-core/index.cjs +315 -0
  97. package/dist/notifications-core/index.d.ts +229 -0
  98. package/dist/notifications-core/index.d.ts.map +1 -0
  99. package/dist/notifications-core/index.js +445 -0
  100. package/dist/notifications-core/index.js.map +1 -0
  101. package/dist/portal-core/index.d.ts +451 -0
  102. package/dist/portal-core/index.d.ts.map +1 -0
  103. package/dist/portal-core/index.js +819 -0
  104. package/dist/portal-core/index.js.map +1 -0
  105. package/dist/portal.d.ts +7 -0
  106. package/dist/portal.d.ts.map +1 -0
  107. package/dist/portal.js +7 -0
  108. package/dist/portal.js.map +1 -0
  109. package/dist/push-core/index.cjs +171 -0
  110. package/dist/push-core/index.d.ts +149 -0
  111. package/dist/push-core/index.d.ts.map +1 -0
  112. package/dist/push-core/index.js +231 -0
  113. package/dist/push-core/index.js.map +1 -0
  114. package/dist/react-marketing.d.ts +1 -17
  115. package/dist/react-marketing.d.ts.map +1 -1
  116. package/dist/react-marketing.js +4 -0
  117. package/dist/react-marketing.js.map +1 -1
  118. package/dist/react.d.ts +294 -101
  119. package/dist/react.d.ts.map +1 -1
  120. package/dist/react.js +302 -246
  121. package/dist/react.js.map +1 -1
  122. package/dist/seed-core/index.d.ts +354 -0
  123. package/dist/seed-core/index.d.ts.map +1 -0
  124. package/dist/seed-core/index.js +420 -0
  125. package/dist/seed-core/index.js.map +1 -0
  126. package/dist/seed.d.ts +7 -0
  127. package/dist/seed.d.ts.map +1 -0
  128. package/dist/seed.js +7 -0
  129. package/dist/seed.js.map +1 -0
  130. package/dist/seo-core/blog.d.ts +94 -0
  131. package/dist/seo-core/blog.d.ts.map +1 -0
  132. package/dist/seo-core/blog.js +149 -0
  133. package/dist/seo-core/blog.js.map +1 -0
  134. package/dist/seo-core/commerce.d.ts +64 -0
  135. package/dist/seo-core/commerce.d.ts.map +1 -0
  136. package/dist/seo-core/commerce.js +87 -0
  137. package/dist/seo-core/commerce.js.map +1 -0
  138. package/dist/seo-core/index.cjs +751 -0
  139. package/dist/seo-core/index.d.ts +226 -0
  140. package/dist/seo-core/index.d.ts.map +1 -0
  141. package/dist/seo-core/index.js +455 -0
  142. package/dist/seo-core/index.js.map +1 -0
  143. package/dist/seo-core/local.d.ts +181 -0
  144. package/dist/seo-core/local.d.ts.map +1 -0
  145. package/dist/seo-core/local.js +193 -0
  146. package/dist/seo-core/local.js.map +1 -0
  147. package/dist/sitemap-core/index.cjs +167 -0
  148. package/dist/sitemap-core/index.d.ts +155 -0
  149. package/dist/sitemap-core/index.d.ts.map +1 -0
  150. package/dist/sitemap-core/index.js +195 -0
  151. package/dist/sitemap-core/index.js.map +1 -0
  152. package/dist/social-core/index.d.ts +74 -0
  153. package/dist/social-core/index.d.ts.map +1 -0
  154. package/dist/social-core/index.js +72 -0
  155. package/dist/social-core/index.js.map +1 -0
  156. package/dist/social.d.ts +7 -0
  157. package/dist/social.d.ts.map +1 -0
  158. package/dist/social.js +7 -0
  159. package/dist/social.js.map +1 -0
  160. package/dist/solid-compat.d.ts +204 -0
  161. package/dist/solid-compat.d.ts.map +1 -0
  162. package/dist/solid-compat.js +221 -0
  163. package/dist/solid-compat.js.map +1 -0
  164. package/dist/store-core/index.d.ts +141 -0
  165. package/dist/store-core/index.d.ts.map +1 -0
  166. package/dist/store-core/index.js +277 -0
  167. package/dist/store-core/index.js.map +1 -0
  168. package/dist/store.d.ts +12 -0
  169. package/dist/store.d.ts.map +1 -0
  170. package/dist/store.js +12 -0
  171. package/dist/store.js.map +1 -0
  172. package/dist/vanilla.d.ts +111 -0
  173. package/dist/vanilla.d.ts.map +1 -1
  174. package/dist/vanilla.js +206 -0
  175. package/dist/vanilla.js.map +1 -1
  176. package/dist/verify-core/index.d.ts +98 -0
  177. package/dist/verify-core/index.d.ts.map +1 -0
  178. package/dist/verify-core/index.js +186 -0
  179. package/dist/verify-core/index.js.map +1 -0
  180. package/dist/verify.d.ts +8 -0
  181. package/dist/verify.d.ts.map +1 -0
  182. package/dist/verify.js +8 -0
  183. package/dist/verify.js.map +1 -0
  184. package/openapi.json +4516 -1311
  185. package/package.json +128 -11
  186. package/src/account-core/index.ts +309 -0
  187. package/src/account.ts +11 -0
  188. package/src/angular/headless.ts +519 -0
  189. package/src/angular/index.ts +3 -0
  190. package/src/auth-links.ts +76 -0
  191. package/src/blog-core/index.ts +423 -0
  192. package/src/blog.ts +11 -0
  193. package/src/chat-core/index.ts +335 -0
  194. package/src/chat.ts +11 -0
  195. package/src/checkout-core/index.ts +225 -0
  196. package/src/checkout.ts +10 -0
  197. package/src/cli.ts +184 -28
  198. package/src/client.ts +1097 -0
  199. package/src/consent-core/index.ts +213 -0
  200. package/src/contracts.ts +768 -11
  201. package/src/data-model-schema.ts +1 -1
  202. package/src/element.ts +102 -0
  203. package/src/embed-protocol.ts +100 -0
  204. package/src/framework-support.ts +440 -0
  205. package/src/legal-core/index.ts +276 -0
  206. package/src/marketing-core/index.ts +209 -0
  207. package/src/marketing.ts +7 -0
  208. package/src/mcp-adapters/index.ts +264 -0
  209. package/src/mcp-adapters.ts +7 -0
  210. package/src/model3d-core/index.ts +248 -0
  211. package/src/notifications-core/index.ts +650 -0
  212. package/src/portal-core/index.ts +1564 -0
  213. package/src/portal.ts +7 -0
  214. package/src/push-core/index.ts +335 -0
  215. package/src/qwik.tsx +602 -2
  216. package/src/react-marketing.tsx +5 -0
  217. package/src/react.tsx +434 -319
  218. package/src/seed-core/index.ts +735 -0
  219. package/src/seed.ts +7 -0
  220. package/src/seo-core/blog.ts +216 -0
  221. package/src/seo-core/commerce.ts +138 -0
  222. package/src/seo-core/index.ts +605 -0
  223. package/src/seo-core/local.ts +333 -0
  224. package/src/sitemap-core/index.ts +296 -0
  225. package/src/social-core/index.ts +103 -0
  226. package/src/social.ts +7 -0
  227. package/src/solid-compat.tsx +347 -0
  228. package/src/solid-surfaces.tsx +1311 -0
  229. package/src/solid.tsx +82 -60
  230. package/src/store-core/index.ts +426 -0
  231. package/src/store.ts +12 -0
  232. package/src/svelte/SocialLinks.svelte +84 -0
  233. package/src/svelte/headless.ts +435 -0
  234. package/src/svelte/index.ts +9 -0
  235. package/src/vanilla.ts +367 -0
  236. package/src/verify-core/index.ts +288 -0
  237. package/src/verify.ts +8 -0
  238. package/src/vue/headless.ts +527 -0
  239. package/src/vue/index.ts +3 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * `@businessdash/sdk/social` — everything needed to render social links,
3
+ * without any framework.
4
+ *
5
+ * Social links were React-and-Solid-only, and unlike every other gap this one
6
+ * was never about logic: `resolveSocialProfiles` already gave all eight
7
+ * bindings the data. What was missing was MARKUP, which cannot be shared
8
+ * between frameworks the way a controller can.
9
+ *
10
+ * So this module shares everything up to the markup — the resolved rows, the
11
+ * icon URL, and the handful of layout styles the list needs to work at all —
12
+ * and each binding writes the six elements around it. That keeps the part that
13
+ * can drift (which platforms exist, how a handle becomes a URL, where the icon
14
+ * comes from) in one place, and leaves each framework only the part that
15
+ * genuinely differs.
16
+ *
17
+ * @module
18
+ */
19
+
20
+ import { resolveSocialProfiles } from "../socials.js";
21
+
22
+ export type SocialLinkRow = {
23
+ /** Stable platform key — usable as a list key. */
24
+ key: string;
25
+ /** Human label, e.g. "Instagram". Used for `aria-label` and `title`. */
26
+ label: string;
27
+ /** Fully-qualified profile URL. */
28
+ url: string;
29
+ /**
30
+ * Icon URL on the simple-icons CDN, or `null` for a platform with no brand
31
+ * icon — render the generic globe glyph in that case.
32
+ */
33
+ iconUrl: string | null;
34
+ };
35
+
36
+ export type SocialLinksOptions = {
37
+ /**
38
+ * Icon colour for branded glyphs — a hex WITHOUT `#`, or a CSS colour
39
+ * keyword. Omit to use each brand's official colour.
40
+ */
41
+ color?: string | undefined;
42
+ };
43
+
44
+ /**
45
+ * Resolve a company's `socials` record into render-ready rows.
46
+ *
47
+ * The icon URL is built here rather than in each binding because it encodes a
48
+ * CDN path and a colour convention — two things that should change in one
49
+ * place, not eight.
50
+ */
51
+ export function resolveSocialLinks(
52
+ socials: unknown,
53
+ options: SocialLinksOptions = {},
54
+ ): SocialLinkRow[] {
55
+ return resolveSocialProfiles(socials).map((row) => ({
56
+ key: row.key,
57
+ label: row.label,
58
+ url: row.url,
59
+ iconUrl: row.iconSlug
60
+ ? options.color
61
+ ? `https://cdn.simpleicons.org/${row.iconSlug}/${encodeURIComponent(options.color)}`
62
+ : `https://cdn.simpleicons.org/${row.iconSlug}`
63
+ : null,
64
+ }));
65
+ }
66
+
67
+ /**
68
+ * The inline styles the list needs to lay out at all.
69
+ *
70
+ * Deliberately minimal — flex, wrap, gap, and resetting the list defaults. No
71
+ * colour, no borders, no sizing beyond the gap: everything visual is left to
72
+ * the consumer's own CSS via the class props each binding accepts. A component
73
+ * that ships opinions here is one the consumer has to fight.
74
+ */
75
+ export const SOCIAL_LIST_STYLE: Readonly<Record<string, string>> = {
76
+ display: "flex",
77
+ "flex-wrap": "wrap",
78
+ "align-items": "center",
79
+ gap: "0.75rem",
80
+ "list-style": "none",
81
+ margin: "0",
82
+ padding: "0",
83
+ };
84
+
85
+ /** The same, as a `style="…"` string for bindings that take one. */
86
+ export const SOCIAL_LIST_STYLE_STRING: string = Object.entries(
87
+ SOCIAL_LIST_STYLE,
88
+ )
89
+ .map(([k, v]) => `${k}:${v}`)
90
+ .join(";");
91
+
92
+ /**
93
+ * The fallback glyph for a platform with no brand icon, as an SVG path set.
94
+ *
95
+ * Shared so a consumer sees the same generic mark whichever framework they
96
+ * chose, rather than eight subtly different circles.
97
+ */
98
+ export const SOCIAL_FALLBACK_GLYPH = {
99
+ viewBox: "0 0 24 24",
100
+ strokeWidth: 1.8,
101
+ circle: { cx: 12, cy: 12, r: 9 },
102
+ path: "M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18",
103
+ } as const;
package/src/social.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `@businessdash/sdk/social` — framework-free social-link resolution.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ export * from "./social-core/index.js";
@@ -0,0 +1,347 @@
1
+ /**
2
+ * The slice of Solid this SDK uses, written so one binding works on **both**
3
+ * Solid 1 and Solid 2.
4
+ *
5
+ * ## Why this exists
6
+ *
7
+ * Solid 2 reorganised its public API. Measured against the published packages
8
+ * rather than assumed:
9
+ *
10
+ * | symbol | Solid 1 | Solid 2 |
11
+ * |---|---|---|
12
+ * | `createSignal` `createMemo` `createEffect` | `solid-js` | `solid-js` |
13
+ * | `createContext` `useContext` `onCleanup` | `solid-js` | `solid-js` |
14
+ * | `mapArray` `children` `untrack` | `solid-js` | `solid-js` |
15
+ * | `For` `Show` `Switch` `Match` | `solid-js` | **`@solidjs/web`** |
16
+ * | `onMount` `createUniqueId` `from` | `solid-js` | **gone** |
17
+ * | `Accessor` `JSX` (types) | `solid-js` | **gone** |
18
+ *
19
+ * A static import cannot branch on which version is installed, and the build
20
+ * here is plain `tsc` with no aliasing step — so the two-entry-point trick is
21
+ * not available either. What IS available is the intersection: every primitive
22
+ * in the first three rows exists, unchanged, in both versions.
23
+ *
24
+ * So this module rebuilds the missing pieces on top of that intersection. The
25
+ * binding then imports control flow from here instead of from `solid-js`, and
26
+ * the same compiled output runs on either version.
27
+ *
28
+ * ## Why not just use plain JSX instead
29
+ *
30
+ * `{items().map(...)}` would have been less code. It also throws away `For`'s
31
+ * keyed reconciliation — Solid re-creates every row when the array identity
32
+ * changes, instead of moving the DOM nodes it already has. That is a real
33
+ * performance characteristic of a form renderer with 200 fields, not sugar, so
34
+ * `For` here is built over `mapArray`, which is the same primitive Solid's own
35
+ * `For` uses and which exists in both versions.
36
+ *
37
+ * @module
38
+ */
39
+
40
+ import {
41
+ children,
42
+ createMemo,
43
+ createSignal,
44
+ mapArray,
45
+ onCleanup,
46
+ untrack,
47
+ } from "solid-js";
48
+
49
+ /**
50
+ * A reactive getter. `Accessor` in Solid 1; nothing in Solid 2, where it is
51
+ * spelled as the plain function type it always was.
52
+ */
53
+ export type Accessor<T> = () => T;
54
+
55
+ /**
56
+ * Anything Solid will render.
57
+ *
58
+ * Deliberately not `JSX.Element`: that type lives in `solid-js` on 1 and in
59
+ * `@solidjs/web` on 2, so naming it would reintroduce the split this module
60
+ * exists to remove. Written structurally instead, as the union both versions
61
+ * accept — measured from their `jsx.d.ts`:
62
+ *
63
+ * Solid 1: `Node | ArrayElement | (string & {}) | number | boolean | null | undefined`
64
+ * Solid 2: `SolidElement | Node | ArrayElement`
65
+ *
66
+ * The function arm matters: every component here returns a `createMemo`, and a
67
+ * memo is a function. Solid renders it lazily, which is what keeps unselected
68
+ * `Switch` branches from evaluating.
69
+ */
70
+ export type SolidNode = unknown;
71
+
72
+ /**
73
+ * Hand a `SolidNode` back to JSX.
74
+ *
75
+ * One cast, in one place, and it is unavoidable rather than lazy: the two
76
+ * versions disagree about what `JSX.Element` IS — Solid 2 adds a
77
+ * `RenderedElement` arm Solid 1 has never heard of — so no single named type
78
+ * is assignable in both directions. Accepting children permissively and
79
+ * narrowing once at the boundary is what Solid's own components do, and it
80
+ * keeps the cast auditable instead of scattering `as` through the render tree.
81
+ */
82
+ export function renderNode(node: SolidNode): Node {
83
+ return node as Node;
84
+ }
85
+
86
+ /**
87
+ * What these components RETURN.
88
+ *
89
+ * `Node` rather than `SolidNode`, because TypeScript checks a component's
90
+ * return type against the installed version's `JSX.Element`, and `Node` is the
91
+ * only arm those two unions share:
92
+ *
93
+ * Solid 1: `Node | ArrayElement | (string & {}) | number | boolean | null | undefined`
94
+ * Solid 2: `SolidElement | Node | ArrayElement`
95
+ *
96
+ * The values really returned are memos — functions — which Solid renders
97
+ * lazily. Solid's own `For` does the same thing and casts for the same reason;
98
+ * the cast is at the JSX boundary only, and every value flowing THROUGH these
99
+ * components stays typed as `SolidNode`.
100
+ */
101
+
102
+ /**
103
+ * An inline style object.
104
+ *
105
+ * `JSX.CSSProperties` lives in `solid-js` on Solid 1 and `@solidjs/web` on
106
+ * Solid 2, so naming it would reintroduce the split. Solid accepts a plain
107
+ * record of CSS property names to values, which is all these are.
108
+ */
109
+ export type SolidStyle = Record<string, string | number | undefined>;
110
+
111
+ /**
112
+ * Keyed list rendering.
113
+ *
114
+ * `mapArray` is the primitive under Solid's own `For` and is exported by both
115
+ * versions, so this keeps the reconciliation behaviour rather than degrading
116
+ * to a re-render on every array identity change.
117
+ */
118
+ export function For<T>(props: {
119
+ each: readonly T[] | undefined | null;
120
+ children: (item: T, index: Accessor<number>) => SolidNode;
121
+ }): Node {
122
+ // `props.each` is read inside the memo so the list stays reactive; reading
123
+ // it here would snapshot it once and never update.
124
+ const mapped = createMemo(
125
+ mapArray(
126
+ () => props.each ?? [],
127
+ (item, index) => props.children(item, index),
128
+ ),
129
+ );
130
+ return mapped as unknown as Node;
131
+ }
132
+
133
+ /**
134
+ * Conditional rendering.
135
+ *
136
+ * The children are only evaluated when the condition holds, which is the whole
137
+ * point — an eagerly-evaluated branch would run effects and fetches for UI
138
+ * nobody is looking at.
139
+ */
140
+ // Overloads, not a union. `SolidNode` is `unknown`, and `unknown | Fn`
141
+ // collapses back to `unknown` — which makes TypeScript unable to see the
142
+ // function arm at all, so `{(item) => ...}` children get an implicit-any
143
+ // parameter. Overload resolution picks the callable form properly.
144
+ export function Show<T>(props: {
145
+ when: T | undefined | null | false;
146
+ fallback?: SolidNode;
147
+ children: (item: Accessor<NonNullable<T>>) => SolidNode;
148
+ }): Node;
149
+ export function Show<T>(props: {
150
+ when: T | undefined | null | false;
151
+ fallback?: SolidNode;
152
+ children: SolidNode;
153
+ }): Node;
154
+ export function Show<T>(props: {
155
+ when: T | undefined | null | false;
156
+ fallback?: SolidNode;
157
+ children: SolidNode | ((item: Accessor<NonNullable<T>>) => SolidNode);
158
+ }): Node {
159
+ const condition = createMemo(() => props.when);
160
+ return createMemo(() => {
161
+ const value = condition();
162
+ if (!value) return props.fallback;
163
+ const body = props.children;
164
+ if (typeof body !== "function") return body;
165
+ // An accessor, matching Solid's own signature: the child is written as
166
+ // `{(item) => <b>{item()}</b>}` and calling it keeps the read tracked
167
+ // inside the child rather than at the boundary.
168
+ const accessor: Accessor<NonNullable<T>> = () =>
169
+ condition() as NonNullable<T>;
170
+ return untrack(() =>
171
+ (body as (item: Accessor<NonNullable<T>>) => SolidNode)(accessor),
172
+ );
173
+ }) as unknown as Node;
174
+ }
175
+
176
+ /** The marker a `Match` leaves behind for its enclosing `Switch`. */
177
+ type MatchMarker = {
178
+ readonly __biabMatch: true;
179
+ readonly when: unknown;
180
+ readonly children: SolidNode;
181
+ };
182
+
183
+ function isMatchMarker(value: unknown): value is MatchMarker {
184
+ return (
185
+ typeof value === "object" &&
186
+ value !== null &&
187
+ (value as { __biabMatch?: unknown }).__biabMatch === true
188
+ );
189
+ }
190
+
191
+ /**
192
+ * One branch of a `Switch`.
193
+ *
194
+ * Returns a descriptor rather than DOM. `children` is a GETTER so the branch
195
+ * body is not evaluated unless `Switch` picks it — Solid's props are lazy, and
196
+ * forwarding that laziness is what keeps unselected branches inert.
197
+ */
198
+ export function Match(props: { when: unknown; children: SolidNode }): Node {
199
+ const marker: MatchMarker = {
200
+ __biabMatch: true,
201
+ get when() {
202
+ return props.when;
203
+ },
204
+ get children() {
205
+ return props.children;
206
+ },
207
+ };
208
+ return marker as unknown as Node;
209
+ }
210
+
211
+ /**
212
+ * First matching branch wins, else `fallback`.
213
+ *
214
+ * `children()` resolves the JSX children — which invokes each `Match` and
215
+ * yields its marker — and the memo re-picks whenever any `when` changes.
216
+ */
217
+ export function Switch(props: {
218
+ fallback?: SolidNode;
219
+ children: SolidNode;
220
+ }): Node {
221
+ const resolved = children(() => props.children as never);
222
+ return createMemo(() => {
223
+ const list = resolved.toArray() as unknown[];
224
+ for (const entry of list) {
225
+ if (!isMatchMarker(entry)) continue;
226
+ if (entry.when) return entry.children;
227
+ }
228
+ return props.fallback;
229
+ }) as unknown as Node;
230
+ }
231
+
232
+ /**
233
+ * Run once, after the first render.
234
+ *
235
+ * Solid 2 dropped `onMount`; it was always `createEffect` with the body
236
+ * untracked and a guard. Written out rather than imported so the binding does
237
+ * not care which version it is on.
238
+ */
239
+ export function onMount(fn: () => void): void {
240
+ const [mounted, setMounted] = createSignal(false);
241
+ createMemo(() => {
242
+ if (mounted()) return;
243
+ setMounted(true);
244
+ untrack(fn);
245
+ });
246
+ }
247
+
248
+ /**
249
+ * The provider component for a context, on either Solid version.
250
+ *
251
+ * Solid 1 hangs a `.Provider` off the context object. Solid 2 makes the
252
+ * context object **its own provider** — you write `<Ctx value={x}>` where you
253
+ * used to write `<Ctx.Provider value={x}>`.
254
+ *
255
+ * Unlike the import differences, this one CAN be resolved at runtime: the
256
+ * shape is inspectable. `.Provider` when it exists, the context itself
257
+ * otherwise.
258
+ */
259
+ export function providerFor<T>(
260
+ context: unknown,
261
+ ): (props: { value: T; children: SolidNode }) => Node {
262
+ const withProvider = context as {
263
+ Provider?: (props: { value: T; children: SolidNode }) => Node;
264
+ };
265
+ return (
266
+ withProvider.Provider ??
267
+ (context as (props: { value: T; children: SolidNode }) => Node)
268
+ );
269
+ }
270
+
271
+ /**
272
+ * Build a `class` string from a base plus conditional modifiers.
273
+ *
274
+ * Replaces Solid 1's `classList={{ "mod": cond }}`, which Solid 2 removed —
275
+ * verified against both versions' `jsx.d.ts`, where `classList` is declared in
276
+ * 1 and absent in 2.
277
+ *
278
+ * A plain `class` string behaves identically on both, so this is the portable
279
+ * spelling rather than a workaround.
280
+ */
281
+ export function cx(
282
+ base: string | undefined,
283
+ modifiers?: Record<string, boolean | undefined>,
284
+ ): string {
285
+ const parts = base ? [base] : [];
286
+ if (modifiers) {
287
+ for (const [name, on] of Object.entries(modifiers)) {
288
+ if (on) parts.push(name);
289
+ }
290
+ }
291
+ return parts.join(" ");
292
+ }
293
+
294
+ let uniqueIdCounter = 0;
295
+
296
+ /**
297
+ * A stable id for `for`/`aria-*` pairs.
298
+ *
299
+ * Solid 2 dropped `createUniqueId`. A module-level counter is enough here
300
+ * because these ids are only ever used to link a label to its own input within
301
+ * one document — they are not persisted, sent anywhere, or compared across
302
+ * renders.
303
+ *
304
+ * The `biab-` prefix keeps them from colliding with ids the consumer's own
305
+ * page already uses.
306
+ */
307
+ export function createUniqueId(): string {
308
+ uniqueIdCounter += 1;
309
+ return `biab-${uniqueIdCounter}`;
310
+ }
311
+
312
+ /**
313
+ * Bridge an external store into a signal.
314
+ *
315
+ * Replaces Solid 1's `from`, which Solid 2 dropped. The shape is exactly what
316
+ * every shared core exposes — `snapshot()` / `subscribe()` / `destroy()` — so
317
+ * this is the one seam where the framework-agnostic controllers meet Solid.
318
+ *
319
+ * `subscribe` does NOT replay, so the initial value is read separately. A
320
+ * bridge that waited for the first notification would render empty until
321
+ * something happened to change, which on a quiet screen is forever.
322
+ */
323
+ export function fromStore<T>(store: {
324
+ snapshot: () => T;
325
+ subscribe: (listener: () => void) => () => void;
326
+ }): Accessor<T> {
327
+ // Boxed, and not for tidiness: Solid 2 types the value overload of
328
+ // `createSignal` as `Exclude<T, Function>`, because a bare function
329
+ // argument is read as a compute function instead of a value. A snapshot
330
+ // type that could be callable is therefore rejected outright. A wrapper
331
+ // object is never a Function, so the same call compiles on both versions
332
+ // and cannot be mistaken for a computation.
333
+ const [box, setBox] = createSignal<{ value: T }>(
334
+ { value: store.snapshot() },
335
+ {
336
+ // Snapshots are replaced wholesale, and two structurally-equal
337
+ // snapshots are still a real update to the controller. Comparing
338
+ // would drop legitimate notifications.
339
+ equals: false,
340
+ },
341
+ );
342
+ const unsubscribe = store.subscribe(() =>
343
+ setBox({ value: store.snapshot() }),
344
+ );
345
+ onCleanup(unsubscribe);
346
+ return () => box().value;
347
+ }