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.
- package/README.md +25 -32
- package/dist/controlledDomState.d.ts.map +1 -1
- package/dist/controlledDomState.js +5 -1
- package/dist/experimental/server/serialize.d.ts.map +1 -1
- package/dist/experimental/server/serialize.js +11 -1
- package/dist/experimental/server/server.d.ts +10 -7
- package/dist/experimental/server/server.d.ts.map +1 -1
- package/dist/experimental/server/template.d.ts +5 -1
- package/dist/experimental/server/template.d.ts.map +1 -1
- package/dist/experimental/server/template.js +16 -4
- package/dist/hydrate.d.ts.map +1 -1
- package/dist/hydrate.js +48 -0
- package/dist/hydrationMarker.d.ts +4 -3
- package/dist/hydrationMarker.d.ts.map +1 -1
- package/dist/hydrationMarker.js +4 -3
- package/dist/message/index.d.ts +2 -1
- package/dist/message/index.d.ts.map +1 -1
- package/dist/message/index.js +1 -1
- package/dist/message/public.d.ts +2 -1
- package/dist/message/public.d.ts.map +1 -1
- package/dist/message/public.js +1 -1
- package/dist/runtime/runtime.d.ts +9 -8
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +81 -29
- package/dist/schema/index.d.ts +53 -8
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +179 -21
- package/dist/subscription/subscription.d.ts +1 -1
- package/dist/subscription/subscription.js +1 -1
- package/dist/test/apps/attributes.d.ts +7 -4
- package/dist/test/apps/attributes.d.ts.map +1 -1
- package/dist/test/apps/attributes.js +6 -6
- package/dist/test/apps/bubbling.d.ts +8 -2
- package/dist/test/apps/bubbling.d.ts.map +1 -1
- package/dist/test/apps/bubbling.js +10 -9
- package/dist/test/apps/colorPicker.d.ts +7 -6
- package/dist/test/apps/colorPicker.d.ts.map +1 -1
- package/dist/test/apps/colorPicker.js +8 -7
- package/dist/test/apps/contextMenu.d.ts +14 -4
- package/dist/test/apps/contextMenu.d.ts.map +1 -1
- package/dist/test/apps/contextMenu.js +8 -9
- package/dist/test/apps/counter.d.ts +52 -24
- package/dist/test/apps/counter.d.ts.map +1 -1
- package/dist/test/apps/counter.js +23 -34
- package/dist/test/apps/crashOnRender.d.ts +44 -28
- package/dist/test/apps/crashOnRender.d.ts.map +1 -1
- package/dist/test/apps/crashOnRender.js +17 -20
- package/dist/test/apps/drafts.d.ts +19 -8
- package/dist/test/apps/drafts.d.ts.map +1 -1
- package/dist/test/apps/drafts.js +10 -11
- package/dist/test/apps/feedSocket.d.ts +13 -13
- package/dist/test/apps/feedSocket.d.ts.map +1 -1
- package/dist/test/apps/feedSocket.js +17 -28
- package/dist/test/apps/fileUpload.d.ts +7 -6
- package/dist/test/apps/fileUpload.d.ts.map +1 -1
- package/dist/test/apps/fileUpload.js +9 -8
- package/dist/test/apps/formChild.d.ts +40 -24
- package/dist/test/apps/formChild.d.ts.map +1 -1
- package/dist/test/apps/formChild.js +31 -32
- package/dist/test/apps/interactions.d.ts +16 -11
- package/dist/test/apps/interactions.d.ts.map +1 -1
- package/dist/test/apps/interactions.js +21 -24
- package/dist/test/apps/keypress.d.ts +11 -11
- package/dist/test/apps/keypress.d.ts.map +1 -1
- package/dist/test/apps/keypress.js +12 -12
- package/dist/test/apps/login.d.ts +43 -23
- package/dist/test/apps/login.d.ts.map +1 -1
- package/dist/test/apps/login.js +17 -25
- package/dist/test/apps/logoutButton.d.ts +12 -6
- package/dist/test/apps/logoutButton.d.ts.map +1 -1
- package/dist/test/apps/logoutButton.js +15 -11
- package/dist/test/apps/mountPanel.d.ts +19 -20
- package/dist/test/apps/mountPanel.d.ts.map +1 -1
- package/dist/test/apps/mountPanel.js +22 -24
- package/dist/test/apps/multiRole.d.ts +6 -3
- package/dist/test/apps/multiRole.d.ts.map +1 -1
- package/dist/test/apps/multiRole.js +8 -7
- package/dist/test/apps/pointer.d.ts +13 -6
- package/dist/test/apps/pointer.d.ts.map +1 -1
- package/dist/test/apps/pointer.js +11 -10
- package/dist/test/apps/resumeUpload.d.ts +11 -14
- package/dist/test/apps/resumeUpload.d.ts.map +1 -1
- package/dist/test/apps/resumeUpload.js +17 -27
- package/dist/test/apps/scorePanel.d.ts +6 -3
- package/dist/test/apps/scorePanel.d.ts.map +1 -1
- package/dist/test/apps/scorePanel.js +8 -7
- package/dist/test/apps/selectiveKeys.d.ts +5 -1
- package/dist/test/apps/selectiveKeys.d.ts.map +1 -1
- package/dist/test/apps/selectiveKeys.js +13 -12
- package/dist/test/apps/uploads.d.ts +52 -30
- package/dist/test/apps/uploads.d.ts.map +1 -1
- package/dist/test/apps/uploads.js +20 -30
- package/dist/update/update.d.ts +4 -7
- package/dist/update/update.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/runtime/runtime.js
CHANGED
|
@@ -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
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
//
|
|
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.
|
|
237
|
-
'
|
|
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 (
|
|
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: () =>
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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,
|
|
2091
|
-
*
|
|
2092
|
-
* missing Flags payload, an undecodable
|
|
2093
|
-
* deployment terminates startup. Every one of
|
|
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.
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
10
|
-
*
|
|
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
|
|
15
|
-
*
|
|
56
|
+
* export const Message = defineMessageUnion({
|
|
57
|
+
* ClickedReset: {},
|
|
58
|
+
* ChangedCount: { count: S.Number },
|
|
59
|
+
* })
|
|
60
|
+
* export type Message = typeof Message.Type
|
|
16
61
|
*
|
|
17
|
-
*
|
|
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
|
|
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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"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"}
|
package/dist/schema/index.js
CHANGED
|
@@ -1,26 +1,184 @@
|
|
|
1
|
-
import { Schema as S } from 'effect';
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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(
|
|
180
|
+
return makeCallable(tag, fields);
|
|
23
181
|
}
|
|
24
182
|
export function ts(tag, fields = {}) {
|
|
25
|
-
return makeCallable(
|
|
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
|
|
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
|
|
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
|
|
4
|
-
|
|
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 [
|
|
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":"
|
|
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 {
|
|
2
|
+
import { defineMessageUnion } from '../../message/index.js';
|
|
4
3
|
// MESSAGE
|
|
5
|
-
export const
|
|
6
|
-
|
|
4
|
+
export const Message = defineMessageUnion({
|
|
5
|
+
IgnoredInteraction: {},
|
|
6
|
+
});
|
|
7
7
|
// UPDATE
|
|
8
|
-
export const update = (model, message) =>
|
|
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:
|
|
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 [
|
|
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,
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
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) =>
|
|
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
|
|
16
|
-
|
|
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 [
|
|
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
|