foldkit 0.148.1 → 0.148.2-canary.85e5fda4e854

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 (95) hide show
  1. package/README.md +25 -32
  2. package/dist/controlledDomState.d.ts.map +1 -1
  3. package/dist/controlledDomState.js +5 -1
  4. package/dist/experimental/server/serialize.d.ts.map +1 -1
  5. package/dist/experimental/server/serialize.js +11 -1
  6. package/dist/experimental/server/server.d.ts +10 -7
  7. package/dist/experimental/server/server.d.ts.map +1 -1
  8. package/dist/experimental/server/template.d.ts +5 -1
  9. package/dist/experimental/server/template.d.ts.map +1 -1
  10. package/dist/experimental/server/template.js +16 -4
  11. package/dist/hydrate.d.ts.map +1 -1
  12. package/dist/hydrate.js +48 -0
  13. package/dist/hydrationMarker.d.ts +4 -3
  14. package/dist/hydrationMarker.d.ts.map +1 -1
  15. package/dist/hydrationMarker.js +4 -3
  16. package/dist/message/index.d.ts +2 -1
  17. package/dist/message/index.d.ts.map +1 -1
  18. package/dist/message/index.js +1 -1
  19. package/dist/message/public.d.ts +2 -1
  20. package/dist/message/public.d.ts.map +1 -1
  21. package/dist/message/public.js +1 -1
  22. package/dist/runtime/runtime.d.ts +9 -8
  23. package/dist/runtime/runtime.d.ts.map +1 -1
  24. package/dist/runtime/runtime.js +81 -29
  25. package/dist/schema/index.d.ts +53 -8
  26. package/dist/schema/index.d.ts.map +1 -1
  27. package/dist/schema/index.js +179 -21
  28. package/dist/subscription/subscription.d.ts +1 -1
  29. package/dist/subscription/subscription.js +1 -1
  30. package/dist/test/apps/attributes.d.ts +7 -4
  31. package/dist/test/apps/attributes.d.ts.map +1 -1
  32. package/dist/test/apps/attributes.js +6 -6
  33. package/dist/test/apps/bubbling.d.ts +8 -2
  34. package/dist/test/apps/bubbling.d.ts.map +1 -1
  35. package/dist/test/apps/bubbling.js +10 -9
  36. package/dist/test/apps/colorPicker.d.ts +7 -6
  37. package/dist/test/apps/colorPicker.d.ts.map +1 -1
  38. package/dist/test/apps/colorPicker.js +8 -7
  39. package/dist/test/apps/contextMenu.d.ts +14 -4
  40. package/dist/test/apps/contextMenu.d.ts.map +1 -1
  41. package/dist/test/apps/contextMenu.js +8 -9
  42. package/dist/test/apps/counter.d.ts +52 -24
  43. package/dist/test/apps/counter.d.ts.map +1 -1
  44. package/dist/test/apps/counter.js +23 -34
  45. package/dist/test/apps/crashOnRender.d.ts +44 -28
  46. package/dist/test/apps/crashOnRender.d.ts.map +1 -1
  47. package/dist/test/apps/crashOnRender.js +17 -20
  48. package/dist/test/apps/drafts.d.ts +19 -8
  49. package/dist/test/apps/drafts.d.ts.map +1 -1
  50. package/dist/test/apps/drafts.js +10 -11
  51. package/dist/test/apps/feedSocket.d.ts +13 -13
  52. package/dist/test/apps/feedSocket.d.ts.map +1 -1
  53. package/dist/test/apps/feedSocket.js +17 -28
  54. package/dist/test/apps/fileUpload.d.ts +7 -6
  55. package/dist/test/apps/fileUpload.d.ts.map +1 -1
  56. package/dist/test/apps/fileUpload.js +9 -8
  57. package/dist/test/apps/formChild.d.ts +40 -24
  58. package/dist/test/apps/formChild.d.ts.map +1 -1
  59. package/dist/test/apps/formChild.js +31 -32
  60. package/dist/test/apps/interactions.d.ts +16 -11
  61. package/dist/test/apps/interactions.d.ts.map +1 -1
  62. package/dist/test/apps/interactions.js +21 -24
  63. package/dist/test/apps/keypress.d.ts +11 -11
  64. package/dist/test/apps/keypress.d.ts.map +1 -1
  65. package/dist/test/apps/keypress.js +12 -12
  66. package/dist/test/apps/login.d.ts +43 -23
  67. package/dist/test/apps/login.d.ts.map +1 -1
  68. package/dist/test/apps/login.js +17 -25
  69. package/dist/test/apps/logoutButton.d.ts +12 -6
  70. package/dist/test/apps/logoutButton.d.ts.map +1 -1
  71. package/dist/test/apps/logoutButton.js +15 -11
  72. package/dist/test/apps/mountPanel.d.ts +19 -20
  73. package/dist/test/apps/mountPanel.d.ts.map +1 -1
  74. package/dist/test/apps/mountPanel.js +22 -24
  75. package/dist/test/apps/multiRole.d.ts +6 -3
  76. package/dist/test/apps/multiRole.d.ts.map +1 -1
  77. package/dist/test/apps/multiRole.js +8 -7
  78. package/dist/test/apps/pointer.d.ts +13 -6
  79. package/dist/test/apps/pointer.d.ts.map +1 -1
  80. package/dist/test/apps/pointer.js +11 -10
  81. package/dist/test/apps/resumeUpload.d.ts +11 -14
  82. package/dist/test/apps/resumeUpload.d.ts.map +1 -1
  83. package/dist/test/apps/resumeUpload.js +17 -27
  84. package/dist/test/apps/scorePanel.d.ts +6 -3
  85. package/dist/test/apps/scorePanel.d.ts.map +1 -1
  86. package/dist/test/apps/scorePanel.js +8 -7
  87. package/dist/test/apps/selectiveKeys.d.ts +5 -1
  88. package/dist/test/apps/selectiveKeys.d.ts.map +1 -1
  89. package/dist/test/apps/selectiveKeys.js +13 -12
  90. package/dist/test/apps/uploads.d.ts +52 -30
  91. package/dist/test/apps/uploads.d.ts.map +1 -1
  92. package/dist/test/apps/uploads.js +20 -30
  93. package/dist/update/update.d.ts +4 -7
  94. package/dist/update/update.d.ts.map +1 -1
  95. package/package.json +1 -1
@@ -206,13 +206,10 @@ const hydrationForRoot = (root, isFlagsRequired) => {
206
206
  // id (a descendant `id="root"`) and `getElementById` returned that inner element
207
207
  // instead of the stamped root above it. That inner element resolves to the app
208
208
  // root only when the page has exactly one stamped root and this container sits
209
- // inside it, so a sibling widget (a stamped root the container is not inside) or
210
- // an outer application's root (when a nested page carries more than one stamped
211
- // root) is never wrongly adopted; those fall through to a fresh boot or a hard
212
- // failure instead. A null container is the replace-parity case, where the server
213
- // root took the placeholder's place and `getElementById` no longer finds it, so
214
- // the stamp is the only handle; more than one stamped root is then ambiguous and
215
- // a hard error rather than a silent wrong-DOM adoption.
209
+ // inside it. A container outside that root is a refused handoff, not a fresh
210
+ // application beside server markup that remains live. A null container is the
211
+ // replace-parity case, where the server root took the placeholder's place and
212
+ // `getElementById` no longer finds it, so the stamp is the only handle.
216
213
  // A runtime id names one application for the whole page: it pairs a root with
217
214
  // its Flags payload, and it keys the Model and scroll position hot reloading
218
215
  // preserves. Two roots sharing one are not two applications but one claimed
@@ -227,18 +224,42 @@ const assertRuntimeIdsAreUnique = (stampedRoots) => {
227
224
  const seen = new Set();
228
225
  for (const root of stampedRoots) {
229
226
  const runtimeId = root.getAttribute(FOLDKIT_APP_ATTRIBUTE) ?? '';
227
+ if (runtimeId === '') {
228
+ containRefusedPage(root.ownerDocument);
229
+ throw new Error('[foldkit] Found a server-rendered root with an empty ' +
230
+ `\`${FOLDKIT_APP_ATTRIBUTE}\` stamp. A hydratable root must carry ` +
231
+ 'a nonempty runtime id so the runtime can pair it with its Flags ' +
232
+ 'payload and preserved HMR state.');
233
+ }
234
+ if (!root.ownerDocument.body?.contains(root)) {
235
+ containRefusedPage(root.ownerDocument);
236
+ throw new Error('[foldkit] Found a server-rendered root outside the document body. ' +
237
+ 'Runtime.hydrate supports one page-owning application in the ' +
238
+ 'document light DOM. Do not hydrate a root in the head, a shadow ' +
239
+ 'tree, or a detached subtree.');
240
+ }
230
241
  if (seen.has(runtimeId)) {
231
242
  containRefusedPage(root.ownerDocument);
232
243
  throw new Error(`[foldkit] Found more than one server-rendered root stamped ` +
233
244
  `"${runtimeId}". A runtime id names one application for the whole ` +
234
245
  'page: it pairs a root with its Flags payload and keys the Model and ' +
235
246
  'scroll position hot reloading preserves, so two roots sharing one ' +
236
- "would take each other's state. Render each application with its " +
237
- 'own `runtimeId`.');
247
+ "would take each other's state. Remove the duplicate root. Foldkit " +
248
+ 'hydrates one page-owning application per document.');
238
249
  }
239
250
  seen.add(runtimeId);
240
251
  }
241
252
  };
253
+ const assertSinglePageApplication = (stampedRoots) => {
254
+ if (stampedRoots.length > 1) {
255
+ containRefusedPage(document);
256
+ throw new Error('[foldkit] Found more than one page-owning application stamped with ' +
257
+ `\`${FOLDKIT_APP_ATTRIBUTE}\`. Hydrating multiple applications in ` +
258
+ 'one document is not supported: each application owns the document ' +
259
+ 'metadata and installs document-wide navigation listeners. Render ' +
260
+ 'one application per page.');
261
+ }
262
+ };
242
263
  // The reason this page cannot be adopted by this client, or `undefined` when
243
264
  // the two name the same deployment.
244
265
  //
@@ -290,16 +311,28 @@ const preventRefusalShieldInteraction = (event) => {
290
311
  event.preventDefault();
291
312
  event.stopImmediatePropagation();
292
313
  };
314
+ // NOTE: `showModal` throws when the owning document is not fully active. A
315
+ // foreign or detached document still needs the original Foldkit refusal rather
316
+ // than a browser exception from its containment UI.
317
+ const tryOpenRefusalShieldAsModal = (shield) => {
318
+ if (typeof shield.showModal !== 'function') {
319
+ return false;
320
+ }
321
+ try {
322
+ shield.showModal();
323
+ return true;
324
+ }
325
+ catch {
326
+ return false;
327
+ }
328
+ };
293
329
  const openRefusalShield = (shield) => {
294
330
  shield.inert = true;
295
331
  shield.setAttribute('inert', '');
296
332
  if (shield.open && typeof shield.close === 'function') {
297
333
  shield.close();
298
334
  }
299
- if (typeof shield.showModal === 'function') {
300
- shield.showModal();
301
- }
302
- else {
335
+ if (!tryOpenRefusalShieldAsModal(shield)) {
303
336
  shield.setAttribute('open', '');
304
337
  }
305
338
  shield.inert = false;
@@ -373,28 +406,46 @@ const hasServerRenderedMarkup = (ownerDocument) => ownerDocument.querySelector(`
373
406
  const findDocumentHydration = (container, isFlagsRequired) => {
374
407
  const stampedRoots = Array.fromIterable(document.querySelectorAll(`[${FOLDKIT_APP_ATTRIBUTE}]`));
375
408
  assertRuntimeIdsAreUnique(stampedRoots);
409
+ assertSinglePageApplication(stampedRoots);
376
410
  if (container !== null) {
411
+ const stampedAncestor = container.closest(`[${FOLDKIT_APP_ATTRIBUTE}]`);
377
412
  if (container.hasAttribute(FOLDKIT_APP_ATTRIBUTE)) {
413
+ const isDocumentRoot = Option.match(Array.head(stampedRoots), {
414
+ onNone: () => false,
415
+ onSome: root => root === container,
416
+ });
417
+ if (!isDocumentRoot) {
418
+ containRefusedPage(container.ownerDocument);
419
+ throw new Error('[foldkit] Runtime.hydrate received a stamped container that is ' +
420
+ 'not the single server root in the document light DOM. Hydration ' +
421
+ 'supports one page-owning application under the document body. ' +
422
+ 'Do not hydrate a root in a shadow tree or detached subtree.');
423
+ }
378
424
  return hydrationForRoot(container, isFlagsRequired);
379
425
  }
380
426
  return Array.match(stampedRoots, {
381
- onEmpty: () => undefined,
427
+ onEmpty: () => {
428
+ if (stampedAncestor === null) {
429
+ return undefined;
430
+ }
431
+ containRefusedPage(container.ownerDocument);
432
+ throw new Error('[foldkit] Runtime.hydrate received a container under a stamped ' +
433
+ 'root outside the document body light DOM. Do not hydrate a root ' +
434
+ 'in a shadow tree, detached subtree, or another document.');
435
+ },
382
436
  onNonEmpty: roots => {
383
437
  const onlyRoot = Array.headNonEmpty(roots);
384
- return roots.length === 1 &&
385
- container.closest(`[${FOLDKIT_APP_ATTRIBUTE}]`) === onlyRoot
386
- ? hydrationForRoot(onlyRoot, isFlagsRequired)
387
- : undefined;
438
+ if (stampedAncestor === onlyRoot) {
439
+ return hydrationForRoot(onlyRoot, isFlagsRequired);
440
+ }
441
+ containRefusedPage(container.ownerDocument);
442
+ throw new Error('[foldkit] Runtime.hydrate received a container outside the ' +
443
+ "document's server-rendered application root. A page-owning " +
444
+ 'application must adopt that single root rather than boot beside ' +
445
+ 'server markup it does not own.');
388
446
  },
389
447
  });
390
448
  }
391
- if (stampedRoots.length > 1) {
392
- containRefusedPage(document);
393
- throw new Error('[foldkit] Found multiple server-rendered roots stamped with ' +
394
- `\`${FOLDKIT_APP_ATTRIBUTE}\` but no container to disambiguate them. ` +
395
- 'Give each app its own container element so the runtime can tell ' +
396
- 'which root to hydrate.');
397
- }
398
449
  return Option.match(Array.head(stampedRoots), {
399
450
  onNone: () => undefined,
400
451
  onSome: root => hydrationForRoot(root, isFlagsRequired),
@@ -2087,10 +2138,11 @@ export function run(program, options) {
2087
2138
  * of building it fresh. Use this as the client entry for a page served by
2088
2139
  * `renderToString`: the first render attaches to the stamped root, keeps the
2089
2140
  * existing nodes, and reconstructs the Model from the Flags the server
2090
- * embedded. The handoff is strict: a missing server root, a root stamped more
2091
- * than once, more than one root with no container to choose between them, a
2092
- * missing Flags payload, an undecodable payload, or a page from another
2093
- * deployment terminates startup. Every one of those contains the page first:
2141
+ * embedded. The handoff is strict: a missing server root, an empty or
2142
+ * duplicated root stamp, more than one stamped root, a requested root outside
2143
+ * the document body light DOM, a missing Flags payload, an undecodable
2144
+ * payload, or a page from another deployment terminates startup. Every one of
2145
+ * those contains the page first:
2094
2146
  * the document's body is marked `inert` and a nondismissable modal shield is
2095
2147
  * opened above existing top-layer content, so pointer and physical keyboard
2096
2148
  * input do not activate same-document native links, forms, or controls.
@@ -5,21 +5,65 @@ export type CallableTaggedStruct<Tag extends string, Fields extends S.Struct.Fie
5
5
  } & Fields>> : (value: Parameters<S.TaggedStruct<Tag, Fields>['make']>[0]) => Types.Simplify<S.Struct.Type<{
6
6
  readonly _tag: S.tag<Tag>;
7
7
  } & Fields>>);
8
+ type TaggedUnionProperty = keyof S.TaggedUnion<{}>;
9
+ type MessageVariantNameCollision<Name extends PropertyKey> = Readonly<{
10
+ 'Message variant names must not conflict with Schema.TaggedUnion properties': Name;
11
+ }>;
12
+ type ValidateMessageVariantNames<CasesByTag extends Record<string, S.Struct.Fields>> = Extract<keyof CasesByTag, TaggedUnionProperty> extends infer Name ? [Name] extends [never] ? unknown : MessageVariantNameCollision<Name & PropertyKey> : never;
13
+ type TaggedMessageUnion<CasesByTag extends Record<string, S.Struct.Fields>> = S.TaggedUnion<{
14
+ readonly [Tag in keyof CasesByTag & string]: S.TaggedStruct<Tag, CasesByTag[Tag]>;
15
+ }>;
16
+ interface MessageSchema<CasesByTag extends Record<string, S.Struct.Fields>> extends S.BottomLazy<TaggedMessageUnion<CasesByTag>['ast'], MessageSchema<CasesByTag>> {
17
+ readonly Type: TaggedMessageUnion<CasesByTag>['Type'];
18
+ readonly Encoded: TaggedMessageUnion<CasesByTag>['Encoded'];
19
+ readonly DecodingServices: TaggedMessageUnion<CasesByTag>['DecodingServices'];
20
+ readonly EncodingServices: TaggedMessageUnion<CasesByTag>['EncodingServices'];
21
+ readonly '~type.make.in': TaggedMessageUnion<CasesByTag>['~type.make.in'];
22
+ readonly '~type.make': TaggedMessageUnion<CasesByTag>['~type.make'];
23
+ readonly Iso: TaggedMessageUnion<CasesByTag>['Iso'];
24
+ readonly match: TaggedMessageUnion<CasesByTag>['match'];
25
+ }
26
+ /** The union `defineMessageUnion` returns. A Schema with exhaustive matching
27
+ * and one callable constructor per variant, reachable by tag. */
28
+ export type MessageUnion<CasesByTag extends Record<string, S.Struct.Fields>> = MessageSchema<CasesByTag> & {
29
+ readonly [Tag in keyof CasesByTag & string]: CallableTaggedStruct<Tag, CasesByTag[Tag]>;
30
+ };
8
31
  /**
9
- * Wraps `Schema.TaggedStruct` to create a message variant you can call directly as a constructor.
10
- * Use `m` for message types — enabling `ClickedReset()` instead of `ClickedReset.make()`.
32
+ * Declares a whole Message union from one record of fields per variant, naming
33
+ * each variant once instead of once per constructor and once in the union list.
34
+ *
35
+ * The result is a Schema, so it decodes and nests in a Model. Its focused
36
+ * Message surface is exhaustive `match` plus one callable constructor per
37
+ * variant. Each constructor is itself a schema, which is what `Command.define`
38
+ * needs for its `messages` list.
39
+ *
40
+ * Use `Message.match` for exhaustive dispatch. The values are ordinary tagged
41
+ * objects, so Effect `Match` remains available for partial matching, one
42
+ * handler over several tags, and fallbacks.
43
+ *
44
+ * A Submodel's OutMessage is declared the same way, with variants of its own. A
45
+ * Message is a fact the Submodel handles; an OutMessage is a fact it reports to
46
+ * its parent. Sharing one variant between the two unions puts the child's
47
+ * internal vocabulary in the parent's contract, so declare them separately even
48
+ * when a pair happens to carry the same fields.
49
+ *
50
+ * A variant may not be named after the schema surface it would shadow, such as
51
+ * `make`, `match`, `cases`, or `ast`. TypeScript reports the conflicting names,
52
+ * and untyped calls fail with a runtime error.
11
53
  *
12
54
  * @example
13
55
  * ```typescript
14
- * const ClickedReset = m('ClickedReset')
15
- * ClickedReset() // { _tag: 'ClickedReset' }
56
+ * export const Message = defineMessageUnion({
57
+ * ClickedReset: {},
58
+ * ChangedCount: { count: S.Number },
59
+ * })
60
+ * export type Message = typeof Message.Type
16
61
  *
17
- * const ChangedCount = m('ChangedCount', { count: S.Number })
18
- * ChangedCount({ count: 1 }) // { _tag: 'ChangedCount', count: 1 }
62
+ * Message.ClickedReset() // { _tag: 'ClickedReset' }
63
+ * Message.ChangedCount({ count: 1 }) // { _tag: 'ChangedCount', count: 1 }
19
64
  * ```
20
65
  */
21
- export declare function m<Tag extends string>(tag: Tag): CallableTaggedStruct<Tag, {}>;
22
- export declare function m<Tag extends string, Fields extends S.Struct.Fields>(tag: Tag, fields: Fields): CallableTaggedStruct<Tag, Fields>;
66
+ export declare function defineMessageUnion<const CasesByTag extends Record<string, S.Struct.Fields>>(casesByTag: CasesByTag & ValidateMessageVariantNames<CasesByTag>): MessageUnion<CasesByTag>;
23
67
  /**
24
68
  * Wraps `Schema.TaggedStruct` to create a route variant you can call directly as a constructor.
25
69
  * Use `r` for route types — enabling `Home()` instead of `Home.make()`.
@@ -51,4 +95,5 @@ export declare function r<Tag extends string, Fields extends S.Struct.Fields>(ta
51
95
  */
52
96
  export declare function ts<Tag extends string>(tag: Tag): CallableTaggedStruct<Tag, {}>;
53
97
  export declare function ts<Tag extends string, Fields extends S.Struct.Fields>(tag: Tag, fields: Fields): CallableTaggedStruct<Tag, Fields>;
98
+ export {};
54
99
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAE3C,qIAAqI;AACrI,MAAM,MAAM,oBAAoB,CAC9B,GAAG,SAAS,MAAM,EAClB,MAAM,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,IAC5B,CAAC,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,GAC7B,CAAC,MAAM,MAAM,SAAS,KAAK,GACvB,CACE,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAC9D,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;CAAE,GAAG,MAAM,CAAC,CAAC,GAC1E,CACE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KACtD,KAAK,CAAC,QAAQ,CACjB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;CAAE,GAAG,MAAM,CAAC,CACtD,CAAC,CAAA;AAqBR;;;;;;;;;;;;GAYG;AACH,wBAAgB,CAAC,CAAC,GAAG,SAAS,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,oBAAoB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;AAC9E,wBAAgB,CAAC,CAAC,GAAG,SAAS,MAAM,EAAE,MAAM,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,EAClE,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,GACb,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AAKpC;;;;;;;;;;;;GAYG;AACH,wBAAgB,CAAC,CAAC,GAAG,SAAS,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,oBAAoB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;AAC9E,wBAAgB,CAAC,CAAC,GAAG,SAAS,MAAM,EAAE,MAAM,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,EAClE,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,GACb,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AAKpC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,EAAE,CAAC,GAAG,SAAS,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,oBAAoB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;AAC/E,wBAAgB,EAAE,CAAC,GAAG,SAAS,MAAM,EAAE,MAAM,SAAS,CAAC,CAAC,MAAM,CAAC,MAAM,EACnE,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,GACb,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"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"}
@@ -1,26 +1,184 @@
1
- import { Schema as S } from 'effect';
2
- const makeCallable = (schema) =>
3
- /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
4
- new Proxy(function () { }, {
5
- apply(_target, _thisArg, argumentsList) {
6
- return schema.make(argumentsList[0] ?? {});
7
- },
8
- get(_target, property, receiver) {
9
- return Reflect.get(schema, property, receiver);
10
- },
11
- has(_target, property) {
12
- return Reflect.has(schema, property);
13
- },
14
- getPrototypeOf() {
15
- return Reflect.getPrototypeOf(schema);
16
- },
17
- });
18
- export function m(tag, fields = {}) {
19
- return makeCallable(S.TaggedStruct(tag, fields));
1
+ import { Array, Match as M, Schema as S, SchemaAST } from 'effect';
2
+ const assignPlainProperty = (output, name, value) => {
3
+ if (name === '__proto__') {
4
+ Object.defineProperty(output, name, {
5
+ value,
6
+ writable: true,
7
+ enumerable: true,
8
+ configurable: true,
9
+ });
10
+ }
11
+ else {
12
+ output[name] = value;
13
+ }
14
+ };
15
+ const isDirectlyCopyable = (ast) => {
16
+ if (ast.checks !== undefined ||
17
+ ast.encoding !== undefined ||
18
+ ast.context !== undefined ||
19
+ ast.annotations?.['parseOptions'] !== undefined) {
20
+ return false;
21
+ }
22
+ return M.value(ast).pipe(M.withReturnType(), M.tagsExhaustive({
23
+ Declaration: () => false,
24
+ Null: () => true,
25
+ Undefined: () => true,
26
+ Void: () => true,
27
+ Never: () => true,
28
+ Unknown: () => true,
29
+ Any: () => true,
30
+ String: () => true,
31
+ Number: () => true,
32
+ Boolean: () => true,
33
+ BigInt: () => true,
34
+ Symbol: () => true,
35
+ Literal: () => true,
36
+ UniqueSymbol: () => true,
37
+ ObjectKeyword: () => true,
38
+ Enum: () => true,
39
+ TemplateLiteral: ({ parts }) => parts.every(isDirectlyCopyable),
40
+ Arrays: () => false,
41
+ Objects: () => false,
42
+ Union: ({ mode, types }) => mode !== 'oneOf' && types.every(isDirectlyCopyable),
43
+ Suspend: () => false,
44
+ }));
45
+ };
46
+ const getDirectPropertyNames = (propertySignatures) => {
47
+ const names = [];
48
+ for (const { name, type } of propertySignatures) {
49
+ if (name !== '_tag' && !isDirectlyCopyable(SchemaAST.toType(type))) {
50
+ return undefined;
51
+ }
52
+ names.push(name);
53
+ }
54
+ return names;
55
+ };
56
+ const makeCallable = (tag, fields) => {
57
+ const schema = S.TaggedStruct(tag, fields);
58
+ const propertyNames = Object.hasOwn(fields, '_tag')
59
+ ? undefined
60
+ : getDirectPropertyNames(schema.ast.propertySignatures);
61
+ const make = (value) => schema.make(
62
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
63
+ (value ?? {}));
64
+ const construct = propertyNames !== undefined
65
+ ? (value) => {
66
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
67
+ const input = (value ?? {});
68
+ if (Object(input) !== input) {
69
+ return make(value);
70
+ }
71
+ const output = {};
72
+ for (const name of propertyNames) {
73
+ const descriptor = Object.getOwnPropertyDescriptor(input, name);
74
+ if (name !== '_tag' &&
75
+ descriptor === undefined &&
76
+ Reflect.has(input, name)) {
77
+ return make(value);
78
+ }
79
+ if (descriptor !== undefined && !('value' in descriptor)) {
80
+ return make(value);
81
+ }
82
+ const inputValue = descriptor === undefined ? undefined : input[name];
83
+ if (name === '_tag') {
84
+ if (inputValue !== undefined && inputValue !== tag) {
85
+ return make(value);
86
+ }
87
+ if (descriptor !== undefined && inputValue === undefined) {
88
+ assignPlainProperty(output, name, inputValue);
89
+ }
90
+ assignPlainProperty(output, name, tag);
91
+ }
92
+ else {
93
+ assignPlainProperty(output, name, inputValue);
94
+ }
95
+ }
96
+ return output;
97
+ }
98
+ : make;
99
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
100
+ return new Proxy(function () { }, {
101
+ apply(_target, _thisArg, argumentsList) {
102
+ return construct(argumentsList[0]);
103
+ },
104
+ get(_target, property, receiver) {
105
+ return Reflect.get(schema, property, receiver);
106
+ },
107
+ has(_target, property) {
108
+ return Reflect.has(schema, property);
109
+ },
110
+ getPrototypeOf() {
111
+ return Reflect.getPrototypeOf(schema);
112
+ },
113
+ });
114
+ };
115
+ const taggedUnionTypeOnlyPropertyNames = new Set([
116
+ 'Rebuild',
117
+ '~type.parameters',
118
+ 'Type',
119
+ 'Encoded',
120
+ 'DecodingServices',
121
+ 'EncodingServices',
122
+ '~type.make.in',
123
+ '~type.make',
124
+ '~type.constructor.default',
125
+ 'Iso',
126
+ '~type.mutability',
127
+ '~type.optionality',
128
+ '~encoded.mutability',
129
+ '~encoded.optionality',
130
+ ]);
131
+ /**
132
+ * Declares a whole Message union from one record of fields per variant, naming
133
+ * each variant once instead of once per constructor and once in the union list.
134
+ *
135
+ * The result is a Schema, so it decodes and nests in a Model. Its focused
136
+ * Message surface is exhaustive `match` plus one callable constructor per
137
+ * variant. Each constructor is itself a schema, which is what `Command.define`
138
+ * needs for its `messages` list.
139
+ *
140
+ * Use `Message.match` for exhaustive dispatch. The values are ordinary tagged
141
+ * objects, so Effect `Match` remains available for partial matching, one
142
+ * handler over several tags, and fallbacks.
143
+ *
144
+ * A Submodel's OutMessage is declared the same way, with variants of its own. A
145
+ * Message is a fact the Submodel handles; an OutMessage is a fact it reports to
146
+ * its parent. Sharing one variant between the two unions puts the child's
147
+ * internal vocabulary in the parent's contract, so declare them separately even
148
+ * when a pair happens to carry the same fields.
149
+ *
150
+ * A variant may not be named after the schema surface it would shadow, such as
151
+ * `make`, `match`, `cases`, or `ast`. TypeScript reports the conflicting names,
152
+ * and untyped calls fail with a runtime error.
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * export const Message = defineMessageUnion({
157
+ * ClickedReset: {},
158
+ * ChangedCount: { count: S.Number },
159
+ * })
160
+ * export type Message = typeof Message.Type
161
+ *
162
+ * Message.ClickedReset() // { _tag: 'ClickedReset' }
163
+ * Message.ChangedCount({ count: 1 }) // { _tag: 'ChangedCount', count: 1 }
164
+ * ```
165
+ */
166
+ export function defineMessageUnion(casesByTag) {
167
+ const union = S.TaggedUnion(casesByTag);
168
+ const conflictingNames = Array.filter(Object.keys(casesByTag), name => Reflect.has(union, name) || taggedUnionTypeOnlyPropertyNames.has(name));
169
+ if (Array.isArrayNonEmpty(conflictingNames)) {
170
+ throw new Error(`Message variant names conflict with Schema.TaggedUnion properties: ${conflictingNames.join(', ')}`);
171
+ }
172
+ const callables = {};
173
+ for (const [tag, fields] of Object.entries(casesByTag)) {
174
+ callables[tag] = makeCallable(tag, fields);
175
+ }
176
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
177
+ return Object.assign(union, callables);
20
178
  }
21
179
  export function r(tag, fields = {}) {
22
- return makeCallable(S.TaggedStruct(tag, fields));
180
+ return makeCallable(tag, fields);
23
181
  }
24
182
  export function ts(tag, fields = {}) {
25
- return makeCallable(S.TaggedStruct(tag, fields));
183
+ return makeCallable(tag, fields);
26
184
  }
@@ -225,7 +225,7 @@ type PerEntryGatedLiftedSubscriptions<ParentModel, ParentMessage, Subscriptions,
225
225
  *
226
226
  * const roomSubscriptions = Subscription.lift(Room.subscriptions)({
227
227
  * toChildModel: (model: Model) => model.room,
228
- * toParentMessage: (message: Room.Message.Message): Message =>
228
+ * toParentMessage: (message: Room.Message): Message =>
229
229
  * GotRoomMessage({ message }),
230
230
  * when: { roomKeyboard: ({ route }) => route._tag === 'Room' },
231
231
  * })
@@ -164,7 +164,7 @@ const toGatedEntry = (subscription, config, when) => {
164
164
  *
165
165
  * const roomSubscriptions = Subscription.lift(Room.subscriptions)({
166
166
  * toChildModel: (model: Model) => model.room,
167
- * toParentMessage: (message: Room.Message.Message): Message =>
167
+ * toParentMessage: (message: Room.Message): Message =>
168
168
  * GotRoomMessage({ message }),
169
169
  * when: { roomKeyboard: ({ route }) => route._tag === 'Room' },
170
170
  * })
@@ -1,13 +1,16 @@
1
- import { Schema as S } from 'effect';
2
1
  import type { Attribute, Html, HtmlBuilder } from '../../html/index.js';
3
- export declare const IgnoredInteraction: import("../../schema/index.js").CallableTaggedStruct<"IgnoredInteraction", {}>;
4
- export declare const Message: S.Union<readonly [import("../../schema/index.js").CallableTaggedStruct<"IgnoredInteraction", {}>]>;
2
+ export declare const Message: import("../../schema/index.js").MessageUnion<{
3
+ readonly IgnoredInteraction: {};
4
+ }>;
5
5
  export type Message = typeof Message.Type;
6
6
  export type Model = Readonly<{
7
7
  attribute: Attribute<Message>;
8
8
  tagName?: string;
9
9
  }>;
10
- export declare const update: (model: Model, message: Message) => readonly [Model, ReadonlyArray<never>];
10
+ export declare const update: (model: Model, message: Message) => readonly [Readonly<{
11
+ attribute: Attribute<Message>;
12
+ tagName?: string;
13
+ }>, readonly never[]];
11
14
  export declare const testId = "attribute-host";
12
15
  export declare const view: (model: Model, h: HtmlBuilder<Message>) => Html;
13
16
  //# sourceMappingURL=attributes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/test/apps/attributes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAGhD,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAKvE,eAAO,MAAM,kBAAkB,gFAA0B,CAAA;AAEzD,eAAO,MAAM,OAAO,oGAAgC,CAAA;AACpD,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,MAAM,MAAM,KAAK,GAAG,QAAQ,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAIF,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAMrC,CAAA;AAMH,eAAO,MAAM,MAAM,mBAAU,CAAA;AAE7B,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,KAAG,IAO5D,CAAA"}
1
+ {"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../../../src/test/apps/attributes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAKvE,eAAO,MAAM,OAAO;;EAElB,CAAA;AAEF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,MAAM,MAAM,KAAK,GAAG,QAAQ,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC,CAAA;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAIF,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,EAAE,SAAS,OAAO;eANxC,SAAS,CAAC,OAAO,CAAC;cACnB,MAAM;qBAQd,CAAA;AAMJ,eAAO,MAAM,MAAM,mBAAU,CAAA;AAE7B,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,KAAG,IAO5D,CAAA"}
@@ -1,13 +1,13 @@
1
- import { Match as M, Schema as S } from 'effect';
2
1
  import { customElement } from '../../html/index.js';
3
- import { m } from '../../message/index.js';
2
+ import { defineMessageUnion } from '../../message/index.js';
4
3
  // MESSAGE
5
- export const IgnoredInteraction = m('IgnoredInteraction');
6
- export const Message = S.Union([IgnoredInteraction]);
4
+ export const Message = defineMessageUnion({
5
+ IgnoredInteraction: {},
6
+ });
7
7
  // UPDATE
8
- export const update = (model, message) => M.value(message).pipe(M.withReturnType(), M.tagsExhaustive({
8
+ export const update = (model, message) => Message.match(message, {
9
9
  IgnoredInteraction: () => [model, []],
10
- }));
10
+ });
11
11
  // VIEW
12
12
  const TEST_ID = 'attribute-host';
13
13
  export const testId = TEST_ID;
@@ -5,10 +5,16 @@ export declare const Model: S.Struct<{
5
5
  readonly doubleClicks: S.Number;
6
6
  }>;
7
7
  export type Model = typeof Model.Type;
8
- declare const Message: S.Union<readonly [import("../../schema/index.js").CallableTaggedStruct<"ClickedContainer", {}>, import("../../schema/index.js").CallableTaggedStruct<"DoubleClickedContainer", {}>]>;
8
+ declare const Message: import("../../schema/index.js").MessageUnion<{
9
+ readonly ClickedContainer: {};
10
+ readonly DoubleClickedContainer: {};
11
+ }>;
9
12
  type Message = typeof Message.Type;
10
13
  export declare const initialModel: Model;
11
- export declare const update: (model: Model, message: Message) => readonly [Model, ReadonlyArray<never>];
14
+ export declare const update: (model: Model, message: Message) => readonly [{
15
+ readonly clicks: number;
16
+ readonly doubleClicks: number;
17
+ }, readonly never[]];
12
18
  export declare const view: (model: Model, h: HtmlBuilder<Message>) => Html;
13
19
  export {};
14
20
  //# sourceMappingURL=bubbling.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bubbling.d.ts","sourceRoot":"","sources":["../../../src/test/apps/bubbling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAK5D,eAAO,MAAM,KAAK;;;EAGhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAOrC,QAAA,MAAM,OAAO,sLAAsD,CAAA;AACnE,KAAK,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIlC,eAAO,MAAM,YAAY,EAAE,KAG1B,CAAA;AAID,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAUrC,CAAA;AAIH,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,KAAG,IAc5D,CAAA"}
1
+ {"version":3,"file":"bubbling.d.ts","sourceRoot":"","sources":["../../../src/test/apps/bubbling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEpC,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAK5D,eAAO,MAAM,KAAK;;;EAGhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,QAAA,MAAM,OAAO;;;EAGX,CAAA;AACF,KAAK,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIlC,eAAO,MAAM,YAAY,EAAE,KAG1B,CAAA;AAID,eAAO,MAAM,MAAM,GAAI,OAAO,KAAK,EAAE,SAAS,OAAO;;;oBAOjD,CAAA;AAIJ,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,KAAG,IAc5D,CAAA"}
@@ -1,31 +1,32 @@
1
- import { Match as M, Schema as S } from 'effect';
2
- import { m } from '../../message/index.js';
1
+ import { Schema as S } from 'effect';
2
+ import { defineMessageUnion } from '../../message/index.js';
3
3
  // MODEL
4
4
  export const Model = S.Struct({
5
5
  clicks: S.Number,
6
6
  doubleClicks: S.Number,
7
7
  });
8
8
  // MESSAGE
9
- const ClickedContainer = m('ClickedContainer');
10
- const DoubleClickedContainer = m('DoubleClickedContainer');
11
- const Message = S.Union([ClickedContainer, DoubleClickedContainer]);
9
+ const Message = defineMessageUnion({
10
+ ClickedContainer: {},
11
+ DoubleClickedContainer: {},
12
+ });
12
13
  // INIT
13
14
  export const initialModel = {
14
15
  clicks: 0,
15
16
  doubleClicks: 0,
16
17
  };
17
18
  // UPDATE
18
- export const update = (model, message) => M.value(message).pipe(M.withReturnType(), M.tagsExhaustive({
19
+ export const update = (model, message) => Message.match(message, {
19
20
  ClickedContainer: () => [{ ...model, clicks: model.clicks + 1 }, []],
20
21
  DoubleClickedContainer: () => [
21
22
  { ...model, doubleClicks: model.doubleClicks + 1 },
22
23
  [],
23
24
  ],
24
- }));
25
+ });
25
26
  // VIEW
26
27
  export const view = (model, h) => {
27
28
  return h.div([], [
28
- h.div([h.Role('option'), h.OnClick(ClickedContainer())], [h.span([], [`clicks=${model.clicks}`])]),
29
- h.div([h.Role('listitem'), h.OnDoubleClick(DoubleClickedContainer())], [h.span([], [`dbl=${model.doubleClicks}`])]),
29
+ h.div([h.Role('option'), h.OnClick(Message.ClickedContainer())], [h.span([], [`clicks=${model.clicks}`])]),
30
+ h.div([h.Role('listitem'), h.OnDoubleClick(Message.DoubleClickedContainer())], [h.span([], [`dbl=${model.doubleClicks}`])]),
30
31
  ]);
31
32
  };
@@ -12,17 +12,18 @@ export declare const Model: S.Struct<{
12
12
  readonly color: S.String;
13
13
  }>;
14
14
  export type Model = typeof Model.Type;
15
- export declare const ChangedColor: import("../../schema/index.js").CallableTaggedStruct<"ChangedColor", {
16
- value: S.String;
15
+ export declare const Message: import("../../schema/index.js").MessageUnion<{
16
+ readonly ChangedColor: {
17
+ readonly value: S.String;
18
+ };
17
19
  }>;
18
- export declare const Message: S.Union<readonly [import("../../schema/index.js").CallableTaggedStruct<"ChangedColor", {
19
- value: S.String;
20
- }>]>;
21
20
  export type Message = typeof Message.Type;
22
21
  export declare const initialModel: {
23
22
  readonly color: string;
24
23
  };
25
- export declare const update: (model: Model, message: Message) => readonly [Model, ReadonlyArray<never>];
24
+ export declare const update: (model: Model, message: Message) => readonly [{
25
+ readonly color: string;
26
+ }, readonly never[]];
26
27
  export declare const view: (model: Model, h: HtmlBuilder<Message>) => Html;
27
28
  export declare const viewWithoutHandler: (model: Model, h: HtmlBuilder<Message>) => Html;
28
29
  //# sourceMappingURL=colorPicker.d.ts.map