@gyral/core 0.1.0 → 0.3.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.
- package/CHANGELOG.md +112 -0
- package/LICENSE +1 -1
- package/NOTICE +1 -1
- package/README.md +9 -7
- package/dist/children.d.ts +5 -2
- package/dist/children.js +8 -3
- package/dist/children.js.map +1 -1
- package/dist/command.js +6 -0
- package/dist/command.js.map +1 -1
- package/dist/compiler/ast.d.ts +21 -0
- package/dist/compiler/ast.js +145 -0
- package/dist/compiler/ast.js.map +1 -0
- package/dist/compiler/compile.d.ts +38 -0
- package/dist/compiler/compile.js +129 -0
- package/dist/compiler/compile.js.map +1 -0
- package/dist/compiler/hooks.d.ts +18 -0
- package/dist/compiler/hooks.js +186 -0
- package/dist/compiler/hooks.js.map +1 -0
- package/dist/compiler/parse5-check.d.ts +21 -0
- package/dist/compiler/parse5-check.js +55 -0
- package/dist/compiler/parse5-check.js.map +1 -0
- package/dist/compiler/scan.d.ts +31 -0
- package/dist/compiler/scan.js +152 -0
- package/dist/compiler/scan.js.map +1 -0
- package/dist/compiler/source.d.ts +34 -0
- package/dist/compiler/source.js +114 -0
- package/dist/compiler/source.js.map +1 -0
- package/dist/define.d.ts +2 -2
- package/dist/define.js +35 -334
- package/dist/define.js.map +1 -1
- package/dist/devtools-events.d.ts +5 -0
- package/dist/devtools-events.js.map +1 -1
- package/dist/devtools-off.d.ts +1 -0
- package/dist/devtools-off.js +1 -0
- package/dist/devtools-off.js.map +1 -1
- package/dist/devtools.d.ts +1 -0
- package/dist/devtools.js +3 -0
- package/dist/devtools.js.map +1 -1
- package/dist/drivers-scope.d.ts +1 -1
- package/dist/drivers-scope.js +1 -8
- package/dist/drivers-scope.js.map +1 -1
- package/dist/element-types.d.ts +5 -3
- package/dist/element-types.js.map +1 -1
- package/dist/element.d.ts +3 -0
- package/dist/element.js +269 -0
- package/dist/element.js.map +1 -0
- package/dist/eslint/imports.d.ts +24 -0
- package/dist/eslint/imports.js +78 -0
- package/dist/eslint/imports.js.map +1 -0
- package/dist/eslint/index.d.ts +15 -0
- package/dist/eslint/index.js +15 -0
- package/dist/eslint/index.js.map +1 -0
- package/dist/eslint/locate.d.ts +33 -0
- package/dist/eslint/locate.js +132 -0
- package/dist/eslint/locate.js.map +1 -0
- package/dist/eslint/row-purity-rule.d.ts +2 -0
- package/dist/eslint/row-purity-rule.js +103 -0
- package/dist/eslint/row-purity-rule.js.map +1 -0
- package/dist/eslint/template-rule.d.ts +5 -0
- package/dist/eslint/template-rule.js +66 -0
- package/dist/eslint/template-rule.js.map +1 -0
- package/dist/features.d.ts +39 -0
- package/dist/features.js +2 -0
- package/dist/features.js.map +1 -0
- package/dist/focus.d.ts +9 -2
- package/dist/focus.js +17 -2
- package/dist/focus.js.map +1 -1
- package/dist/hooks/invalid.d.ts +14 -0
- package/dist/hooks/invalid.js +72 -0
- package/dist/hooks/invalid.js.map +1 -0
- package/dist/hooks/labelled-by.d.ts +13 -0
- package/dist/hooks/labelled-by.js +51 -0
- package/dist/hooks/labelled-by.js.map +1 -0
- package/dist/host-model.d.ts +48 -0
- package/dist/host-model.js +155 -0
- package/dist/host-model.js.map +1 -0
- package/dist/hydration-client.d.ts +9 -0
- package/dist/hydration-client.js +35 -0
- package/dist/hydration-client.js.map +1 -0
- package/dist/hydration.d.ts +13 -19
- package/dist/hydration.js +45 -45
- package/dist/hydration.js.map +1 -1
- package/dist/index.d.ts +9 -16
- package/dist/index.js +9 -16
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +31 -8
- package/dist/intent.js +82 -27
- package/dist/intent.js.map +1 -1
- package/dist/internal/interpreter.d.ts +7 -1
- package/dist/internal/interpreter.js +118 -73
- package/dist/internal/interpreter.js.map +1 -1
- package/dist/invokers-shim.d.ts +6 -0
- package/dist/invokers-shim.js +32 -0
- package/dist/invokers-shim.js.map +1 -0
- package/dist/invokers.d.ts +3 -5
- package/dist/invokers.js +11 -51
- package/dist/invokers.js.map +1 -1
- package/dist/islands.d.ts +0 -2
- package/dist/islands.js +4 -11
- package/dist/islands.js.map +1 -1
- package/dist/light-dom.d.ts +3 -52
- package/dist/light-dom.js +5 -100
- package/dist/light-dom.js.map +1 -1
- package/dist/prop.d.ts +66 -0
- package/dist/prop.js +22 -0
- package/dist/prop.js.map +1 -0
- package/dist/props.d.ts +52 -21
- package/dist/props.js +150 -34
- package/dist/props.js.map +1 -1
- package/dist/scheduler.d.ts +39 -0
- package/dist/scheduler.js +230 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/server-component.d.ts +10 -0
- package/dist/server-component.js +102 -0
- package/dist/server-component.js.map +1 -0
- package/dist/server-specs.d.ts +6 -0
- package/dist/server-specs.js +13 -0
- package/dist/server-specs.js.map +1 -0
- package/dist/server.d.ts +28 -0
- package/dist/server.js +50 -0
- package/dist/server.js.map +1 -0
- package/dist/settled.d.ts +10 -0
- package/dist/settled.js +40 -0
- package/dist/settled.js.map +1 -0
- package/dist/states.d.ts +3 -3
- package/dist/states.js +4 -7
- package/dist/states.js.map +1 -1
- package/dist/store-binding.d.ts +6 -3
- package/dist/store-binding.js +5 -6
- package/dist/store-binding.js.map +1 -1
- package/dist/store-scope.d.ts +6 -7
- package/dist/store-scope.js +11 -26
- package/dist/store-scope.js.map +1 -1
- package/dist/store.d.ts +3 -0
- package/dist/store.js +9 -2
- package/dist/store.js.map +1 -1
- package/dist/stores-provider.d.ts +2 -6
- package/dist/stores-provider.js +21 -55
- package/dist/stores-provider.js.map +1 -1
- package/dist/transitions.d.ts +5 -6
- package/dist/transitions.js +11 -20
- package/dist/transitions.js.map +1 -1
- package/dist/types.d.ts +19 -23
- package/dist/types.js.map +1 -1
- package/dist/view/attributes.d.ts +6 -0
- package/dist/view/attributes.js +10 -0
- package/dist/view/attributes.js.map +1 -0
- package/dist/view/compiled.d.ts +1 -0
- package/dist/view/compiled.js +6 -0
- package/dist/view/compiled.js.map +1 -0
- package/dist/view/dev-off.d.ts +1 -0
- package/dist/view/dev-off.js +4 -0
- package/dist/view/dev-off.js.map +1 -0
- package/dist/view/dev.d.ts +1 -0
- package/dist/view/dev.js +5 -0
- package/dist/view/dev.js.map +1 -0
- package/dist/view/flags.d.ts +1 -0
- package/dist/view/flags.js +4 -0
- package/dist/view/flags.js.map +1 -0
- package/dist/view/index.d.ts +21 -0
- package/dist/view/index.js +21 -0
- package/dist/view/index.js.map +1 -0
- package/dist/view/normalize/check.d.ts +20 -0
- package/dist/view/normalize/check.js +78 -0
- package/dist/view/normalize/check.js.map +1 -0
- package/dist/view/normalize/emit.d.ts +13 -0
- package/dist/view/normalize/emit.js +136 -0
- package/dist/view/normalize/emit.js.map +1 -0
- package/dist/view/normalize/entities.d.ts +4 -0
- package/dist/view/normalize/entities.js +38 -0
- package/dist/view/normalize/entities.js.map +1 -0
- package/dist/view/normalize/errors.d.ts +9 -0
- package/dist/view/normalize/errors.js +19 -0
- package/dist/view/normalize/errors.js.map +1 -0
- package/dist/view/normalize/id.d.ts +7 -0
- package/dist/view/normalize/id.js +48 -0
- package/dist/view/normalize/id.js.map +1 -0
- package/dist/view/normalize/normalize.d.ts +15 -0
- package/dist/view/normalize/normalize.js +36 -0
- package/dist/view/normalize/normalize.js.map +1 -0
- package/dist/view/normalize/repairs.d.ts +24 -0
- package/dist/view/normalize/repairs.js +166 -0
- package/dist/view/normalize/repairs.js.map +1 -0
- package/dist/view/normalize/rules.d.ts +30 -0
- package/dist/view/normalize/rules.js +90 -0
- package/dist/view/normalize/rules.js.map +1 -0
- package/dist/view/normalize/shape.d.ts +5 -0
- package/dist/view/normalize/shape.js +38 -0
- package/dist/view/normalize/shape.js.map +1 -0
- package/dist/view/normalize/source.d.ts +13 -0
- package/dist/view/normalize/source.js +34 -0
- package/dist/view/normalize/source.js.map +1 -0
- package/dist/view/normalize/tag-states.d.ts +23 -0
- package/dist/view/normalize/tag-states.js +192 -0
- package/dist/view/normalize/tag-states.js.map +1 -0
- package/dist/view/normalize/tokenizer.d.ts +5 -0
- package/dist/view/normalize/tokenizer.js +233 -0
- package/dist/view/normalize/tokenizer.js.map +1 -0
- package/dist/view/normalize/tokens.d.ts +51 -0
- package/dist/view/normalize/tokens.js +24 -0
- package/dist/view/normalize/tokens.js.map +1 -0
- package/dist/view/normalize/tree.d.ts +59 -0
- package/dist/view/normalize/tree.js +190 -0
- package/dist/view/normalize/tree.js.map +1 -0
- package/dist/view/normalize/types.d.ts +126 -0
- package/dist/view/normalize/types.js +26 -0
- package/dist/view/normalize/types.js.map +1 -0
- package/dist/view/normalize/whitespace.d.ts +2 -0
- package/dist/view/normalize/whitespace.js +182 -0
- package/dist/view/normalize/whitespace.js.map +1 -0
- package/dist/view/prepare-stub.d.ts +3 -0
- package/dist/view/prepare-stub.js +7 -0
- package/dist/view/prepare-stub.js.map +1 -0
- package/dist/view/prepare.d.ts +8 -0
- package/dist/view/prepare.js +57 -0
- package/dist/view/prepare.js.map +1 -0
- package/dist/view/registry.d.ts +63 -0
- package/dist/view/registry.js +19 -0
- package/dist/view/registry.js.map +1 -0
- package/dist/view/render/adopt-attr.d.ts +14 -0
- package/dist/view/render/adopt-attr.js +77 -0
- package/dist/view/render/adopt-attr.js.map +1 -0
- package/dist/view/render/adopt-plan.d.ts +10 -0
- package/dist/view/render/adopt-plan.js +26 -0
- package/dist/view/render/adopt-plan.js.map +1 -0
- package/dist/view/render/adopt.d.ts +3 -0
- package/dist/view/render/adopt.js +269 -0
- package/dist/view/render/adopt.js.map +1 -0
- package/dist/view/render/attr-parts.d.ts +61 -0
- package/dist/view/render/attr-parts.js +229 -0
- package/dist/view/render/attr-parts.js.map +1 -0
- package/dist/view/render/child-part.d.ts +51 -0
- package/dist/view/render/child-part.js +170 -0
- package/dist/view/render/child-part.js.map +1 -0
- package/dist/view/render/dev-check.d.ts +12 -0
- package/dist/view/render/dev-check.js +122 -0
- package/dist/view/render/dev-check.js.map +1 -0
- package/dist/view/render/hook-part.d.ts +3 -0
- package/dist/view/render/hook-part.js +20 -0
- package/dist/view/render/hook-part.js.map +1 -0
- package/dist/view/render/hooks.d.ts +40 -0
- package/dist/view/render/hooks.js +46 -0
- package/dist/view/render/hooks.js.map +1 -0
- package/dist/view/render/hydrate.d.ts +10 -0
- package/dist/view/render/hydrate.js +33 -0
- package/dist/view/render/hydrate.js.map +1 -0
- package/dist/view/render/instance.d.ts +33 -0
- package/dist/view/render/instance.js +107 -0
- package/dist/view/render/instance.js.map +1 -0
- package/dist/view/render/list.d.ts +20 -0
- package/dist/view/render/list.js +156 -0
- package/dist/view/render/list.js.map +1 -0
- package/dist/view/render/mismatch.d.ts +10 -0
- package/dist/view/render/mismatch.js +53 -0
- package/dist/view/render/mismatch.js.map +1 -0
- package/dist/view/render/nodes.d.ts +10 -0
- package/dist/view/render/nodes.js +37 -0
- package/dist/view/render/nodes.js.map +1 -0
- package/dist/view/render/place.d.ts +18 -0
- package/dist/view/render/place.js +69 -0
- package/dist/view/render/place.js.map +1 -0
- package/dist/view/render/plan.d.ts +26 -0
- package/dist/view/render/plan.js +124 -0
- package/dist/view/render/plan.js.map +1 -0
- package/dist/view/render/raw.d.ts +18 -0
- package/dist/view/render/raw.js +68 -0
- package/dist/view/render/raw.js.map +1 -0
- package/dist/view/render/render.d.ts +12 -0
- package/dist/view/render/render.js +60 -0
- package/dist/view/render/render.js.map +1 -0
- package/dist/view/render/reorder.d.ts +4 -0
- package/dist/view/render/reorder.js +101 -0
- package/dist/view/render/reorder.js.map +1 -0
- package/dist/view/render/rows.d.ts +23 -0
- package/dist/view/render/rows.js +95 -0
- package/dist/view/render/rows.js.map +1 -0
- package/dist/view/render/seen.d.ts +11 -0
- package/dist/view/render/seen.js +17 -0
- package/dist/view/render/seen.js.map +1 -0
- package/dist/view/render/values.d.ts +29 -0
- package/dist/view/render/values.js +13 -0
- package/dist/view/render/values.js.map +1 -0
- package/dist/view/render/warn.d.ts +14 -0
- package/dist/view/render/warn.js +69 -0
- package/dist/view/render/warn.js.map +1 -0
- package/dist/view/server/component.d.ts +9 -0
- package/dist/view/server/component.js +42 -0
- package/dist/view/server/component.js.map +1 -0
- package/dist/view/server/escape.d.ts +10 -0
- package/dist/view/server/escape.js +34 -0
- package/dist/view/server/escape.js.map +1 -0
- package/dist/view/server/index.d.ts +28 -0
- package/dist/view/server/index.js +72 -0
- package/dist/view/server/index.js.map +1 -0
- package/dist/view/server/sha256.d.ts +2 -0
- package/dist/view/server/sha256.js +75 -0
- package/dist/view/server/sha256.js.map +1 -0
- package/dist/view/server/tags.d.ts +57 -0
- package/dist/view/server/tags.js +121 -0
- package/dist/view/server/tags.js.map +1 -0
- package/dist/view/server/values.d.ts +16 -0
- package/dist/view/server/values.js +86 -0
- package/dist/view/server/values.js.map +1 -0
- package/dist/view/server/writer.d.ts +16 -0
- package/dist/view/server/writer.js +155 -0
- package/dist/view/server/writer.js.map +1 -0
- package/dist/view/styles.d.ts +25 -0
- package/dist/view/styles.js +58 -0
- package/dist/view/styles.js.map +1 -0
- package/dist/view/template-element.d.ts +3 -0
- package/dist/view/template-element.js +25 -0
- package/dist/view/template-element.js.map +1 -0
- package/dist/view/template.d.ts +21 -0
- package/dist/view/template.js +30 -0
- package/dist/view/template.js.map +1 -0
- package/dist/vite.d.ts +45 -6
- package/dist/vite.js +150 -25
- package/dist/vite.js.map +1 -1
- package/package.json +41 -7
- package/dist/accessible-name.d.ts +0 -19
- package/dist/accessible-name.js +0 -56
- package/dist/accessible-name.js.map +0 -1
- package/dist/element-directive.d.ts +0 -20
- package/dist/element-directive.js +0 -31
- package/dist/element-directive.js.map +0 -1
- package/dist/internal/runtime.d.ts +0 -7
- package/dist/internal/runtime.js +0 -8
- package/dist/internal/runtime.js.map +0 -1
- package/dist/invalid.d.ts +0 -21
- package/dist/invalid.js +0 -70
- package/dist/invalid.js.map +0 -1
- package/dist/live-boolean.d.ts +0 -22
- package/dist/live-boolean.js +0 -34
- package/dist/live-boolean.js.map +0 -1
- package/dist/styles.d.ts +0 -12
- package/dist/styles.js +0 -11
- package/dist/styles.js.map +0 -1
- package/dist/textarea.d.ts +0 -26
- package/dist/textarea.js +0 -85
- package/dist/textarea.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -4,32 +4,25 @@ export { focus } from './focus.js';
|
|
|
4
4
|
export { invokersSupported } from './invokers.js';
|
|
5
5
|
export { ISLAND_ATTRIBUTE } from './islands.js';
|
|
6
6
|
export { define } from './define.js';
|
|
7
|
+
export { intents } from './intent.js';
|
|
8
|
+
export { settled } from './settled.js';
|
|
7
9
|
export { changed, defineStore, send } from './store.js';
|
|
8
10
|
export { STORE_SEND } from './store.js';
|
|
9
11
|
// Store scoping: used by @gyral/ssr (server scope + page seed) and @gyral/testing.
|
|
10
12
|
export { resetDocumentStores, scriptSafeJson, STORE_SEED_ATTRIBUTE, StoreRegistry, STORES_ELEMENT, withStoreScope, } from './store-scope.js';
|
|
11
|
-
export { defineStoresProvider } from './stores-provider.js';
|
|
12
13
|
export { DRIVERS_ELEMENT, provideDrivers } from './drivers-scope.js';
|
|
13
14
|
export { jsonHazard, warnJsonHazard } from './json-safety.js';
|
|
14
15
|
export { random, randomDriver, randomInt, toInt } from './random.js';
|
|
15
16
|
export { defineForm, field, fieldErrors, form, formDataToObject, formFields, intentRejectedSchema, redirectedTo, validateForm, } from './forms.js';
|
|
16
|
-
export { ElementDirective } from './element-directive.js';
|
|
17
17
|
export { runInit } from './init.js';
|
|
18
|
-
export { invalid } from './invalid.js';
|
|
19
|
-
export { findInScope, labelledBy } from './
|
|
20
|
-
export {
|
|
21
|
-
export { textarea, textareaMarkup } from './textarea.js';
|
|
18
|
+
export { invalid } from './hooks/invalid.js';
|
|
19
|
+
export { findInScope, labelledBy } from './hooks/labelled-by.js';
|
|
20
|
+
export { prop } from './prop.js';
|
|
22
21
|
// Devtools event stream (ADR 0017): emitted in development builds only.
|
|
23
22
|
export { DEVTOOLS_ENABLED as devtoolsEnabled, devLiveComponents as devtoolsLiveComponents, } from '#devtools';
|
|
24
23
|
export { DEVTOOLS_GLOBAL } from './devtools-events.js';
|
|
25
|
-
// Used by @gyral/ssr
|
|
26
|
-
export {
|
|
27
|
-
// The view layer
|
|
28
|
-
export { css, html,
|
|
29
|
-
export { directive } from 'lit/directive.js';
|
|
30
|
-
export { classMap } from 'lit/directives/class-map.js';
|
|
31
|
-
export { keyed } from 'lit/directives/keyed.js';
|
|
32
|
-
export { live } from 'lit/directives/live.js';
|
|
33
|
-
export { repeat } from 'lit/directives/repeat.js';
|
|
34
|
-
export { styleMap } from 'lit/directives/style-map.js';
|
|
24
|
+
// Used by the server renderer and @gyral/ssr (ADR 0014, view/06-server.md).
|
|
25
|
+
export { isLightComponent, LIGHT_ATTRIBUTE } from './light-dom.js';
|
|
26
|
+
// The view layer (ADR 0018, docs/design-docs/view/): templates, lists, hooks and styles.
|
|
27
|
+
export { css, defineHook, each, html, HydrationMismatch, nothing, raw } from './view/index.js';
|
|
35
28
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAYrD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAexD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,mFAAmF;AACnF,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAYrD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAexD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,mFAAmF;AACnF,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAoB,MAAM,aAAa,CAAC;AACvF,OAAO,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,IAAI,EACJ,gBAAgB,EAChB,UAAU,EACV,oBAAoB,EACpB,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,wEAAwE;AACxE,OAAO,EACL,gBAAgB,IAAI,eAAe,EACnC,iBAAiB,IAAI,sBAAsB,GAC5C,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAQvD,4EAA4E;AAC5E,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAqBnE,yFAAyF;AACzF,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC","sourcesContent":["export { command, defineDriver } from './command.js';\nexport type {\n AnyDriver,\n Command,\n CommandHandlers,\n Concurrency,\n Driver,\n DriverContext,\n DriverOverrides,\n Next,\n RetryPolicy,\n} from './command.js';\nexport { child, emit } from './children.js';\nexport { focus } from './focus.js';\nexport type { FocusOptions } from './focus.js';\nexport { invokersSupported } from './invokers.js';\nexport type { CommandInfo } from './invokers.js';\nexport { ISLAND_ATTRIBUTE } from './islands.js';\nexport type { HydrateStrategy } from './islands.js';\nexport type { ChildSource, OutputSource } from './children.js';\nexport { define } from './define.js';\nexport { intents } from './intent.js';\nexport { settled } from './settled.js';\nexport { changed, defineStore, send } from './store.js';\nexport type {\n AnyStore,\n AnyStoreInstance,\n Store,\n StoreChanged,\n StoreInstance,\n StoreOverrides,\n StoreRef,\n StoreSendInput,\n SeedCheck,\n StoreResolver,\n StoreSpec,\n StoreUpdate,\n} from './store.js';\nexport { STORE_SEND } from './store.js';\n// Store scoping: used by @gyral/ssr (server scope + page seed) and @gyral/testing.\nexport {\n resetDocumentStores,\n scriptSafeJson,\n STORE_SEED_ATTRIBUTE,\n StoreRegistry,\n STORES_ELEMENT,\n withStoreScope,\n} from './store-scope.js';\nexport { DRIVERS_ELEMENT, provideDrivers } from './drivers-scope.js';\nexport { jsonHazard, warnJsonHazard } from './json-safety.js';\nexport { random, randomDriver, randomInt, toInt, type RandomInput } from './random.js';\nexport {\n defineForm,\n field,\n fieldErrors,\n form,\n formDataToObject,\n formFields,\n intentRejectedSchema,\n redirectedTo,\n validateForm,\n} from './forms.js';\nexport type { FormDefinition, FormRedirected, FormResult, FormValue } from './forms.js';\nexport { runInit } from './init.js';\nexport { invalid } from './hooks/invalid.js';\nexport { findInScope, labelledBy } from './hooks/labelled-by.js';\nexport { prop } from './prop.js';\nexport type { Prop, PropKind, PropsOf } from './prop.js';\n// Devtools event stream (ADR 0017): emitted in development builds only.\nexport {\n DEVTOOLS_ENABLED as devtoolsEnabled,\n devLiveComponents as devtoolsLiveComponents,\n} from '#devtools';\nexport { DEVTOOLS_GLOBAL } from './devtools-events.js';\nexport type {\n CommandPhase,\n CommandTraceEvent,\n DevComponentRef,\n DevEvent,\n DevtoolsHook,\n} from './devtools-events.js';\n// Used by the server renderer and @gyral/ssr (ADR 0014, view/06-server.md).\nexport { isLightComponent, LIGHT_ATTRIBUTE } from './light-dom.js';\nexport type { GyralElement, GyralElementClass } from './define.js';\nexport type {\n ComponentSpec,\n Ctx,\n FieldIssue,\n FormFields,\n IntentInput,\n IntentNames,\n IntentParser,\n IntentRejected,\n Intents,\n PropDeclarations,\n Hydrated,\n PropsChanged,\n Stateless,\n StoreReader,\n Tagged,\n Update,\n} from './types.js';\n\n// The view layer (ADR 0018, docs/design-docs/view/): templates, lists, hooks and styles.\nexport { css, defineHook, each, html, HydrationMismatch, nothing, raw } from './view/index.js';\nexport type {\n ChildValue,\n CssValue,\n HookAttributes,\n HookResult,\n HookSpec,\n ListResult,\n RawResult,\n StyleSource,\n Styles,\n TemplateResult,\n} from './view/index.js';\n"]}
|
package/dist/intent.d.ts
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
|
-
import type { IntentInput, IntentParser, Tagged } from './types.js';
|
|
1
|
+
import type { IntentInput, IntentNames, IntentParser, Tagged } from './types.js';
|
|
2
|
+
import type { Markup } from './view/index.js';
|
|
2
3
|
/** Event a child component dispatches on its host to send an output up (ADR 0010). */
|
|
3
4
|
export declare const OUTPUT_EVENT = "gyral-output";
|
|
5
|
+
/** Default triggers (`defaultTrigger`): every component root listens for these. */
|
|
6
|
+
export declare const DEFAULT_EVENTS: readonly string[];
|
|
4
7
|
/**
|
|
5
|
-
*
|
|
6
|
-
* non-bubbling events such as `toggle` are seen too)
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
8
|
+
* The events `data-intent-on` usually names. A component root listens (capture phase, so
|
|
9
|
+
* non-bubbling events such as `toggle` are seen too) for the defaults, for `spec.events`, and
|
|
10
|
+
* for the `data-intent-on` values in the templates it renders; a template that binds
|
|
11
|
+
* `data-intent-on` dynamically adds all of these (view/05-element.md "Intent events").
|
|
9
12
|
*/
|
|
10
|
-
export declare const INTENT_EVENTS: readonly [
|
|
13
|
+
export declare const INTENT_EVENTS: readonly string[];
|
|
14
|
+
/** The intent events a template's (or `raw()` markup's) `data-intent-on` attributes name. */
|
|
15
|
+
export declare function eventsOf(markup: Markup): readonly string[];
|
|
11
16
|
/** The event that fires an intent unless `data-intent-on` overrides it. */
|
|
12
17
|
export declare function defaultTrigger(el: Element): string;
|
|
13
18
|
export declare function markGyralHost(ctor: object): void;
|
|
19
|
+
/**
|
|
20
|
+
* The number of Gyral host ancestors of `el` in the composed tree (across shadow roots): the
|
|
21
|
+
* scheduler renders smaller depths first (view/04-scheduler.md "Marking").
|
|
22
|
+
*/
|
|
23
|
+
export declare function hostDepth(el: Element): number;
|
|
14
24
|
/**
|
|
15
25
|
* Finds the nearest `data-intent` element for this event that belongs to `root`. Elements
|
|
16
26
|
* inside nested components are ignored: that is component isolation.
|
|
@@ -19,7 +29,20 @@ export declare function findIntentElement(event: Event, root: Node): Element | u
|
|
|
19
29
|
/** Reads an event into an IntentInput. Form submissions are prevented and turned into FormData. */
|
|
20
30
|
export declare function readIntent(event: Event, root: Node): IntentInput | undefined;
|
|
21
31
|
export declare const intentNames: unknown;
|
|
22
|
-
/**
|
|
23
|
-
|
|
32
|
+
/**
|
|
33
|
+
* A component's intent names as a module-level constant, so pure list rows can name intents
|
|
34
|
+
* without passing them through `pick` (view/03-lists.md "Rows must be pure"). The same object
|
|
35
|
+
* the view gets as `i`:
|
|
36
|
+
*
|
|
37
|
+
* const i = intents<Msg>();
|
|
38
|
+
* const Row = (t: Todo) => html`<input value=${t.id} data-intent=${i.Toggle} />`;
|
|
39
|
+
*/
|
|
40
|
+
export declare function intents<M extends Tagged>(): IntentNames<M>;
|
|
41
|
+
/**
|
|
42
|
+
* Adds capture listeners for `types` to a component root, skipping those in `listening`. A
|
|
43
|
+
* root that listens for `command` in a browser without invoker commands loads the fallback
|
|
44
|
+
* (invokers-shim.ts, ADR 0003 tier 3); settled() waits for it.
|
|
45
|
+
*/
|
|
46
|
+
export declare function listenForIntents(root: Node, listening: Set<string>, types: readonly string[], handler: (event: Event) => void): void;
|
|
24
47
|
/** Parses one event with its matching parser and delivers the message (sync or async). */
|
|
25
48
|
export declare function handleIntent<M>(event: Event, root: Node, parsers: Readonly<Record<string, IntentParser<M> | undefined>>, tag: string, deliver: (msg: Tagged | undefined) => void): void;
|
package/dist/intent.js
CHANGED
|
@@ -1,25 +1,44 @@
|
|
|
1
|
-
import { commandOf,
|
|
1
|
+
import { commandOf, invokersSupported } from './invokers.js';
|
|
2
|
+
import { hold } from './scheduler.js';
|
|
2
3
|
/** Event a child component dispatches on its host to send an output up (ADR 0010). */
|
|
3
4
|
export const OUTPUT_EVENT = 'gyral-output';
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
* non-bubbling events such as `toggle` are seen too). Only `click`, `submit`, `input`,
|
|
7
|
-
* `change` and outputs are default triggers; the rest fire via `data-intent-on="…"`.
|
|
8
|
-
* Components may add more with `spec.events`.
|
|
9
|
-
*/
|
|
10
|
-
export const INTENT_EVENTS = [
|
|
5
|
+
/** Default triggers (`defaultTrigger`): every component root listens for these. */
|
|
6
|
+
export const DEFAULT_EVENTS = [
|
|
11
7
|
'click',
|
|
12
8
|
'submit',
|
|
13
9
|
'input',
|
|
14
10
|
'change',
|
|
11
|
+
OUTPUT_EVENT,
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* The events `data-intent-on` usually names. A component root listens (capture phase, so
|
|
15
|
+
* non-bubbling events such as `toggle` are seen too) for the defaults, for `spec.events`, and
|
|
16
|
+
* for the `data-intent-on` values in the templates it renders; a template that binds
|
|
17
|
+
* `data-intent-on` dynamically adds all of these (view/05-element.md "Intent events").
|
|
18
|
+
*/
|
|
19
|
+
export const INTENT_EVENTS = [
|
|
20
|
+
...DEFAULT_EVENTS,
|
|
15
21
|
'keydown',
|
|
16
22
|
'keyup',
|
|
17
23
|
'focusin',
|
|
18
24
|
'focusout',
|
|
19
25
|
'toggle',
|
|
20
26
|
'command',
|
|
21
|
-
OUTPUT_EVENT,
|
|
22
27
|
];
|
|
28
|
+
/** A static `data-intent-on` value, quoted or not (event names have no spaces or quotes). */
|
|
29
|
+
const INTENT_ON = /\sdata-intent-on=["']?([^"'\s>]+)/gi;
|
|
30
|
+
const eventsByTemplate = new WeakMap();
|
|
31
|
+
/** The intent events a template's (or `raw()` markup's) `data-intent-on` attributes name. */
|
|
32
|
+
export function eventsOf(markup) {
|
|
33
|
+
let events = eventsByTemplate.get(markup);
|
|
34
|
+
if (events === undefined) {
|
|
35
|
+
events = markup.parts?.some((p) => p[2] === 'data-intent-on') // a bound attribute's name
|
|
36
|
+
? INTENT_EVENTS
|
|
37
|
+
: Array.from(markup.html.matchAll(INTENT_ON), (m) => m[1] ?? '');
|
|
38
|
+
eventsByTemplate.set(markup, events);
|
|
39
|
+
}
|
|
40
|
+
return events;
|
|
41
|
+
}
|
|
23
42
|
const isToggle = (el) => el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');
|
|
24
43
|
// ToggleEvent (popover, <details>) is not Baseline widely available; read it structurally.
|
|
25
44
|
function toggleState(event) {
|
|
@@ -27,24 +46,25 @@ function toggleState(event) {
|
|
|
27
46
|
return undefined;
|
|
28
47
|
return event.newState === 'open' ? 'open' : 'closed';
|
|
29
48
|
}
|
|
30
|
-
const CLICK_INPUT_TYPES =
|
|
49
|
+
const CLICK_INPUT_TYPES = /^(button|submit|reset|image)$/;
|
|
31
50
|
/** The event that fires an intent unless `data-intent-on` overrides it. */
|
|
32
51
|
export function defaultTrigger(el) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
switch (el.localName) {
|
|
53
|
+
case 'form':
|
|
54
|
+
return 'submit';
|
|
55
|
+
case 'select':
|
|
56
|
+
return 'change';
|
|
57
|
+
case 'textarea':
|
|
58
|
+
return 'input';
|
|
59
|
+
case 'input': {
|
|
60
|
+
const type = el.type;
|
|
61
|
+
if (CLICK_INPUT_TYPES.test(type))
|
|
62
|
+
return 'click';
|
|
63
|
+
return type === 'checkbox' || type === 'radio' ? 'change' : 'input';
|
|
64
|
+
}
|
|
43
65
|
}
|
|
44
66
|
// Custom elements (autonomous: the name has a dash) talk to their parent via outputs.
|
|
45
|
-
|
|
46
|
-
return OUTPUT_EVENT;
|
|
47
|
-
return 'click';
|
|
67
|
+
return el.localName.includes('-') ? OUTPUT_EVENT : 'click';
|
|
48
68
|
}
|
|
49
69
|
function triggerOf(el) {
|
|
50
70
|
return el.getAttribute('data-intent-on') ?? defaultTrigger(el);
|
|
@@ -55,6 +75,19 @@ export function markGyralHost(ctor) {
|
|
|
55
75
|
hostClasses.add(ctor);
|
|
56
76
|
}
|
|
57
77
|
const isGyralHost = (node) => hostClasses.has(node.constructor);
|
|
78
|
+
/**
|
|
79
|
+
* The number of Gyral host ancestors of `el` in the composed tree (across shadow roots): the
|
|
80
|
+
* scheduler renders smaller depths first (view/04-scheduler.md "Marking").
|
|
81
|
+
*/
|
|
82
|
+
export function hostDepth(el) {
|
|
83
|
+
let depth = 0;
|
|
84
|
+
for (let node = el.parentNode; node !== null;) {
|
|
85
|
+
if (isGyralHost(node))
|
|
86
|
+
depth += 1;
|
|
87
|
+
node = node instanceof ShadowRoot ? node.host : node.parentNode;
|
|
88
|
+
}
|
|
89
|
+
return depth;
|
|
90
|
+
}
|
|
58
91
|
/**
|
|
59
92
|
* Does `node` belong to the component whose intent root is `root`? `root` is the component's
|
|
60
93
|
* shadow root, or the host itself for light-DOM components (ADR 0014). The node must be in the
|
|
@@ -127,12 +160,34 @@ export function readIntent(event, root) {
|
|
|
127
160
|
// Any property read returns its own name, so `intents.Increment === 'Increment'`.
|
|
128
161
|
// Types restrict reads to real message tags; a tag without a parser warns at event time.
|
|
129
162
|
export const intentNames = new Proxy({}, { get: (_target, key) => (typeof key === 'string' ? key : undefined) });
|
|
130
|
-
/**
|
|
131
|
-
|
|
132
|
-
|
|
163
|
+
/**
|
|
164
|
+
* A component's intent names as a module-level constant, so pure list rows can name intents
|
|
165
|
+
* without passing them through `pick` (view/03-lists.md "Rows must be pure"). The same object
|
|
166
|
+
* the view gets as `i`:
|
|
167
|
+
*
|
|
168
|
+
* const i = intents<Msg>();
|
|
169
|
+
* const Row = (t: Todo) => html`<input value=${t.id} data-intent=${i.Toggle} />`;
|
|
170
|
+
*/
|
|
171
|
+
export function intents() {
|
|
172
|
+
return intentNames;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Adds capture listeners for `types` to a component root, skipping those in `listening`. A
|
|
176
|
+
* root that listens for `command` in a browser without invoker commands loads the fallback
|
|
177
|
+
* (invokers-shim.ts, ADR 0003 tier 3); settled() waits for it.
|
|
178
|
+
*/
|
|
179
|
+
export function listenForIntents(root, listening, types, handler) {
|
|
180
|
+
for (const type of types) {
|
|
181
|
+
if (listening.has(type))
|
|
182
|
+
continue;
|
|
183
|
+
listening.add(type);
|
|
133
184
|
root.addEventListener(type, handler, { capture: true });
|
|
185
|
+
if (type === 'command' && !invokersSupported()) {
|
|
186
|
+
hold(import('./invokers-shim.js').then((shim) => {
|
|
187
|
+
shim.shimInvokers(root);
|
|
188
|
+
}, reportError));
|
|
189
|
+
}
|
|
134
190
|
}
|
|
135
|
-
shimInvokers(root); // `command` intents in browsers without invoker commands
|
|
136
191
|
}
|
|
137
192
|
/** Parses one event with its matching parser and delivers the message (sync or async). */
|
|
138
193
|
export function handleIntent(event, root, parsers, tag, deliver) {
|
package/dist/intent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.js","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"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","sourcesContent":["import { commandOf, shimInvokers } from './invokers.js';\nimport type { IntentInput, IntentParser, Tagged } from './types.js';\n\n/** Event a child component dispatches on its host to send an output up (ADR 0010). */\nexport const OUTPUT_EVENT = 'gyral-output';\n\n/**\n * Events the intent layer listens for on each component's shadow root (capture phase, so\n * non-bubbling events such as `toggle` are seen too). Only `click`, `submit`, `input`,\n * `change` and outputs are default triggers; the rest fire via `data-intent-on=\"…\"`.\n * Components may add more with `spec.events`.\n */\nexport const INTENT_EVENTS = [\n 'click',\n 'submit',\n 'input',\n 'change',\n 'keydown',\n 'keyup',\n 'focusin',\n 'focusout',\n 'toggle',\n 'command',\n OUTPUT_EVENT,\n] as const;\n\nconst isToggle = (el: Element): el is HTMLInputElement =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');\n\n// ToggleEvent (popover, <details>) is not Baseline widely available; read it structurally.\nfunction toggleState(event: Event): 'open' | 'closed' | undefined {\n if (event.type !== 'toggle' || !('newState' in event)) return undefined;\n return event.newState === 'open' ? 'open' : 'closed';\n}\n\nconst CLICK_INPUT_TYPES = new Set(['button', 'submit', 'reset', 'image']);\n\n/** The event that fires an intent unless `data-intent-on` overrides it. */\nexport function defaultTrigger(el: Element): string {\n if (el instanceof HTMLFormElement) return 'submit';\n if (el instanceof HTMLSelectElement) return 'change';\n if (el instanceof HTMLTextAreaElement) return 'input';\n if (el instanceof HTMLInputElement) {\n if (CLICK_INPUT_TYPES.has(el.type)) return 'click';\n return el.type === 'checkbox' || el.type === 'radio' ? 'change' : 'input';\n }\n // Custom elements (autonomous: the name has a dash) talk to their parent via outputs.\n if (el.localName.includes('-')) return OUTPUT_EVENT;\n return 'click';\n}\n\nfunction triggerOf(el: Element): string {\n return el.getAttribute('data-intent-on') ?? defaultTrigger(el);\n}\n\n/** Every class `define()` creates, so intent lookup can tell where a component begins. */\nconst hostClasses = new WeakSet<object>();\n\nexport function markGyralHost(ctor: object): void {\n hostClasses.add(ctor);\n}\n\nconst isGyralHost = (node: Node): boolean => hostClasses.has(node.constructor);\n\n/**\n * Does `node` belong to the component whose intent root is `root`? `root` is the component's\n * shadow root, or the host itself for light-DOM components (ADR 0014). The node must be in the\n * same tree (not inside a nested shadow root) with no other Gyral host between them: a nested\n * component, light-DOM or shadow, owns its own content. The nested host element itself (with\n * the parent's `data-intent` on it) still belongs to the parent.\n */\nfunction ownedBy(node: Element, root: Node): boolean {\n const tree = root instanceof ShadowRoot ? root : root.getRootNode();\n if (node.getRootNode() !== tree) return false;\n for (let p = node.parentNode; p !== null; p = p.parentNode) {\n if (p === root) return true;\n if (isGyralHost(p)) return false;\n }\n return false;\n}\n\n/**\n * Finds the nearest `data-intent` element for this event that belongs to `root`. Elements\n * inside nested components are ignored: that is component isolation.\n */\nexport function findIntentElement(event: Event, root: Node): Element | undefined {\n for (const node of event.composedPath()) {\n if (node === root) return undefined;\n if (\n node instanceof Element &&\n node.hasAttribute('data-intent') &&\n triggerOf(node) === event.type &&\n ownedBy(node, root)\n ) {\n return node;\n }\n }\n return undefined;\n}\n\nfunction valueOf(el: Element): string | undefined {\n if (\n el instanceof HTMLInputElement ||\n el instanceof HTMLSelectElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLButtonElement\n ) {\n return el.value;\n }\n return undefined;\n}\n\n/** Reads an event into an IntentInput. Form submissions are prevented and turned into FormData. */\nexport function readIntent(event: Event, root: Node): IntentInput | undefined {\n const target = findIntentElement(event, root);\n const name = target?.getAttribute('data-intent');\n if (target === undefined || name == null) return undefined;\n let formData: FormData | undefined;\n if (target instanceof HTMLFormElement && event instanceof SubmitEvent) {\n event.preventDefault();\n formData = new FormData(target, event.submitter);\n }\n return {\n name,\n event,\n target,\n value: valueOf(target),\n checked: isToggle(target) ? target.checked : undefined,\n formData,\n detail: event instanceof CustomEvent ? (event.detail as unknown) : undefined,\n key: event instanceof KeyboardEvent ? event.key : undefined,\n newState: toggleState(event),\n command: commandOf(event),\n };\n}\n\n// Any property read returns its own name, so `intents.Increment === 'Increment'`.\n// Types restrict reads to real message tags; a tag without a parser warns at event time.\nexport const intentNames: unknown = new Proxy(\n {},\n { get: (_target, key) => (typeof key === 'string' ? key : undefined) },\n);\n\n/** Listens for intent events on a component root (capture phase: `toggle` doesn't bubble). */\nexport function listenForIntents(\n root: Node,\n extra: readonly string[],\n handler: (event: Event) => void,\n): void {\n for (const type of new Set([...INTENT_EVENTS, ...extra])) {\n root.addEventListener(type, handler, { capture: true });\n }\n shimInvokers(root); // `command` intents in browsers without invoker commands\n}\n\n/** Parses one event with its matching parser and delivers the message (sync or async). */\nexport function handleIntent<M>(\n event: Event,\n root: Node,\n parsers: Readonly<Record<string, IntentParser<M> | undefined>>,\n tag: string,\n deliver: (msg: Tagged | undefined) => void,\n): void {\n const input = readIntent(event, root);\n if (input === undefined) return;\n const parser = parsers[input.name];\n if (parser === undefined) {\n console.warn(`<${tag}> has no intent parser for data-intent=\"${input.name}\".`);\n return;\n }\n const result = parser(input) as Tagged | undefined | Promise<Tagged | undefined>;\n if (result instanceof Promise) {\n result.then(deliver, (error: unknown) => {\n console.error(`<${tag}> intent parser for \"${input.name}\" failed`, error);\n });\n } else {\n deliver(result);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"intent.js","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAItC,sFAAsF;AACtF,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC;AAE3C,mFAAmF;AACnF,MAAM,CAAC,MAAM,cAAc,GAAsB;IAC/C,OAAO;IACP,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,YAAY;CACb,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAsB;IAC9C,GAAG,cAAc;IACjB,SAAS;IACT,OAAO;IACP,SAAS;IACT,UAAU;IACV,QAAQ;IACR,SAAS;CACV,CAAC;AAEF,6FAA6F;AAC7F,MAAM,SAAS,GAAG,qCAAqC,CAAC;AACxD,MAAM,gBAAgB,GAAG,IAAI,OAAO,EAA6B,CAAC;AAElE,6FAA6F;AAC7F,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,IAAI,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,gBAAgB,CAAC,CAAC,2BAA2B;YACvF,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnE,gBAAgB,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,EAAW,EAA0B,EAAE,CACvD,EAAE,YAAY,gBAAgB,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,UAAU,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;AAEpF,2FAA2F;AAC3F,SAAS,WAAW,CAAC,KAAY;IAC/B,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACxE,OAAO,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AACvD,CAAC;AAED,MAAM,iBAAiB,GAAG,+BAA+B,CAAC;AAE1D,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,EAAW;IACxC,QAAQ,EAAE,CAAC,SAAS,EAAE,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,QAAQ,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAC;QAClB,KAAK,UAAU;YACb,OAAO,OAAO,CAAC;QACjB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,IAAI,GAAI,EAAuB,CAAC,IAAI,CAAC;YAC3C,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,OAAO,CAAC;YACjD,OAAO,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;QACtE,CAAC;IACH,CAAC;IACD,sFAAsF;IACtF,OAAO,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;AAC7D,CAAC;AAED,SAAS,SAAS,CAAC,EAAW;IAC5B,OAAO,EAAE,CAAC,YAAY,CAAC,gBAAgB,CAAC,IAAI,cAAc,CAAC,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,0FAA0F;AAC1F,MAAM,WAAW,GAAG,IAAI,OAAO,EAAU,CAAC;AAE1C,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,IAAU,EAAW,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAE/E;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,EAAW;IACnC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,IAAI,GAAgB,EAAE,CAAC,UAAU,EAAE,IAAI,KAAK,IAAI,GAAG,CAAC;QAC3D,IAAI,WAAW,CAAC,IAAI,CAAC;YAAE,KAAK,IAAI,CAAC,CAAC;QAClC,IAAI,GAAG,IAAI,YAAY,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,SAAS,OAAO,CAAC,IAAa,EAAE,IAAU;IACxC,MAAM,IAAI,GAAG,IAAI,YAAY,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACpE,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9C,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC;QAC3D,IAAI,CAAC,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC5B,IAAI,WAAW,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IACnC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAY,EAAE,IAAU;IACxD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,YAAY,EAAE,EAAE,CAAC;QACxC,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QACpC,IACE,IAAI,YAAY,OAAO;YACvB,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC;YAChC,SAAS,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI;YAC9B,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EACnB,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,OAAO,CAAC,EAAW;IAC1B,IACE,EAAE,YAAY,gBAAgB;QAC9B,EAAE,YAAY,iBAAiB;QAC/B,EAAE,YAAY,mBAAmB;QACjC,EAAE,YAAY,iBAAiB,EAC/B,CAAC;QACD,OAAO,EAAE,CAAC,KAAK,CAAC;IAClB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,UAAU,CAAC,KAAY,EAAE,IAAU;IACjD,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,MAAM,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC;IACjD,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IAC3D,IAAI,QAA8B,CAAC;IACnC,IAAI,MAAM,YAAY,eAAe,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;QACtE,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;IACD,OAAO;QACL,IAAI;QACJ,KAAK;QACL,MAAM;QACN,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC;QACtB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QACtD,QAAQ;QACR,MAAM,EAAE,KAAK,YAAY,WAAW,CAAC,CAAC,CAAE,KAAK,CAAC,MAAkB,CAAC,CAAC,CAAC,SAAS;QAC5E,GAAG,EAAE,KAAK,YAAY,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QAC3D,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC;QAC5B,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC;KAC1B,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,yFAAyF;AACzF,MAAM,CAAC,MAAM,WAAW,GAAY,IAAI,KAAK,CAC3C,EAAE,EACF,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CACvE,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO;IACrB,OAAO,WAA6B,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAU,EACV,SAAsB,EACtB,KAAwB,EACxB,OAA+B;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC;YAC/C,IAAI,CACF,MAAM,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;gBACzC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC,EAAE,WAAW,CAAC,CAChB,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,YAAY,CAC1B,KAAY,EACZ,IAAU,EACV,OAA8D,EAC9D,GAAW,EACX,OAA0C;IAE1C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO;IAChC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,2CAA2C,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;QAC/E,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAqD,CAAC;IACjF,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAc,EAAE,EAAE;YACtC,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,wBAAwB,KAAK,CAAC,IAAI,UAAU,EAAE,KAAK,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;AACH,CAAC","sourcesContent":["import { commandOf, invokersSupported } from './invokers.js';\nimport { hold } from './scheduler.js';\nimport type { IntentInput, IntentNames, IntentParser, Tagged } from './types.js';\nimport type { Markup } from './view/index.js';\n\n/** Event a child component dispatches on its host to send an output up (ADR 0010). */\nexport const OUTPUT_EVENT = 'gyral-output';\n\n/** Default triggers (`defaultTrigger`): every component root listens for these. */\nexport const DEFAULT_EVENTS: readonly string[] = [\n 'click',\n 'submit',\n 'input',\n 'change',\n OUTPUT_EVENT,\n];\n\n/**\n * The events `data-intent-on` usually names. A component root listens (capture phase, so\n * non-bubbling events such as `toggle` are seen too) for the defaults, for `spec.events`, and\n * for the `data-intent-on` values in the templates it renders; a template that binds\n * `data-intent-on` dynamically adds all of these (view/05-element.md \"Intent events\").\n */\nexport const INTENT_EVENTS: readonly string[] = [\n ...DEFAULT_EVENTS,\n 'keydown',\n 'keyup',\n 'focusin',\n 'focusout',\n 'toggle',\n 'command',\n];\n\n/** A static `data-intent-on` value, quoted or not (event names have no spaces or quotes). */\nconst INTENT_ON = /\\sdata-intent-on=[\"']?([^\"'\\s>]+)/gi;\nconst eventsByTemplate = new WeakMap<Markup, readonly string[]>();\n\n/** The intent events a template's (or `raw()` markup's) `data-intent-on` attributes name. */\nexport function eventsOf(markup: Markup): readonly string[] {\n let events = eventsByTemplate.get(markup);\n if (events === undefined) {\n events = markup.parts?.some((p) => p[2] === 'data-intent-on') // a bound attribute's name\n ? INTENT_EVENTS\n : Array.from(markup.html.matchAll(INTENT_ON), (m) => m[1] ?? '');\n eventsByTemplate.set(markup, events);\n }\n return events;\n}\n\nconst isToggle = (el: Element): el is HTMLInputElement =>\n el instanceof HTMLInputElement && (el.type === 'checkbox' || el.type === 'radio');\n\n// ToggleEvent (popover, <details>) is not Baseline widely available; read it structurally.\nfunction toggleState(event: Event): 'open' | 'closed' | undefined {\n if (event.type !== 'toggle' || !('newState' in event)) return undefined;\n return event.newState === 'open' ? 'open' : 'closed';\n}\n\nconst CLICK_INPUT_TYPES = /^(button|submit|reset|image)$/;\n\n/** The event that fires an intent unless `data-intent-on` overrides it. */\nexport function defaultTrigger(el: Element): string {\n switch (el.localName) {\n case 'form':\n return 'submit';\n case 'select':\n return 'change';\n case 'textarea':\n return 'input';\n case 'input': {\n const type = (el as HTMLInputElement).type;\n if (CLICK_INPUT_TYPES.test(type)) return 'click';\n return type === 'checkbox' || type === 'radio' ? 'change' : 'input';\n }\n }\n // Custom elements (autonomous: the name has a dash) talk to their parent via outputs.\n return el.localName.includes('-') ? OUTPUT_EVENT : 'click';\n}\n\nfunction triggerOf(el: Element): string {\n return el.getAttribute('data-intent-on') ?? defaultTrigger(el);\n}\n\n/** Every class `define()` creates, so intent lookup can tell where a component begins. */\nconst hostClasses = new WeakSet<object>();\n\nexport function markGyralHost(ctor: object): void {\n hostClasses.add(ctor);\n}\n\nconst isGyralHost = (node: Node): boolean => hostClasses.has(node.constructor);\n\n/**\n * The number of Gyral host ancestors of `el` in the composed tree (across shadow roots): the\n * scheduler renders smaller depths first (view/04-scheduler.md \"Marking\").\n */\nexport function hostDepth(el: Element): number {\n let depth = 0;\n for (let node: Node | null = el.parentNode; node !== null;) {\n if (isGyralHost(node)) depth += 1;\n node = node instanceof ShadowRoot ? node.host : node.parentNode;\n }\n return depth;\n}\n\n/**\n * Does `node` belong to the component whose intent root is `root`? `root` is the component's\n * shadow root, or the host itself for light-DOM components (ADR 0014). The node must be in the\n * same tree (not inside a nested shadow root) with no other Gyral host between them: a nested\n * component, light-DOM or shadow, owns its own content. The nested host element itself (with\n * the parent's `data-intent` on it) still belongs to the parent.\n */\nfunction ownedBy(node: Element, root: Node): boolean {\n const tree = root instanceof ShadowRoot ? root : root.getRootNode();\n if (node.getRootNode() !== tree) return false;\n for (let p = node.parentNode; p !== null; p = p.parentNode) {\n if (p === root) return true;\n if (isGyralHost(p)) return false;\n }\n return false;\n}\n\n/**\n * Finds the nearest `data-intent` element for this event that belongs to `root`. Elements\n * inside nested components are ignored: that is component isolation.\n */\nexport function findIntentElement(event: Event, root: Node): Element | undefined {\n for (const node of event.composedPath()) {\n if (node === root) return undefined;\n if (\n node instanceof Element &&\n node.hasAttribute('data-intent') &&\n triggerOf(node) === event.type &&\n ownedBy(node, root)\n ) {\n return node;\n }\n }\n return undefined;\n}\n\nfunction valueOf(el: Element): string | undefined {\n if (\n el instanceof HTMLInputElement ||\n el instanceof HTMLSelectElement ||\n el instanceof HTMLTextAreaElement ||\n el instanceof HTMLButtonElement\n ) {\n return el.value;\n }\n return undefined;\n}\n\n/** Reads an event into an IntentInput. Form submissions are prevented and turned into FormData. */\nexport function readIntent(event: Event, root: Node): IntentInput | undefined {\n const target = findIntentElement(event, root);\n const name = target?.getAttribute('data-intent');\n if (target === undefined || name == null) return undefined;\n let formData: FormData | undefined;\n if (target instanceof HTMLFormElement && event instanceof SubmitEvent) {\n event.preventDefault();\n formData = new FormData(target, event.submitter);\n }\n return {\n name,\n event,\n target,\n value: valueOf(target),\n checked: isToggle(target) ? target.checked : undefined,\n formData,\n detail: event instanceof CustomEvent ? (event.detail as unknown) : undefined,\n key: event instanceof KeyboardEvent ? event.key : undefined,\n newState: toggleState(event),\n command: commandOf(event),\n };\n}\n\n// Any property read returns its own name, so `intents.Increment === 'Increment'`.\n// Types restrict reads to real message tags; a tag without a parser warns at event time.\nexport const intentNames: unknown = new Proxy(\n {},\n { get: (_target, key) => (typeof key === 'string' ? key : undefined) },\n);\n\n/**\n * A component's intent names as a module-level constant, so pure list rows can name intents\n * without passing them through `pick` (view/03-lists.md \"Rows must be pure\"). The same object\n * the view gets as `i`:\n *\n * const i = intents<Msg>();\n * const Row = (t: Todo) => html`<input value=${t.id} data-intent=${i.Toggle} />`;\n */\nexport function intents<M extends Tagged>(): IntentNames<M> {\n return intentNames as IntentNames<M>;\n}\n\n/**\n * Adds capture listeners for `types` to a component root, skipping those in `listening`. A\n * root that listens for `command` in a browser without invoker commands loads the fallback\n * (invokers-shim.ts, ADR 0003 tier 3); settled() waits for it.\n */\nexport function listenForIntents(\n root: Node,\n listening: Set<string>,\n types: readonly string[],\n handler: (event: Event) => void,\n): void {\n for (const type of types) {\n if (listening.has(type)) continue;\n listening.add(type);\n root.addEventListener(type, handler, { capture: true });\n if (type === 'command' && !invokersSupported()) {\n hold(\n import('./invokers-shim.js').then((shim) => {\n shim.shimInvokers(root);\n }, reportError),\n );\n }\n }\n}\n\n/** Parses one event with its matching parser and delivers the message (sync or async). */\nexport function handleIntent<M>(\n event: Event,\n root: Node,\n parsers: Readonly<Record<string, IntentParser<M> | undefined>>,\n tag: string,\n deliver: (msg: Tagged | undefined) => void,\n): void {\n const input = readIntent(event, root);\n if (input === undefined) return;\n const parser = parsers[input.name];\n if (parser === undefined) {\n console.warn(`<${tag}> has no intent parser for data-intent=\"${input.name}\".`);\n return;\n }\n const result = parser(input) as Tagged | undefined | Promise<Tagged | undefined>;\n if (result instanceof Promise) {\n result.then(deliver, (error: unknown) => {\n console.error(`<${tag}> intent parser for \"${input.name}\" failed`, error);\n });\n } else {\n deliver(result);\n }\n}\n"]}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import type { AnyDriver, Command } from '../command.js';
|
|
1
|
+
import type { AnyDriver, Command, DriverOverrides } from '../command.js';
|
|
2
2
|
import type { CommandTrace } from '../devtools-events.js';
|
|
3
|
+
import type { FeatureHost } from '../features.js';
|
|
3
4
|
/** Runs commands for one connected element. Disposed on disconnect. */
|
|
4
5
|
export interface Interpreter<M> {
|
|
5
6
|
readonly run: (cmd: Command<M>) => void;
|
|
6
7
|
readonly dispose: () => void;
|
|
7
8
|
}
|
|
8
9
|
export declare function makeInterpreter<M>(resolve: (driver: AnyDriver) => AnyDriver, dispatch: (msg: M) => void, trace?: CommandTrace): Interpreter<M>;
|
|
10
|
+
/**
|
|
11
|
+
* A component's interpreter (registered by `command()`, features.ts). Drivers resolve by name:
|
|
12
|
+
* el.drivers → nearest provider → spec.drivers → the command's own (gyral-czi.35).
|
|
13
|
+
*/
|
|
14
|
+
export declare const hostInterpreter: <M>(el: FeatureHost, drivers: DriverOverrides | undefined, dispatch: (msg: M) => void, trace: CommandTrace | undefined) => Interpreter<M>;
|
|
@@ -1,66 +1,99 @@
|
|
|
1
|
-
import { Data, Duration, Effect, Fiber, Schedule } from 'effect';
|
|
2
1
|
import { DEVTOOLS_ENABLED } from '#devtools';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
import { providedDriver } from '../drivers-scope.js';
|
|
3
|
+
/** Rejects when `signal` aborts. Marked handled so a losing race never reports it. */
|
|
4
|
+
const aborted = (signal) => {
|
|
5
|
+
const promise = new Promise((_resolve, reject) => {
|
|
6
|
+
// Callers check `signal.aborted`, never the reason.
|
|
7
|
+
const interrupt = () => {
|
|
8
|
+
reject(signal.reason);
|
|
9
|
+
};
|
|
10
|
+
if (signal.aborted)
|
|
11
|
+
interrupt();
|
|
12
|
+
else
|
|
13
|
+
signal.addEventListener('abort', interrupt, { once: true });
|
|
14
|
+
});
|
|
15
|
+
promise.catch(() => undefined);
|
|
16
|
+
return promise;
|
|
13
17
|
};
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
/** Waits `ms`, or rejects as soon as `signal` aborts. */
|
|
19
|
+
const sleep = (ms, signal) => Promise.race([
|
|
20
|
+
new Promise((resolve) => {
|
|
21
|
+
const timer = setTimeout(resolve, ms);
|
|
22
|
+
signal.addEventListener('abort', () => {
|
|
23
|
+
clearTimeout(timer);
|
|
24
|
+
}, { once: true });
|
|
25
|
+
}),
|
|
26
|
+
aborted(signal),
|
|
27
|
+
]);
|
|
28
|
+
/** Delay before retry number `retry` (0-based): fixed, or doubling from `delayMs`. */
|
|
29
|
+
const delayFor = (policy, retry) => {
|
|
30
|
+
const base = policy.delayMs ?? 0;
|
|
31
|
+
return policy.backoff === 'exponential' ? base * 2 ** retry : base;
|
|
32
|
+
};
|
|
33
|
+
async function attemptWithRetry(driver, cmd, signal, emit) {
|
|
34
|
+
const policy = driver.retry;
|
|
35
|
+
for (let retry = 0;; retry += 1) {
|
|
16
36
|
try {
|
|
17
|
-
const msg = cmd.onSuccess(output);
|
|
18
|
-
if (msg !== undefined)
|
|
19
|
-
dispatch(msg);
|
|
20
|
-
}
|
|
21
|
-
catch (defect) {
|
|
22
|
-
console.error('gyral: command mapper threw', defect);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
const attempt = Effect.tryPromise({
|
|
26
|
-
try: (signal) => {
|
|
27
|
-
let settled = false;
|
|
28
|
-
const emit = (output) => {
|
|
29
|
-
if (!settled && !signal.aborted)
|
|
30
|
-
deliver(output);
|
|
31
|
-
};
|
|
32
37
|
// The input type was erased by command(); it was built for this driver's name.
|
|
33
38
|
const result = Promise.resolve(driver.run(cmd.input, { signal, emit }));
|
|
34
|
-
return
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (DEVTOOLS_ENABLED)
|
|
48
|
-
report?.('settled', output);
|
|
49
|
-
return Effect.succeed(cmd.onSuccess(output));
|
|
50
|
-
},
|
|
51
|
-
onFailure: (failure) => {
|
|
52
|
-
const error = driver.toError === undefined ? failure.cause : driver.toError(failure.cause);
|
|
53
|
-
if (DEVTOOLS_ENABLED)
|
|
54
|
-
report?.('failed', error);
|
|
55
|
-
return cmd.onFailure === undefined
|
|
56
|
-
? Effect.logWarning(`gyral: unhandled failure from driver "${driver.name}"`, error).pipe(Effect.as(undefined))
|
|
57
|
-
: Effect.succeed(cmd.onFailure(error));
|
|
58
|
-
},
|
|
59
|
-
}), Effect.flatMap((msg) => Effect.sync(() => {
|
|
39
|
+
return await Promise.race([result, aborted(signal)]);
|
|
40
|
+
}
|
|
41
|
+
catch (cause) {
|
|
42
|
+
if (signal.aborted || policy === undefined || retry >= policy.times)
|
|
43
|
+
throw cause;
|
|
44
|
+
await sleep(delayFor(policy, retry), signal);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Maps a result to a message and dispatches it; a throwing mapper is logged, not fatal. */
|
|
49
|
+
const deliver = (map, dispatch) => {
|
|
50
|
+
try {
|
|
51
|
+
const msg = map();
|
|
60
52
|
if (msg !== undefined)
|
|
61
53
|
dispatch(msg);
|
|
62
|
-
}
|
|
54
|
+
}
|
|
55
|
+
catch (defect) {
|
|
56
|
+
console.error('gyral: command mapper threw', defect);
|
|
57
|
+
}
|
|
63
58
|
};
|
|
59
|
+
async function execute(driver, cmd, signal, dispatch, report) {
|
|
60
|
+
let settled = false;
|
|
61
|
+
const emit = (output) => {
|
|
62
|
+
if (!settled && !signal.aborted)
|
|
63
|
+
deliver(() => cmd.onSuccess(output), dispatch);
|
|
64
|
+
};
|
|
65
|
+
if (DEVTOOLS_ENABLED && report !== undefined) {
|
|
66
|
+
signal.addEventListener('abort', () => {
|
|
67
|
+
if (!settled)
|
|
68
|
+
report('interrupted');
|
|
69
|
+
}, { once: true });
|
|
70
|
+
}
|
|
71
|
+
let output;
|
|
72
|
+
try {
|
|
73
|
+
output = await attemptWithRetry(driver, cmd, signal, emit);
|
|
74
|
+
}
|
|
75
|
+
catch (cause) {
|
|
76
|
+
if (signal.aborted)
|
|
77
|
+
return; // interrupted: reported by the abort listener
|
|
78
|
+
settled = true;
|
|
79
|
+
const error = driver.toError === undefined ? cause : driver.toError(cause);
|
|
80
|
+
if (DEVTOOLS_ENABLED)
|
|
81
|
+
report?.('failed', error);
|
|
82
|
+
if (cmd.onFailure === undefined) {
|
|
83
|
+
console.warn(`gyral: unhandled failure from driver "${driver.name}"`, error);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const onFailure = cmd.onFailure;
|
|
87
|
+
deliver(() => onFailure(error), dispatch);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (signal.aborted)
|
|
91
|
+
return;
|
|
92
|
+
settled = true;
|
|
93
|
+
if (DEVTOOLS_ENABLED)
|
|
94
|
+
report?.('settled', output);
|
|
95
|
+
deliver(() => cmd.onSuccess(output), dispatch);
|
|
96
|
+
}
|
|
64
97
|
export function makeInterpreter(resolve, dispatch, trace) {
|
|
65
98
|
const reporter = (driver, lane, policy, input) => trace === undefined
|
|
66
99
|
? undefined
|
|
@@ -81,16 +114,24 @@ export function makeInterpreter(resolve, dispatch, trace) {
|
|
|
81
114
|
if (active)
|
|
82
115
|
dispatch(msg);
|
|
83
116
|
};
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
117
|
+
const start = (lane, driver, cmd, report, after) => {
|
|
118
|
+
const controller = new AbortController();
|
|
119
|
+
const task = { controller, running: true, done: Promise.resolve() };
|
|
120
|
+
const body = async () => {
|
|
121
|
+
if (after !== undefined)
|
|
122
|
+
await after.done;
|
|
123
|
+
if (!controller.signal.aborted) {
|
|
124
|
+
await execute(driver, cmd, controller.signal, guardedDispatch, report);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
task.done = body().finally(() => {
|
|
128
|
+
task.running = false;
|
|
129
|
+
all.delete(task);
|
|
130
|
+
if (lanes.get(lane) === task)
|
|
92
131
|
lanes.delete(lane);
|
|
93
132
|
});
|
|
133
|
+
lanes.set(lane, task);
|
|
134
|
+
all.add(task);
|
|
94
135
|
};
|
|
95
136
|
const run = (cmd) => {
|
|
96
137
|
if (!active)
|
|
@@ -99,9 +140,8 @@ export function makeInterpreter(resolve, dispatch, trace) {
|
|
|
99
140
|
const lane = cmd.key ?? driver.name;
|
|
100
141
|
const policy = cmd.concurrency ?? driver.concurrency ?? 'merge';
|
|
101
142
|
const previous = lanes.get(lane);
|
|
102
|
-
const inFlight =
|
|
143
|
+
const inFlight = previous?.running === true ? previous : undefined;
|
|
103
144
|
const report = DEVTOOLS_ENABLED ? reporter(driver.name, lane, policy, cmd.input) : undefined;
|
|
104
|
-
let program = execute(driver, cmd, guardedDispatch, report);
|
|
105
145
|
if (inFlight !== undefined && policy === 'exhaust') {
|
|
106
146
|
if (DEVTOOLS_ENABLED)
|
|
107
147
|
report?.('dropped');
|
|
@@ -109,20 +149,25 @@ export function makeInterpreter(resolve, dispatch, trace) {
|
|
|
109
149
|
}
|
|
110
150
|
if (DEVTOOLS_ENABLED)
|
|
111
151
|
report?.('issued');
|
|
112
|
-
if (inFlight !== undefined)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (policy === 'queue')
|
|
116
|
-
program = Effect.zipRight(Fiber.await(inFlight), program);
|
|
117
|
-
}
|
|
118
|
-
track(lane, fork(program));
|
|
152
|
+
if (inFlight !== undefined && policy === 'switch')
|
|
153
|
+
inFlight.controller.abort();
|
|
154
|
+
start(lane, driver, cmd, report, inFlight !== undefined && policy === 'queue' ? inFlight : undefined);
|
|
119
155
|
};
|
|
120
156
|
const dispose = () => {
|
|
121
157
|
active = false;
|
|
122
|
-
|
|
158
|
+
for (const task of all)
|
|
159
|
+
task.controller.abort();
|
|
123
160
|
all.clear();
|
|
124
161
|
lanes.clear();
|
|
125
162
|
};
|
|
126
163
|
return { run, dispose };
|
|
127
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* A component's interpreter (registered by `command()`, features.ts). Drivers resolve by name:
|
|
167
|
+
* el.drivers → nearest provider → spec.drivers → the command's own (gyral-czi.35).
|
|
168
|
+
*/
|
|
169
|
+
export const hostInterpreter = (el, drivers, dispatch, trace) => makeInterpreter((driver) => el.drivers[driver.name] ??
|
|
170
|
+
providedDriver(el, driver.name) ??
|
|
171
|
+
drivers?.[driver.name] ??
|
|
172
|
+
driver, dispatch, trace);
|
|
128
173
|
//# sourceMappingURL=interpreter.js.map
|