@spearwolf/shadow-objects 0.34.0 → 0.35.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 +30 -0
- package/README.md +11 -2
- package/bundle.js +4 -2
- package/package.json +9 -1
- package/src/WorkerTimeoutError.d.ts +4 -3
- package/src/WorkerTimeoutError.d.ts.map +1 -1
- package/src/WorkerTimeoutError.js.map +2 -2
- package/src/constants.d.ts +8 -4
- package/src/constants.d.ts.map +1 -1
- package/src/constants.js +6 -0
- package/src/constants.js.map +2 -2
- package/src/elements/ShaePropElement.d.ts +15 -6
- package/src/elements/ShaePropElement.d.ts.map +1 -1
- package/src/elements/ShaePropElement.js +31 -7
- package/src/elements/ShaePropElement.js.map +2 -2
- package/src/elements/ShaeWorkerElement.d.ts +10 -0
- package/src/elements/ShaeWorkerElement.d.ts.map +1 -1
- package/src/elements/ShaeWorkerElement.js +69 -2
- package/src/elements/ShaeWorkerElement.js.map +2 -2
- package/src/elements/constants.d.ts +3 -0
- package/src/elements/constants.d.ts.map +1 -1
- package/src/elements/constants.js +6 -0
- package/src/elements/constants.js.map +2 -2
- package/src/in-the-dark/Entity.d.ts +30 -0
- package/src/in-the-dark/Entity.d.ts.map +1 -1
- package/src/in-the-dark/Entity.js +38 -0
- package/src/in-the-dark/Entity.js.map +2 -2
- package/src/in-the-dark/Kernel.d.ts +24 -1
- package/src/in-the-dark/Kernel.d.ts.map +1 -1
- package/src/in-the-dark/Kernel.js +56 -1
- package/src/in-the-dark/Kernel.js.map +2 -2
- package/src/in-the-dark/Registry.d.ts +7 -1
- package/src/in-the-dark/Registry.d.ts.map +1 -1
- package/src/in-the-dark/Registry.js +29 -0
- package/src/in-the-dark/Registry.js.map +2 -2
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts +8 -1
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts.map +1 -1
- package/src/in-the-dark/ShadowObjectCreationScope.js +23 -16
- package/src/in-the-dark/ShadowObjectCreationScope.js.map +2 -2
- package/src/in-the-dark/SignalsPath.d.ts +2 -0
- package/src/in-the-dark/SignalsPath.d.ts.map +1 -1
- package/src/in-the-dark/SignalsPath.js +4 -0
- package/src/in-the-dark/SignalsPath.js.map +2 -2
- package/src/in-the-dark/displayName.d.ts +4 -0
- package/src/in-the-dark/displayName.d.ts.map +1 -0
- package/src/in-the-dark/displayName.js +5 -0
- package/src/in-the-dark/displayName.js.map +7 -0
- package/src/index.d.ts +2 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +4 -0
- package/src/index.js.map +2 -2
- package/src/inspect/createKernelSnapshot.d.ts +17 -0
- package/src/inspect/createKernelSnapshot.d.ts.map +1 -0
- package/src/inspect/createKernelSnapshot.js +247 -0
- package/src/inspect/createKernelSnapshot.js.map +7 -0
- package/src/inspect/createViewSnapshot.d.ts +10 -0
- package/src/inspect/createViewSnapshot.d.ts.map +1 -0
- package/src/inspect/createViewSnapshot.js +143 -0
- package/src/inspect/createViewSnapshot.js.map +7 -0
- package/src/inspect/normalizeInspectRequest.d.ts +32 -0
- package/src/inspect/normalizeInspectRequest.d.ts.map +1 -0
- package/src/inspect/normalizeInspectRequest.js +55 -0
- package/src/inspect/normalizeInspectRequest.js.map +7 -0
- package/src/inspect/serializeValue.d.ts +13 -0
- package/src/inspect/serializeValue.d.ts.map +1 -0
- package/src/inspect/serializeValue.js +149 -0
- package/src/inspect/serializeValue.js.map +7 -0
- package/src/inspect/types.d.ts +260 -0
- package/src/inspect/types.d.ts.map +1 -0
- package/src/inspect/types.js +1 -0
- package/src/inspect/types.js.map +7 -0
- package/src/model-context/ModelContextLike.d.ts +59 -0
- package/src/model-context/ModelContextLike.d.ts.map +1 -0
- package/src/model-context/ModelContextLike.js +15 -0
- package/src/model-context/ModelContextLike.js.map +7 -0
- package/src/model-context/exposeShadowEnvsToModelContext.d.ts +48 -0
- package/src/model-context/exposeShadowEnvsToModelContext.d.ts.map +1 -0
- package/src/model-context/exposeShadowEnvsToModelContext.js +39 -0
- package/src/model-context/exposeShadowEnvsToModelContext.js.map +7 -0
- package/src/model-context/redactProps.d.ts +14 -0
- package/src/model-context/redactProps.d.ts.map +1 -0
- package/src/model-context/redactProps.js +22 -0
- package/src/model-context/redactProps.js.map +7 -0
- package/src/model-context/sharedExposure.d.ts +32 -0
- package/src/model-context/sharedExposure.d.ts.map +1 -0
- package/src/model-context/sharedExposure.js +108 -0
- package/src/model-context/sharedExposure.js.map +7 -0
- package/src/model-context/toolSupport.d.ts +72 -0
- package/src/model-context/toolSupport.d.ts.map +1 -0
- package/src/model-context/toolSupport.js +145 -0
- package/src/model-context/toolSupport.js.map +7 -0
- package/src/model-context/tools/findEntities.d.ts +15 -0
- package/src/model-context/tools/findEntities.d.ts.map +1 -0
- package/src/model-context/tools/findEntities.js +71 -0
- package/src/model-context/tools/findEntities.js.map +7 -0
- package/src/model-context/tools/getEntity.d.ts +16 -0
- package/src/model-context/tools/getEntity.d.ts.map +1 -0
- package/src/model-context/tools/getEntity.js +65 -0
- package/src/model-context/tools/getEntity.js.map +7 -0
- package/src/model-context/tools/getEntityTree.d.ts +4 -0
- package/src/model-context/tools/getEntityTree.d.ts.map +1 -0
- package/src/model-context/tools/getEntityTree.js +58 -0
- package/src/model-context/tools/getEntityTree.js.map +7 -0
- package/src/model-context/tools/getRegistry.d.ts +14 -0
- package/src/model-context/tools/getRegistry.d.ts.map +1 -0
- package/src/model-context/tools/getRegistry.js +39 -0
- package/src/model-context/tools/getRegistry.js.map +7 -0
- package/src/model-context/tools/index.d.ts +9 -0
- package/src/model-context/tools/index.d.ts.map +1 -0
- package/src/model-context/tools/index.js +16 -0
- package/src/model-context/tools/index.js.map +7 -0
- package/src/model-context/tools/listEnvs.d.ts +33 -0
- package/src/model-context/tools/listEnvs.d.ts.map +1 -0
- package/src/model-context/tools/listEnvs.js +37 -0
- package/src/model-context/tools/listEnvs.js.map +7 -0
- package/src/model-context.d.ts +13 -0
- package/src/model-context.d.ts.map +1 -0
- package/src/model-context.js +15 -0
- package/src/model-context.js.map +7 -0
- package/src/shadow-objects.d.ts +2 -0
- package/src/shadow-objects.d.ts.map +1 -1
- package/src/shadow-objects.js +4 -0
- package/src/shadow-objects.js.map +2 -2
- package/src/testing/TestEntity.d.ts +79 -0
- package/src/testing/TestEntity.d.ts.map +1 -0
- package/src/testing/TestEntity.js +135 -0
- package/src/testing/TestEntity.js.map +7 -0
- package/src/testing/createTestKernel.d.ts +11 -0
- package/src/testing/createTestKernel.d.ts.map +1 -0
- package/src/testing/createTestKernel.js +132 -0
- package/src/testing/createTestKernel.js.map +7 -0
- package/src/testing/mountShadowObject.d.ts +21 -0
- package/src/testing/mountShadowObject.d.ts.map +1 -0
- package/src/testing/mountShadowObject.js +72 -0
- package/src/testing/mountShadowObject.js.map +7 -0
- package/src/testing/recordKernelErrors.d.ts +21 -0
- package/src/testing/recordKernelErrors.d.ts.map +1 -0
- package/src/testing/recordKernelErrors.js +39 -0
- package/src/testing/recordKernelErrors.js.map +7 -0
- package/src/testing/settle.d.ts +15 -0
- package/src/testing/settle.d.ts.map +1 -0
- package/src/testing/settle.js +19 -0
- package/src/testing/settle.js.map +7 -0
- package/src/testing/types.d.ts +119 -0
- package/src/testing/types.d.ts.map +1 -0
- package/src/testing/types.js +1 -0
- package/src/testing/types.js.map +7 -0
- package/src/testing.d.ts +14 -0
- package/src/testing.d.ts.map +1 -0
- package/src/testing.js +11 -0
- package/src/testing.js.map +7 -0
- package/src/types.d.ts +150 -10
- package/src/types.d.ts.map +1 -1
- package/src/utils/attr-utils.d.ts +5 -0
- package/src/utils/attr-utils.d.ts.map +1 -1
- package/src/utils/attr-utils.js +2 -0
- package/src/utils/attr-utils.js.map +2 -2
- package/src/view/IShadowObjectEnvProxy.d.ts +9 -0
- package/src/view/IShadowObjectEnvProxy.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.d.ts +7 -0
- package/src/view/LocalShadowObjectEnv.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.js +15 -1
- package/src/view/LocalShadowObjectEnv.js.map +2 -2
- package/src/view/RemoteWorkerEnv.d.ts +22 -3
- package/src/view/RemoteWorkerEnv.d.ts.map +1 -1
- package/src/view/RemoteWorkerEnv.js +50 -1
- package/src/view/RemoteWorkerEnv.js.map +2 -2
- package/src/view/ShadowEnv.d.ts +30 -0
- package/src/view/ShadowEnv.d.ts.map +1 -1
- package/src/view/ShadowEnv.js +101 -0
- package/src/view/ShadowEnv.js.map +3 -3
- package/src/worker/MessageRouter.d.ts.map +1 -1
- package/src/worker/MessageRouter.js +23 -0
- package/src/worker/MessageRouter.js.map +2 -2
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@spearwolf/shadow-objects",
|
|
3
3
|
"description": "a reactive entity-component framework that feels at home in the shadows",
|
|
4
4
|
"homepage": "https://github.com/spearwolf/shadow-objects/",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.35.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Wolfger Schramm",
|
|
8
8
|
"email": "wolfger@spearwolf.de",
|
|
@@ -58,6 +58,14 @@
|
|
|
58
58
|
"./FrameLoop.js": {
|
|
59
59
|
"import": "./src/utils/FrameLoop.js",
|
|
60
60
|
"types": "./src/utils/FrameLoop.d.ts"
|
|
61
|
+
},
|
|
62
|
+
"./model-context.js": {
|
|
63
|
+
"import": "./src/model-context.js",
|
|
64
|
+
"types": "./src/model-context.d.ts"
|
|
65
|
+
},
|
|
66
|
+
"./testing.js": {
|
|
67
|
+
"import": "./src/testing.js",
|
|
68
|
+
"types": "./src/testing.d.ts"
|
|
61
69
|
}
|
|
62
70
|
},
|
|
63
71
|
"sideEffects": [
|
|
@@ -2,13 +2,14 @@ import type { WorkerReplyType } from './constants.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* The reason a reply from the worker did not arrive in time.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Five replies of a `RemoteWorkerEnv` have a deadline, and `messageType` names the one that stayed
|
|
6
6
|
* out: the `Loaded` greeting of the load handshake, the `ImportedModule` answer to an
|
|
7
7
|
* `importScript()`, the `AppliedChangeTrail` confirmation of a change trail sent with
|
|
8
|
-
* `waitForConfirmation`, and the `Destroyed` receipt of
|
|
8
|
+
* `waitForConfirmation`, the `Inspected` answer to an `inspect()`, and the `Destroyed` receipt of
|
|
9
|
+
* a teardown.
|
|
9
10
|
*
|
|
10
11
|
* `timeout` carries the number of milliseconds that were waited, so a diagnosis knows which of the
|
|
11
|
-
*
|
|
12
|
+
* five values was in force without reaching for the `timeouts` of the `RemoteWorkerEnv`.
|
|
12
13
|
*
|
|
13
14
|
* The error says nothing about what the worker did with the request. A confirmation window that
|
|
14
15
|
* ran out leaves a Shadow Environment that may well have applied the whole change trail, which is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerTimeoutError.d.ts","sourceRoot":"","sources":["../../src/WorkerTimeoutError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAEpD
|
|
1
|
+
{"version":3,"file":"WorkerTimeoutError.d.ts","sourceRoot":"","sources":["../../src/WorkerTimeoutError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gBAAgB,CAAC;AAEpD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,kDAAkD;IAClD,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;IAEtC,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,YAAY,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAKxD;CACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/WorkerTimeoutError.ts"],
|
|
4
|
-
"sourcesContent": ["import type {WorkerReplyType} from './constants.js';\n\n/**\n * The reason a reply from the worker did not arrive in time.\n *\n *
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type {WorkerReplyType} from './constants.js';\n\n/**\n * The reason a reply from the worker did not arrive in time.\n *\n * Five replies of a `RemoteWorkerEnv` have a deadline, and `messageType` names the one that stayed\n * out: the `Loaded` greeting of the load handshake, the `ImportedModule` answer to an\n * `importScript()`, the `AppliedChangeTrail` confirmation of a change trail sent with\n * `waitForConfirmation`, the `Inspected` answer to an `inspect()`, and the `Destroyed` receipt of\n * a teardown.\n *\n * `timeout` carries the number of milliseconds that were waited, so a diagnosis knows which of the\n * five values was in force without reaching for the `timeouts` of the `RemoteWorkerEnv`.\n *\n * The error says nothing about what the worker did with the request. A confirmation window that\n * ran out leaves a Shadow Environment that may well have applied the whole change trail, which is\n * why `ShadowEnv` books it as applied; `ChangeTrailRefusedError` is the only refusal that names a\n * number.\n */\nexport class WorkerTimeoutError extends Error {\n /** the type of the message that did not arrive */\n readonly messageType: WorkerReplyType;\n\n /** how many milliseconds were waited for it */\n readonly timeout: number;\n\n constructor(messageType: WorkerReplyType, timeout: number) {\n super(`no ${messageType} message arrived from the worker within ${timeout}ms`);\n this.name = 'WorkerTimeoutError';\n this.messageType = messageType;\n this.timeout = timeout;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAmBO,MAAM,2BAA2B,MAAM;AAAA,EAO5C,YAAY,aAA8B,SAAiB;AACzD,UAAM,MAAM,WAAW,2CAA2C,OAAO,IAAI;AAC7E,SAAK,OAAO;AACZ,SAAK,cAAc;AACnB,SAAK,UAAU;AAAA,EACjB;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/src/constants.d.ts
CHANGED
|
@@ -22,18 +22,21 @@ export declare const VoidToken = "#void";
|
|
|
22
22
|
export declare const ContextLost = "contextLost";
|
|
23
23
|
export declare const Configure = "configure";
|
|
24
24
|
export declare const ChangeTrail = "changeTrail";
|
|
25
|
+
export declare const Inspect = "inspect";
|
|
25
26
|
export declare const Destroy = "destroy";
|
|
26
27
|
export declare const Loaded = "loaded";
|
|
27
28
|
export declare const AppliedChangeTrail = "appliedChangeTrail";
|
|
28
29
|
export declare const ImportedModule = "importedModule";
|
|
29
30
|
export declare const Destroyed = "destroyed";
|
|
31
|
+
export declare const Inspected = "inspected";
|
|
30
32
|
/**
|
|
31
|
-
* The
|
|
33
|
+
* The five replies a `RemoteWorkerEnv` waits for, each behind a deadline of its own: the `Loaded`
|
|
32
34
|
* greeting of the load handshake, the `ImportedModule` answer to an `importScript()`, the
|
|
33
|
-
* `AppliedChangeTrail` confirmation of a change trail sent with `waitForConfirmation`,
|
|
34
|
-
* `Destroyed` receipt of a teardown. A `switch` over
|
|
35
|
+
* `AppliedChangeTrail` confirmation of a change trail sent with `waitForConfirmation`, the
|
|
36
|
+
* `Inspected` answer to an `inspect()`, and the `Destroyed` receipt of a teardown. A `switch` over
|
|
37
|
+
* the five is exhaustive.
|
|
35
38
|
*/
|
|
36
|
-
export type WorkerReplyType = typeof Loaded | typeof AppliedChangeTrail | typeof ImportedModule | typeof Destroyed;
|
|
39
|
+
export type WorkerReplyType = typeof Loaded | typeof AppliedChangeTrail | typeof ImportedModule | typeof Inspected | typeof Destroyed;
|
|
37
40
|
/**
|
|
38
41
|
* The `messageToView` event is fired when the kernel receives a message from an entity (to its view component counterpart)
|
|
39
42
|
*/
|
|
@@ -41,6 +44,7 @@ export declare const MessageToView = "messageToView";
|
|
|
41
44
|
export declare const WorkerLoadTimeout = 60000;
|
|
42
45
|
export declare const WorkerConfigureTimeout = 60000;
|
|
43
46
|
export declare const WorkerChangeTrailTimeout = 5000;
|
|
47
|
+
export declare const WorkerInspectTimeout = 5000;
|
|
44
48
|
export declare const WorkerDestroyTimeout = 5000;
|
|
45
49
|
export declare const ShadowObjectsExport = "shadowObjects";
|
|
46
50
|
//# sourceMappingURL=constants.d.ts.map
|
package/src/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,YAAY,CAAC;AAE9C,oBAAY,gBAAgB;IAC1B,iBAAiB,IAAI;IACrB,cAAc,IAAA;IACd,OAAO,IAAA;CACR;AAED,oBAAY,mBAAmB;IAC7B,cAAc,IAAI;IAClB,eAAe,IAAA;IACf,SAAS,IAAA;IACT,WAAW,IAAA;IACX,gBAAgB,IAAA;IAChB,WAAW,IAAA;IACX,UAAU,IAAA;CACX;AAED,eAAO,MAAM,QAAQ,EAAE,aAAmD,CAAC;AAE3E,eAAO,MAAM,SAAS,UAAU,CAAC;AAEjC;;;GAGG;AACH,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,eAAO,MAAM,SAAS,cAAc,CAAC;AACrC,eAAO,MAAM,WAAW,gBAAgB,CAAC;AACzC,eAAO,MAAM,OAAO,YAAY,CAAC;AAEjC,eAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AACvD,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAC/C,eAAO,MAAM,SAAS,cAAc,CAAC;AAErC
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,YAAY,CAAC;AAE9C,oBAAY,gBAAgB;IAC1B,iBAAiB,IAAI;IACrB,cAAc,IAAA;IACd,OAAO,IAAA;CACR;AAED,oBAAY,mBAAmB;IAC7B,cAAc,IAAI;IAClB,eAAe,IAAA;IACf,SAAS,IAAA;IACT,WAAW,IAAA;IACX,gBAAgB,IAAA;IAChB,WAAW,IAAA;IACX,UAAU,IAAA;CACX;AAED,eAAO,MAAM,QAAQ,EAAE,aAAmD,CAAC;AAE3E,eAAO,MAAM,SAAS,UAAU,CAAC;AAEjC;;;GAGG;AACH,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,eAAO,MAAM,SAAS,cAAc,CAAC;AACrC,eAAO,MAAM,WAAW,gBAAgB,CAAC;AACzC,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,eAAO,MAAM,OAAO,YAAY,CAAC;AAEjC,eAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AACvD,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAC/C,eAAO,MAAM,SAAS,cAAc,CAAC;AACrC,eAAO,MAAM,SAAS,cAAc,CAAC;AAErC;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GACvB,OAAO,MAAM,GACb,OAAO,kBAAkB,GACzB,OAAO,cAAc,GACrB,OAAO,SAAS,GAChB,OAAO,SAAS,CAAC;AAErB;;GAEG;AACH,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AACvC,eAAO,MAAM,sBAAsB,QAAQ,CAAC;AAC5C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,oBAAoB,OAAO,CAAC;AACzC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,eAAO,MAAM,mBAAmB,kBAAkB,CAAC"}
|
package/src/constants.js
CHANGED
|
@@ -19,15 +19,18 @@ const VoidToken = "#void";
|
|
|
19
19
|
const ContextLost = "contextLost";
|
|
20
20
|
const Configure = "configure";
|
|
21
21
|
const ChangeTrail = "changeTrail";
|
|
22
|
+
const Inspect = "inspect";
|
|
22
23
|
const Destroy = "destroy";
|
|
23
24
|
const Loaded = "loaded";
|
|
24
25
|
const AppliedChangeTrail = "appliedChangeTrail";
|
|
25
26
|
const ImportedModule = "importedModule";
|
|
26
27
|
const Destroyed = "destroyed";
|
|
28
|
+
const Inspected = "inspected";
|
|
27
29
|
const MessageToView = "messageToView";
|
|
28
30
|
const WorkerLoadTimeout = 6e4;
|
|
29
31
|
const WorkerConfigureTimeout = 6e4;
|
|
30
32
|
const WorkerChangeTrailTimeout = 5e3;
|
|
33
|
+
const WorkerInspectTimeout = 5e3;
|
|
31
34
|
const WorkerDestroyTimeout = 5e3;
|
|
32
35
|
const ShadowObjectsExport = "shadowObjects";
|
|
33
36
|
export {
|
|
@@ -41,6 +44,8 @@ export {
|
|
|
41
44
|
Destroyed,
|
|
42
45
|
GlobalNS,
|
|
43
46
|
ImportedModule,
|
|
47
|
+
Inspect,
|
|
48
|
+
Inspected,
|
|
44
49
|
Loaded,
|
|
45
50
|
MessageToView,
|
|
46
51
|
ShadowObjectsExport,
|
|
@@ -48,6 +53,7 @@ export {
|
|
|
48
53
|
WorkerChangeTrailTimeout,
|
|
49
54
|
WorkerConfigureTimeout,
|
|
50
55
|
WorkerDestroyTimeout,
|
|
56
|
+
WorkerInspectTimeout,
|
|
51
57
|
WorkerLoadTimeout
|
|
52
58
|
};
|
|
53
59
|
//# sourceMappingURL=constants.js.map
|
package/src/constants.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/constants.ts"],
|
|
4
|
-
"sourcesContent": ["import type {NamespaceType} from './types.js';\n\nexport enum ChangeTrailPhase {\n StructuralChanges = 1,\n ContentUpdates,\n Removal,\n}\n\nexport enum ComponentChangeType {\n CreateEntities = 1,\n DestroyEntities,\n SetParent,\n UpdateOrder,\n ChangeProperties,\n ChangeToken,\n SendEvents,\n}\n\nexport const GlobalNS: NamespaceType = Symbol.for('ShadowObjectsGlobalNS');\n\nexport const VoidToken = '#void';\n\n/**\n * The event is dispatched by `ComponentContext.reCreateChanges` and forwarded to all `ViewComponent`.\n * The event itself has no other data.\n */\nexport const ContextLost = 'contextLost';\n\nexport const Configure = 'configure';\nexport const ChangeTrail = 'changeTrail';\nexport const Destroy = 'destroy';\n\nexport const Loaded = 'loaded';\nexport const AppliedChangeTrail = 'appliedChangeTrail';\nexport const ImportedModule = 'importedModule';\nexport const Destroyed = 'destroyed';\n\n/**\n * The
|
|
5
|
-
"mappings": "AAEO,IAAK,mBAAL,kBAAKA,sBAAL;AACL,EAAAA,oCAAA,uBAAoB,KAApB;AACA,EAAAA,oCAAA;AACA,EAAAA,oCAAA;AAHU,SAAAA;AAAA,GAAA;AAML,IAAK,sBAAL,kBAAKC,yBAAL;AACL,EAAAA,0CAAA,oBAAiB,KAAjB;AACA,EAAAA,0CAAA;AACA,EAAAA,0CAAA;AACA,EAAAA,0CAAA;AACA,EAAAA,0CAAA;AACA,EAAAA,0CAAA;AACA,EAAAA,0CAAA;AAPU,SAAAA;AAAA,GAAA;AAUL,MAAM,WAA0B,uBAAO,IAAI,uBAAuB;AAElE,MAAM,YAAY;AAMlB,MAAM,cAAc;AAEpB,MAAM,YAAY;AAClB,MAAM,cAAc;AACpB,MAAM,UAAU;AAEhB,MAAM,SAAS;AACf,MAAM,qBAAqB;AAC3B,MAAM,iBAAiB;AACvB,MAAM,YAAY;
|
|
4
|
+
"sourcesContent": ["import type {NamespaceType} from './types.js';\n\nexport enum ChangeTrailPhase {\n StructuralChanges = 1,\n ContentUpdates,\n Removal,\n}\n\nexport enum ComponentChangeType {\n CreateEntities = 1,\n DestroyEntities,\n SetParent,\n UpdateOrder,\n ChangeProperties,\n ChangeToken,\n SendEvents,\n}\n\nexport const GlobalNS: NamespaceType = Symbol.for('ShadowObjectsGlobalNS');\n\nexport const VoidToken = '#void';\n\n/**\n * The event is dispatched by `ComponentContext.reCreateChanges` and forwarded to all `ViewComponent`.\n * The event itself has no other data.\n */\nexport const ContextLost = 'contextLost';\n\nexport const Configure = 'configure';\nexport const ChangeTrail = 'changeTrail';\nexport const Inspect = 'inspect';\nexport const Destroy = 'destroy';\n\nexport const Loaded = 'loaded';\nexport const AppliedChangeTrail = 'appliedChangeTrail';\nexport const ImportedModule = 'importedModule';\nexport const Destroyed = 'destroyed';\nexport const Inspected = 'inspected';\n\n/**\n * The five replies a `RemoteWorkerEnv` waits for, each behind a deadline of its own: the `Loaded`\n * greeting of the load handshake, the `ImportedModule` answer to an `importScript()`, the\n * `AppliedChangeTrail` confirmation of a change trail sent with `waitForConfirmation`, the\n * `Inspected` answer to an `inspect()`, and the `Destroyed` receipt of a teardown. A `switch` over\n * the five is exhaustive.\n */\nexport type WorkerReplyType =\n | typeof Loaded\n | typeof AppliedChangeTrail\n | typeof ImportedModule\n | typeof Inspected\n | typeof Destroyed;\n\n/**\n * The `messageToView` event is fired when the kernel receives a message from an entity (to its view component counterpart)\n */\nexport const MessageToView = 'messageToView';\n\nexport const WorkerLoadTimeout = 60000;\nexport const WorkerConfigureTimeout = 60000;\nexport const WorkerChangeTrailTimeout = 5000;\nexport const WorkerInspectTimeout = 5000;\nexport const WorkerDestroyTimeout = 5000;\n\nexport const ShadowObjectsExport = 'shadowObjects';\n"],
|
|
5
|
+
"mappings": "AAEO,IAAK,mBAAL,kBAAKA,sBAAL;AACL,EAAAA,oCAAA,uBAAoB,KAApB;AACA,EAAAA,oCAAA;AACA,EAAAA,oCAAA;AAHU,SAAAA;AAAA,GAAA;AAML,IAAK,sBAAL,kBAAKC,yBAAL;AACL,EAAAA,0CAAA,oBAAiB,KAAjB;AACA,EAAAA,0CAAA;AACA,EAAAA,0CAAA;AACA,EAAAA,0CAAA;AACA,EAAAA,0CAAA;AACA,EAAAA,0CAAA;AACA,EAAAA,0CAAA;AAPU,SAAAA;AAAA,GAAA;AAUL,MAAM,WAA0B,uBAAO,IAAI,uBAAuB;AAElE,MAAM,YAAY;AAMlB,MAAM,cAAc;AAEpB,MAAM,YAAY;AAClB,MAAM,cAAc;AACpB,MAAM,UAAU;AAChB,MAAM,UAAU;AAEhB,MAAM,SAAS;AACf,MAAM,qBAAqB;AAC3B,MAAM,iBAAiB;AACvB,MAAM,YAAY;AAClB,MAAM,YAAY;AAmBlB,MAAM,gBAAgB;AAEtB,MAAM,oBAAoB;AAC1B,MAAM,yBAAyB;AAC/B,MAAM,2BAA2B;AACjC,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAE7B,MAAM,sBAAsB;",
|
|
6
6
|
"names": ["ChangeTrailPhase", "ComponentChangeType"]
|
|
7
7
|
}
|
|
@@ -45,6 +45,14 @@ export declare class ShaePropElement extends ShaeLifecycleElement {
|
|
|
45
45
|
/** The logger this element reports through: a subclass reads it and does not replace it. */
|
|
46
46
|
protected get logger(): ConsoleLogger;
|
|
47
47
|
get name(): string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Writes the `name` attribute, and `attributeChangedCallback` takes it from there: the attribute
|
|
50
|
+
* is the one source the name is read from, so a write through the property and a write through
|
|
51
|
+
* `setAttribute` end up in the same place and are trimmed by the same rule. A framework renderer
|
|
52
|
+
* assigns every prop it finds `in` the element as a property — React 19 and Vue 3 both do — and
|
|
53
|
+
* a name it cannot assign is a property that never binds.
|
|
54
|
+
*/
|
|
55
|
+
set name(name: string | undefined);
|
|
48
56
|
get value(): unknown;
|
|
49
57
|
set value(val: unknown);
|
|
50
58
|
get shouldTrim(): boolean;
|
|
@@ -55,12 +63,13 @@ export declare class ShaePropElement extends ShaeLifecycleElement {
|
|
|
55
63
|
/**
|
|
56
64
|
* Take this element's own subscriptions up. Its half of {@link ShaeLifecycleElement.restore}.
|
|
57
65
|
*
|
|
58
|
-
* There is nothing to catch up on here, unlike `<shae-ent>`: `connectedCallback` reads `
|
|
59
|
-
* `
|
|
60
|
-
* this
|
|
61
|
-
* `prop.
|
|
62
|
-
*
|
|
63
|
-
*
|
|
66
|
+
* There is nothing to catch up on here, unlike `<shae-ent>`: `connectedCallback` reads `name`,
|
|
67
|
+
* `type` and `no-trim` off the attributes, `value` where the attribute carries one, and looks the
|
|
68
|
+
* host up again straight after this. So the attributes decide, and whatever was written to a
|
|
69
|
+
* released element through `prop.entNode` — or through `prop.value` against a `value` attribute
|
|
70
|
+
* that says something — is replaced by what the tree and the markup say. That is this element's
|
|
71
|
+
* rule in and out of a teardown alike — a `<shae-prop>` reads its position on every connect — and
|
|
72
|
+
* the documentation states it as the difference it is.
|
|
64
73
|
*/
|
|
65
74
|
protected restore(): void;
|
|
66
75
|
connectedCallback(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShaePropElement.d.ts","sourceRoot":"","sources":["../../../src/elements/ShaePropElement.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,aAAa,EAAC,MAAM,2BAA2B,CAAC;AAExD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAC;AAI5D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAC,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAuC/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,eAAgB,SAAQ,oBAAoB;;IACvD,MAAM,CAAC,kBAAkB,WAAoD;IAE7E,QAAQ,CAAC,iBAAiB,QAAQ;IAElC,SAAS,CAAC,QAAQ,CAAC,QAAQ,oEAA8C;IACzE,SAAS,CAAC,QAAQ,CAAC,cAAc,mEAA6C;IAE9E,SAAS,CAAC,QAAQ,CAAC,KAAK,4DAAsC;IAC9D,SAAS,CAAC,QAAQ,CAAC,QAAQ,iDAAkB;IAC7C,SAAS,CAAC,QAAQ,CAAC,SAAS,iDAAkB;IAC9C,SAAS,CAAC,QAAQ,CAAC,KAAK,4DAAsC;IAC9D,SAAS,CAAC,QAAQ,CAAC,WAAW,iDAAsB;IAIpD,4FAA4F;IAC5F,SAAS,KAAK,MAAM,IAAI,aAAa,CAEpC;IAqCD,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,IAAI,KAAK,CAAC,GAAG,EAAE,OAAO,EAErB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,OAAO,IAAI,cAAc,GAAG,SAAS,CAExC;IAED,IAAI,OAAO,CAAC,EAAE,EAAE,cAAc,GAAG,SAAS,EAEzC;IAED,IAAI,aAAa,IAAI,aAAa,GAAG,SAAS,CAE7C;IAED,cAWC;IAyHD
|
|
1
|
+
{"version":3,"file":"ShaePropElement.d.ts","sourceRoot":"","sources":["../../../src/elements/ShaePropElement.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,aAAa,EAAC,MAAM,2BAA2B,CAAC;AAExD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAC;AAI5D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAC,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAuC/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,eAAgB,SAAQ,oBAAoB;;IACvD,MAAM,CAAC,kBAAkB,WAAoD;IAE7E,QAAQ,CAAC,iBAAiB,QAAQ;IAElC,SAAS,CAAC,QAAQ,CAAC,QAAQ,oEAA8C;IACzE,SAAS,CAAC,QAAQ,CAAC,cAAc,mEAA6C;IAE9E,SAAS,CAAC,QAAQ,CAAC,KAAK,4DAAsC;IAC9D,SAAS,CAAC,QAAQ,CAAC,QAAQ,iDAAkB;IAC7C,SAAS,CAAC,QAAQ,CAAC,SAAS,iDAAkB;IAC9C,SAAS,CAAC,QAAQ,CAAC,KAAK,4DAAsC;IAC9D,SAAS,CAAC,QAAQ,CAAC,WAAW,iDAAsB;IAIpD,4FAA4F;IAC5F,SAAS,KAAK,MAAM,IAAI,aAAa,CAEpC;IAqCD,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IAED;;;;;;OAMG;IACH,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAOhC;IAED,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,IAAI,KAAK,CAAC,GAAG,EAAE,OAAO,EAErB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,OAAO,IAAI,cAAc,GAAG,SAAS,CAExC;IAED,IAAI,OAAO,CAAC,EAAE,EAAE,cAAc,GAAG,SAAS,EAEzC;IAED,IAAI,aAAa,IAAI,aAAa,GAAG,SAAS,CAE7C;IAED,cAWC;IAyHD;;;;;;;;;;OAUG;IACH,UAAmB,OAAO,IAAI,IAAI,CAIjC;IAEQ,iBAAiB,SAsBzB;IAED,wBAAwB,CAAC,IAAI,EAAE,MAAM,QAkBpC;IAEQ,oBAAoB,SAU5B;IAED;;;;;;OAMG;IACH,UAAmB,QAAQ,IAAI,IAAI,CAgBlC;CAuHF"}
|
|
@@ -127,6 +127,11 @@ class ShaePropElement extends ShaeLifecycleElement {
|
|
|
127
127
|
const value = this.getAttribute(ATTR_VALUE);
|
|
128
128
|
this.valueIn$.set(value === null || value === "" ? void 0 : value);
|
|
129
129
|
};
|
|
130
|
+
/** Whether the `value` attribute says something — absent and empty are the same "nothing" here. */
|
|
131
|
+
this.#valueAttributeCarriesValue = () => {
|
|
132
|
+
const value = this.getAttribute(ATTR_VALUE);
|
|
133
|
+
return value !== null && value !== "";
|
|
134
|
+
};
|
|
130
135
|
this.#readTypeAttribute = () => {
|
|
131
136
|
let type = this.getAttribute(ATTR_TYPE)?.trim().toLowerCase();
|
|
132
137
|
if (type && !propValueConverters.has(type)) {
|
|
@@ -170,6 +175,21 @@ class ShaePropElement extends ShaeLifecycleElement {
|
|
|
170
175
|
get name() {
|
|
171
176
|
return this.name$.value;
|
|
172
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Writes the `name` attribute, and `attributeChangedCallback` takes it from there: the attribute
|
|
180
|
+
* is the one source the name is read from, so a write through the property and a write through
|
|
181
|
+
* `setAttribute` end up in the same place and are trimmed by the same rule. A framework renderer
|
|
182
|
+
* assigns every prop it finds `in` the element as a property — React 19 and Vue 3 both do — and
|
|
183
|
+
* a name it cannot assign is a property that never binds.
|
|
184
|
+
*/
|
|
185
|
+
set name(name) {
|
|
186
|
+
const trimmed = name?.trim();
|
|
187
|
+
if (!trimmed) {
|
|
188
|
+
this.removeAttribute(ATTR_NAME);
|
|
189
|
+
} else if (this.getAttribute(ATTR_NAME) !== trimmed) {
|
|
190
|
+
this.setAttribute(ATTR_NAME, trimmed);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
173
193
|
get value() {
|
|
174
194
|
return this.valueOut$.value;
|
|
175
195
|
}
|
|
@@ -264,12 +284,13 @@ class ShaePropElement extends ShaeLifecycleElement {
|
|
|
264
284
|
/**
|
|
265
285
|
* Take this element's own subscriptions up. Its half of {@link ShaeLifecycleElement.restore}.
|
|
266
286
|
*
|
|
267
|
-
* There is nothing to catch up on here, unlike `<shae-ent>`: `connectedCallback` reads `
|
|
268
|
-
* `
|
|
269
|
-
* this
|
|
270
|
-
* `prop.
|
|
271
|
-
*
|
|
272
|
-
*
|
|
287
|
+
* There is nothing to catch up on here, unlike `<shae-ent>`: `connectedCallback` reads `name`,
|
|
288
|
+
* `type` and `no-trim` off the attributes, `value` where the attribute carries one, and looks the
|
|
289
|
+
* host up again straight after this. So the attributes decide, and whatever was written to a
|
|
290
|
+
* released element through `prop.entNode` — or through `prop.value` against a `value` attribute
|
|
291
|
+
* that says something — is replaced by what the tree and the markup say. That is this element's
|
|
292
|
+
* rule in and out of a teardown alike — a `<shae-prop>` reads its position on every connect — and
|
|
293
|
+
* the documentation states it as the difference it is.
|
|
273
294
|
*/
|
|
274
295
|
restore() {
|
|
275
296
|
super.restore();
|
|
@@ -281,7 +302,9 @@ class ShaePropElement extends ShaeLifecycleElement {
|
|
|
281
302
|
batch(() => {
|
|
282
303
|
this.#findEntNode();
|
|
283
304
|
this.#readNameAttribute();
|
|
284
|
-
this.#
|
|
305
|
+
if (this.#valueAttributeCarriesValue()) {
|
|
306
|
+
this.#readValueAttribute();
|
|
307
|
+
}
|
|
285
308
|
this.#readTypeAttribute();
|
|
286
309
|
this.#readNoTrimAttribute();
|
|
287
310
|
});
|
|
@@ -334,6 +357,7 @@ class ShaePropElement extends ShaeLifecycleElement {
|
|
|
334
357
|
#disconnectFromEntNode;
|
|
335
358
|
#readNameAttribute;
|
|
336
359
|
#readValueAttribute;
|
|
360
|
+
#valueAttributeCarriesValue;
|
|
337
361
|
#readTypeAttribute;
|
|
338
362
|
#readNoTrimAttribute;
|
|
339
363
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/elements/ShaePropElement.ts"],
|
|
4
|
-
"sourcesContent": ["import {batch, createEffect, createSignal, Effect, hibernate, link} from '@spearwolf/signalize';\nimport {readBooleanAttribute} from '../utils/attr-utils.js';\nimport {ConsoleLogger} from '../utils/ConsoleLogger.js';\nimport {MicrotaskGate} from '../utils/MicrotaskGate.js';\nimport type {ViewComponent} from '../view/ViewComponent.js';\nimport {ATTR_NAME, ATTR_NO_TRIM, ATTR_TYPE, ATTR_VALUE, ReRequestEntHostEventName} from './constants.js';\nimport {propValueConverters} from './propValueConverters.js';\nimport {requestEntAncestor} from './requestEntAncestor.js';\nimport type {ShaeEntElement} from './ShaeEntElement.js';\nimport {ShaeLifecycleElement} from './ShaeLifecycleElement.js';\n\n/**\n * How many `<shae-prop>` elements declare a given name on a given view component right now.\n *\n * A property belongs to the entity, not to the element that wrote it last: two elements may\n * declare the same name, and the entity keeps the property until the last of them lets go. The\n * count is keyed the way the binding is \u2014 (view component, name) \u2014 and lives beside the elements\n * because no single one of them can answer the question.\n */\nconst declarantsPerComponent = new WeakMap<ViewComponent, Map<string, number>>();\n\nconst addDeclarant = (vc: ViewComponent, name: string) => {\n let names = declarantsPerComponent.get(vc);\n if (names == null) {\n names = new Map();\n declarantsPerComponent.set(vc, names);\n }\n names.set(name, (names.get(name) ?? 0) + 1);\n};\n\n/** @returns `true` if this was the last element declaring `name` on `vc`. */\nconst removeDeclarant = (vc: ViewComponent, name: string): boolean => {\n const names = declarantsPerComponent.get(vc);\n if (names == null) return true;\n\n const remaining = (names.get(name) ?? 1) - 1;\n if (remaining > 0) {\n names.set(name, remaining);\n return false;\n }\n\n names.delete(name);\n if (names.size === 0) {\n declarantsPerComponent.delete(vc);\n }\n return true;\n};\n\n/**\n * Sets a property on the entity above it.\n *\n * Unlike `<shae-ent>` and `<shae-worker>`, this element does not extend `ShaeElement` and has no\n * namespace of its own. It shares their lifecycle: `ShaeLifecycleElement` is the base of all three,\n * and `destroy()`, `isDestroyed`, `restore()` and `teardown()` mean the same thing on every one of\n * them. What it does not share is the layer above it. `ShaeElement` exists for elements that pick\n * an environment: their `ns` attribute names the one they live in, and what they do goes there by\n * default \u2014 `ShaeElement.syncShadowObjectsOf()` is the explicit way to reach a different one, as\n * `<shae-ent>` does on a namespace change. A property picks nothing. It belongs to the closest\n * entity above it in the flattened tree, whatever namespace that entity happens to be in \u2014\n * proximity decides, not membership. An `ns` on a `<shae-prop>` would therefore be an attribute\n * that changes no answer, and the convention both siblings follow \u2014 spreading\n * `ShaeElement.observedAttributes` into their own \u2014 would put it into `observedAttributes` for\n * every reader to trip over.\n *\n * That is also why the sync runs through the host: the environment that has to hear about a\n * property is the one the host entity lives in, and this element is the wrong place to ask.\n * `entNode.syncShadowObjects()` reaches it; a sync of this element's own would target the\n * global namespace instead \u2014 nothing happens if no environment answers to that, and if one\n * does, it still leaves the namespaced host waiting.\n *\n * The marker for this element is `isShaePropElement`, beside `isShaeEntElement` and\n * `isShaeWorkerElement`. Each names one tag for consumers and tests to check \u2014 this file's own\n * spec included \u2014 not for the host lookup itself: that is decided by `ShaeEntElement`'s\n * `#onRequestParent`, the only listener registered for the request, and it checks `requester`,\n * `answer` and `ns`, never a flag. A `<shae-prop>` never answers a host request because it never\n * listens for one, not because its marker reads differently.\n */\nexport class ShaePropElement extends ShaeLifecycleElement {\n static observedAttributes = [ATTR_NAME, ATTR_VALUE, ATTR_TYPE, ATTR_NO_TRIM];\n\n readonly isShaePropElement = true;\n\n protected readonly entNode$ = createSignal<ShaeEntElement | undefined>();\n protected readonly viewComponent$ = createSignal<ViewComponent | undefined>();\n\n protected readonly name$ = createSignal<string | undefined>();\n protected readonly valueIn$ = createSignal();\n protected readonly valueOut$ = createSignal();\n protected readonly type$ = createSignal<string | undefined>();\n protected readonly shouldTrim$ = createSignal(true);\n\n readonly #logger = new ConsoleLogger('ShaePropElement');\n\n /** The logger this element reports through: a subclass reads it and does not replace it. */\n protected get logger(): ConsoleLogger {\n return this.#logger;\n }\n\n /** Binds this element to the entity above it, and carries that entity's component through. */\n #hostBinding?: (() => void) | undefined;\n\n /** Holds the property on the entity for as long as this element declares it. */\n #declareProperty?: Effect | undefined;\n\n /** Writes the value through to the entity. */\n #writePropertyValue?: Effect | undefined;\n\n /** Turns what the attribute spells into the value the property carries. */\n #convertValue?: Effect | undefined;\n\n /** Whether the missing-host warning has already gone out for this element. */\n #reportedMissingHost = false;\n\n /** The node this element listens on for a re-request of the host, so the listener can come off the same one. */\n #reRequestHostTarget?: EventTarget | undefined;\n\n // Two terms, and they answer two different questions. `isConnected` separates an element in\n // the tree from one on its way out: a round booked before the departure has nothing left to\n // say about a position the element no longer holds. `isDestroyed` separates one that is\n // listening from one that was ended by hand and is still standing where it was \u2014 a lookup\n // running there would write `entNode$` and register the re-request listener again that the\n // teardown just took off, and the element would be listening after it let go of everything.\n // The flag is up before `teardown()` runs, so a round that comes due afterwards reads the\n // state the element is actually in.\n //\n // The test stands here and not in the gate: a `MicrotaskGate` has no way to call a booking\n // off, and that is the whole of what tells it apart from `DeferredTeardown`.\n readonly #hostLookup = new MicrotaskGate(() => {\n if (this.isConnected && !this.isDestroyed) {\n this.#findEntNode();\n }\n });\n\n get name(): string | undefined {\n return this.name$.value;\n }\n\n get value(): unknown {\n return this.valueOut$.value;\n }\n\n set value(val: unknown) {\n this.valueIn$.set(val);\n }\n\n get shouldTrim(): boolean {\n return this.shouldTrim$.value;\n }\n\n get entNode(): ShaeEntElement | undefined {\n return this.entNode$.value;\n }\n\n set entNode(el: ShaeEntElement | undefined) {\n this.entNode$.set(el);\n }\n\n get viewComponent(): ViewComponent | undefined {\n return this.viewComponent$.value;\n }\n\n constructor() {\n super();\n\n // read with nothing listening yet: what this puts into the signals is the state the attributes\n // already spell out, and the effects read it on their first run at the first connect\n batch(() => {\n this.#readNameAttribute();\n this.#readValueAttribute();\n this.#readTypeAttribute();\n this.#readNoTrimAttribute();\n });\n }\n\n /**\n * Take up everything this element listens to.\n *\n * Called from {@link ShaePropElement.restore} and from nowhere else \u2014 that is the one place where\n * this element's subscriptions begin, at the first connect as well as after a teardown.\n */\n #subscribe(): void {\n this.#hostBinding = this.entNode$.onChange((entNode) => {\n if (entNode) {\n const con = link(entNode.viewComponent$, this.viewComponent$);\n return () => {\n con.destroy();\n };\n } else {\n this.viewComponent$.set(undefined);\n }\n });\n\n // The binding this element holds is the pair (view component, name). It ends in three ways \u2014\n // the element leaves the tree, its name changes, or it moves to another entity \u2014 and each of\n // them moves one of these two signals. The cleanup takes the property back for exactly the\n // binding that ended, which is why the three need no code of their own.\n //\n // `valueOut$` is deliberately not read here: a value change would otherwise run a removal\n // against a set of the same key on every keystroke.\n //\n // Standing before the value effect is a matter of reading order, not of behaviour: the order a\n // rename ends up with in the trail is decided in `ComponentChanges.#propsChangeOrder`, where\n // `appendToEnd` moves a key that is already queued to the back. Measured both ways, the trail\n // reads `[['x', undefined], ['y', 7]]` either way.\n this.#declareProperty = createEffect(() => {\n const vc = this.viewComponent$.get();\n const name = this.name$.get();\n\n if (vc == null || !name) return;\n\n // untracked on purpose: the host is where the sync has to go, not something this binding\n // depends on. And it is the host and not this element, because the host carries the\n // namespace \u2014 the environment holding this property is its, and nothing else knows which.\n const entNode = this.entNode$.value;\n\n addDeclarant(vc, name);\n\n return () => {\n // only the last declarant clears the property \u2014 another element may be holding the same\n // name on the same entity, and the entity is what the property belongs to\n if (removeDeclarant(vc, name)) {\n vc.removeProperty(name);\n // no `isConnected` guard, unlike the value effect below: an element that is no longer\n // connected is precisely the case this has to reach\n entNode?.syncShadowObjects();\n }\n };\n });\n\n this.#writePropertyValue = createEffect(() => {\n const vc = this.viewComponent$.get();\n if (vc) {\n const name = this.name$.get();\n if (name) {\n const value = this.valueOut$.get();\n\n this.logger.debug(`[${this.name}] view-component set-property`, name, value, vc.uuid, {\n viewComponent: vc,\n shaeProp: this,\n });\n\n vc.setProperty(name, value);\n\n if (this.isConnected) {\n this.entNode?.syncShadowObjects();\n }\n }\n }\n });\n\n this.#convertValue = createEffect(() => {\n const type = this.type$.get();\n const shouldTrim = this.shouldTrim$.get();\n let value = this.valueIn$.get();\n\n if (shouldTrim && typeof value === 'string') {\n value = value.trim();\n }\n\n // only null and undefined mean \"no value\" \u2014 0, false and the empty string are values\n value = value ?? undefined;\n\n if (typeof value === 'string' && type) {\n const convert = propValueConverters.get(type);\n if (convert != null) {\n // invalid input is an operating case for this element, not an exceptional state: it is\n // reported and clears the value instead of throwing out of a reactive effect. A numeric\n // type throws when its conversion does not come out as a number, and takes the same way\n // out of here as malformed JSON does.\n try {\n value = convert(value);\n } catch (error) {\n // reported through `error`, not `warn`: `warn` is gated behind\n // `ConsoleLogger.sharedConfig.enable`, which defaults to \"the page is served from a\n // loopback host\". A dropped property value has to stay visible in production too.\n this.logger.error(`[${this.name}] could not convert the value into the type \"${type}\"`, {\n value,\n error,\n shaeProp: this,\n });\n value = undefined;\n }\n }\n // an unrecognized type name \u2014 `convert == null` \u2014 passes the string through untouched and\n // unreported here. `type$` is `protected`; the only writer in this repository is\n // `#readTypeAttribute`, which already warns for every unknown name it sees, so this branch\n // stays silent on purpose rather than warning a second time for the same name.\n }\n\n this.valueOut$.set(value);\n });\n }\n\n /**\n * Take this element's own subscriptions up. Its half of {@link ShaeLifecycleElement.restore}.\n *\n * There is nothing to catch up on here, unlike `<shae-ent>`: `connectedCallback` reads `value`,\n * `name`, `type` and `no-trim` off the attributes and looks the host up again straight after\n * this, so the attributes decide, and whatever was written to a released element through\n * `prop.value` or `prop.entNode` is replaced by what the tree and the markup say. That is this\n * element's rule in and out of a teardown alike \u2014 a `<shae-prop>` reads its position on every\n * connect \u2014 and the documentation states it as the difference it is.\n */\n protected override restore(): void {\n super.restore();\n\n this.#subscribe();\n }\n\n override connectedCallback() {\n // the whole body outside any reactive context of the caller, for the reason spelled out in\n // `ShaeLifecycleElement.connectedCallback`: an `append()` inside a foreign effect would\n // otherwise own the effects taken up here, and that effect's next run would release them\n hibernate(() => {\n super.connectedCallback();\n\n batch(() => {\n this.#findEntNode();\n this.#readNameAttribute();\n this.#readValueAttribute();\n this.#readTypeAttribute();\n this.#readNoTrimAttribute();\n });\n });\n }\n\n attributeChangedCallback(name: string) {\n switch (name) {\n case ATTR_NAME:\n this.#readNameAttribute();\n break;\n\n case ATTR_VALUE:\n this.#readValueAttribute();\n break;\n\n case ATTR_TYPE:\n this.#readTypeAttribute();\n break;\n\n case ATTR_NO_TRIM:\n this.#readNoTrimAttribute();\n break;\n }\n }\n\n override disconnectedCallback() {\n this.#stopListeningForHostChanges();\n this.#disconnectFromEntNode();\n\n // last, and the order is not a matter of taste. `#disconnectFromEntNode` queues a microtask of\n // its own that writes `entNode$`, and microtasks run in the order they were queued: booked\n // second, the teardown runs second, so the unbinding still reaches a subscription that is on\n // and the property is taken off the entity it left. The other way round the write would arrive\n // after the release and reach nobody\n super.disconnectedCallback();\n }\n\n /**\n * Release this element's own subscriptions. Its half of {@link ShaeLifecycleElement.teardown}.\n *\n * The `link()` between the host's `viewComponent$` and this element's own needs no line of its\n * own: it is built in the cleanup path of the `entNode$` subscription, and taking that\n * subscription off runs the cleanup that destroys it.\n */\n protected override teardown(): void {\n this.#stopListeningForHostChanges();\n\n this.#hostBinding?.();\n this.#hostBinding = undefined;\n\n this.#declareProperty?.destroy();\n this.#declareProperty = undefined;\n\n this.#writePropertyValue?.destroy();\n this.#writePropertyValue = undefined;\n\n this.#convertValue?.destroy();\n this.#convertValue = undefined;\n\n super.teardown();\n }\n\n // Determines the host from where the element stands right now. The request runs *without* a\n // namespace: a property belongs to the closest entity above it, whatever namespace that entity\n // is in.\n //\n // Nobody answering means there is no entity above this position, and the element says so. A\n // binding without an answer belongs to a place that is no longer there \u2014 whether the element\n // moved or the entity above it did. The cleanup on `entNode$` then takes the property off the\n // entity that no longer holds it, instead of leaving the element writing into an entity it does\n // not sit under any more.\n #findEntNode = () => {\n let found: ShaeEntElement | undefined;\n requestEntAncestor(this, {answer: (entNode) => (found = entNode)});\n\n this.entNode$.set(found);\n\n // Reported once per element, not once per request: the re-request channel repeats this very\n // lookup, so a property that never gets a host would otherwise report again on every upgrade\n // happening anywhere above it.\n //\n // The `isConnected` term is what separates \"no host\" from \"no position\": an element on its way\n // out of the tree is not missing a host.\n //\n // The limit of this report belongs next to it: `logger.warn` hangs on\n // `ConsoleLogger.sharedConfig.enable`, which means \"the page is served from a loopback host\" \u2014\n // elsewhere the case stays silent. `warn` and not `error`, because in the upgrade path this\n // framework supports by design, \"no entity above me yet\" is a state to pass through: a\n // <shae-prop> under an element whose tag is registered later reports once and finds its host\n // right afterwards.\n if (found == null && this.isConnected && !this.#reportedMissingHost) {\n this.#reportedMissingHost = true;\n this.logger.warn(`[${this.name}] no entity above this element, the property is set nowhere`, {\n shaeProp: this,\n });\n }\n\n this.#listenForHostChanges();\n };\n\n // Someone above started or stopped answering. Two things separate this from a lookup the\n // element makes on arrival, and both belong here, to the trigger \u2014 what an unanswered\n // request means is the same on either path.\n //\n // It waits a microtask: the message arrives while the tree is still moving. An entity that\n // announces its departure has left, but everything below it can be back in place before the\n // tick ends, and an ancestor answering *right now* can be one this element is about to\n // leave behind. Asking after the dust settles is asking once, about the tree that is\n // actually there.\n //\n // And it drops the question if this element has left in the meantime, for the same reason\n // `#reRequestParent` does in `ShaeEntElement`: an element that is out of the tree answers\n // nothing about its position any more. Whether its departure is a move or an end is\n // `#disconnectFromEntNode`'s to answer, one microtask later, and it is answered in one place.\n // Nothing observable turns on the test \u2014 a departing entity takes its properties with it\n // either way \u2014 so it stands as a rule, not as a measurement.\n //\n // `#hostLookup` bounds the cost: a page filling up with entities sends many messages through the\n // same ancestor chain, and the gate turns them into one lookup per microtask and per element.\n #onReRequestHost = () => {\n this.#hostLookup.schedule();\n };\n\n #listenForHostChanges = () => {\n // bound: the entity itself is on the ascent of every element that could take the property\n // away from it. Unbound: the document is the one node every such event reaches, out of any\n // tree, closed roots included\n const target: EventTarget = this.entNode$.value ?? this.ownerDocument;\n if (target === this.#reRequestHostTarget) return;\n this.#stopListeningForHostChanges();\n target.addEventListener(ReRequestEntHostEventName, this.#onReRequestHost);\n this.#reRequestHostTarget = target;\n };\n\n #stopListeningForHostChanges = () => {\n this.#reRequestHostTarget?.removeEventListener(ReRequestEntHostEventName, this.#onReRequestHost);\n this.#reRequestHostTarget = undefined;\n };\n\n #disconnectFromEntNode = () => {\n queueMicrotask(() => {\n if (!this.isConnected) {\n this.entNode$.set(undefined);\n }\n });\n };\n\n #readNameAttribute = () => {\n this.name$.set(this.getAttribute(ATTR_NAME)?.trim() ?? undefined);\n };\n\n #readValueAttribute = () => {\n // an empty value attribute means \"no value\", exactly like a missing one. Everything else\n // goes in raw: valueIn$ is the source a no-trim switch recalculates from, so whitespace\n // has to survive until the trim decides what is left of it.\n const value = this.getAttribute(ATTR_VALUE);\n this.valueIn$.set(value === null || value === '' ? undefined : value);\n };\n\n #readTypeAttribute = () => {\n let type = this.getAttribute(ATTR_TYPE)?.trim().toLowerCase();\n if (type && !propValueConverters.has(type)) {\n this.logger.warn(`[${this.name}] unknown type \"${type}\"`, {\n shaeProp: this,\n });\n type = undefined;\n }\n this.type$.set(type);\n };\n\n #readNoTrimAttribute = () => {\n this.shouldTrim$.set(!readBooleanAttribute(this, ATTR_NO_TRIM));\n };\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAQ,OAAO,cAAc,cAAc,QAAQ,WAAW,YAAW;AACzE,SAAQ,4BAA2B;AACnC,SAAQ,qBAAoB;AAC5B,SAAQ,qBAAoB;AAE5B,SAAQ,WAAW,cAAc,WAAW,YAAY,iCAAgC;AACxF,SAAQ,2BAA0B;AAClC,SAAQ,0BAAyB;AAEjC,SAAQ,4BAA2B;AAUnC,MAAM,yBAAyB,oBAAI,QAA4C;AAE/E,MAAM,eAAe,CAAC,IAAmB,SAAiB;AACxD,MAAI,QAAQ,uBAAuB,IAAI,EAAE;AACzC,MAAI,SAAS,MAAM;AACjB,YAAQ,oBAAI,IAAI;AAChB,2BAAuB,IAAI,IAAI,KAAK;AAAA,EACtC;AACA,QAAM,IAAI,OAAO,MAAM,IAAI,IAAI,KAAK,KAAK,CAAC;AAC5C;AAGA,MAAM,kBAAkB,CAAC,IAAmB,SAA0B;AACpE,QAAM,QAAQ,uBAAuB,IAAI,EAAE;AAC3C,MAAI,SAAS,KAAM,QAAO;AAE1B,QAAM,aAAa,MAAM,IAAI,IAAI,KAAK,KAAK;AAC3C,MAAI,YAAY,GAAG;AACjB,UAAM,IAAI,MAAM,SAAS;AACzB,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,IAAI;AACjB,MAAI,MAAM,SAAS,GAAG;AACpB,2BAAuB,OAAO,EAAE;AAAA,EAClC;AACA,SAAO;AACT;AA+BO,MAAM,wBAAwB,qBAAqB;AAAA,
|
|
4
|
+
"sourcesContent": ["import {batch, createEffect, createSignal, Effect, hibernate, link} from '@spearwolf/signalize';\nimport {readBooleanAttribute} from '../utils/attr-utils.js';\nimport {ConsoleLogger} from '../utils/ConsoleLogger.js';\nimport {MicrotaskGate} from '../utils/MicrotaskGate.js';\nimport type {ViewComponent} from '../view/ViewComponent.js';\nimport {ATTR_NAME, ATTR_NO_TRIM, ATTR_TYPE, ATTR_VALUE, ReRequestEntHostEventName} from './constants.js';\nimport {propValueConverters} from './propValueConverters.js';\nimport {requestEntAncestor} from './requestEntAncestor.js';\nimport type {ShaeEntElement} from './ShaeEntElement.js';\nimport {ShaeLifecycleElement} from './ShaeLifecycleElement.js';\n\n/**\n * How many `<shae-prop>` elements declare a given name on a given view component right now.\n *\n * A property belongs to the entity, not to the element that wrote it last: two elements may\n * declare the same name, and the entity keeps the property until the last of them lets go. The\n * count is keyed the way the binding is \u2014 (view component, name) \u2014 and lives beside the elements\n * because no single one of them can answer the question.\n */\nconst declarantsPerComponent = new WeakMap<ViewComponent, Map<string, number>>();\n\nconst addDeclarant = (vc: ViewComponent, name: string) => {\n let names = declarantsPerComponent.get(vc);\n if (names == null) {\n names = new Map();\n declarantsPerComponent.set(vc, names);\n }\n names.set(name, (names.get(name) ?? 0) + 1);\n};\n\n/** @returns `true` if this was the last element declaring `name` on `vc`. */\nconst removeDeclarant = (vc: ViewComponent, name: string): boolean => {\n const names = declarantsPerComponent.get(vc);\n if (names == null) return true;\n\n const remaining = (names.get(name) ?? 1) - 1;\n if (remaining > 0) {\n names.set(name, remaining);\n return false;\n }\n\n names.delete(name);\n if (names.size === 0) {\n declarantsPerComponent.delete(vc);\n }\n return true;\n};\n\n/**\n * Sets a property on the entity above it.\n *\n * Unlike `<shae-ent>` and `<shae-worker>`, this element does not extend `ShaeElement` and has no\n * namespace of its own. It shares their lifecycle: `ShaeLifecycleElement` is the base of all three,\n * and `destroy()`, `isDestroyed`, `restore()` and `teardown()` mean the same thing on every one of\n * them. What it does not share is the layer above it. `ShaeElement` exists for elements that pick\n * an environment: their `ns` attribute names the one they live in, and what they do goes there by\n * default \u2014 `ShaeElement.syncShadowObjectsOf()` is the explicit way to reach a different one, as\n * `<shae-ent>` does on a namespace change. A property picks nothing. It belongs to the closest\n * entity above it in the flattened tree, whatever namespace that entity happens to be in \u2014\n * proximity decides, not membership. An `ns` on a `<shae-prop>` would therefore be an attribute\n * that changes no answer, and the convention both siblings follow \u2014 spreading\n * `ShaeElement.observedAttributes` into their own \u2014 would put it into `observedAttributes` for\n * every reader to trip over.\n *\n * That is also why the sync runs through the host: the environment that has to hear about a\n * property is the one the host entity lives in, and this element is the wrong place to ask.\n * `entNode.syncShadowObjects()` reaches it; a sync of this element's own would target the\n * global namespace instead \u2014 nothing happens if no environment answers to that, and if one\n * does, it still leaves the namespaced host waiting.\n *\n * The marker for this element is `isShaePropElement`, beside `isShaeEntElement` and\n * `isShaeWorkerElement`. Each names one tag for consumers and tests to check \u2014 this file's own\n * spec included \u2014 not for the host lookup itself: that is decided by `ShaeEntElement`'s\n * `#onRequestParent`, the only listener registered for the request, and it checks `requester`,\n * `answer` and `ns`, never a flag. A `<shae-prop>` never answers a host request because it never\n * listens for one, not because its marker reads differently.\n */\nexport class ShaePropElement extends ShaeLifecycleElement {\n static observedAttributes = [ATTR_NAME, ATTR_VALUE, ATTR_TYPE, ATTR_NO_TRIM];\n\n readonly isShaePropElement = true;\n\n protected readonly entNode$ = createSignal<ShaeEntElement | undefined>();\n protected readonly viewComponent$ = createSignal<ViewComponent | undefined>();\n\n protected readonly name$ = createSignal<string | undefined>();\n protected readonly valueIn$ = createSignal();\n protected readonly valueOut$ = createSignal();\n protected readonly type$ = createSignal<string | undefined>();\n protected readonly shouldTrim$ = createSignal(true);\n\n readonly #logger = new ConsoleLogger('ShaePropElement');\n\n /** The logger this element reports through: a subclass reads it and does not replace it. */\n protected get logger(): ConsoleLogger {\n return this.#logger;\n }\n\n /** Binds this element to the entity above it, and carries that entity's component through. */\n #hostBinding?: (() => void) | undefined;\n\n /** Holds the property on the entity for as long as this element declares it. */\n #declareProperty?: Effect | undefined;\n\n /** Writes the value through to the entity. */\n #writePropertyValue?: Effect | undefined;\n\n /** Turns what the attribute spells into the value the property carries. */\n #convertValue?: Effect | undefined;\n\n /** Whether the missing-host warning has already gone out for this element. */\n #reportedMissingHost = false;\n\n /** The node this element listens on for a re-request of the host, so the listener can come off the same one. */\n #reRequestHostTarget?: EventTarget | undefined;\n\n // Two terms, and they answer two different questions. `isConnected` separates an element in\n // the tree from one on its way out: a round booked before the departure has nothing left to\n // say about a position the element no longer holds. `isDestroyed` separates one that is\n // listening from one that was ended by hand and is still standing where it was \u2014 a lookup\n // running there would write `entNode$` and register the re-request listener again that the\n // teardown just took off, and the element would be listening after it let go of everything.\n // The flag is up before `teardown()` runs, so a round that comes due afterwards reads the\n // state the element is actually in.\n //\n // The test stands here and not in the gate: a `MicrotaskGate` has no way to call a booking\n // off, and that is the whole of what tells it apart from `DeferredTeardown`.\n readonly #hostLookup = new MicrotaskGate(() => {\n if (this.isConnected && !this.isDestroyed) {\n this.#findEntNode();\n }\n });\n\n get name(): string | undefined {\n return this.name$.value;\n }\n\n /**\n * Writes the `name` attribute, and `attributeChangedCallback` takes it from there: the attribute\n * is the one source the name is read from, so a write through the property and a write through\n * `setAttribute` end up in the same place and are trimmed by the same rule. A framework renderer\n * assigns every prop it finds `in` the element as a property \u2014 React 19 and Vue 3 both do \u2014 and\n * a name it cannot assign is a property that never binds.\n */\n set name(name: string | undefined) {\n const trimmed = name?.trim();\n if (!trimmed) {\n this.removeAttribute(ATTR_NAME);\n } else if (this.getAttribute(ATTR_NAME) !== trimmed) {\n this.setAttribute(ATTR_NAME, trimmed);\n }\n }\n\n get value(): unknown {\n return this.valueOut$.value;\n }\n\n set value(val: unknown) {\n this.valueIn$.set(val);\n }\n\n get shouldTrim(): boolean {\n return this.shouldTrim$.value;\n }\n\n get entNode(): ShaeEntElement | undefined {\n return this.entNode$.value;\n }\n\n set entNode(el: ShaeEntElement | undefined) {\n this.entNode$.set(el);\n }\n\n get viewComponent(): ViewComponent | undefined {\n return this.viewComponent$.value;\n }\n\n constructor() {\n super();\n\n // read with nothing listening yet: what this puts into the signals is the state the attributes\n // already spell out, and the effects read it on their first run at the first connect\n batch(() => {\n this.#readNameAttribute();\n this.#readValueAttribute();\n this.#readTypeAttribute();\n this.#readNoTrimAttribute();\n });\n }\n\n /**\n * Take up everything this element listens to.\n *\n * Called from {@link ShaePropElement.restore} and from nowhere else \u2014 that is the one place where\n * this element's subscriptions begin, at the first connect as well as after a teardown.\n */\n #subscribe(): void {\n this.#hostBinding = this.entNode$.onChange((entNode) => {\n if (entNode) {\n const con = link(entNode.viewComponent$, this.viewComponent$);\n return () => {\n con.destroy();\n };\n } else {\n this.viewComponent$.set(undefined);\n }\n });\n\n // The binding this element holds is the pair (view component, name). It ends in three ways \u2014\n // the element leaves the tree, its name changes, or it moves to another entity \u2014 and each of\n // them moves one of these two signals. The cleanup takes the property back for exactly the\n // binding that ended, which is why the three need no code of their own.\n //\n // `valueOut$` is deliberately not read here: a value change would otherwise run a removal\n // against a set of the same key on every keystroke.\n //\n // Standing before the value effect is a matter of reading order, not of behaviour: the order a\n // rename ends up with in the trail is decided in `ComponentChanges.#propsChangeOrder`, where\n // `appendToEnd` moves a key that is already queued to the back. Measured both ways, the trail\n // reads `[['x', undefined], ['y', 7]]` either way.\n this.#declareProperty = createEffect(() => {\n const vc = this.viewComponent$.get();\n const name = this.name$.get();\n\n if (vc == null || !name) return;\n\n // untracked on purpose: the host is where the sync has to go, not something this binding\n // depends on. And it is the host and not this element, because the host carries the\n // namespace \u2014 the environment holding this property is its, and nothing else knows which.\n const entNode = this.entNode$.value;\n\n addDeclarant(vc, name);\n\n return () => {\n // only the last declarant clears the property \u2014 another element may be holding the same\n // name on the same entity, and the entity is what the property belongs to\n if (removeDeclarant(vc, name)) {\n vc.removeProperty(name);\n // no `isConnected` guard, unlike the value effect below: an element that is no longer\n // connected is precisely the case this has to reach\n entNode?.syncShadowObjects();\n }\n };\n });\n\n this.#writePropertyValue = createEffect(() => {\n const vc = this.viewComponent$.get();\n if (vc) {\n const name = this.name$.get();\n if (name) {\n const value = this.valueOut$.get();\n\n this.logger.debug(`[${this.name}] view-component set-property`, name, value, vc.uuid, {\n viewComponent: vc,\n shaeProp: this,\n });\n\n vc.setProperty(name, value);\n\n if (this.isConnected) {\n this.entNode?.syncShadowObjects();\n }\n }\n }\n });\n\n this.#convertValue = createEffect(() => {\n const type = this.type$.get();\n const shouldTrim = this.shouldTrim$.get();\n let value = this.valueIn$.get();\n\n if (shouldTrim && typeof value === 'string') {\n value = value.trim();\n }\n\n // only null and undefined mean \"no value\" \u2014 0, false and the empty string are values\n value = value ?? undefined;\n\n if (typeof value === 'string' && type) {\n const convert = propValueConverters.get(type);\n if (convert != null) {\n // invalid input is an operating case for this element, not an exceptional state: it is\n // reported and clears the value instead of throwing out of a reactive effect. A numeric\n // type throws when its conversion does not come out as a number, and takes the same way\n // out of here as malformed JSON does.\n try {\n value = convert(value);\n } catch (error) {\n // reported through `error`, not `warn`: `warn` is gated behind\n // `ConsoleLogger.sharedConfig.enable`, which defaults to \"the page is served from a\n // loopback host\". A dropped property value has to stay visible in production too.\n this.logger.error(`[${this.name}] could not convert the value into the type \"${type}\"`, {\n value,\n error,\n shaeProp: this,\n });\n value = undefined;\n }\n }\n // an unrecognized type name \u2014 `convert == null` \u2014 passes the string through untouched and\n // unreported here. `type$` is `protected`; the only writer in this repository is\n // `#readTypeAttribute`, which already warns for every unknown name it sees, so this branch\n // stays silent on purpose rather than warning a second time for the same name.\n }\n\n this.valueOut$.set(value);\n });\n }\n\n /**\n * Take this element's own subscriptions up. Its half of {@link ShaeLifecycleElement.restore}.\n *\n * There is nothing to catch up on here, unlike `<shae-ent>`: `connectedCallback` reads `name`,\n * `type` and `no-trim` off the attributes, `value` where the attribute carries one, and looks the\n * host up again straight after this. So the attributes decide, and whatever was written to a\n * released element through `prop.entNode` \u2014 or through `prop.value` against a `value` attribute\n * that says something \u2014 is replaced by what the tree and the markup say. That is this element's\n * rule in and out of a teardown alike \u2014 a `<shae-prop>` reads its position on every connect \u2014 and\n * the documentation states it as the difference it is.\n */\n protected override restore(): void {\n super.restore();\n\n this.#subscribe();\n }\n\n override connectedCallback() {\n // the whole body outside any reactive context of the caller, for the reason spelled out in\n // `ShaeLifecycleElement.connectedCallback`: an `append()` inside a foreign effect would\n // otherwise own the effects taken up here, and that effect's next run would release them\n hibernate(() => {\n super.connectedCallback();\n\n batch(() => {\n this.#findEntNode();\n this.#readNameAttribute();\n // `value` is the one attribute that is read only where it carries something: a value\n // written through `prop.value` never reaches the attribute, and a renderer that assigns\n // properties before it inserts the element \u2014 every framework renderer does \u2014 would\n // otherwise see its write replaced by \"no attribute, no value\" on the way in. An attribute\n // that carries a value decides, as it does for the three others.\n if (this.#valueAttributeCarriesValue()) {\n this.#readValueAttribute();\n }\n this.#readTypeAttribute();\n this.#readNoTrimAttribute();\n });\n });\n }\n\n attributeChangedCallback(name: string) {\n switch (name) {\n case ATTR_NAME:\n this.#readNameAttribute();\n break;\n\n case ATTR_VALUE:\n this.#readValueAttribute();\n break;\n\n case ATTR_TYPE:\n this.#readTypeAttribute();\n break;\n\n case ATTR_NO_TRIM:\n this.#readNoTrimAttribute();\n break;\n }\n }\n\n override disconnectedCallback() {\n this.#stopListeningForHostChanges();\n this.#disconnectFromEntNode();\n\n // last, and the order is not a matter of taste. `#disconnectFromEntNode` queues a microtask of\n // its own that writes `entNode$`, and microtasks run in the order they were queued: booked\n // second, the teardown runs second, so the unbinding still reaches a subscription that is on\n // and the property is taken off the entity it left. The other way round the write would arrive\n // after the release and reach nobody\n super.disconnectedCallback();\n }\n\n /**\n * Release this element's own subscriptions. Its half of {@link ShaeLifecycleElement.teardown}.\n *\n * The `link()` between the host's `viewComponent$` and this element's own needs no line of its\n * own: it is built in the cleanup path of the `entNode$` subscription, and taking that\n * subscription off runs the cleanup that destroys it.\n */\n protected override teardown(): void {\n this.#stopListeningForHostChanges();\n\n this.#hostBinding?.();\n this.#hostBinding = undefined;\n\n this.#declareProperty?.destroy();\n this.#declareProperty = undefined;\n\n this.#writePropertyValue?.destroy();\n this.#writePropertyValue = undefined;\n\n this.#convertValue?.destroy();\n this.#convertValue = undefined;\n\n super.teardown();\n }\n\n // Determines the host from where the element stands right now. The request runs *without* a\n // namespace: a property belongs to the closest entity above it, whatever namespace that entity\n // is in.\n //\n // Nobody answering means there is no entity above this position, and the element says so. A\n // binding without an answer belongs to a place that is no longer there \u2014 whether the element\n // moved or the entity above it did. The cleanup on `entNode$` then takes the property off the\n // entity that no longer holds it, instead of leaving the element writing into an entity it does\n // not sit under any more.\n #findEntNode = () => {\n let found: ShaeEntElement | undefined;\n requestEntAncestor(this, {answer: (entNode) => (found = entNode)});\n\n this.entNode$.set(found);\n\n // Reported once per element, not once per request: the re-request channel repeats this very\n // lookup, so a property that never gets a host would otherwise report again on every upgrade\n // happening anywhere above it.\n //\n // The `isConnected` term is what separates \"no host\" from \"no position\": an element on its way\n // out of the tree is not missing a host.\n //\n // The limit of this report belongs next to it: `logger.warn` hangs on\n // `ConsoleLogger.sharedConfig.enable`, which means \"the page is served from a loopback host\" \u2014\n // elsewhere the case stays silent. `warn` and not `error`, because in the upgrade path this\n // framework supports by design, \"no entity above me yet\" is a state to pass through: a\n // <shae-prop> under an element whose tag is registered later reports once and finds its host\n // right afterwards.\n if (found == null && this.isConnected && !this.#reportedMissingHost) {\n this.#reportedMissingHost = true;\n this.logger.warn(`[${this.name}] no entity above this element, the property is set nowhere`, {\n shaeProp: this,\n });\n }\n\n this.#listenForHostChanges();\n };\n\n // Someone above started or stopped answering. Two things separate this from a lookup the\n // element makes on arrival, and both belong here, to the trigger \u2014 what an unanswered\n // request means is the same on either path.\n //\n // It waits a microtask: the message arrives while the tree is still moving. An entity that\n // announces its departure has left, but everything below it can be back in place before the\n // tick ends, and an ancestor answering *right now* can be one this element is about to\n // leave behind. Asking after the dust settles is asking once, about the tree that is\n // actually there.\n //\n // And it drops the question if this element has left in the meantime, for the same reason\n // `#reRequestParent` does in `ShaeEntElement`: an element that is out of the tree answers\n // nothing about its position any more. Whether its departure is a move or an end is\n // `#disconnectFromEntNode`'s to answer, one microtask later, and it is answered in one place.\n // Nothing observable turns on the test \u2014 a departing entity takes its properties with it\n // either way \u2014 so it stands as a rule, not as a measurement.\n //\n // `#hostLookup` bounds the cost: a page filling up with entities sends many messages through the\n // same ancestor chain, and the gate turns them into one lookup per microtask and per element.\n #onReRequestHost = () => {\n this.#hostLookup.schedule();\n };\n\n #listenForHostChanges = () => {\n // bound: the entity itself is on the ascent of every element that could take the property\n // away from it. Unbound: the document is the one node every such event reaches, out of any\n // tree, closed roots included\n const target: EventTarget = this.entNode$.value ?? this.ownerDocument;\n if (target === this.#reRequestHostTarget) return;\n this.#stopListeningForHostChanges();\n target.addEventListener(ReRequestEntHostEventName, this.#onReRequestHost);\n this.#reRequestHostTarget = target;\n };\n\n #stopListeningForHostChanges = () => {\n this.#reRequestHostTarget?.removeEventListener(ReRequestEntHostEventName, this.#onReRequestHost);\n this.#reRequestHostTarget = undefined;\n };\n\n #disconnectFromEntNode = () => {\n queueMicrotask(() => {\n if (!this.isConnected) {\n this.entNode$.set(undefined);\n }\n });\n };\n\n #readNameAttribute = () => {\n this.name$.set(this.getAttribute(ATTR_NAME)?.trim() ?? undefined);\n };\n\n #readValueAttribute = () => {\n // an empty value attribute means \"no value\", exactly like a missing one. Everything else\n // goes in raw: valueIn$ is the source a no-trim switch recalculates from, so whitespace\n // has to survive until the trim decides what is left of it.\n const value = this.getAttribute(ATTR_VALUE);\n this.valueIn$.set(value === null || value === '' ? undefined : value);\n };\n\n /** Whether the `value` attribute says something \u2014 absent and empty are the same \"nothing\" here. */\n #valueAttributeCarriesValue = (): boolean => {\n const value = this.getAttribute(ATTR_VALUE);\n return value !== null && value !== '';\n };\n\n #readTypeAttribute = () => {\n let type = this.getAttribute(ATTR_TYPE)?.trim().toLowerCase();\n if (type && !propValueConverters.has(type)) {\n this.logger.warn(`[${this.name}] unknown type \"${type}\"`, {\n shaeProp: this,\n });\n type = undefined;\n }\n this.type$.set(type);\n };\n\n #readNoTrimAttribute = () => {\n this.shouldTrim$.set(!readBooleanAttribute(this, ATTR_NO_TRIM));\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ExposeHandle } from '../model-context/exposeShadowEnvsToModelContext.js';
|
|
1
2
|
import { ConsoleLogger } from '../utils/ConsoleLogger.js';
|
|
2
3
|
import { FrameLoop } from '../utils/FrameLoop.js';
|
|
3
4
|
import { ShadowEnv } from '../view/ShadowEnv.js';
|
|
@@ -18,6 +19,15 @@ export declare class ShaeWorkerElement extends ShaeElement {
|
|
|
18
19
|
isConnected$: import("@spearwolf/signalize").Signal<boolean>;
|
|
19
20
|
autoSync$: import("@spearwolf/signalize").Signal<string>;
|
|
20
21
|
src$: import("@spearwolf/signalize").Signal<string>;
|
|
22
|
+
/** The `redact-props` attribute as a list of property names, read as it stands right now. */
|
|
23
|
+
get redactProps(): string[];
|
|
24
|
+
/**
|
|
25
|
+
* This element's share of the page's model-context registration, while it has one: a promise
|
|
26
|
+
* of the `ExposeHandle`, resolving once the tools are on the platform (`available: false` where
|
|
27
|
+
* there is none) and rejecting with what `registerTool()` rejected with. `undefined` without
|
|
28
|
+
* the `expose-to-model-context` attribute, before the first connect, and after a teardown.
|
|
29
|
+
*/
|
|
30
|
+
get modelContextExposure(): Promise<ExposeHandle> | undefined;
|
|
21
31
|
/**
|
|
22
32
|
* Take the subscriptions up. Runs exactly once for this element, on the first connect.
|
|
23
33
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShaeWorkerElement.d.ts","sourceRoot":"","sources":["../../../src/elements/ShaeWorkerElement.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ShaeWorkerElement.d.ts","sourceRoot":"","sources":["../../../src/elements/ShaeWorkerElement.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oDAAoD,CAAC;AAErF,OAAO,EAAC,aAAa,EAAC,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAIhD,OAAO,EAAC,SAAS,EAA0B,MAAM,sBAAsB,CAAC;AAgBxE,OAAO,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAC;AA6B7C,qBAAa,iBAAkB,SAAQ,WAAW;;IAiNhD;;OAEG;IACH,CAAC,SAAS,CAAC,OAAO,CAAC;IAnNnB,OAAgB,kBAAkB,WAOhC;IAEF,MAAM,CAAC,eAAe,SAAW;IAEjC,QAAQ,CAAC,mBAAmB,QAAQ;IAEpC,QAAQ,CAAC,SAAS,YAAmB;IAIrC,+CAA+C;IAC/C,IAAI,MAAM,IAAI,aAAa,CAE1B;IAED,SAAS,UAAQ;IAEjB,YAAY,iDAAuB;IACnC,SAAS,gDAAmD;IAC5D,IAAI,gDAAoB;IASxB,6FAA6F;IAC7F,IAAI,WAAW,IAAI,MAAM,EAAE,CAE1B;IAED;;;;;OAKG;IACH,IAAI,oBAAoB,IAAI,OAAO,CAAC,YAAY,CAAC,GAAG,SAAS,CAE5D;IAWD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,UAAmB,OAAO,IAAI,IAAI,CAiEjC;IAyCD,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED,IAAI,QAAQ,IAIM,MAAM,GAAG,OAAO,GAAG,MAAM,CAF1C;IAED,IAAI,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAE1C;IAED,IAAI,SAAS,IAAI,SAAS,CAEzB;IASK,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAehE;IAEQ,iBAAiB,SA8BzB;IAEQ,oBAAoB,SAG5B;IAEQ,wBAAwB,CAAC,IAAI,EAAE,MAAM,QA8B7C;IAoBD,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC,CAgB1B;IAED;;;;;;;;;;;;;;;OAeG;IACH,UAAmB,QAAQ,SAa1B;CA6IF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { on } from "@spearwolf/eventize";
|
|
2
2
|
import { batch, createEffect, createSignal, destroySignal, Effect, hibernate } from "@spearwolf/signalize";
|
|
3
|
-
import { readBooleanAttribute, readNumberAttribute } from "../utils/attr-utils.js";
|
|
3
|
+
import { readBooleanAttribute, readListAttribute, readNumberAttribute } from "../utils/attr-utils.js";
|
|
4
4
|
import { ConsoleLogger } from "../utils/ConsoleLogger.js";
|
|
5
5
|
import { FrameLoop } from "../utils/FrameLoop.js";
|
|
6
6
|
import { ComponentContext } from "../view/ComponentContext.js";
|
|
@@ -12,10 +12,13 @@ import {
|
|
|
12
12
|
ATTR_CHANGE_TRAIL_TIMEOUT,
|
|
13
13
|
ATTR_CONFIGURE_TIMEOUT,
|
|
14
14
|
ATTR_DESTROY_TIMEOUT,
|
|
15
|
+
ATTR_EXPOSE_TO_MODEL_CONTEXT,
|
|
16
|
+
ATTR_INSPECT_TIMEOUT,
|
|
15
17
|
ATTR_LOAD_TIMEOUT,
|
|
16
18
|
ATTR_LOCAL,
|
|
17
19
|
ATTR_NO_AUTOSTART,
|
|
18
20
|
ATTR_NO_STRUCTURED_CLONE,
|
|
21
|
+
ATTR_REDACT_PROPS,
|
|
19
22
|
ATTR_SRC
|
|
20
23
|
} from "./constants.js";
|
|
21
24
|
import { ensureDisplayContentsRule } from "./displayContentsRule.js";
|
|
@@ -24,6 +27,7 @@ const WorkerTimeoutAttributes = [
|
|
|
24
27
|
["loadTimeout", ATTR_LOAD_TIMEOUT],
|
|
25
28
|
["configureTimeout", ATTR_CONFIGURE_TIMEOUT],
|
|
26
29
|
["changeTrailTimeout", ATTR_CHANGE_TRAIL_TIMEOUT],
|
|
30
|
+
["inspectTimeout", ATTR_INSPECT_TIMEOUT],
|
|
27
31
|
["destroyTimeout", ATTR_DESTROY_TIMEOUT]
|
|
28
32
|
];
|
|
29
33
|
function normaliseAutoSync(value) {
|
|
@@ -61,7 +65,8 @@ class ShaeWorkerElement extends ShaeElement {
|
|
|
61
65
|
ATTR_LOCAL,
|
|
62
66
|
ATTR_SRC,
|
|
63
67
|
ATTR_NO_STRUCTURED_CLONE,
|
|
64
|
-
ATTR_AUTO_SYNC
|
|
68
|
+
ATTR_AUTO_SYNC,
|
|
69
|
+
ATTR_EXPOSE_TO_MODEL_CONTEXT
|
|
65
70
|
];
|
|
66
71
|
}
|
|
67
72
|
static {
|
|
@@ -75,6 +80,20 @@ class ShaeWorkerElement extends ShaeElement {
|
|
|
75
80
|
#started;
|
|
76
81
|
#autoSync;
|
|
77
82
|
#importScript;
|
|
83
|
+
#modelContextExposure;
|
|
84
|
+
/** The `redact-props` attribute as a list of property names, read as it stands right now. */
|
|
85
|
+
get redactProps() {
|
|
86
|
+
return readListAttribute(this, ATTR_REDACT_PROPS);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* This element's share of the page's model-context registration, while it has one: a promise
|
|
90
|
+
* of the `ExposeHandle`, resolving once the tools are on the platform (`available: false` where
|
|
91
|
+
* there is none) and rejecting with what `registerTool()` rejected with. `undefined` without
|
|
92
|
+
* the `expose-to-model-context` attribute, before the first connect, and after a teardown.
|
|
93
|
+
*/
|
|
94
|
+
get modelContextExposure() {
|
|
95
|
+
return this.#modelContextExposure?.handle;
|
|
96
|
+
}
|
|
78
97
|
/**
|
|
79
98
|
* Points the environment at the namespace this element names.
|
|
80
99
|
*
|
|
@@ -229,6 +248,7 @@ class ShaeWorkerElement extends ShaeElement {
|
|
|
229
248
|
if (this.shouldAutostart) {
|
|
230
249
|
this.start().catch(this.#onUnobservedRejection);
|
|
231
250
|
}
|
|
251
|
+
this.#syncModelContextExposure();
|
|
232
252
|
});
|
|
233
253
|
}
|
|
234
254
|
disconnectedCallback() {
|
|
@@ -254,6 +274,9 @@ class ShaeWorkerElement extends ShaeElement {
|
|
|
254
274
|
this.#importScript?.run();
|
|
255
275
|
}
|
|
256
276
|
}
|
|
277
|
+
if (name === ATTR_EXPOSE_TO_MODEL_CONTEXT) {
|
|
278
|
+
this.#syncModelContextExposure();
|
|
279
|
+
}
|
|
257
280
|
}
|
|
258
281
|
/**
|
|
259
282
|
* The timeout attributes, read at the one moment they are needed: when the worker environment
|
|
@@ -299,6 +322,7 @@ class ShaeWorkerElement extends ShaeElement {
|
|
|
299
322
|
* {@link ShaeWorkerElement.restore} a method that runs once and never again.
|
|
300
323
|
*/
|
|
301
324
|
teardown() {
|
|
325
|
+
this.#leaveModelContextExposure();
|
|
302
326
|
this.#envViewBinding?.();
|
|
303
327
|
this.#envViewBinding = void 0;
|
|
304
328
|
this.#autoSync?.destroy();
|
|
@@ -390,6 +414,49 @@ class ShaeWorkerElement extends ShaeElement {
|
|
|
390
414
|
}
|
|
391
415
|
});
|
|
392
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* Joins or leaves the page's model-context registration, whichever the attribute and the
|
|
419
|
+
* element's state ask for. The attribute is a truthy attribute like `local`; the join waits
|
|
420
|
+
* for a connect, because an element that is never connected has no environment to expose, and
|
|
421
|
+
* it ends with the teardown. `redact-props` is not watched: the share reads it at every call.
|
|
422
|
+
*/
|
|
423
|
+
#syncModelContextExposure() {
|
|
424
|
+
const wanted = !this.isDestroyed && this.isConnected && readBooleanAttribute(this, ATTR_EXPOSE_TO_MODEL_CONTEXT);
|
|
425
|
+
if (wanted) {
|
|
426
|
+
this.#joinModelContextExposure();
|
|
427
|
+
} else {
|
|
428
|
+
this.#leaveModelContextExposure();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* The share is an ordinary call of `exposeShadowEnvsToModelContext()` with two live rules:
|
|
433
|
+
* this element's namespace, and the names its `redact-props` attribute carries at the moment
|
|
434
|
+
* a tool asks. The function comes in through a dynamic import, and that is the whole reason
|
|
435
|
+
* this element can carry the attribute at all -- a static import would pull the model-context
|
|
436
|
+
* layer into every consumer of `<shae-worker>`, and the layer is meant to stay out of
|
|
437
|
+
* `index.ts` and the worker bundle. The single-file bundle inlines the import; the lib layout
|
|
438
|
+
* keeps the modules apart. A leave before the import is back aborts the signal the call is
|
|
439
|
+
* made with, and the function registers nothing for an aborted signal.
|
|
440
|
+
*/
|
|
441
|
+
#joinModelContextExposure() {
|
|
442
|
+
if (this.#modelContextExposure !== void 0) return;
|
|
443
|
+
const controller = new AbortController();
|
|
444
|
+
const handle = import("../model-context/exposeShadowEnvsToModelContext.js").then(
|
|
445
|
+
({ exposeShadowEnvsToModelContext }) => exposeShadowEnvsToModelContext({
|
|
446
|
+
signal: controller.signal,
|
|
447
|
+
namespaces: (ns) => ns === this.ns,
|
|
448
|
+
redactProps: (name) => this.redactProps.includes(name)
|
|
449
|
+
})
|
|
450
|
+
);
|
|
451
|
+
this.#modelContextExposure = { controller, handle };
|
|
452
|
+
handle.catch((error) => this.logger.error("expose-to-model-context: registering the tools failed", error));
|
|
453
|
+
}
|
|
454
|
+
#leaveModelContextExposure() {
|
|
455
|
+
const exposure = this.#modelContextExposure;
|
|
456
|
+
if (exposure === void 0) return;
|
|
457
|
+
this.#modelContextExposure = void 0;
|
|
458
|
+
exposure.controller.abort();
|
|
459
|
+
}
|
|
393
460
|
}
|
|
394
461
|
export {
|
|
395
462
|
ShaeWorkerElement
|