@getforma/core 0.1.0

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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +48 -0
  3. package/dist/chunk-27QSSN4E.cjs +1773 -0
  4. package/dist/chunk-27QSSN4E.cjs.map +1 -0
  5. package/dist/chunk-DB2ULMOM.js +1750 -0
  6. package/dist/chunk-DB2ULMOM.js.map +1 -0
  7. package/dist/chunk-FPSLC62A.cjs +31 -0
  8. package/dist/chunk-FPSLC62A.cjs.map +1 -0
  9. package/dist/chunk-KX5WRZH7.js +27 -0
  10. package/dist/chunk-KX5WRZH7.js.map +1 -0
  11. package/dist/formajs-runtime-hardened.global.js +2 -0
  12. package/dist/formajs-runtime-hardened.global.js.map +1 -0
  13. package/dist/formajs-runtime.global.js +2 -0
  14. package/dist/formajs-runtime.global.js.map +1 -0
  15. package/dist/formajs.global.js +2 -0
  16. package/dist/formajs.global.js.map +1 -0
  17. package/dist/index.cjs +1626 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +1382 -0
  20. package/dist/index.d.ts +1382 -0
  21. package/dist/index.js +1482 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/runtime-hardened.cjs +2805 -0
  24. package/dist/runtime-hardened.cjs.map +1 -0
  25. package/dist/runtime-hardened.js +2786 -0
  26. package/dist/runtime-hardened.js.map +1 -0
  27. package/dist/runtime.cjs +2531 -0
  28. package/dist/runtime.cjs.map +1 -0
  29. package/dist/runtime.d.cts +86 -0
  30. package/dist/runtime.d.ts +86 -0
  31. package/dist/runtime.js +2512 -0
  32. package/dist/runtime.js.map +1 -0
  33. package/dist/signal-CfLDwMyg.d.cts +29 -0
  34. package/dist/signal-CfLDwMyg.d.ts +29 -0
  35. package/dist/ssr/index.cjs +381 -0
  36. package/dist/ssr/index.cjs.map +1 -0
  37. package/dist/ssr/index.d.cts +154 -0
  38. package/dist/ssr/index.d.ts +154 -0
  39. package/dist/ssr/index.js +371 -0
  40. package/dist/ssr/index.js.map +1 -0
  41. package/dist/tc39-compat.cjs +45 -0
  42. package/dist/tc39-compat.cjs.map +1 -0
  43. package/dist/tc39-compat.d.cts +50 -0
  44. package/dist/tc39-compat.d.ts +50 -0
  45. package/dist/tc39-compat.js +42 -0
  46. package/dist/tc39-compat.js.map +1 -0
  47. package/package.json +122 -0
@@ -0,0 +1,154 @@
1
+ interface VNode {
2
+ tag: string;
3
+ props: Record<string, unknown> | null;
4
+ children: unknown[];
5
+ }
6
+ /**
7
+ * Server-side hyperscript — creates a virtual node instead of a DOM element.
8
+ */
9
+ declare function sh(tag: string, props?: Record<string, unknown> | null, ...children: unknown[]): VNode;
10
+ /**
11
+ * Render a FormaJS virtual tree to an HTML string.
12
+ *
13
+ * Uses an array buffer internally to avoid O(n^2) string concatenation
14
+ * for large trees, then joins once at the end.
15
+ *
16
+ * Usage:
17
+ * ```ts
18
+ * import { renderToString, sh } from '@getforma/core/ssr';
19
+ *
20
+ * const html = renderToString(
21
+ * sh('div', { class: 'app' },
22
+ * sh('h1', null, 'Hello SSR!'),
23
+ * sh('p', null, () => count()), // signal getters resolved synchronously
24
+ * )
25
+ * );
26
+ * ```
27
+ */
28
+ declare function renderToString(node: unknown): string;
29
+ /**
30
+ * Render a FormaJS virtual tree to an HTML string with hydration markers.
31
+ *
32
+ * Like `renderToString`, but injects comment markers and `data-forma-h`
33
+ * attributes so the client-side `hydrate()` function can adopt existing
34
+ * DOM nodes without re-creating them.
35
+ *
36
+ * Marker types:
37
+ * - `data-forma-h="N"` — element boundary (attribute on the element)
38
+ * - `<!--forma-t:N-->` / `<!--/forma-t:N-->` — reactive text boundary
39
+ * - `<!--forma-l:N-->` / `<!--/forma-l:N-->` — list boundary
40
+ *
41
+ * Usage:
42
+ * ```ts
43
+ * import { renderToStringWithHydration, sh } from '@getforma/core/ssr';
44
+ *
45
+ * const html = renderToStringWithHydration(
46
+ * sh('div', { class: 'app' },
47
+ * sh('h1', null, 'Hello SSR!'),
48
+ * sh('p', null, () => count()),
49
+ * )
50
+ * );
51
+ * ```
52
+ */
53
+ declare function renderToStringWithHydration(node: unknown): string;
54
+
55
+ /**
56
+ * SSR-only signal: just a value holder, no reactivity needed on server.
57
+ */
58
+ declare function ssrSignal<T>(initial: T): [get: () => T, set: (v: T) => void];
59
+ declare function ssrComputed<T>(fn: () => T): () => T;
60
+
61
+ /**
62
+ * FormaJS SSR - Streaming Renderer
63
+ *
64
+ * Implements true chunked HTML streaming with out-of-order Suspense resolution.
65
+ *
66
+ * How it works:
67
+ * 1. Yields shell HTML immediately (opening tags, head, static content)
68
+ * 2. When a Suspense boundary is encountered:
69
+ * - Yields the fallback HTML with a placeholder: <div id="forma-s:N" style="display:contents">
70
+ * - Continues rendering siblings without blocking
71
+ * - When the async resource resolves, yields a swap script
72
+ * 3. Suspense boundaries resolve independently in any order (out-of-order streaming)
73
+ */
74
+ /**
75
+ * A Suspense boundary VNode has a special shape:
76
+ * { tag: 'forma-suspense', props: { fallback: VNode }, children: [asyncChildren] }
77
+ *
78
+ * Or it can be created via:
79
+ * shSuspense(fallbackVNode, () => asyncContent)
80
+ */
81
+ interface SuspenseVNode {
82
+ tag: 'forma-suspense';
83
+ props: {
84
+ fallback: unknown;
85
+ };
86
+ children: [() => Promise<unknown>];
87
+ }
88
+ /**
89
+ * Create a server-side Suspense VNode for streaming SSR.
90
+ */
91
+ declare function shSuspense(fallback: unknown, asyncChildren: () => Promise<unknown>): SuspenseVNode;
92
+ interface StreamOptions {
93
+ /** Script to bootstrap the client-side app (e.g. <script src="/app.js"></script>) */
94
+ bootstrapScript?: string;
95
+ /** Whether to inject the $FORMA_SWAP client script (default: true) */
96
+ injectSwapScript?: boolean;
97
+ }
98
+ /**
99
+ * Render a FormaJS virtual tree to a stream of HTML chunks.
100
+ *
101
+ * Yields HTML progressively:
102
+ * - Static content is yielded immediately
103
+ * - Suspense boundaries yield their fallback, then resolve later
104
+ * - Resolved Suspense boundaries yield swap scripts
105
+ *
106
+ * Usage:
107
+ * ```ts
108
+ * import { renderToStream, shSuspense, sh } from '@getforma/core/ssr';
109
+ *
110
+ * const stream = renderToStream(
111
+ * sh('html', null,
112
+ * sh('body', null,
113
+ * sh('h1', null, 'Hello'),
114
+ * shSuspense(
115
+ * sh('div', null, 'Loading...'),
116
+ * async () => {
117
+ * const data = await fetchData();
118
+ * return sh('div', null, data.name);
119
+ * },
120
+ * ),
121
+ * ),
122
+ * ),
123
+ * );
124
+ *
125
+ * for await (const chunk of stream) {
126
+ * response.write(chunk);
127
+ * }
128
+ * ```
129
+ */
130
+ declare function renderToStreamNew(node: unknown, options?: StreamOptions): AsyncGenerator<string>;
131
+
132
+ /**
133
+ * FormaJS SSR - Client Swap Script
134
+ *
135
+ * Tiny inline script injected into the HTML stream that handles
136
+ * out-of-order Suspense boundary resolution.
137
+ *
138
+ * When the server resolves a Suspense boundary, it sends:
139
+ * <script>$FORMA_SWAP("forma-s:0","<resolved html>")</script>
140
+ *
141
+ * This script replaces the fallback placeholder with the resolved content.
142
+ */
143
+ /**
144
+ * Returns the client-side swap script as a string, ready to be injected
145
+ * into the HTML stream (typically right after <body> or before first Suspense).
146
+ */
147
+ declare function getSwapScript(): string;
148
+ /**
149
+ * Returns a script tag that swaps a specific Suspense boundary's content.
150
+ * Called when an async resource resolves during streaming.
151
+ */
152
+ declare function getSwapTag(id: string, html: string): string;
153
+
154
+ export { type StreamOptions, type SuspenseVNode, type VNode, getSwapScript, getSwapTag, renderToStreamNew as renderToStream, renderToString, renderToStringWithHydration, sh, shSuspense, ssrComputed, ssrSignal };
@@ -0,0 +1,371 @@
1
+ // src/ssr/render.ts
2
+ var VOID_ELEMENTS = /* @__PURE__ */ new Set([
3
+ "area",
4
+ "base",
5
+ "br",
6
+ "col",
7
+ "embed",
8
+ "hr",
9
+ "img",
10
+ "input",
11
+ "link",
12
+ "meta",
13
+ "param",
14
+ "source",
15
+ "track",
16
+ "wbr"
17
+ ]);
18
+ var PROP_TO_ATTR = {
19
+ className: "class",
20
+ htmlFor: "for",
21
+ tabIndex: "tabindex"
22
+ };
23
+ function escapeHtml(str) {
24
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
25
+ }
26
+ function escapeAttr(str) {
27
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
28
+ }
29
+ function sh(tag, props, ...children) {
30
+ return { tag, props: props ?? null, children };
31
+ }
32
+ function renderToString(node) {
33
+ const parts = [];
34
+ renderToBuffer(node, parts);
35
+ return parts.join("");
36
+ }
37
+ function renderToBuffer(node, parts) {
38
+ if (node == null || node === true || node === false) return;
39
+ if (typeof node === "string") {
40
+ parts.push(escapeHtml(node));
41
+ return;
42
+ }
43
+ if (typeof node === "number") {
44
+ parts.push(String(node));
45
+ return;
46
+ }
47
+ if (typeof node === "function") {
48
+ renderToBuffer(node(), parts);
49
+ return;
50
+ }
51
+ if (Array.isArray(node)) {
52
+ for (const child of node) renderToBuffer(child, parts);
53
+ return;
54
+ }
55
+ if (isVNode(node)) {
56
+ const { tag, props, children } = node;
57
+ parts.push("<", tag);
58
+ if (props) {
59
+ for (const [key, value] of Object.entries(props)) {
60
+ if (key.startsWith("on") || key === "ref" || key === "dangerouslySetInnerHTML") continue;
61
+ const attrName = PROP_TO_ATTR[key] ?? key;
62
+ const resolved = typeof value === "function" ? value() : value;
63
+ if (resolved === true) {
64
+ parts.push(" ", attrName);
65
+ } else if (resolved !== false && resolved != null) {
66
+ parts.push(" ", attrName, '="', escapeAttr(String(resolved)), '"');
67
+ }
68
+ }
69
+ }
70
+ if (VOID_ELEMENTS.has(tag)) {
71
+ parts.push(" />");
72
+ return;
73
+ }
74
+ parts.push(">");
75
+ if (props?.["dangerouslySetInnerHTML"]) {
76
+ parts.push(props["dangerouslySetInnerHTML"].__html);
77
+ } else {
78
+ for (const child of children) {
79
+ renderToBuffer(child, parts);
80
+ }
81
+ }
82
+ parts.push("</", tag, ">");
83
+ return;
84
+ }
85
+ parts.push(escapeHtml(String(node)));
86
+ }
87
+ function isVNode(v) {
88
+ return v != null && typeof v === "object" && "tag" in v && "children" in v;
89
+ }
90
+ var hydrationId = 0;
91
+ function renderToStringWithHydration(node) {
92
+ hydrationId = 0;
93
+ const parts = [];
94
+ renderToBufferHydrated(node, parts);
95
+ return parts.join("");
96
+ }
97
+ function renderToBufferHydrated(node, parts) {
98
+ if (node == null || node === true || node === false) return;
99
+ if (typeof node === "string") {
100
+ parts.push(escapeHtml(node));
101
+ return;
102
+ }
103
+ if (typeof node === "number") {
104
+ parts.push(String(node));
105
+ return;
106
+ }
107
+ if (typeof node === "function") {
108
+ const id = hydrationId++;
109
+ parts.push(`<!--forma-t:${id}-->`);
110
+ renderToBufferHydrated(node(), parts);
111
+ parts.push(`<!--/forma-t:${id}-->`);
112
+ return;
113
+ }
114
+ if (Array.isArray(node)) {
115
+ const id = hydrationId++;
116
+ parts.push(`<!--forma-l:${id}-->`);
117
+ for (const child of node) renderToBufferHydrated(child, parts);
118
+ parts.push(`<!--/forma-l:${id}-->`);
119
+ return;
120
+ }
121
+ if (isVNode(node)) {
122
+ const id = hydrationId++;
123
+ const { tag, props, children } = node;
124
+ parts.push("<", tag, ` data-forma-h="${id}"`);
125
+ if (props) {
126
+ for (const [key, value] of Object.entries(props)) {
127
+ if (key.startsWith("on") || key === "ref" || key === "dangerouslySetInnerHTML") continue;
128
+ const attrName = PROP_TO_ATTR[key] ?? key;
129
+ const resolved = typeof value === "function" ? value() : value;
130
+ if (resolved === true) {
131
+ parts.push(" ", attrName);
132
+ } else if (resolved !== false && resolved != null) {
133
+ parts.push(" ", attrName, '="', escapeAttr(String(resolved)), '"');
134
+ }
135
+ }
136
+ }
137
+ if (VOID_ELEMENTS.has(tag)) {
138
+ parts.push(" />");
139
+ return;
140
+ }
141
+ parts.push(">");
142
+ if (props?.["dangerouslySetInnerHTML"]) {
143
+ parts.push(props["dangerouslySetInnerHTML"].__html);
144
+ } else {
145
+ for (const child of children) {
146
+ renderToBufferHydrated(child, parts);
147
+ }
148
+ }
149
+ parts.push("</", tag, ">");
150
+ return;
151
+ }
152
+ parts.push(escapeHtml(String(node)));
153
+ }
154
+
155
+ // src/ssr/ssr-reactive.ts
156
+ function ssrSignal(initial) {
157
+ let value = initial;
158
+ return [() => value, (v) => {
159
+ value = v;
160
+ }];
161
+ }
162
+ function ssrComputed(fn) {
163
+ return fn;
164
+ }
165
+
166
+ // src/ssr/client-script.ts
167
+ function getSwapScript() {
168
+ return `<script>
169
+ function $FORMA_SWAP(id,html){
170
+ var el=document.getElementById(id);
171
+ if(el){
172
+ var tpl=document.createElement('template');
173
+ tpl.innerHTML=html;
174
+ el.replaceWith(tpl.content);
175
+ }
176
+ }
177
+ </script>`;
178
+ }
179
+ function getSwapTag(id, html) {
180
+ const escaped = html.replace(/<\/script>/gi, "<\\/script>");
181
+ return `<script>$FORMA_SWAP("${id}","${escaped.replace(/"/g, '\\"').replace(/\n/g, "\\n")}")</script>`;
182
+ }
183
+
184
+ // src/ssr/stream.ts
185
+ function isVNode2(v) {
186
+ return v != null && typeof v === "object" && "tag" in v && "children" in v;
187
+ }
188
+ function escapeHtml2(str) {
189
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
190
+ }
191
+ function escapeAttr2(str) {
192
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
193
+ }
194
+ var VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
195
+ "area",
196
+ "base",
197
+ "br",
198
+ "col",
199
+ "embed",
200
+ "hr",
201
+ "img",
202
+ "input",
203
+ "link",
204
+ "meta",
205
+ "param",
206
+ "source",
207
+ "track",
208
+ "wbr"
209
+ ]);
210
+ var PROP_TO_ATTR2 = {
211
+ className: "class",
212
+ htmlFor: "for",
213
+ tabIndex: "tabindex"
214
+ };
215
+ var suspenseCounter = 0;
216
+ var pendingBoundaries = [];
217
+ function renderSync(node, parts) {
218
+ if (node == null || node === true || node === false) return;
219
+ if (typeof node === "string") {
220
+ parts.push(escapeHtml2(node));
221
+ return;
222
+ }
223
+ if (typeof node === "number") {
224
+ parts.push(String(node));
225
+ return;
226
+ }
227
+ if (typeof node === "function") {
228
+ renderSync(node(), parts);
229
+ return;
230
+ }
231
+ if (Array.isArray(node)) {
232
+ for (const child of node) renderSync(child, parts);
233
+ return;
234
+ }
235
+ if (isVNode2(node)) {
236
+ const { tag, props, children } = node;
237
+ parts.push("<", tag);
238
+ if (props) {
239
+ for (const [key, value] of Object.entries(props)) {
240
+ if (key.startsWith("on") || key === "ref" || key === "dangerouslySetInnerHTML") continue;
241
+ const attrName = PROP_TO_ATTR2[key] ?? key;
242
+ const resolved = typeof value === "function" ? value() : value;
243
+ if (resolved === true) {
244
+ parts.push(" ", attrName);
245
+ } else if (resolved !== false && resolved != null) {
246
+ parts.push(" ", attrName, '="', escapeAttr2(String(resolved)), '"');
247
+ }
248
+ }
249
+ }
250
+ if (VOID_ELEMENTS2.has(tag)) {
251
+ parts.push(" />");
252
+ return;
253
+ }
254
+ parts.push(">");
255
+ if (props?.["dangerouslySetInnerHTML"]) {
256
+ parts.push(props["dangerouslySetInnerHTML"].__html);
257
+ } else {
258
+ for (const child of children) renderSync(child, parts);
259
+ }
260
+ parts.push("</", tag, ">");
261
+ return;
262
+ }
263
+ parts.push(escapeHtml2(String(node)));
264
+ }
265
+ function isSuspenseVNode(v) {
266
+ return isVNode2(v) && v.tag === "forma-suspense";
267
+ }
268
+ function shSuspense(fallback, asyncChildren) {
269
+ return {
270
+ tag: "forma-suspense",
271
+ props: { fallback },
272
+ children: [asyncChildren]
273
+ };
274
+ }
275
+ async function* renderToStreamNew(node, options) {
276
+ suspenseCounter = 0;
277
+ pendingBoundaries = [];
278
+ const injectSwap = options?.injectSwapScript !== false;
279
+ if (injectSwap) {
280
+ yield getSwapScript();
281
+ }
282
+ const mainParts = [];
283
+ renderStreamNode(node, mainParts);
284
+ yield mainParts.join("");
285
+ if (options?.bootstrapScript) {
286
+ yield options.bootstrapScript;
287
+ }
288
+ while (pendingBoundaries.length > 0) {
289
+ const pending = [...pendingBoundaries];
290
+ pendingBoundaries = [];
291
+ const results = await Promise.allSettled(
292
+ pending.map(async (boundary) => {
293
+ const html = await boundary.promise;
294
+ return { id: boundary.id, html };
295
+ })
296
+ );
297
+ for (const result of results) {
298
+ if (result.status === "fulfilled") {
299
+ yield getSwapTag(result.value.id, result.value.html);
300
+ }
301
+ }
302
+ }
303
+ }
304
+ function renderStreamNode(node, parts) {
305
+ if (node == null || node === true || node === false) return;
306
+ if (typeof node === "string") {
307
+ parts.push(escapeHtml2(node));
308
+ return;
309
+ }
310
+ if (typeof node === "number") {
311
+ parts.push(String(node));
312
+ return;
313
+ }
314
+ if (typeof node === "function") {
315
+ renderStreamNode(node(), parts);
316
+ return;
317
+ }
318
+ if (Array.isArray(node)) {
319
+ for (const child of node) renderStreamNode(child, parts);
320
+ return;
321
+ }
322
+ if (isSuspenseVNode(node)) {
323
+ const id = `forma-s:${suspenseCounter++}`;
324
+ const fallback = node.props.fallback;
325
+ const asyncFn = node.children[0];
326
+ parts.push(`<div id="${id}" style="display:contents">`);
327
+ renderSync(fallback, parts);
328
+ parts.push("</div>");
329
+ const promise = asyncFn().then((resolved) => {
330
+ const resolvedParts = [];
331
+ renderSync(resolved, resolvedParts);
332
+ return resolvedParts.join("");
333
+ });
334
+ pendingBoundaries.push({ id, promise });
335
+ return;
336
+ }
337
+ if (isVNode2(node)) {
338
+ const { tag, props, children } = node;
339
+ parts.push("<", tag);
340
+ if (props) {
341
+ for (const [key, value] of Object.entries(props)) {
342
+ if (key === "fallback") continue;
343
+ if (key.startsWith("on") || key === "ref" || key === "dangerouslySetInnerHTML") continue;
344
+ const attrName = PROP_TO_ATTR2[key] ?? key;
345
+ const resolved = typeof value === "function" ? value() : value;
346
+ if (resolved === true) {
347
+ parts.push(" ", attrName);
348
+ } else if (resolved !== false && resolved != null) {
349
+ parts.push(" ", attrName, '="', escapeAttr2(String(resolved)), '"');
350
+ }
351
+ }
352
+ }
353
+ if (VOID_ELEMENTS2.has(tag)) {
354
+ parts.push(" />");
355
+ return;
356
+ }
357
+ parts.push(">");
358
+ if (props?.["dangerouslySetInnerHTML"]) {
359
+ parts.push(props["dangerouslySetInnerHTML"].__html);
360
+ } else {
361
+ for (const child of children) renderStreamNode(child, parts);
362
+ }
363
+ parts.push("</", tag, ">");
364
+ return;
365
+ }
366
+ parts.push(escapeHtml2(String(node)));
367
+ }
368
+
369
+ export { getSwapScript, getSwapTag, renderToStreamNew as renderToStream, renderToString, renderToStringWithHydration, sh, shSuspense, ssrComputed, ssrSignal };
370
+ //# sourceMappingURL=index.js.map
371
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ssr/render.ts","../../src/ssr/ssr-reactive.ts","../../src/ssr/client-script.ts","../../src/ssr/stream.ts"],"names":["isVNode","escapeHtml","escapeAttr","VOID_ELEMENTS","PROP_TO_ATTR"],"mappings":";AAGA,IAAM,aAAA,uBAAoB,GAAA,CAAI;AAAA,EAC5B,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,OAAA;AAAA,EAAS,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,OAAA;AAAA,EACnD,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,OAAA;AAAA,EAAS,QAAA;AAAA,EAAU,OAAA;AAAA,EAAS;AAC9C,CAAC,CAAA;AAGD,IAAM,YAAA,GAAuC;AAAA,EAC3C,SAAA,EAAW,OAAA;AAAA,EACX,OAAA,EAAS,KAAA;AAAA,EACT,QAAA,EAAU;AACZ,CAAA;AAGA,SAAS,WAAW,GAAA,EAAqB;AACvC,EAAA,OAAO,IACJ,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA,CACrB,OAAA,CAAQ,MAAM,MAAM,CAAA,CACpB,QAAQ,IAAA,EAAM,MAAM,EACpB,OAAA,CAAQ,IAAA,EAAM,QAAQ,CAAA,CACtB,OAAA,CAAQ,MAAM,QAAQ,CAAA;AAC3B;AAGA,SAAS,WAAW,GAAA,EAAqB;AACvC,EAAA,OAAO,IAAI,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA,CAAE,OAAA,CAAQ,MAAM,QAAQ,CAAA;AAC1D;AAWO,SAAS,EAAA,CAAG,GAAA,EAAa,KAAA,EAAA,GAA2C,QAAA,EAA4B;AACrG,EAAA,OAAO,EAAE,GAAA,EAAK,KAAA,EAAO,KAAA,IAAS,MAAM,QAAA,EAAS;AAC/C;AAoBO,SAAS,eAAe,IAAA,EAAuB;AACpD,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,cAAA,CAAe,MAAM,KAAK,CAAA;AAC1B,EAAA,OAAO,KAAA,CAAM,KAAK,EAAE,CAAA;AACtB;AAMA,SAAS,cAAA,CAAe,MAAe,KAAA,EAAuB;AAE5D,EAAA,IAAI,IAAA,IAAQ,IAAA,IAAQ,IAAA,KAAS,IAAA,IAAQ,SAAS,KAAA,EAAO;AAGrD,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAAE,IAAA,KAAA,CAAM,IAAA,CAAK,UAAA,CAAW,IAAI,CAAC,CAAA;AAAG,IAAA;AAAA,EAAQ;AAGtE,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAAE,IAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,IAAI,CAAC,CAAA;AAAG,IAAA;AAAA,EAAQ;AAGlE,EAAA,IAAI,OAAO,SAAS,UAAA,EAAY;AAAE,IAAA,cAAA,CAAe,IAAA,IAAQ,KAAK,CAAA;AAAG,IAAA;AAAA,EAAQ;AAGzE,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACvB,IAAA,KAAA,MAAW,KAAA,IAAS,IAAA,EAAM,cAAA,CAAe,KAAA,EAAO,KAAK,CAAA;AACrD,IAAA;AAAA,EACF;AAGA,EAAA,IAAI,OAAA,CAAQ,IAAI,CAAA,EAAG;AACjB,IAAA,MAAM,EAAE,GAAA,EAAK,KAAA,EAAO,QAAA,EAAS,GAAI,IAAA;AACjC,IAAA,KAAA,CAAM,IAAA,CAAK,KAAK,GAAG,CAAA;AAGnB,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAEhD,QAAA,IAAI,IAAI,UAAA,CAAW,IAAI,KAAK,GAAA,KAAQ,KAAA,IAAS,QAAQ,yBAAA,EAA2B;AAEhF,QAAA,MAAM,QAAA,GAAW,YAAA,CAAa,GAAG,CAAA,IAAK,GAAA;AAGtC,QAAA,MAAM,QAAA,GAAW,OAAO,KAAA,KAAU,UAAA,GAAa,OAAM,GAAI,KAAA;AAEzD,QAAA,IAAI,aAAa,IAAA,EAAM;AACrB,UAAA,KAAA,CAAM,IAAA,CAAK,KAAK,QAAQ,CAAA;AAAA,QAC1B,CAAA,MAAA,IAAW,QAAA,KAAa,KAAA,IAAS,QAAA,IAAY,IAAA,EAAM;AACjD,UAAA,KAAA,CAAM,IAAA,CAAK,KAAK,QAAA,EAAU,IAAA,EAAM,WAAW,MAAA,CAAO,QAAQ,CAAC,CAAA,EAAG,GAAG,CAAA;AAAA,QACnE;AAAA,MACF;AAAA,IACF;AAGA,IAAA,IAAI,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA,EAAG;AAAE,MAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAAG,MAAA;AAAA,IAAQ;AAEzD,IAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AAGd,IAAA,IAAI,KAAA,GAAQ,yBAAyB,CAAA,EAAG;AACtC,MAAA,KAAA,CAAM,IAAA,CAAM,KAAA,CAAM,yBAAyB,CAAA,CAAyB,MAAM,CAAA;AAAA,IAC5E,CAAA,MAAO;AAEL,MAAA,KAAA,MAAW,SAAS,QAAA,EAAU;AAC5B,QAAA,cAAA,CAAe,OAAO,KAAK,CAAA;AAAA,MAC7B;AAAA,IACF;AAEA,IAAA,KAAA,CAAM,IAAA,CAAK,IAAA,EAAM,GAAA,EAAK,GAAG,CAAA;AACzB,IAAA;AAAA,EACF;AAGA,EAAA,KAAA,CAAM,IAAA,CAAK,UAAA,CAAW,MAAA,CAAO,IAAI,CAAC,CAAC,CAAA;AACrC;AAEA,SAAS,QAAQ,CAAA,EAAwB;AACvC,EAAA,OAAO,KAAK,IAAA,IAAQ,OAAO,MAAM,QAAA,IAAY,KAAA,IAAS,KAAK,UAAA,IAAc,CAAA;AAC3E;AAWA,IAAI,WAAA,GAAc,CAAA;AA0BX,SAAS,4BAA4B,IAAA,EAAuB;AACjE,EAAA,WAAA,GAAc,CAAA;AACd,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,sBAAA,CAAuB,MAAM,KAAK,CAAA;AAClC,EAAA,OAAO,KAAA,CAAM,KAAK,EAAE,CAAA;AACtB;AAKA,SAAS,sBAAA,CAAuB,MAAe,KAAA,EAAuB;AAEpE,EAAA,IAAI,IAAA,IAAQ,IAAA,IAAQ,IAAA,KAAS,IAAA,IAAQ,SAAS,KAAA,EAAO;AAGrD,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAAE,IAAA,KAAA,CAAM,IAAA,CAAK,UAAA,CAAW,IAAI,CAAC,CAAA;AAAG,IAAA;AAAA,EAAQ;AAGtE,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAAE,IAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,IAAI,CAAC,CAAA;AAAG,IAAA;AAAA,EAAQ;AAGlE,EAAA,IAAI,OAAO,SAAS,UAAA,EAAY;AAC9B,IAAA,MAAM,EAAA,GAAK,WAAA,EAAA;AACX,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,YAAA,EAAe,EAAE,CAAA,GAAA,CAAK,CAAA;AACjC,IAAA,sBAAA,CAAuB,IAAA,IAAQ,KAAK,CAAA;AACpC,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,aAAA,EAAgB,EAAE,CAAA,GAAA,CAAK,CAAA;AAClC,IAAA;AAAA,EACF;AAGA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACvB,IAAA,MAAM,EAAA,GAAK,WAAA,EAAA;AACX,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,YAAA,EAAe,EAAE,CAAA,GAAA,CAAK,CAAA;AACjC,IAAA,KAAA,MAAW,KAAA,IAAS,IAAA,EAAM,sBAAA,CAAuB,KAAA,EAAO,KAAK,CAAA;AAC7D,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,aAAA,EAAgB,EAAE,CAAA,GAAA,CAAK,CAAA;AAClC,IAAA;AAAA,EACF;AAGA,EAAA,IAAI,OAAA,CAAQ,IAAI,CAAA,EAAG;AACjB,IAAA,MAAM,EAAA,GAAK,WAAA,EAAA;AACX,IAAA,MAAM,EAAE,GAAA,EAAK,KAAA,EAAO,QAAA,EAAS,GAAI,IAAA;AAGjC,IAAA,KAAA,CAAM,IAAA,CAAK,GAAA,EAAK,GAAA,EAAK,CAAA,eAAA,EAAkB,EAAE,CAAA,CAAA,CAAG,CAAA;AAG5C,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAEhD,QAAA,IAAI,IAAI,UAAA,CAAW,IAAI,KAAK,GAAA,KAAQ,KAAA,IAAS,QAAQ,yBAAA,EAA2B;AAEhF,QAAA,MAAM,QAAA,GAAW,YAAA,CAAa,GAAG,CAAA,IAAK,GAAA;AAGtC,QAAA,MAAM,QAAA,GAAW,OAAO,KAAA,KAAU,UAAA,GAAa,OAAM,GAAI,KAAA;AAEzD,QAAA,IAAI,aAAa,IAAA,EAAM;AACrB,UAAA,KAAA,CAAM,IAAA,CAAK,KAAK,QAAQ,CAAA;AAAA,QAC1B,CAAA,MAAA,IAAW,QAAA,KAAa,KAAA,IAAS,QAAA,IAAY,IAAA,EAAM;AACjD,UAAA,KAAA,CAAM,IAAA,CAAK,KAAK,QAAA,EAAU,IAAA,EAAM,WAAW,MAAA,CAAO,QAAQ,CAAC,CAAA,EAAG,GAAG,CAAA;AAAA,QACnE;AAAA,MACF;AAAA,IACF;AAGA,IAAA,IAAI,aAAA,CAAc,GAAA,CAAI,GAAG,CAAA,EAAG;AAAE,MAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAAG,MAAA;AAAA,IAAQ;AAEzD,IAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AAGd,IAAA,IAAI,KAAA,GAAQ,yBAAyB,CAAA,EAAG;AACtC,MAAA,KAAA,CAAM,IAAA,CAAM,KAAA,CAAM,yBAAyB,CAAA,CAAyB,MAAM,CAAA;AAAA,IAC5E,CAAA,MAAO;AAEL,MAAA,KAAA,MAAW,SAAS,QAAA,EAAU;AAC5B,QAAA,sBAAA,CAAuB,OAAO,KAAK,CAAA;AAAA,MACrC;AAAA,IACF;AAEA,IAAA,KAAA,CAAM,IAAA,CAAK,IAAA,EAAM,GAAA,EAAK,GAAG,CAAA;AACzB,IAAA;AAAA,EACF;AAGA,EAAA,KAAA,CAAM,IAAA,CAAK,UAAA,CAAW,MAAA,CAAO,IAAI,CAAC,CAAC,CAAA;AACrC;;;ACnQO,SAAS,UAAa,OAAA,EAAiD;AAC5E,EAAA,IAAI,KAAA,GAAQ,OAAA;AACZ,EAAA,OAAO,CAAC,MAAM,KAAA,EAAO,CAAC,CAAA,KAAM;AAAE,IAAA,KAAA,GAAQ,CAAA;AAAA,EAAG,CAAC,CAAA;AAC5C;AAEO,SAAS,YAAe,EAAA,EAAsB;AACnD,EAAA,OAAO,EAAA;AACT;;;ACMO,SAAS,aAAA,GAAwB;AACtC,EAAA,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAA,CAAA;AAUT;AAMO,SAAS,UAAA,CAAW,IAAY,IAAA,EAAsB;AAE3D,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,cAAA,EAAgB,aAAa,CAAA;AAC1D,EAAA,OAAO,CAAA,qBAAA,EAAwB,EAAE,CAAA,GAAA,EAAM,OAAA,CAAQ,OAAA,CAAQ,IAAA,EAAM,KAAK,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,KAAK,CAAC,CAAA,WAAA,CAAA;AAC3F;;;ACbA,SAASA,SAAQ,CAAA,EAAwB;AACvC,EAAA,OAAO,KAAK,IAAA,IAAQ,OAAO,MAAM,QAAA,IAAY,KAAA,IAAS,KAAK,UAAA,IAAc,CAAA;AAC3E;AAGA,SAASC,YAAW,GAAA,EAAqB;AACvC,EAAA,OAAO,IACJ,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA,CACrB,OAAA,CAAQ,MAAM,MAAM,CAAA,CACpB,QAAQ,IAAA,EAAM,MAAM,EACpB,OAAA,CAAQ,IAAA,EAAM,QAAQ,CAAA,CACtB,OAAA,CAAQ,MAAM,QAAQ,CAAA;AAC3B;AAEA,SAASC,YAAW,GAAA,EAAqB;AACvC,EAAA,OAAO,IAAI,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA,CAAE,OAAA,CAAQ,MAAM,QAAQ,CAAA;AAC1D;AAEA,IAAMC,cAAAA,uBAAoB,GAAA,CAAI;AAAA,EAC5B,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,OAAA;AAAA,EAAS,IAAA;AAAA,EAAM,KAAA;AAAA,EAAO,OAAA;AAAA,EACnD,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,OAAA;AAAA,EAAS,QAAA;AAAA,EAAU,OAAA;AAAA,EAAS;AAC9C,CAAC,CAAA;AAED,IAAMC,aAAAA,GAAuC;AAAA,EAC3C,SAAA,EAAW,OAAA;AAAA,EACX,OAAA,EAAS,KAAA;AAAA,EACT,QAAA,EAAU;AACZ,CAAA;AAWA,IAAI,eAAA,GAAkB,CAAA;AACtB,IAAI,oBAAuC,EAAC;AAM5C,SAAS,UAAA,CAAW,MAAe,KAAA,EAAuB;AACxD,EAAA,IAAI,IAAA,IAAQ,IAAA,IAAQ,IAAA,KAAS,IAAA,IAAQ,SAAS,KAAA,EAAO;AACrD,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAAE,IAAA,KAAA,CAAM,IAAA,CAAKH,WAAAA,CAAW,IAAI,CAAC,CAAA;AAAG,IAAA;AAAA,EAAQ;AACtE,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAAE,IAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,IAAI,CAAC,CAAA;AAAG,IAAA;AAAA,EAAQ;AAClE,EAAA,IAAI,OAAO,SAAS,UAAA,EAAY;AAAE,IAAA,UAAA,CAAW,IAAA,IAAQ,KAAK,CAAA;AAAG,IAAA;AAAA,EAAQ;AACrE,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACvB,IAAA,KAAA,MAAW,KAAA,IAAS,IAAA,EAAM,UAAA,CAAW,KAAA,EAAO,KAAK,CAAA;AACjD,IAAA;AAAA,EACF;AACA,EAAA,IAAID,QAAAA,CAAQ,IAAI,CAAA,EAAG;AACjB,IAAA,MAAM,EAAE,GAAA,EAAK,KAAA,EAAO,QAAA,EAAS,GAAI,IAAA;AACjC,IAAA,KAAA,CAAM,IAAA,CAAK,KAAK,GAAG,CAAA;AACnB,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAChD,QAAA,IAAI,IAAI,UAAA,CAAW,IAAI,KAAK,GAAA,KAAQ,KAAA,IAAS,QAAQ,yBAAA,EAA2B;AAChF,QAAA,MAAM,QAAA,GAAWI,aAAAA,CAAa,GAAG,CAAA,IAAK,GAAA;AACtC,QAAA,MAAM,QAAA,GAAW,OAAO,KAAA,KAAU,UAAA,GAAa,OAAM,GAAI,KAAA;AACzD,QAAA,IAAI,aAAa,IAAA,EAAM;AACrB,UAAA,KAAA,CAAM,IAAA,CAAK,KAAK,QAAQ,CAAA;AAAA,QAC1B,CAAA,MAAA,IAAW,QAAA,KAAa,KAAA,IAAS,QAAA,IAAY,IAAA,EAAM;AACjD,UAAA,KAAA,CAAM,IAAA,CAAK,KAAK,QAAA,EAAU,IAAA,EAAMF,YAAW,MAAA,CAAO,QAAQ,CAAC,CAAA,EAAG,GAAG,CAAA;AAAA,QACnE;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAIC,cAAAA,CAAc,GAAA,CAAI,GAAG,CAAA,EAAG;AAAE,MAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAAG,MAAA;AAAA,IAAQ;AACzD,IAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AACd,IAAA,IAAI,KAAA,GAAQ,yBAAyB,CAAA,EAAG;AACtC,MAAA,KAAA,CAAM,IAAA,CAAM,KAAA,CAAM,yBAAyB,CAAA,CAAyB,MAAM,CAAA;AAAA,IAC5E,CAAA,MAAO;AACL,MAAA,KAAA,MAAW,KAAA,IAAS,QAAA,EAAU,UAAA,CAAW,KAAA,EAAO,KAAK,CAAA;AAAA,IACvD;AACA,IAAA,KAAA,CAAM,IAAA,CAAK,IAAA,EAAM,GAAA,EAAK,GAAG,CAAA;AACzB,IAAA;AAAA,EACF;AACA,EAAA,KAAA,CAAM,IAAA,CAAKF,WAAAA,CAAW,MAAA,CAAO,IAAI,CAAC,CAAC,CAAA;AACrC;AAmBA,SAAS,gBAAgB,CAAA,EAAgC;AACvD,EAAA,OAAOD,QAAAA,CAAQ,CAAC,CAAA,IAAK,CAAA,CAAE,GAAA,KAAQ,gBAAA;AACjC;AAKO,SAAS,UAAA,CAAW,UAAmB,aAAA,EAAsD;AAClG,EAAA,OAAO;AAAA,IACL,GAAA,EAAK,gBAAA;AAAA,IACL,KAAA,EAAO,EAAE,QAAA,EAAS;AAAA,IAClB,QAAA,EAAU,CAAC,aAAa;AAAA,GAC1B;AACF;AA6CA,gBAAuB,iBAAA,CACrB,MACA,OAAA,EACwB;AAExB,EAAA,eAAA,GAAkB,CAAA;AAClB,EAAA,iBAAA,GAAoB,EAAC;AAErB,EAAA,MAAM,UAAA,GAAa,SAAS,gBAAA,KAAqB,KAAA;AAGjD,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,MAAM,aAAA,EAAc;AAAA,EACtB;AAGA,EAAA,MAAM,YAAsB,EAAC;AAC7B,EAAA,gBAAA,CAAiB,MAAM,SAAS,CAAA;AAChC,EAAA,MAAM,SAAA,CAAU,KAAK,EAAE,CAAA;AAGvB,EAAA,IAAI,SAAS,eAAA,EAAiB;AAC5B,IAAA,MAAM,OAAA,CAAQ,eAAA;AAAA,EAChB;AAIA,EAAA,OAAO,iBAAA,CAAkB,SAAS,CAAA,EAAG;AACnC,IAAA,MAAM,OAAA,GAAU,CAAC,GAAG,iBAAiB,CAAA;AACrC,IAAA,iBAAA,GAAoB,EAAC;AAGrB,IAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,UAAA;AAAA,MAC5B,OAAA,CAAQ,GAAA,CAAI,OAAO,QAAA,KAAa;AAC9B,QAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,OAAA;AAC5B,QAAA,OAAO,EAAE,EAAA,EAAI,QAAA,CAAS,EAAA,EAAI,IAAA,EAAK;AAAA,MACjC,CAAC;AAAA,KACH;AAEA,IAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,MAAA,IAAI,MAAA,CAAO,WAAW,WAAA,EAAa;AACjC,QAAA,MAAM,WAAW,MAAA,CAAO,KAAA,CAAM,EAAA,EAAI,MAAA,CAAO,MAAM,IAAI,CAAA;AAAA,MACrD;AAAA,IAEF;AAAA,EACF;AACF;AAMA,SAAS,gBAAA,CAAiB,MAAe,KAAA,EAAuB;AAC9D,EAAA,IAAI,IAAA,IAAQ,IAAA,IAAQ,IAAA,KAAS,IAAA,IAAQ,SAAS,KAAA,EAAO;AACrD,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAAE,IAAA,KAAA,CAAM,IAAA,CAAKC,WAAAA,CAAW,IAAI,CAAC,CAAA;AAAG,IAAA;AAAA,EAAQ;AACtE,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAAE,IAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,IAAI,CAAC,CAAA;AAAG,IAAA;AAAA,EAAQ;AAClE,EAAA,IAAI,OAAO,SAAS,UAAA,EAAY;AAAE,IAAA,gBAAA,CAAiB,IAAA,IAAQ,KAAK,CAAA;AAAG,IAAA;AAAA,EAAQ;AAC3E,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACvB,IAAA,KAAA,MAAW,KAAA,IAAS,IAAA,EAAM,gBAAA,CAAiB,KAAA,EAAO,KAAK,CAAA;AACvD,IAAA;AAAA,EACF;AAGA,EAAA,IAAI,eAAA,CAAgB,IAAI,CAAA,EAAG;AACzB,IAAA,MAAM,EAAA,GAAK,WAAW,eAAA,EAAiB,CAAA,CAAA;AACvC,IAAA,MAAM,QAAA,GAAW,KAAK,KAAA,CAAM,QAAA;AAC5B,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,QAAA,CAAS,CAAC,CAAA;AAG/B,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,SAAA,EAAY,EAAE,CAAA,2BAAA,CAA6B,CAAA;AACtD,IAAA,UAAA,CAAW,UAAU,KAAK,CAAA;AAC1B,IAAA,KAAA,CAAM,KAAK,QAAQ,CAAA;AAGnB,IAAA,MAAM,OAAA,GAAU,OAAA,EAAQ,CAAE,IAAA,CAAK,CAAC,QAAA,KAAsB;AACpD,MAAA,MAAM,gBAA0B,EAAC;AACjC,MAAA,UAAA,CAAW,UAAU,aAAa,CAAA;AAClC,MAAA,OAAO,aAAA,CAAc,KAAK,EAAE,CAAA;AAAA,IAC9B,CAAC,CAAA;AACD,IAAA,iBAAA,CAAkB,IAAA,CAAK,EAAE,EAAA,EAAI,OAAA,EAAS,CAAA;AACtC,IAAA;AAAA,EACF;AAGA,EAAA,IAAID,QAAAA,CAAQ,IAAI,CAAA,EAAG;AACjB,IAAA,MAAM,EAAE,GAAA,EAAK,KAAA,EAAO,QAAA,EAAS,GAAI,IAAA;AACjC,IAAA,KAAA,CAAM,IAAA,CAAK,KAAK,GAAG,CAAA;AACnB,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAChD,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,IAAI,IAAI,UAAA,CAAW,IAAI,KAAK,GAAA,KAAQ,KAAA,IAAS,QAAQ,yBAAA,EAA2B;AAChF,QAAA,MAAM,QAAA,GAAWI,aAAAA,CAAa,GAAG,CAAA,IAAK,GAAA;AACtC,QAAA,MAAM,QAAA,GAAW,OAAO,KAAA,KAAU,UAAA,GAAa,OAAM,GAAI,KAAA;AACzD,QAAA,IAAI,aAAa,IAAA,EAAM;AACrB,UAAA,KAAA,CAAM,IAAA,CAAK,KAAK,QAAQ,CAAA;AAAA,QAC1B,CAAA,MAAA,IAAW,QAAA,KAAa,KAAA,IAAS,QAAA,IAAY,IAAA,EAAM;AACjD,UAAA,KAAA,CAAM,IAAA,CAAK,KAAK,QAAA,EAAU,IAAA,EAAMF,YAAW,MAAA,CAAO,QAAQ,CAAC,CAAA,EAAG,GAAG,CAAA;AAAA,QACnE;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAIC,cAAAA,CAAc,GAAA,CAAI,GAAG,CAAA,EAAG;AAAE,MAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAAG,MAAA;AAAA,IAAQ;AACzD,IAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AACd,IAAA,IAAI,KAAA,GAAQ,yBAAyB,CAAA,EAAG;AACtC,MAAA,KAAA,CAAM,IAAA,CAAM,KAAA,CAAM,yBAAyB,CAAA,CAAyB,MAAM,CAAA;AAAA,IAC5E,CAAA,MAAO;AACL,MAAA,KAAA,MAAW,KAAA,IAAS,QAAA,EAAU,gBAAA,CAAiB,KAAA,EAAO,KAAK,CAAA;AAAA,IAC7D;AACA,IAAA,KAAA,CAAM,IAAA,CAAK,IAAA,EAAM,GAAA,EAAK,GAAG,CAAA;AACzB,IAAA;AAAA,EACF;AAEA,EAAA,KAAA,CAAM,IAAA,CAAKF,WAAAA,CAAW,MAAA,CAAO,IAAI,CAAC,CAAC,CAAA;AACrC","file":"index.js","sourcesContent":["import { ssrSignal } from './ssr-reactive.js';\n\n// Void elements that don't have closing tags\nconst VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n// Map of prop names to HTML attribute names\nconst PROP_TO_ATTR: Record<string, string> = {\n className: 'class',\n htmlFor: 'for',\n tabIndex: 'tabindex',\n};\n\n// Escape HTML entities\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#x27;');\n}\n\n// Escape attribute values\nfunction escapeAttr(str: string): string {\n return str.replace(/&/g, '&amp;').replace(/\"/g, '&quot;');\n}\n\nexport interface VNode {\n tag: string;\n props: Record<string, unknown> | null;\n children: unknown[];\n}\n\n/**\n * Server-side hyperscript — creates a virtual node instead of a DOM element.\n */\nexport function sh(tag: string, props?: Record<string, unknown> | null, ...children: unknown[]): VNode {\n return { tag, props: props ?? null, children };\n}\n\n/**\n * Render a FormaJS virtual tree to an HTML string.\n *\n * Uses an array buffer internally to avoid O(n^2) string concatenation\n * for large trees, then joins once at the end.\n *\n * Usage:\n * ```ts\n * import { renderToString, sh } from '@getforma/core/ssr';\n *\n * const html = renderToString(\n * sh('div', { class: 'app' },\n * sh('h1', null, 'Hello SSR!'),\n * sh('p', null, () => count()), // signal getters resolved synchronously\n * )\n * );\n * ```\n */\nexport function renderToString(node: unknown): string {\n const parts: string[] = [];\n renderToBuffer(node, parts);\n return parts.join('');\n}\n\n/**\n * Internal: recursively render into a string array buffer.\n * Avoids O(n^2) string concatenation for large trees.\n */\nfunction renderToBuffer(node: unknown, parts: string[]): void {\n // null/undefined/boolean → empty\n if (node == null || node === true || node === false) return;\n\n // String → escaped text\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n\n // Number → stringified\n if (typeof node === 'number') { parts.push(String(node)); return; }\n\n // Function (signal getter) → resolve and render\n if (typeof node === 'function') { renderToBuffer(node(), parts); return; }\n\n // Array → render each\n if (Array.isArray(node)) {\n for (const child of node) renderToBuffer(child, parts);\n return;\n }\n\n // VNode\n if (isVNode(node)) {\n const { tag, props, children } = node;\n parts.push('<', tag);\n\n // Render props as attributes\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n // Skip event handlers, refs, and internal props\n if (key.startsWith('on') || key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n\n const attrName = PROP_TO_ATTR[key] ?? key;\n\n // Resolve reactive values\n const resolved = typeof value === 'function' ? value() : value;\n\n if (resolved === true) {\n parts.push(' ', attrName);\n } else if (resolved !== false && resolved != null) {\n parts.push(' ', attrName, '=\"', escapeAttr(String(resolved)), '\"');\n }\n }\n }\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n\n parts.push('>');\n\n // dangerouslySetInnerHTML\n if (props?.['dangerouslySetInnerHTML']) {\n parts.push((props['dangerouslySetInnerHTML'] as { __html: string }).__html);\n } else {\n // Render children\n for (const child of children) {\n renderToBuffer(child, parts);\n }\n }\n\n parts.push('</', tag, '>');\n return;\n }\n\n // Fallback: stringify\n parts.push(escapeHtml(String(node)));\n}\n\nfunction isVNode(v: unknown): v is VNode {\n return v != null && typeof v === 'object' && 'tag' in v && 'children' in v;\n}\n\n// ---------------------------------------------------------------------------\n// Hydration-aware rendering\n// ---------------------------------------------------------------------------\n\nexport interface RenderOptions {\n /** Enable hydration markers in output. */\n hydrate?: boolean;\n}\n\nlet hydrationId = 0;\n\n/**\n * Render a FormaJS virtual tree to an HTML string with hydration markers.\n *\n * Like `renderToString`, but injects comment markers and `data-forma-h`\n * attributes so the client-side `hydrate()` function can adopt existing\n * DOM nodes without re-creating them.\n *\n * Marker types:\n * - `data-forma-h=\"N\"` — element boundary (attribute on the element)\n * - `<!--forma-t:N-->` / `<!--/forma-t:N-->` — reactive text boundary\n * - `<!--forma-l:N-->` / `<!--/forma-l:N-->` — list boundary\n *\n * Usage:\n * ```ts\n * import { renderToStringWithHydration, sh } from '@getforma/core/ssr';\n *\n * const html = renderToStringWithHydration(\n * sh('div', { class: 'app' },\n * sh('h1', null, 'Hello SSR!'),\n * sh('p', null, () => count()),\n * )\n * );\n * ```\n */\nexport function renderToStringWithHydration(node: unknown): string {\n hydrationId = 0; // reset counter per render\n const parts: string[] = [];\n renderToBufferHydrated(node, parts);\n return parts.join('');\n}\n\n/**\n * Internal: recursively render into a string array buffer with hydration markers.\n */\nfunction renderToBufferHydrated(node: unknown, parts: string[]): void {\n // null/undefined/boolean → empty\n if (node == null || node === true || node === false) return;\n\n // String → escaped text\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n\n // Number → stringified\n if (typeof node === 'number') { parts.push(String(node)); return; }\n\n // Function (signal getter / reactive text) → wrap with text markers\n if (typeof node === 'function') {\n const id = hydrationId++;\n parts.push(`<!--forma-t:${id}-->`);\n renderToBufferHydrated(node(), parts);\n parts.push(`<!--/forma-t:${id}-->`);\n return;\n }\n\n // Array → wrap with list markers\n if (Array.isArray(node)) {\n const id = hydrationId++;\n parts.push(`<!--forma-l:${id}-->`);\n for (const child of node) renderToBufferHydrated(child, parts);\n parts.push(`<!--/forma-l:${id}-->`);\n return;\n }\n\n // VNode\n if (isVNode(node)) {\n const id = hydrationId++;\n const { tag, props, children } = node;\n\n // Add hydration data-attribute to element\n parts.push('<', tag, ` data-forma-h=\"${id}\"`);\n\n // Render props as attributes\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n // Skip event handlers, refs, and internal props\n if (key.startsWith('on') || key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n\n const attrName = PROP_TO_ATTR[key] ?? key;\n\n // Resolve reactive values\n const resolved = typeof value === 'function' ? value() : value;\n\n if (resolved === true) {\n parts.push(' ', attrName);\n } else if (resolved !== false && resolved != null) {\n parts.push(' ', attrName, '=\"', escapeAttr(String(resolved)), '\"');\n }\n }\n }\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n\n parts.push('>');\n\n // dangerouslySetInnerHTML\n if (props?.['dangerouslySetInnerHTML']) {\n parts.push((props['dangerouslySetInnerHTML'] as { __html: string }).__html);\n } else {\n // Render children\n for (const child of children) {\n renderToBufferHydrated(child, parts);\n }\n }\n\n parts.push('</', tag, '>');\n return;\n }\n\n // Fallback: stringify\n parts.push(escapeHtml(String(node)));\n}\n\n","/**\n * SSR-only signal: just a value holder, no reactivity needed on server.\n */\nexport function ssrSignal<T>(initial: T): [get: () => T, set: (v: T) => void] {\n let value = initial;\n return [() => value, (v) => { value = v; }];\n}\n\nexport function ssrComputed<T>(fn: () => T): () => T {\n return fn; // On server, just call the function directly\n}\n","/**\n * FormaJS SSR - Client Swap Script\n *\n * Tiny inline script injected into the HTML stream that handles\n * out-of-order Suspense boundary resolution.\n *\n * When the server resolves a Suspense boundary, it sends:\n * <script>$FORMA_SWAP(\"forma-s:0\",\"<resolved html>\")</script>\n *\n * This script replaces the fallback placeholder with the resolved content.\n */\n\n/**\n * Returns the client-side swap script as a string, ready to be injected\n * into the HTML stream (typically right after <body> or before first Suspense).\n */\nexport function getSwapScript(): string {\n return `<script>\nfunction $FORMA_SWAP(id,html){\n var el=document.getElementById(id);\n if(el){\n var tpl=document.createElement('template');\n tpl.innerHTML=html;\n el.replaceWith(tpl.content);\n }\n}\n</script>`;\n}\n\n/**\n * Returns a script tag that swaps a specific Suspense boundary's content.\n * Called when an async resource resolves during streaming.\n */\nexport function getSwapTag(id: string, html: string): string {\n // Escape </script> in the HTML to prevent breaking out of the script tag\n const escaped = html.replace(/<\\/script>/gi, '<\\\\/script>');\n return `<script>$FORMA_SWAP(\"${id}\",\"${escaped.replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n')}\")</script>`;\n}\n","/**\n * FormaJS SSR - Streaming Renderer\n *\n * Implements true chunked HTML streaming with out-of-order Suspense resolution.\n *\n * How it works:\n * 1. Yields shell HTML immediately (opening tags, head, static content)\n * 2. When a Suspense boundary is encountered:\n * - Yields the fallback HTML with a placeholder: <div id=\"forma-s:N\" style=\"display:contents\">\n * - Continues rendering siblings without blocking\n * - When the async resource resolves, yields a swap script\n * 3. Suspense boundaries resolve independently in any order (out-of-order streaming)\n */\n\nimport { getSwapScript, getSwapTag } from './client-script.js';\n\n// Re-use render helpers from render.ts\n// We need the VNode type and rendering logic\ninterface VNode {\n tag: string;\n props: Record<string, unknown> | null;\n children: unknown[];\n}\n\nfunction isVNode(v: unknown): v is VNode {\n return v != null && typeof v === 'object' && 'tag' in v && 'children' in v;\n}\n\n// Escape functions\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#x27;');\n}\n\nfunction escapeAttr(str: string): string {\n return str.replace(/&/g, '&amp;').replace(/\"/g, '&quot;');\n}\n\nconst VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\nconst PROP_TO_ATTR: Record<string, string> = {\n className: 'class',\n htmlFor: 'for',\n tabIndex: 'tabindex',\n};\n\n// ---------------------------------------------------------------------------\n// Suspense boundary tracking\n// ---------------------------------------------------------------------------\n\ninterface PendingBoundary {\n id: string;\n promise: Promise<string>;\n}\n\nlet suspenseCounter = 0;\nlet pendingBoundaries: PendingBoundary[] = [];\n\n// ---------------------------------------------------------------------------\n// Synchronous render to buffer (for non-async content)\n// ---------------------------------------------------------------------------\n\nfunction renderSync(node: unknown, parts: string[]): void {\n if (node == null || node === true || node === false) return;\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n if (typeof node === 'number') { parts.push(String(node)); return; }\n if (typeof node === 'function') { renderSync(node(), parts); return; }\n if (Array.isArray(node)) {\n for (const child of node) renderSync(child, parts);\n return;\n }\n if (isVNode(node)) {\n const { tag, props, children } = node;\n parts.push('<', tag);\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n if (key.startsWith('on') || key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n const attrName = PROP_TO_ATTR[key] ?? key;\n const resolved = typeof value === 'function' ? value() : value;\n if (resolved === true) {\n parts.push(' ', attrName);\n } else if (resolved !== false && resolved != null) {\n parts.push(' ', attrName, '=\"', escapeAttr(String(resolved)), '\"');\n }\n }\n }\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n parts.push('>');\n if (props?.['dangerouslySetInnerHTML']) {\n parts.push((props['dangerouslySetInnerHTML'] as { __html: string }).__html);\n } else {\n for (const child of children) renderSync(child, parts);\n }\n parts.push('</', tag, '>');\n return;\n }\n parts.push(escapeHtml(String(node)));\n}\n\n// ---------------------------------------------------------------------------\n// Suspense VNode detection\n// ---------------------------------------------------------------------------\n\n/**\n * A Suspense boundary VNode has a special shape:\n * { tag: 'forma-suspense', props: { fallback: VNode }, children: [asyncChildren] }\n *\n * Or it can be created via:\n * shSuspense(fallbackVNode, () => asyncContent)\n */\nexport interface SuspenseVNode {\n tag: 'forma-suspense';\n props: { fallback: unknown };\n children: [() => Promise<unknown>];\n}\n\nfunction isSuspenseVNode(v: unknown): v is SuspenseVNode {\n return isVNode(v) && v.tag === 'forma-suspense';\n}\n\n/**\n * Create a server-side Suspense VNode for streaming SSR.\n */\nexport function shSuspense(fallback: unknown, asyncChildren: () => Promise<unknown>): SuspenseVNode {\n return {\n tag: 'forma-suspense',\n props: { fallback },\n children: [asyncChildren],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Streaming renderer\n// ---------------------------------------------------------------------------\n\nexport interface StreamOptions {\n /** Script to bootstrap the client-side app (e.g. <script src=\"/app.js\"></script>) */\n bootstrapScript?: string;\n /** Whether to inject the $FORMA_SWAP client script (default: true) */\n injectSwapScript?: boolean;\n}\n\n/**\n * Render a FormaJS virtual tree to a stream of HTML chunks.\n *\n * Yields HTML progressively:\n * - Static content is yielded immediately\n * - Suspense boundaries yield their fallback, then resolve later\n * - Resolved Suspense boundaries yield swap scripts\n *\n * Usage:\n * ```ts\n * import { renderToStream, shSuspense, sh } from '@getforma/core/ssr';\n *\n * const stream = renderToStream(\n * sh('html', null,\n * sh('body', null,\n * sh('h1', null, 'Hello'),\n * shSuspense(\n * sh('div', null, 'Loading...'),\n * async () => {\n * const data = await fetchData();\n * return sh('div', null, data.name);\n * },\n * ),\n * ),\n * ),\n * );\n *\n * for await (const chunk of stream) {\n * response.write(chunk);\n * }\n * ```\n */\nexport async function* renderToStreamNew(\n node: unknown,\n options?: StreamOptions,\n): AsyncGenerator<string> {\n // Reset state\n suspenseCounter = 0;\n pendingBoundaries = [];\n\n const injectSwap = options?.injectSwapScript !== false;\n\n // Inject the swap script first (if there could be Suspense boundaries)\n if (injectSwap) {\n yield getSwapScript();\n }\n\n // Render the main tree synchronously, collecting Suspense boundaries\n const mainParts: string[] = [];\n renderStreamNode(node, mainParts);\n yield mainParts.join('');\n\n // Yield bootstrap script if provided\n if (options?.bootstrapScript) {\n yield options.bootstrapScript;\n }\n\n // Wait for and yield all pending Suspense boundaries\n // Each resolves independently (out-of-order)\n while (pendingBoundaries.length > 0) {\n const pending = [...pendingBoundaries];\n pendingBoundaries = [];\n\n // Race all pending boundaries — yield each as it resolves\n const results = await Promise.allSettled(\n pending.map(async (boundary) => {\n const html = await boundary.promise;\n return { id: boundary.id, html };\n }),\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled') {\n yield getSwapTag(result.value.id, result.value.html);\n }\n // On rejection, the fallback stays visible (no swap)\n }\n }\n}\n\n/**\n * Render a node synchronously. If a Suspense boundary is encountered,\n * render the fallback inline and queue the async resolution.\n */\nfunction renderStreamNode(node: unknown, parts: string[]): void {\n if (node == null || node === true || node === false) return;\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n if (typeof node === 'number') { parts.push(String(node)); return; }\n if (typeof node === 'function') { renderStreamNode(node(), parts); return; }\n if (Array.isArray(node)) {\n for (const child of node) renderStreamNode(child, parts);\n return;\n }\n\n // Suspense boundary: render fallback, queue async resolution\n if (isSuspenseVNode(node)) {\n const id = `forma-s:${suspenseCounter++}`;\n const fallback = node.props.fallback;\n const asyncFn = node.children[0]!;\n\n // Render fallback inside a replaceable container\n parts.push(`<div id=\"${id}\" style=\"display:contents\">`);\n renderSync(fallback, parts);\n parts.push('</div>');\n\n // Queue the async resolution\n const promise = asyncFn().then((resolved: unknown) => {\n const resolvedParts: string[] = [];\n renderSync(resolved, resolvedParts);\n return resolvedParts.join('');\n });\n pendingBoundaries.push({ id, promise });\n return;\n }\n\n // Regular VNode\n if (isVNode(node)) {\n const { tag, props, children } = node;\n parts.push('<', tag);\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n if (key === 'fallback') continue; // skip internal suspense props\n if (key.startsWith('on') || key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n const attrName = PROP_TO_ATTR[key] ?? key;\n const resolved = typeof value === 'function' ? value() : value;\n if (resolved === true) {\n parts.push(' ', attrName);\n } else if (resolved !== false && resolved != null) {\n parts.push(' ', attrName, '=\"', escapeAttr(String(resolved)), '\"');\n }\n }\n }\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n parts.push('>');\n if (props?.['dangerouslySetInnerHTML']) {\n parts.push((props['dangerouslySetInnerHTML'] as { __html: string }).__html);\n } else {\n for (const child of children) renderStreamNode(child, parts);\n }\n parts.push('</', tag, '>');\n return;\n }\n\n parts.push(escapeHtml(String(node)));\n}\n"]}
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ var chunkFPSLC62A_cjs = require('./chunk-FPSLC62A.cjs');
4
+
5
+ // src/reactive/tc39-compat.ts
6
+ var State = class {
7
+ _get;
8
+ _set;
9
+ constructor(initialValue, options) {
10
+ const [getter, setter] = chunkFPSLC62A_cjs.createSignal(initialValue);
11
+ this._get = getter;
12
+ if (options?.equals) {
13
+ const eq = options.equals;
14
+ this._set = (v) => {
15
+ const next = typeof v === "function" ? v(getter()) : v;
16
+ if (!eq(getter(), next)) setter(() => next);
17
+ };
18
+ } else {
19
+ this._set = setter;
20
+ }
21
+ if (options?.name) {
22
+ Object.defineProperty(getter, "name", { value: options.name });
23
+ }
24
+ }
25
+ get() {
26
+ return this._get();
27
+ }
28
+ set(value) {
29
+ this._set(value);
30
+ }
31
+ };
32
+ var Computed = class {
33
+ _get;
34
+ constructor(fn) {
35
+ this._get = chunkFPSLC62A_cjs.createComputed(fn);
36
+ }
37
+ get() {
38
+ return this._get();
39
+ }
40
+ };
41
+
42
+ exports.Computed = Computed;
43
+ exports.State = State;
44
+ //# sourceMappingURL=tc39-compat.cjs.map
45
+ //# sourceMappingURL=tc39-compat.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/reactive/tc39-compat.ts"],"names":["createSignal","createComputed"],"mappings":";;;;;AAyBO,IAAM,QAAN,MAAe;AAAA,EACZ,IAAA;AAAA,EACA,IAAA;AAAA,EAER,WAAA,CAAY,cAAiB,OAAA,EAA4B;AACvD,IAAA,MAAM,CAAC,MAAA,EAAQ,MAAM,CAAA,GAAIA,+BAAa,YAAY,CAAA;AAClD,IAAA,IAAA,CAAK,IAAA,GAAO,MAAA;AAGZ,IAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,MAAA,MAAM,KAAK,OAAA,CAAQ,MAAA;AACnB,MAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,KAA4B;AACvC,QAAA,MAAM,OAAO,OAAO,CAAA,KAAM,aAAc,CAAA,CAAqB,MAAA,EAAQ,CAAA,GAAI,CAAA;AACzE,QAAA,IAAI,CAAC,GAAG,MAAA,EAAO,EAAG,IAAI,CAAA,EAAG,MAAA,CAAO,MAAM,IAAI,CAAA;AAAA,MAC5C,CAAA;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,IAAA,GAAO,MAAA;AAAA,IACd;AAEA,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,MAAA,CAAO,eAAe,MAAA,EAAQ,MAAA,EAAQ,EAAE,KAAA,EAAO,OAAA,CAAQ,MAAM,CAAA;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,GAAA,GAAS;AACP,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB;AAAA,EAEA,IAAI,KAAA,EAAgB;AAClB,IAAA,IAAA,CAAK,KAAK,KAAK,CAAA;AAAA,EACjB;AACF;AAcO,IAAM,WAAN,MAAkB;AAAA,EACf,IAAA;AAAA,EAER,YAAY,EAAA,EAAa;AACvB,IAAA,IAAA,CAAK,IAAA,GAAOC,iCAAe,EAAE,CAAA;AAAA,EAC/B;AAAA,EAEA,GAAA,GAAS;AACP,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB;AACF","file":"tc39-compat.cjs","sourcesContent":["/**\n * Forma Reactive - TC39 Signals Compatibility Layer\n *\n * Optional class-based API matching the TC39 Signals proposal (Stage 1).\n * Not included in main bundle — import from '@getforma/core/tc39' subpath.\n *\n * When the TC39 proposal advances, these classes can be replaced with\n * native Signal.State and Signal.Computed.\n *\n * @see https://github.com/tc39/proposal-signals\n */\n\nimport { createSignal, type SignalOptions } from './signal.js';\nimport { createComputed } from './computed.js';\n\n/**\n * TC39-compatible reactive state container.\n * Equivalent to `Signal.State` in the TC39 proposal.\n *\n * ```ts\n * const count = new State(0);\n * console.log(count.get()); // 0\n * count.set(1);\n * ```\n */\nexport class State<T> {\n private _get: () => T;\n private _set: (v: T | ((prev: T) => T)) => void;\n\n constructor(initialValue: T, options?: SignalOptions<T>) {\n const [getter, setter] = createSignal(initialValue);\n this._get = getter;\n\n // Handle custom equality in the TC39 compat layer (not in createSignal hot path)\n if (options?.equals) {\n const eq = options.equals;\n this._set = (v: T | ((prev: T) => T)) => {\n const next = typeof v === 'function' ? (v as (prev: T) => T)(getter()) : v;\n if (!eq(getter(), next)) setter(() => next);\n };\n } else {\n this._set = setter;\n }\n\n if (options?.name) {\n Object.defineProperty(getter, 'name', { value: options.name });\n }\n }\n\n get(): T {\n return this._get();\n }\n\n set(value: T): void {\n this._set(value);\n }\n}\n\n/**\n * TC39-compatible computed value.\n * Equivalent to `Signal.Computed` in the TC39 proposal.\n *\n * ```ts\n * const count = new State(0);\n * const doubled = new Computed(() => count.get() * 2);\n * console.log(doubled.get()); // 0\n * count.set(5);\n * console.log(doubled.get()); // 10\n * ```\n */\nexport class Computed<T> {\n private _get: () => T;\n\n constructor(fn: () => T) {\n this._get = createComputed(fn);\n }\n\n get(): T {\n return this._get();\n }\n}\n"]}