solid-deps 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -3
- package/package.json +8 -4
- package/src/attach-read.mjs +28 -3
package/README.md
CHANGED
|
@@ -7,7 +7,13 @@ Attach additional reactive dependencies to an existing **Solid 2 effect**—even
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
npm install solid-deps@0.0.
|
|
10
|
+
npm install solid-deps@0.0.2 solid-js@2.0.0-rc.17 @solidjs/signals@2.0.0-rc.17
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
With Bun:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
bun add solid-deps@0.0.2 solid-js@2.0.0-rc.17 @solidjs/signals@2.0.0-rc.17
|
|
11
17
|
```
|
|
12
18
|
|
|
13
19
|
**Compatibility:** This release targets **Solid 2.0.0-rc.17** (`solid-js` and `@solidjs/signals`). Both the app and `solid-deps` must resolve to the **same instance** of `@solidjs/signals`. Server-side rendering is not supported. See [Limitations](#limitations).
|
|
@@ -184,9 +190,13 @@ createEffect(async () => {
|
|
|
184
190
|
|
|
185
191
|
`solid-deps` is experimental and restricted to Solid **2.0.0-rc.17** in browser-oriented applications. Dependency registration requires an existing **tracked computation**, not simply any owner.
|
|
186
192
|
|
|
187
|
-
Keep `read()` synchronous and limited to settled reactive reads, such as signals, memos, or store properties. Do not use it to create computations, install cleanups, write signals, or perform side effects. It does not run in the target's original reactive context.
|
|
193
|
+
Keep `read()` synchronous and limited to settled reactive reads, such as signals, memos, or store properties. Do not use it to create computations, install cleanups, write signals, or perform side effects. It does not run in the target's original reactive context. The library rejects reader callbacks that create owned children or register cleanups, and rejects sources owned by its temporary observer, to prevent subscriptions to disposed computations.
|
|
194
|
+
|
|
195
|
+
Pending async memo reads can throw `NotReadyError`; a failed read does not attach dependencies. A memo with a `loadingValue` can return that fallback while pending, but `attachRead` does not restore the target observer's original suspension or transition context. Treat resources, loading boundaries, held transactions, and optimistic state as **unsupported beyond ordinary settled reads**.
|
|
196
|
+
|
|
197
|
+
An `attachRead()` subscription is prospective: signal updates that happen before attachment are not replayed. Grafted dependencies can be removed by the target's next normal recomputation. For async effects, reattach on each pass using `captureObserverPass()`.
|
|
188
198
|
|
|
189
|
-
|
|
199
|
+
Reads that create computations/cleanups, write signals, or depend on `getOwner()`/`getObserver()` seeing the original effect are unsupported, because the callback executes under a temporary observer. Server rendering and independently bundled copies of the signals runtime are not supported. Do not assume compatibility with other Solid versions.
|
|
190
200
|
|
|
191
201
|
## License
|
|
192
202
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solid-deps",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Experimental direct dependency attachment for Solid 2 reactive observers after async boundaries",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,10 +36,14 @@
|
|
|
36
36
|
],
|
|
37
37
|
"scripts": {
|
|
38
38
|
"setup": "node scripts/setup.mjs",
|
|
39
|
-
"test": "node --conditions=browser --test test/test.mjs test/test-async.mjs test/test-tuple.mjs",
|
|
40
|
-
"test:dev": "node --conditions=browser --conditions=development --test test/test.mjs test/test-async.mjs test/test-tuple.mjs",
|
|
41
|
-
"test:observe": "node --conditions=browser --conditions=observe --test test/test.mjs test/test-async.mjs test/test-tuple.mjs",
|
|
39
|
+
"test": "node --conditions=browser --test test/test.mjs test/test-async.mjs test/test-tuple.mjs test/test-manifest.mjs test/test-graph-hardening.mjs test/test-async-transitions.mjs test/test-transaction-integrity.mjs test/test-scheduler-lifecycle.mjs test/test-release-hardening.mjs",
|
|
40
|
+
"test:dev": "node --conditions=browser --conditions=development --test test/test.mjs test/test-async.mjs test/test-tuple.mjs test/test-manifest.mjs test/test-graph-hardening.mjs test/test-async-transitions.mjs test/test-transaction-integrity.mjs test/test-scheduler-lifecycle.mjs test/test-release-hardening.mjs",
|
|
41
|
+
"test:observe": "node --conditions=browser --conditions=observe --test test/test.mjs test/test-async.mjs test/test-tuple.mjs test/test-manifest.mjs test/test-graph-hardening.mjs test/test-async-transitions.mjs test/test-transaction-integrity.mjs test/test-scheduler-lifecycle.mjs test/test-release-hardening.mjs",
|
|
42
42
|
"test:all": "node scripts/run-tests.mjs",
|
|
43
43
|
"test:package": "node scripts/smoke-package.mjs"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@solidjs/signals": "2.0.0-rc.17",
|
|
47
|
+
"solid-js": "2.0.0-rc.17"
|
|
44
48
|
}
|
|
45
49
|
}
|
package/src/attach-read.mjs
CHANGED
|
@@ -8,15 +8,27 @@
|
|
|
8
8
|
import { createRoot, createReaction, getObserver, isDisposed } from '@solidjs/signals';
|
|
9
9
|
|
|
10
10
|
const layouts = {
|
|
11
|
-
dev: { deps: '_deps', tail: '_depsTail', gen: '_depGen', flags: '_flags', dep: '_dep', sub: '_sub', next: '_nextDep', linkGen: '_gen' },
|
|
12
|
-
prod: { deps: 'C', tail: '_', gen: 'Ne', flags: 'R', dep: 'S', sub: 'u', next: 'G', linkGen: 'Te' }
|
|
11
|
+
dev: { deps: '_deps', tail: '_depsTail', gen: '_depGen', flags: '_flags', dep: '_dep', sub: '_sub', next: '_nextDep', linkGen: '_gen', child: '_firstChild', disposal: '_disposal' },
|
|
12
|
+
prod: { deps: 'C', tail: '_', gen: 'Ne', flags: 'R', dep: 'S', sub: 'u', next: 'G', linkGen: 'Te', child: 'De', disposal: 'He' },
|
|
13
|
+
observe: { deps: 'C', tail: '_', gen: 'Ne', flags: 'R', dep: 'S', sub: 'u', next: 'G', linkGen: 'Te', child: 'ze', disposal: 'rt' }
|
|
13
14
|
};
|
|
14
15
|
function layoutOf(node) {
|
|
15
|
-
const shape = node && (('_deps' in node) ? layouts.dev : ('C' in node) ? layouts.prod : null);
|
|
16
|
+
const shape = node && (('_deps' in node) ? layouts.dev : ('C' in node) ? ('ze' in node ? layouts.observe : layouts.prod) : null);
|
|
16
17
|
if (!shape || typeof node[shape.gen] !== 'number' || !(shape.tail in node))
|
|
17
18
|
throw new TypeError('Expected a tracking Computed from @solidjs/signals rc.17, not a plain Owner');
|
|
18
19
|
return shape;
|
|
19
20
|
}
|
|
21
|
+
// Probe-owned sources disappear when the temporary tracking root is disposed.
|
|
22
|
+
// Their dependency edges must never be transferred to a longer-lived target.
|
|
23
|
+
function ownedBy(node, ancestor) {
|
|
24
|
+
const seen = new Set();
|
|
25
|
+
for (let owner = node; owner != null; owner = owner._parent) {
|
|
26
|
+
if (owner === ancestor) return true;
|
|
27
|
+
if (seen.has(owner)) throw new Error('Cyclic owner ancestry');
|
|
28
|
+
seen.add(owner);
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
20
32
|
function links(owner, layout) {
|
|
21
33
|
const out = [];
|
|
22
34
|
let cur = owner[layout.deps];
|
|
@@ -91,6 +103,19 @@ export function attachRead(observerOrPass, read) {
|
|
|
91
103
|
throw new Error('Target dependency chain changed during capture');
|
|
92
104
|
|
|
93
105
|
const captured = links(probe, s);
|
|
106
|
+
// `read()` must not allocate owned children or register cleanups. A child
|
|
107
|
+
// can disappear when the probe is disposed, and a cleanup can throw AFTER
|
|
108
|
+
// a successful graft, leaving the target changed despite a failed call.
|
|
109
|
+
if (probe[s.child] != null || probe[s.disposal] != null)
|
|
110
|
+
throw new Error('attachRead read() cannot create owned children or register cleanups');
|
|
111
|
+
// Guard before touching either graph. A memo or signal created inside
|
|
112
|
+
// `read()` may be owned by the disposable probe, and disposing that probe
|
|
113
|
+
// would otherwise leave the target subscribed to a disposed source.
|
|
114
|
+
for (const edge of captured) {
|
|
115
|
+
const source = edge[s.dep];
|
|
116
|
+
if (isDisposed(source) || ownedBy(source, probe))
|
|
117
|
+
throw new Error('Cannot attach a disposed or temporary-observer-owned source');
|
|
118
|
+
}
|
|
94
119
|
const seen = new Set(valid.map(edge => edge[s.dep]));
|
|
95
120
|
const staleBySource = new Map(stale.map(edge => [edge[s.dep], edge]));
|
|
96
121
|
const moved = [];
|