@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/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
> Build-system, monorepo, lint/format, and dev-workflow changes that don't affect the shipped package are tracked in the top-level [`CHANGELOG.md`](https://github.com/spearwolf/shadow-objects/blob/main/CHANGELOG.md).
|
|
9
9
|
|
|
10
|
+
## [0.35.0] - 2026-09-06
|
|
11
|
+
|
|
12
|
+
> **Minor, and nothing breaks.** Everything below either adds API or repairs a form that could
|
|
13
|
+
> not work before — an application written against `0.34.0` compiles and runs against this
|
|
14
|
+
> release unchanged.
|
|
15
|
+
|
|
16
|
+
### New
|
|
17
|
+
|
|
18
|
+
- **New (public API):** `ShadowObjectCreationAPI<TEvents>` takes an eventize event map, `{[eventName]: argumentTuple}`, and checks the entity-implicit forms of `on()`, `once()` and `emit()` against it: the event name, the argument tuple, the listener's parameters, and the method names of a listener object. Without the map every form is as loose as before. `EventsOf<T>` derives such a map from a Shadow Object — every method of `T` is an event of that name carrying the method's parameters — so `ShadowObjectCreationAPI<EventsOf<PlayerLogic>>` types an `emit('onPowerUp', 100)` against the `onPowerUp(power: number)` that receives it. A `symbol` event name is the escape hatch, as it is in eventize. Exported from `@spearwolf/shadow-objects` and `@spearwolf/shadow-objects/shadow-objects.js`; a constructor written against a map still satisfies `ShadowObjectConstructor` and `ShadowObjectConstructorFunc`. Documented in `docs/api-reference.md`, `docs/guides.md`, `docs/concepts.md` and `docs/cheat-sheet.md`.
|
|
19
|
+
- **Changed (types):** the target forms of `on()`, `once()` and `emit()` on the creation API now mirror eventize's standalone functions: a target with an event map of its own — `eventize<MyEvents>()`, a `class extends Eventize<MyEvents>` — is checked against that map, and any other object is taken as it is, the way eventize duck-types a target. Before this the target had to be an `EventizedObject` of the default map, which turned down a typed emitter, a plain object and an `EntityApi` alike — `emit(entity.children[0], …)` needed eventize's own `emit` to compile. The listener-object and catch-all forms on the entity, and the `[name, priority]` tuple lists, are declared explicitly; `on(...args: SubscribeArgs)` remains for a loose map.
|
|
20
|
+
- **New (public API):** `ShadowEnv.inspect(request?, signal?)` and `ShadowEnv.inspectAll(request?, signal?)` — a plain-data, JSON-safe snapshot of an environment: the View's component tree with the committed properties and, where the proxy is ready and implements `inspect`, the Kernel's Entity Tree with properties, Shadow Objects (display name, tokens, the names each one uses and provides, the lifecycle hooks it implements) and Entity Contexts (provided, inherited and effective value, who provides it, and where the effective value comes from), the global context chains and the Registry. `inspect()` rejects only for the caller's reasons — an aborted signal, a destroyed environment — and reports every reason inside the environment under `error`. `inspectAll()` describes every environment that holds a namespace. An environment destroyed while it answers drops out of that list; the call rejects only for an aborted signal. Documented in `docs/api-reference.md`, `docs/guides.md`, `docs/concepts.md` and `docs/cheat-sheet.md`.
|
|
21
|
+
- **New (public API):** `createKernelSnapshot(kernel, request?)` — the in-environment half of the above, for a test or a Shadow Object module that holds a `Kernel`. Exported from `@spearwolf/shadow-objects` and from `@spearwolf/shadow-objects/shadow-objects.js`, together with the snapshot types (`EnvSnapshot`, `KernelSnapshot`, `ViewSnapshot`, `ViewComponentSnapshot`, `EntityNodeSnapshot`, `ShadowObjectSnapshot`, `EntityContextSnapshot`, `EntityContextSource`, `GlobalContextSnapshot`, `RegistrySnapshot`, `PropSnapshot`, `SerializedValue`, `SerializeLimits`, `InspectRequest`, `InspectInclude`, `TruncationNote`, `ContextName`). Adds `dist/src/inspect/*` and `dist/src/in-the-dark/displayName.js` with their declarations to the published file list. `getEntityGraph()` is unchanged.
|
|
22
|
+
- **New (public API):** `IShadowObjectEnvProxy.inspect?(request, signal?)` — an optional member of the proxy contract; `LocalShadowObjectEnv` implements it synchronously on its Kernel. A proxy without it keeps compiling and keeps working; `ShadowEnv.inspect()` then reports `NotInspectable` under `error`.
|
|
23
|
+
- **New (public API):** read-only accessors the snapshot is built from, each small enough to use on its own: `Entity.contextNames()`, `Entity.describeContext(name)`, `Entity.globalContextNames()`, `Entity.describeGlobalContext(name)`; `Kernel.tokenOf(uuid)`, `Kernel.describeShadowObjects(uuid)`, `Kernel.rootContextNames()`, `Kernel.describeRootContext(name)`; `Registry.describe()`, `Registry.tokensOf(construct)`, `Registry.isDefault(registry)`; `SignalsPath.signals`. `ShadowObjectCreationScope.describe()` is the internal accessor the Kernel builds `describeShadowObjects()` on. None of them creates anything on read, and each answers empty after a teardown rather than throwing. The description types `ShadowObjectDescription`, `ShadowObjectScopeDescription`, `RegistryDescription` and `LifecycleHookName` are exported.
|
|
24
|
+
- **New (public API):** `RemoteWorkerEnv.inspect(request?, signal?)` — the worker half of `ShadowEnv.inspect()`. The request crosses into the worker as an `Inspect` message under a serial of its own, `MessageRouter` builds the snapshot with `createKernelSnapshot()` and answers with `Inspected`; a builder that throws is answered with the error, and an inspection that arrives after the teardown is discarded like every other message. The message runs through the same queue as the change trails, so the snapshot reflects every trail posted before the call and none posted after it. Rejects with a `WorkerReportedError` for a failure the worker reported, a `WorkerTimeoutError` when the answer stays out, a `WorkerDestroyedError` after `destroy()`, and the caller's abort reason when the signal aborts. `ShadowEnv.inspect()` is unchanged and now answers for both shipped proxies.
|
|
25
|
+
- **New (protocol):** the message types `Inspect` (`'inspect'`) and `Inspected` (`'inspected'`) with their wire shapes `InspectEvent` and `InspectedEvent`; `WorkerReplyType` counts five members. `dist/bundle.js` carries the snapshot builder inside the inlined worker from now on: 242.3 kB → 252.5 kB minified, 72.8 kB → 77.3 kB gzipped. The `dist/` file list and the shape of `dist/package.json` are unchanged.
|
|
26
|
+
- **New (public API):** the fifth worker timeout. `WorkerInspectTimeout` (5000 ms), `WorkerTimeouts.inspectTimeout` and the `RemoteWorkerEnv` constructor option of the same name, vetted by the same rule as the four others, and the `<shae-worker inspect-timeout>` attribute (`ATTR_INSPECT_TIMEOUT`), read once when the worker environment is built and ignored under `local`. `WorkerTimeoutError.messageType` can read `'inspected'`.
|
|
27
|
+
- **New (public API):** `InspectRequest.filter` turns an inspection into a search. `{token?, propName?, shadowObject?, contextName?, limit?}`: every Entity that meets every given criterion is listed under `KernelSnapshot.search` as `{matches: [{uuid, token, path}], total}`, with `path` the token chain from the root; `roots` stays empty, the walk limits do not apply, and the View side ignores the field. `EntityNodeSnapshot.ancestors` names the chain above a node the request named in `rootUuids`. The types `EntityFilter`, `EntityMatch` and `EntitySearchSnapshot` are exported. Both are what the model-context tools of this release are built on, and both work from a console without them.
|
|
28
|
+
- **New (public API):** `ShadowEnv.inspectAll(request?, signal?, only?)` -- the third parameter is asked with the namespace an environment is registered under, and an environment it refuses is neither inspected nor listed. The model-context tools use it to keep an environment out of every answer without touching it.
|
|
29
|
+
- **New (public API, subpath):** `@spearwolf/shadow-objects/model-context.js` — `exposeShadowEnvsToModelContext(options?)` registers five read-only tools on the browser's model context (WebMCP, `document.modelContext`): `shae-list-envs`, `shae-get-entity-tree`, `shae-get-entity`, `shae-find-entities`, `shae-get-registry`, every one with `readOnlyHint` and `untrustedContentHint`, every answer `{content, structuredContent, isError?}` — a one-line summary, then the JSON. Options: `modelContext`, `toolPrefix` (`'shae-'`), `signal`, `exposedTo`, `limits`, `redactProps`. One registration per model context and prefix, shared by every call and by every `<shae-worker expose-to-model-context>`: the first share opens it, a later one joins, the tools answer from the union of what the shares expose (`namespaces`, `NamespaceType[]` or a predicate, exported as `NamespaceRule`; a share without it exposes every environment) and hide (`redactProps` cumulates, a rule leaves with its share), and the tools leave with the last share. `limits` and `exposedTo` are the opener's. Resolves with `available: false` where the platform has no model context, rejects with what `registerTool()` rejected with, registers all-or-nothing, and the handle's `dispose()` takes that share back. Nothing registers on import or on an element. Exported with it: `findModelContext()`, `isModelContextLike()`, `DefaultToolPrefix`, and the types `ExposeOptions`, `ExposeHandle`, `ModelContextLike`, `ModelContextToolLike`, `ModelContextToolResult`, `ModelContextRegisterOptions`, `ModelContextToolAnnotations`, `RedactRule`, `NamespaceRule`, `EnvSummary`, `EntityMatchEntry`, `FindEntitiesEntry`, `RegistryEntry`. Documented in `docs/api-reference.md` (*Model Context*, and *Exposing Environments to an Agent* under *Security*), `docs/guides.md`, `docs/cheat-sheet.md`, `docs/best-practices.md` §10 and the README.
|
|
30
|
+
- **Changed (dist contract):** `dist/package.json` gains the `./model-context.js` export, and the published file list gains `dist/src/model-context.*` and the eleven modules under `dist/src/model-context/` with their declarations (48 files in all, the entry included). `index.ts`, `bundle.js` and the worker bundle are unchanged by the subpath; `dist/bundle.js` grows by the search and the ancestors of the snapshot builder: 252.5 kB → 254.7 kB minified, 77.3 kB → 78.0 kB gzipped.
|
|
31
|
+
- **New (public API):** `<shae-worker expose-to-model-context>` and `<shae-worker redact-props="a, b">` (`ATTR_EXPOSE_TO_MODEL_CONTEXT`, `ATTR_REDACT_PROPS`) -- the declarative form of `exposeShadowEnvsToModelContext()`, and a share of the same registration: the attribute exposes the element's own environment and no other, every exposing element and every call on a page share one set of the five tools, the agent sees the union of what the shares expose, and `redact-props` cumulates with every other share's rule. Truthy like `local`, observed: setting it joins, removing it leaves, and the tools leave with the last share. `ShaeWorkerElement.redactProps` reads the list, `ShaeWorkerElement.modelContextExposure` is the promise of this element's `ExposeHandle` (`undefined` without the attribute, before the first connect and after a teardown). The element loads the function through a dynamic `import()` on the first join, so `index.ts` and the worker bundle stay as they were. `readListAttribute()` joins the attribute readers. Documented in `docs/api-reference.md` (`<shae-worker>`, *Model Context*, *Security*), `docs/guides.md`, `docs/cheat-sheet.md`, `docs/best-practices.md` §10 and the README.
|
|
32
|
+
- **Changed (bundle):** `dist/bundle.js` inlines the element's dynamic import and grows by the model-context modules: 254.7 kB → 271.2 kB minified, 78.0 kB → 84.2 kB gzipped. The `dist/` file list and `dist/package.json` are unchanged by the element.
|
|
33
|
+
- **New (public API, subpath):** `@spearwolf/shadow-objects/testing.js` — a testing utility that runs Shadow Objects on the real Kernel instead of a mocked creation API. `createTestKernel(options?)` builds a Kernel on a `Registry` of its own (`registry`, `failOnKernelErrors`, `echoKernelErrors`) and offers `define()`, `route()`, `importModule()`, `createEntity(token, props, options?)`, `entity(uuid)`, `settle()`, `errors`, `clearErrors()`, `dispose()`, plus `kernel` and `registry` for everything the facade does not cover. Each `createEntity()` hands out a `TestEntity`: object-shaped `setProps()`/`removeProps()`, `readProp()` and `readContext()` without a signalize import, `createChild()`, `setToken()`, `setParent()`, `sendViewEvent()`, `emit()`, `shadowObjects()`, `shadowObjectOf(constructa)`, `describe()`, and a `viewMessages` list the test kernel records for every uuid, whether or not a handle for it had been asked for yet. `mountShadowObject(constructa, options?)` is the one-object case on top of both — asynchronous, because the framework is: it settles after the object under test is built, and once more before that where `contexts` gave it a synthetic provider Entity, so a `useParentContext()` read in a constructor body answers with a value. `settle()` drains the whole microtask cascade through a `MessageChannel`, so it also resolves under fake timers, and `recordKernelErrors(logger, echo?)` is exported beside it for a Kernel held directly. Reports the Kernel swallows through `runGuarded()` are recorded and, by default, fail `dispose()`. Nothing imports a test runner, nothing runs at import time, and nothing is cloned on the way in — a DOM node or a WebGL handle reaches a Shadow Object by identity. Adds `dist/src/testing.js`, `dist/src/testing/*` and their declarations to the published file list. Documented in `docs/api-reference.md`, `docs/best-practices.md` §9, `docs/cheat-sheet.md` and the README.
|
|
34
|
+
|
|
35
|
+
### Bugfixes
|
|
36
|
+
|
|
37
|
+
- **Fixed (creation API):** `on()` and `once()` take every subscription form eventize's standalone `on()` / `once()` take, and decide from the arguments alone where it goes. A listener function, a priority or a lone listener object in front means the entity — `on(fn)`, `on(Priority.High, fn)`, `on(Priority.High, 'method', obj)`, `on({ping() {}})` — where before this every one of them was handed to eventize as if the first argument were the target and threw (`subscribeTo() called with insufficient arguments`, or `eventize() cannot attach to a value of type 'number'`), although the declared type accepted them. An object followed by anything is still the target: `on(other, 'ping', fn)`, `on(other, fn)`, `on(other, listenerObject)`. The one form that stays out of reach on the entity is a listener object together with a context object, because `[object, object]` reads as a target and its listener.
|
|
38
|
+
- **Fixed (`<shae-prop>`):** `name` has a setter — it writes the attribute, trimmed, and removes it for an empty name — and a `value` written through the property before the element connects survives the connect as long as the `value` attribute carries nothing. React 19 and Vue 3 assign every prop that is `in` a custom element as a property and insert the element afterwards; before this, the first render of a `<shae-prop>` threw `Cannot set property name … which has only a getter` under React 19, Vue 3 dropped the name with a warning, and both lost the value on insert. An attribute that carries a value still wins on every connect. Documented in `docs/api-reference.md`, `docs/cheat-sheet.md` and the README; the framework walkthrough is in `docs/guides.md` §5.
|
|
39
|
+
|
|
10
40
|
## [0.34.0] - 2026-09-02
|
|
11
41
|
|
|
12
42
|
> **Minor, not major.** The package is below `1.0.0`, so the accumulated breaking changes
|
package/README.md
CHANGED
|
@@ -81,8 +81,9 @@ left with. `destroy()` does it by hand, `isDestroyed` reads the current state.
|
|
|
81
81
|
What a released element is written in the meantime is where the two part company. `<shae-ent>` keeps
|
|
82
82
|
it — `token`, `ns` and `forward-custom-events` stand in the signals and are written out to the
|
|
83
83
|
attributes as the element reconnects. `<shae-prop>` re-reads its attributes and looks its host up
|
|
84
|
-
again on every connect, released or not, so a `prop.
|
|
85
|
-
than applied.
|
|
84
|
+
again on every connect, released or not, so a `prop.entNode` written in that window is replaced
|
|
85
|
+
rather than applied — and so is a `prop.value` where the `value` attribute carries something. Where
|
|
86
|
+
it carries nothing, the property write stands.
|
|
86
87
|
|
|
87
88
|
`<shae-worker>` uses the same two names for something stronger. Its teardown takes the Shadow
|
|
88
89
|
Environment with it, and an environment cannot be rebuilt — a released `<shae-worker>` stays
|
|
@@ -101,6 +102,8 @@ released, and a new one is the way back. See the
|
|
|
101
102
|
|
|
102
103
|
Each domain, what it owns, what it must not touch, and the invariants that hold the whole thing together are written up in the [project README](https://github.com/spearwolf/shadow-objects#the-five-domains) and in [Concepts](https://github.com/spearwolf/shadow-objects/blob/main/packages/shadow-objects/docs/concepts.md).
|
|
103
104
|
|
|
105
|
+
Every environment can be asked what it holds: `ShadowEnv.get(ns).inspect()` answers with a JSON-safe snapshot of the component tree and the Entity Tree behind it, Shadow Objects and Entity Contexts included -- see [Inspecting an Environment](https://github.com/spearwolf/shadow-objects/blob/main/packages/shadow-objects/docs/guides.md#inspecting-an-environment).
|
|
106
|
+
|
|
104
107
|
## Security
|
|
105
108
|
|
|
106
109
|
The `src` of a `<shae-worker>` is a module URL, resolved against the document and run with a dynamic `import()`; the loaded module acts as the application's origin. Set it only from values the application trusts, and constrain it in production with a Content Security Policy delivered on every response of the origin — a policy scoped to only the document's response, or set through `<meta>`, never reaches a worker script loaded from a network URL.
|
|
@@ -111,6 +114,12 @@ Content-Security-Policy: script-src 'self'; worker-src 'self' blob:
|
|
|
111
114
|
|
|
112
115
|
Full detail — why `worker-src` needs `blob:` for the `@spearwolf/shadow-objects/bundle.js` entry point, and which response has to carry the header for every other one — is in the [API Reference](https://github.com/spearwolf/shadow-objects/blob/main/packages/shadow-objects/docs/api-reference.md#security).
|
|
113
116
|
|
|
117
|
+
`exposeShadowEnvsToModelContext()` from `@spearwolf/shadow-objects/model-context.js` is a second way state leaves the page: it hands every Shadow Environment to an AI agent through the browser's model context (WebMCP), read-only, and every value in every answer is application state. Nothing is exposed without a decision; keep the call behind a development switch and name the properties to redact. The declarative form, `<shae-worker expose-to-model-context>`, exposes that element's environment alone as a share of the same registration, and belongs in development markup for the same reason the call belongs behind a switch. Details under [Exposing Environments to an Agent](https://github.com/spearwolf/shadow-objects/blob/main/packages/shadow-objects/docs/api-reference.md#exposing-environments-to-an-agent).
|
|
118
|
+
|
|
119
|
+
## Testing
|
|
120
|
+
|
|
121
|
+
`@spearwolf/shadow-objects/testing.js` runs Shadow Objects on the real Kernel in a unit test -- no DOM, no worker, no View Layer, and no hand-built stand-in for the creation API. `mountShadowObject(constructa, {props, contexts})` is the single-object case: it hands back the instance, the properties and contexts it reads, the messages it sent towards the View, and a `settle()` that waits until the framework has finished reacting. `createTestKernel()` is the layer under it, for a test that needs several Shadow Objects on one Entity, a parent, or a route. Reports the Kernel swallows through `runGuarded()` -- a throwing `onDestroy` among them -- are recorded and fail the teardown instead of passing unseen. The subpath imports no test runner and ships in no application bundle. Details under [Testing Shadow Objects](https://github.com/spearwolf/shadow-objects/blob/main/packages/shadow-objects/docs/best-practices.md#9-testing-shadow-objects).
|
|
122
|
+
|
|
114
123
|
## Documentation
|
|
115
124
|
|
|
116
125
|
- [Overview](https://github.com/spearwolf/shadow-objects/blob/main/packages/shadow-objects/docs/README.md)
|