@solidjs/signals 2.0.0-rc.0 → 2.0.0-rc.10

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.
Files changed (149) hide show
  1. package/README.md +1 -1
  2. package/dist/dev-shared.js +7138 -0
  3. package/dist/dev.attribution.js +3307 -0
  4. package/dist/dev.js +8076 -6692
  5. package/dist/observe/affects.js +129 -0
  6. package/dist/observe/attribution.js +7 -0
  7. package/dist/observe/boundaries.js +793 -0
  8. package/dist/observe/core/action.js +177 -0
  9. package/dist/observe/core/async.js +849 -0
  10. package/dist/observe/core/attribution-costs.js +66 -0
  11. package/dist/observe/core/attribution-feedback.js +280 -0
  12. package/dist/observe/core/attribution-hooks.js +83 -0
  13. package/dist/observe/core/attribution-queries.js +43 -0
  14. package/dist/observe/core/attribution.js +2904 -0
  15. package/dist/observe/core/constants.js +283 -0
  16. package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
  17. package/dist/observe/core/core.js +1999 -0
  18. package/dist/observe/core/dev.js +343 -0
  19. package/dist/observe/core/effect.js +200 -0
  20. package/dist/observe/core/error-hooks.js +71 -0
  21. package/dist/observe/core/error.js +80 -0
  22. package/dist/observe/core/external.js +98 -0
  23. package/dist/observe/core/graph.js +155 -0
  24. package/dist/observe/core/heap.js +147 -0
  25. package/dist/observe/core/invariants.js +45 -0
  26. package/dist/observe/core/lanes.js +205 -0
  27. package/dist/observe/core/optimistic.js +627 -0
  28. package/dist/observe/core/owner.js +353 -0
  29. package/dist/observe/core/scheduler.js +1547 -0
  30. package/dist/observe/core/verdict.js +674 -0
  31. package/dist/observe/index.js +59 -0
  32. package/dist/observe/map.js +484 -0
  33. package/dist/observe/signals.js +731 -0
  34. package/dist/observe/store/index.js +44 -0
  35. package/dist/observe/store/next/optimistic.js +717 -0
  36. package/dist/observe/store/next/projection.js +266 -0
  37. package/dist/observe/store/next/reconcile.js +375 -0
  38. package/dist/observe/store/next/store.js +2480 -0
  39. package/dist/observe/store/next/target.js +51 -0
  40. package/dist/observe/store/store.js +331 -0
  41. package/dist/observe/store/storePath.js +103 -0
  42. package/dist/observe/store/utils.js +1070 -0
  43. package/dist/prod/affects.js +10 -9
  44. package/dist/prod/attribution.js +45 -0
  45. package/dist/prod/boundaries.js +388 -178
  46. package/dist/prod/core/action.js +65 -37
  47. package/dist/prod/core/async.js +477 -208
  48. package/dist/prod/core/attribution-hooks.js +3 -0
  49. package/dist/prod/core/constants.js +186 -1
  50. package/dist/prod/core/context.js +10 -16
  51. package/dist/prod/core/core.js +1556 -415
  52. package/dist/prod/core/dev.js +18 -1
  53. package/dist/prod/core/effect.js +119 -79
  54. package/dist/prod/core/error-hooks.js +71 -0
  55. package/dist/prod/core/error.js +13 -1
  56. package/dist/prod/core/external.js +4 -4
  57. package/dist/prod/core/graph.js +105 -54
  58. package/dist/prod/core/heap.js +72 -65
  59. package/dist/prod/core/invariants.js +3 -2
  60. package/dist/prod/core/lanes.js +117 -52
  61. package/dist/prod/core/optimistic.js +474 -103
  62. package/dist/prod/core/owner.js +131 -84
  63. package/dist/prod/core/scheduler.js +1013 -248
  64. package/dist/prod/core/verdict.js +418 -113
  65. package/dist/prod/index.js +25 -13
  66. package/dist/prod/map.js +314 -151
  67. package/dist/prod/signals.js +382 -32
  68. package/dist/prod/store/index.js +38 -0
  69. package/dist/prod/store/next/optimistic.js +710 -0
  70. package/dist/prod/store/next/projection.js +257 -0
  71. package/dist/prod/store/next/reconcile.js +373 -0
  72. package/dist/prod/store/next/store.js +2356 -0
  73. package/dist/prod/store/next/target.js +51 -0
  74. package/dist/prod/store/store.js +129 -881
  75. package/dist/prod/store/utils.js +982 -242
  76. package/dist/types/attribution.d.ts +19 -0
  77. package/dist/types/attribution.prod.d.ts +22 -0
  78. package/dist/types/boundaries.d.ts +78 -5
  79. package/dist/types/core/action.d.ts +12 -5
  80. package/dist/types/core/async.d.ts +3 -1
  81. package/dist/types/core/attribution-costs.d.ts +35 -0
  82. package/dist/types/core/attribution-feedback.d.ts +133 -0
  83. package/dist/types/core/attribution-hooks.d.ts +304 -0
  84. package/dist/types/core/attribution-queries.d.ts +24 -0
  85. package/dist/types/core/attribution.d.ts +892 -0
  86. package/dist/types/core/constants.d.ts +179 -0
  87. package/dist/types/core/core.d.ts +305 -23
  88. package/dist/types/core/dev.d.ts +421 -8
  89. package/dist/types/core/effect.d.ts +8 -1
  90. package/dist/types/core/error-hooks.d.ts +71 -0
  91. package/dist/types/core/error.d.ts +9 -0
  92. package/dist/types/core/graph.d.ts +23 -0
  93. package/dist/types/core/heap.d.ts +5 -3
  94. package/dist/types/core/index.d.ts +6 -3
  95. package/dist/types/core/invariants.d.ts +5 -1
  96. package/dist/types/core/lanes.d.ts +51 -19
  97. package/dist/types/core/owner.d.ts +15 -3
  98. package/dist/types/core/scheduler.d.ts +252 -3
  99. package/dist/types/core/types.d.ts +124 -41
  100. package/dist/types/index.d.ts +18 -5
  101. package/dist/types/map.d.ts +2 -0
  102. package/dist/types/signals.d.ts +164 -13
  103. package/dist/types/store/index.d.ts +16 -5
  104. package/dist/types/store/next/optimistic.d.ts +25 -0
  105. package/dist/types/store/next/projection.d.ts +27 -0
  106. package/dist/types/store/next/reconcile.d.ts +10 -0
  107. package/dist/types/store/next/store.d.ts +143 -0
  108. package/dist/types/store/next/target.d.ts +196 -0
  109. package/dist/types/store/store.d.ts +46 -112
  110. package/dist/types/store/utils.d.ts +188 -40
  111. package/package.json +32 -26
  112. package/dist/node.cjs +0 -7938
  113. package/dist/prod/store/optimistic.js +0 -217
  114. package/dist/prod/store/projection.js +0 -231
  115. package/dist/prod/store/reconcile.js +0 -707
  116. package/dist/types/store/optimistic.d.ts +0 -45
  117. package/dist/types/store/projection.d.ts +0 -70
  118. package/dist/types/store/reconcile.d.ts +0 -46
  119. package/dist/types-cjs/affects.d.cts +0 -47
  120. package/dist/types-cjs/boundaries.d.cts +0 -175
  121. package/dist/types-cjs/core/action.d.cts +0 -64
  122. package/dist/types-cjs/core/async.d.cts +0 -21
  123. package/dist/types-cjs/core/constants.d.cts +0 -71
  124. package/dist/types-cjs/core/core.d.cts +0 -148
  125. package/dist/types-cjs/core/dev.d.cts +0 -64
  126. package/dist/types-cjs/core/effect.d.cts +0 -30
  127. package/dist/types-cjs/core/error.d.cts +0 -47
  128. package/dist/types-cjs/core/external.d.cts +0 -15
  129. package/dist/types-cjs/core/graph.d.cts +0 -5
  130. package/dist/types-cjs/core/heap.d.cts +0 -22
  131. package/dist/types-cjs/core/index.d.cts +0 -13
  132. package/dist/types-cjs/core/invariants.d.cts +0 -59
  133. package/dist/types-cjs/core/lanes.d.cts +0 -56
  134. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  135. package/dist/types-cjs/core/owner.d.cts +0 -124
  136. package/dist/types-cjs/core/scheduler.d.cts +0 -170
  137. package/dist/types-cjs/core/types.d.cts +0 -148
  138. package/dist/types-cjs/core/verdict.d.cts +0 -2
  139. package/dist/types-cjs/index.d.cts +0 -10
  140. package/dist/types-cjs/map.d.cts +0 -69
  141. package/dist/types-cjs/package.json +0 -3
  142. package/dist/types-cjs/signals.d.cts +0 -605
  143. package/dist/types-cjs/store/index.d.cts +0 -9
  144. package/dist/types-cjs/store/optimistic.d.cts +0 -45
  145. package/dist/types-cjs/store/projection.d.cts +0 -70
  146. package/dist/types-cjs/store/reconcile.d.cts +0 -46
  147. package/dist/types-cjs/store/store.d.cts +0 -213
  148. package/dist/types-cjs/store/storePath.d.cts +0 -58
  149. package/dist/types-cjs/store/utils.d.cts +0 -114
@@ -0,0 +1,66 @@
1
+ import { registerFold, rootsOf } from "./attribution.js";
2
+
3
+ /**
4
+ * `costs()` — the cost tables: scopes ranked by the self-time they burn,
5
+ * root writes ranked by the downstream re-run time they cause. Folded from
6
+ * every `RerunEvent` as the engine records it.
7
+ *
8
+ * Its own module on purpose: the tables register with the engine's fold seam
9
+ * when this module is evaluated, so an observe-tier consumer that only
10
+ * subscribes to records (an APM adapter) never ships the accumulation or the
11
+ * tables; a consumer that imports `costs` gets both. Reset with the engine on
12
+ * `enable()`/`disable()`.
13
+ */ const scopeCosts = new Map;
14
+
15
+ const writeCosts = new Map;
16
+
17
+ function recordCosts(s, o) {
18
+ let t = scopeCosts.get(s);
19
+ if (t === undefined) {
20
+ t = {
21
+ name: o.nodeName,
22
+ kind: o.nodeKind,
23
+ runs: 0,
24
+ selfMs: 0,
25
+ wastedMs: 0,
26
+ overlayMs: 0
27
+ };
28
+ scopeCosts.set(s, t);
29
+ }
30
+ t.runs++;
31
+ t.selfMs += o.selfMs;
32
+ if (o.phase !== "plain") t.overlayMs += o.selfMs; else if (!o.changed && !o.held) t.wastedMs += o.selfMs;
33
+ const e = new Set;
34
+ rootsOf(o.causes, e);
35
+ for (const s of e) {
36
+ let t = writeCosts.get(s);
37
+ if (t === undefined) writeCosts.set(s, t = {
38
+ name: s,
39
+ runs: 0,
40
+ downstreamMs: 0
41
+ });
42
+ t.runs++;
43
+ t.downstreamMs += o.selfMs;
44
+ }
45
+ }
46
+
47
+ registerFold({
48
+ rerun: recordCosts,
49
+ reset() {
50
+ scopeCosts.clear();
51
+ writeCosts.clear();
52
+ }
53
+ });
54
+
55
+ /**
56
+ * Aggregated cost tables since `enable()`: `scopes` ranked by self-time
57
+ * (with `wastedMs` = time spent on unchanged-value runs), `writes` ranked by
58
+ * total downstream re-run time each root write caused.
59
+ */ function costs() {
60
+ return {
61
+ scopes: [ ...scopeCosts.values() ].sort((s, o) => o.selfMs - s.selfMs),
62
+ writes: [ ...writeCosts.values() ].sort((s, o) => o.downstreamMs - s.downstreamMs)
63
+ };
64
+ }
65
+
66
+ export { costs };
@@ -0,0 +1,280 @@
1
+ import { registerFold, now, FALLBACK_FLASH_MS, formatOrigin, nodeName } from "./attribution.js";
2
+
3
+ import { ownerPath } from "./dev.js";
4
+
5
+ /**
6
+ * `feedback()` — what the user waited on, as ranked tables. Folded from the
7
+ * records the engine already keeps (holds, the interaction on each re-run,
8
+ * navigations) and two censuses the engine reports as they happen (flights
9
+ * started/landed/abandoned per async source; fallback shows per boundary).
10
+ *
11
+ * Its own module on purpose: the tables register with the engine's fold seam
12
+ * when this module is evaluated, so an observe-tier consumer that only
13
+ * subscribes to records never ships them. Reset with the engine on
14
+ * `enable()`/`disable()`.
15
+ */ const feedbackSources = new Map;
16
+
17
+ const feedbackInteractions = new Map;
18
+
19
+ const feedbackNavigations = new Map;
20
+
21
+ const flightStats = new Map;
22
+
23
+ const fallbackStats = new Map;
24
+
25
+ function flightBucket(e) {
26
+ let n = flightStats.get(e);
27
+ if (n === undefined) {
28
+ n = {
29
+ source: nodeName(e),
30
+ flights: 0,
31
+ landed: 0,
32
+ abandoned: 0,
33
+ landedMs: 0,
34
+ worstMs: 0
35
+ };
36
+ flightStats.set(e, n);
37
+ }
38
+ return n;
39
+ }
40
+
41
+ function trackFallback(e, n, t) {
42
+ let s = fallbackStats.get(e);
43
+ if (s === undefined) {
44
+ s = {
45
+ row: {
46
+ boundary: "boundary",
47
+ shows: 0,
48
+ shownMs: 0,
49
+ worstMs: 0,
50
+ flashes: 0
51
+ },
52
+ shownAt: null
53
+ };
54
+ fallbackStats.set(e, s);
55
+ }
56
+ // The first show can fire before the subtree exists; name on first sight.
57
+ if (s.row.boundary === "boundary" && n !== undefined) {
58
+ const e = ownerPath(n);
59
+ if (e !== undefined) s.row.boundary = e.join(" › ");
60
+ }
61
+ if (t) {
62
+ if (s.shownAt === null) {
63
+ s.shownAt = now();
64
+ s.row.shows++;
65
+ }
66
+ return;
67
+ }
68
+ if (s.shownAt === null) return;
69
+ const i = now() - s.shownAt;
70
+ s.shownAt = null;
71
+ s.row.shownMs += i;
72
+ if (i > s.row.worstMs) s.row.worstMs = i;
73
+ if (i < FALLBACK_FLASH_MS) s.row.flashes++;
74
+ }
75
+
76
+ function interactionBucket(e) {
77
+ const n = formatOrigin(e);
78
+ let t = feedbackInteractions.get(n);
79
+ if (t === undefined) {
80
+ t = {
81
+ row: {
82
+ interaction: n,
83
+ dispatches: 0,
84
+ runs: 0,
85
+ selfMs: 0,
86
+ worstDispatchMs: 0,
87
+ holds: 0,
88
+ heldMs: 0,
89
+ silentMs: 0,
90
+ worstHoldMs: 0
91
+ },
92
+ dispatches: new Map
93
+ };
94
+ feedbackInteractions.set(n, t);
95
+ }
96
+ const s = e.at ?? 0;
97
+ if (!t.dispatches.has(s)) {
98
+ t.dispatches.set(s, 0);
99
+ t.row.dispatches++;
100
+ }
101
+ return t;
102
+ }
103
+
104
+ function recordFeedbackRun(e) {
105
+ if (e.interaction === undefined) return;
106
+ const n = interactionBucket(e.interaction);
107
+ n.row.runs++;
108
+ n.row.selfMs += e.selfMs;
109
+ const t = e.interaction.at ?? 0;
110
+ const s = n.dispatches.get(t) + e.selfMs;
111
+ n.dispatches.set(t, s);
112
+ if (s > n.row.worstDispatchMs) n.row.worstDispatchMs = s;
113
+ }
114
+
115
+ function recordFeedbackHold(e) {
116
+ const n = [ ...e.blockers ].sort();
117
+ const t = n.join("\0");
118
+ let s = feedbackSources.get(t);
119
+ if (s === undefined) {
120
+ s = {
121
+ row: {
122
+ sources: n,
123
+ holds: 0,
124
+ heldMs: 0,
125
+ worstMs: 0,
126
+ silent: 0,
127
+ silentMs: 0,
128
+ latestOnly: 0,
129
+ long: 0,
130
+ longMs: 0,
131
+ acknowledgedBy: [],
132
+ interactions: [],
133
+ writes: [],
134
+ actions: 0
135
+ },
136
+ acks: new Map,
137
+ interactions: new Map,
138
+ writes: new Set
139
+ };
140
+ feedbackSources.set(t, s);
141
+ }
142
+ const i = s.row;
143
+ const o = e.silent;
144
+ i.holds++;
145
+ i.heldMs += e.holdMs;
146
+ if (e.holdMs > i.worstMs) i.worstMs = e.holdMs;
147
+ if (o) {
148
+ i.silent++;
149
+ i.silentMs += e.holdMs;
150
+ } else if (e.acknowledgements.length > 0 && e.acknowledgements.every(e => e.kind === "latest")) i.latestOnly++;
151
+ if (e.long) {
152
+ i.long++;
153
+ i.longMs += e.tailMs;
154
+ }
155
+ if (e.action) i.actions++;
156
+ for (const n of e.acknowledgements) {
157
+ const e = `${n.kind}:${n.source}`;
158
+ s.acks.set(e, (s.acks.get(e) ?? 0) + 1);
159
+ }
160
+ for (const n of e.heldWrites) s.writes.add(n.name);
161
+ if (e.interaction !== undefined) {
162
+ const n = formatOrigin(e.interaction);
163
+ s.interactions.set(n, (s.interactions.get(n) ?? 0) + 1);
164
+ const t = interactionBucket(e.interaction);
165
+ t.row.holds++;
166
+ t.row.heldMs += e.holdMs;
167
+ if (o) t.row.silentMs += e.holdMs;
168
+ if (e.holdMs > t.row.worstHoldMs) t.row.worstHoldMs = e.holdMs;
169
+ }
170
+ }
171
+
172
+ function recordFeedbackNavigation(e) {
173
+ const n = e.name ?? e.to;
174
+ if (n === undefined) return;
175
+ let t = feedbackNavigations.get(n);
176
+ if (t === undefined) {
177
+ t = {
178
+ name: n,
179
+ navigations: 0,
180
+ settledMs: 0,
181
+ worstMs: 0,
182
+ held: 0,
183
+ heldMs: 0,
184
+ silent: 0,
185
+ superseded: 0,
186
+ redirected: 0
187
+ };
188
+ feedbackNavigations.set(n, t);
189
+ }
190
+ t.navigations++;
191
+ if (e.redirects !== undefined) t.redirected++;
192
+ if (e.outcome === "superseded") {
193
+ t.superseded++;
194
+ return;
195
+ }
196
+ const s = e.settledMs;
197
+ t.settledMs += s;
198
+ if (s > t.worstMs) t.worstMs = s;
199
+ if (e.outcome === "held") {
200
+ t.held++;
201
+ t.heldMs += e.hold?.holdMs ?? s;
202
+ if (e.hold !== undefined && e.hold.silent) t.silent++;
203
+ }
204
+ }
205
+
206
+ function rankedCounts(e, n) {
207
+ return [ ...e ].sort((e, n) => n[1] - e[1]).map(([e, t]) => ({
208
+ [n]: e,
209
+ holds: t
210
+ }));
211
+ }
212
+
213
+ registerFold({
214
+ rerun: (e, n) => recordFeedbackRun(n),
215
+ hold: recordFeedbackHold,
216
+ navigation: recordFeedbackNavigation,
217
+ flightStart(e, n) {
218
+ const t = flightBucket(e);
219
+ t.flights++;
220
+ if (n) t.abandoned++;
221
+ },
222
+ flightLanded(e, n) {
223
+ const t = flightBucket(e);
224
+ t.landed++;
225
+ t.landedMs += n;
226
+ if (n > t.worstMs) t.worstMs = n;
227
+ },
228
+ fallback: trackFallback,
229
+ reset() {
230
+ feedbackSources.clear();
231
+ feedbackInteractions.clear();
232
+ feedbackNavigations.clear();
233
+ flightStats.clear();
234
+ fallbackStats.clear();
235
+ }
236
+ });
237
+
238
+ /**
239
+ * What the user waited on, folded from holds and the interaction on each
240
+ * re-run: `sources` ranks async sources by the silent time writes spent held
241
+ * behind them (with which affordances answered, how often, and which
242
+ * interactions were held); `interactions` ranks user events by the total time
243
+ * they cost — re-run work caused (long-flush hazard) beside time held
244
+ * (silent-hold hazard). Facts at every duration; SILENT_HOLD is the
245
+ * thresholded verdict. Three more tables round out the picture: `navigations`
246
+ * ranks routes by the time spent held navigating to them (folded from settled
247
+ * navigations), `flights` counts each async source's flights and how many
248
+ * were abandoned before landing (the re-ask storm), and `fallbacks` measures
249
+ * how long each loading boundary showed its fallback and how often that was a
250
+ * flash.
251
+ */ function feedback() {
252
+ const e = [ ...feedbackNavigations.values() ].map(e => ({
253
+ ...e
254
+ })).sort((e, n) => n.heldMs - e.heldMs || n.settledMs - e.settledMs);
255
+ const n = [ ...flightStats.values() ].map(e => ({
256
+ ...e
257
+ })).sort((e, n) => n.abandoned - e.abandoned || n.flights - e.flights);
258
+ const t = [ ...fallbackStats.values() ].map(e => ({
259
+ ...e.row
260
+ })).sort((e, n) => n.flashes - e.flashes || n.shownMs - e.shownMs);
261
+ const s = [ ...feedbackSources.values() ].map(e => ({
262
+ ...e.row,
263
+ acknowledgedBy: rankedCounts(e.acks, "by"),
264
+ interactions: rankedCounts(e.interactions, "interaction"),
265
+ writes: [ ...e.writes ]
266
+ })).sort((e, n) => n.silentMs - e.silentMs || n.heldMs - e.heldMs);
267
+ // Ranked by the total time the user spent on it: held plus synchronous work.
268
+ const i = [ ...feedbackInteractions.values() ].map(e => ({
269
+ ...e.row
270
+ })).sort((e, n) => n.heldMs + n.selfMs - (e.heldMs + e.selfMs));
271
+ return {
272
+ sources: s,
273
+ interactions: i,
274
+ navigations: e,
275
+ flights: n,
276
+ fallbacks: t
277
+ };
278
+ }
279
+
280
+ export { feedback };
@@ -0,0 +1,83 @@
1
+ let attrHooks = null;
2
+
3
+ /**
4
+ * The installed engine, registered on `globalThis` as well (the records
5
+ * channel's reason, see `Records`): a wire layer bundled without a framework
6
+ * import — `@solidjs/web`'s server-function client — stamps the records it
7
+ * emits through the engine's `currentOrigin`, and this is its reach. The
8
+ * module binding stays the core's own read (one null check per hook site);
9
+ * the registration mirrors it.
10
+ */ const INSTALLED = Symbol.for("@solidjs/signals/observe/attribution");
11
+
12
+ function setAttributionHooks(t) {
13
+ attrHooks = t;
14
+ globalThis[INSTALLED] = t ?? undefined;
15
+ }
16
+
17
+ /**
18
+ * Run `fn` as the handler of a user interaction: every root write it performs
19
+ * (and every action step, effect or flight the write causes) is attributed to
20
+ * `ref` by whichever engine is installed. The web runtime wraps event
21
+ * dispatch in this; custom renderers and test harnesses call it themselves.
22
+ * With no engine installed it is `fn()` — the wiring, not the engine, so it
23
+ * lives in core and is reachable as `OBSERVE.attribution.withInteraction`.
24
+ *
25
+ * The `finally` is deliberate and safe under the try rule above: this
26
+ * function is referenced only from the `OBSERVE` object, which prod builds
27
+ * fold to `undefined`, so nothing retains it there.
28
+ */ function withInteraction(t, n) {
29
+ // Pin the engine for the frame: a handler that disables it mid-way must
30
+ // still close the frame it opened (the engine tolerates a close after
31
+ // disable()), and one that enables it mid-way opened no frame to close.
32
+ const o = attrHooks;
33
+ if (o === null) return n();
34
+ o.interactionStart(t);
35
+ let i;
36
+ try {
37
+ return i = n();
38
+ } finally {
39
+ o.interactionEnd(i);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Run `fn` as a declared unit of work — a router's navigation: every root
45
+ * write it performs is attributed to `ref` (and, through it, to the enclosing
46
+ * interaction when there is one), so the hold those writes wait in, the
47
+ * re-runs they cause and the verdicts on them all carry the route's name
48
+ * instead of a bare signal's. Same contract as `withInteraction`: the wiring,
49
+ * not the engine; `fn()` with no engine installed. Reachable as
50
+ * `OBSERVE.attribution.withOrigin`.
51
+ *
52
+ * ```ts
53
+ * OBSERVE
54
+ * ? OBSERVE.attribution.withOrigin(
55
+ * { kind: "navigation", name: match.pattern, to, from, params: match.params },
56
+ * () => setLocation(to)
57
+ * )
58
+ * : setLocation(to);
59
+ * ```
60
+ */ function withOrigin(t, n) {
61
+ const o = attrHooks;
62
+ if (o === null) return n();
63
+ o.originStart(t);
64
+ try {
65
+ return n();
66
+ } finally {
67
+ o.originEnd();
68
+ }
69
+ }
70
+
71
+ /**
72
+ * The provenance a root write performed now would carry, as the installed
73
+ * engine sees it (`AttributionHooks.currentOrigin`); `undefined` with no
74
+ * engine, or when nothing is in effect. Reachable as
75
+ * `OBSERVE.attribution.currentOrigin` — how a runtime stamps a fact of its
76
+ * own (a server-function call) with the interaction or navigation it ran
77
+ * for, so an observer joins the two by identity.
78
+ */ function currentOrigin() {
79
+ const t = attrHooks;
80
+ return t === null ? undefined : t.currentOrigin();
81
+ }
82
+
83
+ export { attrHooks, currentOrigin, setAttributionHooks, withInteraction, withOrigin };
@@ -0,0 +1,43 @@
1
+ import { nodeName, attribution, nodeIdOf } from "./attribution.js";
2
+
3
+ import { $REFRESH } from "./constants.js";
4
+
5
+ /**
6
+ * Point queries over the engine's live state — the devtools/console view of
7
+ * one scope. Their own module so a records-only consumer never ships them;
8
+ * they read the engine's ring buffer and the graph, and register nothing.
9
+ */
10
+ /** The node behind a memo/effect accessor, or the raw node passed through. */ function nodeOf(n) {
11
+ return n?.[$REFRESH] ?? n;
12
+ }
13
+
14
+ /**
15
+ * Re-run history for one scope — pass a memo/effect accessor or raw node,
16
+ * or a scope's name as a string (an out-of-process consumer such as the
17
+ * diagnostics bridge holds no node, only the `nodeName` the records
18
+ * carry). Records name their scope by `nodeId`; a node that has never run
19
+ * under the engine has none, and no history. By name, every scope of that
20
+ * name answers. A view of `history("rerun")`, so it shares that buffer's
21
+ * gate: runs nothing wanted a record of (no `rerun` listener, fold or log
22
+ * at the time) left no record and are not here — a console session that
23
+ * wants them subscribes or imports a fold first.
24
+ */ function why(n) {
25
+ const t = attribution.history("rerun");
26
+ if (typeof n === "string") return t.filter(t => t.nodeName === n);
27
+ const o = nodeIdOf(nodeOf(n));
28
+ if (o === undefined) return [];
29
+ return t.filter(n => n.nodeId === o);
30
+ }
31
+
32
+ /**
33
+ * Current dependency names of one scope — the devtools subscription view.
34
+ * Read from the graph, not a record, so it answers with or without an
35
+ * audience for re-run records (and with the engine disabled).
36
+ */ function subscriptions(n) {
37
+ const t = nodeOf(n);
38
+ const o = [];
39
+ for (let n = t?.Ee ?? null; n !== null; n = n.Ie) o.push(nodeName(n.De));
40
+ return o;
41
+ }
42
+
43
+ export { subscriptions, why };