@solidjs/signals 2.0.0-rc.7 → 2.0.0-rc.9

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 (134) hide show
  1. package/dist/dev-shared.js +6357 -0
  2. package/dist/dev.attribution.js +2485 -0
  3. package/dist/dev.js +2851 -7027
  4. package/dist/observe/affects.js +129 -0
  5. package/dist/observe/attribution.js +7 -0
  6. package/dist/observe/boundaries.js +641 -0
  7. package/dist/observe/core/action.js +177 -0
  8. package/dist/observe/core/async.js +846 -0
  9. package/dist/observe/core/attribution-costs.js +66 -0
  10. package/dist/observe/core/attribution-feedback.js +282 -0
  11. package/dist/observe/core/attribution-hooks.js +82 -0
  12. package/dist/observe/core/attribution-queries.js +28 -0
  13. package/dist/observe/core/attribution.js +2095 -0
  14. package/dist/observe/core/constants.js +248 -0
  15. package/dist/{types-cjs/core/context.d.cts → observe/core/context.js} +37 -12
  16. package/dist/observe/core/core.js +1789 -0
  17. package/dist/observe/core/dev.js +293 -0
  18. package/dist/observe/core/effect.js +195 -0
  19. package/dist/observe/core/error-hooks.js +71 -0
  20. package/dist/observe/core/error.js +80 -0
  21. package/dist/observe/core/external.js +98 -0
  22. package/dist/observe/core/graph.js +155 -0
  23. package/dist/observe/core/heap.js +147 -0
  24. package/dist/observe/core/invariants.js +45 -0
  25. package/dist/observe/core/lanes.js +205 -0
  26. package/dist/observe/core/optimistic.js +585 -0
  27. package/dist/observe/core/owner.js +322 -0
  28. package/dist/observe/core/scheduler.js +1428 -0
  29. package/dist/observe/core/verdict.js +669 -0
  30. package/dist/observe/index.js +59 -0
  31. package/dist/observe/map.js +484 -0
  32. package/dist/{types-cjs/signals.d.cts → observe/signals.js} +311 -317
  33. package/dist/observe/store/index.js +40 -0
  34. package/dist/observe/store/next/optimistic.js +660 -0
  35. package/dist/observe/store/next/projection.js +255 -0
  36. package/dist/observe/store/next/reconcile.js +375 -0
  37. package/dist/observe/store/next/store.js +2327 -0
  38. package/dist/observe/store/next/target.js +51 -0
  39. package/dist/observe/store/store.js +325 -0
  40. package/dist/observe/store/storePath.js +103 -0
  41. package/dist/observe/store/utils.js +1023 -0
  42. package/dist/prod/attribution.js +41 -0
  43. package/dist/prod/boundaries.js +128 -76
  44. package/dist/prod/core/action.js +65 -37
  45. package/dist/prod/core/async.js +324 -172
  46. package/dist/prod/core/constants.js +57 -1
  47. package/dist/prod/core/context.js +3 -3
  48. package/dist/prod/core/core.js +1003 -364
  49. package/dist/prod/core/dev.js +17 -1
  50. package/dist/prod/core/effect.js +76 -42
  51. package/dist/prod/core/error-hooks.js +71 -0
  52. package/dist/prod/core/external.js +4 -4
  53. package/dist/prod/core/graph.js +37 -37
  54. package/dist/prod/core/heap.js +61 -56
  55. package/dist/prod/core/lanes.js +107 -63
  56. package/dist/prod/core/optimistic.js +374 -93
  57. package/dist/prod/core/owner.js +62 -46
  58. package/dist/prod/core/scheduler.js +653 -286
  59. package/dist/prod/core/verdict.js +245 -127
  60. package/dist/prod/index.js +15 -5
  61. package/dist/prod/map.js +292 -141
  62. package/dist/prod/signals.js +29 -16
  63. package/dist/prod/store/next/optimistic.js +77 -55
  64. package/dist/prod/store/next/projection.js +31 -20
  65. package/dist/prod/store/next/reconcile.js +87 -84
  66. package/dist/prod/store/next/store.js +718 -401
  67. package/dist/prod/store/next/target.js +32 -10
  68. package/dist/prod/store/store.js +12 -14
  69. package/dist/prod/store/utils.js +950 -130
  70. package/dist/types/attribution.d.ts +20 -0
  71. package/dist/types/attribution.prod.d.ts +22 -0
  72. package/dist/types/boundaries.d.ts +10 -1
  73. package/dist/types/core/action.d.ts +12 -5
  74. package/dist/types/core/async.d.ts +1 -1
  75. package/dist/types/core/attribution-costs.d.ts +35 -0
  76. package/dist/types/core/attribution-feedback.d.ts +133 -0
  77. package/dist/types/core/attribution-hooks.d.ts +147 -9
  78. package/dist/types/core/attribution-queries.d.ts +10 -0
  79. package/dist/types/core/attribution.d.ts +216 -182
  80. package/dist/types/core/constants.d.ts +56 -0
  81. package/dist/types/core/core.d.ts +190 -2
  82. package/dist/types/core/dev.d.ts +246 -28
  83. package/dist/types/core/effect.d.ts +8 -1
  84. package/dist/types/core/error-hooks.d.ts +71 -0
  85. package/dist/types/core/index.d.ts +4 -1
  86. package/dist/types/core/invariants.d.ts +4 -0
  87. package/dist/types/core/lanes.d.ts +39 -5
  88. package/dist/types/core/scheduler.d.ts +153 -0
  89. package/dist/types/core/types.d.ts +32 -9
  90. package/dist/types/index.d.ts +10 -3
  91. package/dist/types/map.d.ts +2 -0
  92. package/dist/types/signals.d.ts +8 -0
  93. package/dist/types/store/index.d.ts +2 -0
  94. package/dist/types/store/next/optimistic.d.ts +1 -1
  95. package/dist/types/store/next/store.d.ts +11 -5
  96. package/dist/types/store/next/target.d.ts +37 -10
  97. package/dist/types/store/utils.d.ts +178 -0
  98. package/package.json +21 -17
  99. package/dist/node.cjs +0 -10541
  100. package/dist/node.dev.cjs +0 -13724
  101. package/dist/types-cjs/affects.d.cts +0 -47
  102. package/dist/types-cjs/boundaries.d.cts +0 -175
  103. package/dist/types-cjs/core/action.d.cts +0 -64
  104. package/dist/types-cjs/core/async.d.cts +0 -23
  105. package/dist/types-cjs/core/attribution-hooks.d.cts +0 -127
  106. package/dist/types-cjs/core/attribution.d.cts +0 -495
  107. package/dist/types-cjs/core/constants.d.cts +0 -159
  108. package/dist/types-cjs/core/core.d.cts +0 -185
  109. package/dist/types-cjs/core/dev.d.cts +0 -136
  110. package/dist/types-cjs/core/effect.d.cts +0 -30
  111. package/dist/types-cjs/core/error.d.cts +0 -56
  112. package/dist/types-cjs/core/external.d.cts +0 -15
  113. package/dist/types-cjs/core/graph.d.cts +0 -28
  114. package/dist/types-cjs/core/heap.d.cts +0 -24
  115. package/dist/types-cjs/core/index.d.cts +0 -13
  116. package/dist/types-cjs/core/invariants.d.cts +0 -59
  117. package/dist/types-cjs/core/lanes.d.cts +0 -54
  118. package/dist/types-cjs/core/optimistic.d.cts +0 -6
  119. package/dist/types-cjs/core/owner.d.cts +0 -124
  120. package/dist/types-cjs/core/scheduler.d.cts +0 -236
  121. package/dist/types-cjs/core/types.d.cts +0 -204
  122. package/dist/types-cjs/core/verdict.d.cts +0 -2
  123. package/dist/types-cjs/index.d.cts +0 -10
  124. package/dist/types-cjs/map.d.cts +0 -69
  125. package/dist/types-cjs/package.json +0 -3
  126. package/dist/types-cjs/store/index.d.cts +0 -18
  127. package/dist/types-cjs/store/next/optimistic.d.cts +0 -25
  128. package/dist/types-cjs/store/next/projection.d.cts +0 -8
  129. package/dist/types-cjs/store/next/reconcile.d.cts +0 -10
  130. package/dist/types-cjs/store/next/store.d.cts +0 -121
  131. package/dist/types-cjs/store/next/target.d.cts +0 -166
  132. package/dist/types-cjs/store/store.d.cts +0 -143
  133. package/dist/types-cjs/store/storePath.d.cts +0 -58
  134. package/dist/types-cjs/store/utils.d.cts +0 -74
@@ -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,282 @@
1
+ import { registerFold, now, isSilentHold, isLongHold, 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
+ const FALLBACK_FLASH_MS = 150;
26
+
27
+ function flightBucket(e) {
28
+ let n = flightStats.get(e);
29
+ if (n === undefined) {
30
+ n = {
31
+ source: nodeName(e),
32
+ flights: 0,
33
+ landed: 0,
34
+ abandoned: 0,
35
+ landedMs: 0,
36
+ worstMs: 0
37
+ };
38
+ flightStats.set(e, n);
39
+ }
40
+ return n;
41
+ }
42
+
43
+ function trackFallback(e, n, t) {
44
+ let i = fallbackStats.get(e);
45
+ if (i === undefined) {
46
+ i = {
47
+ row: {
48
+ boundary: "boundary",
49
+ shows: 0,
50
+ shownMs: 0,
51
+ worstMs: 0,
52
+ flashes: 0
53
+ },
54
+ shownAt: null
55
+ };
56
+ fallbackStats.set(e, i);
57
+ }
58
+ // The first show can fire before the subtree exists; name on first sight.
59
+ if (i.row.boundary === "boundary" && n !== undefined) {
60
+ const e = ownerPath(n);
61
+ if (e !== undefined) i.row.boundary = e.join(" › ");
62
+ }
63
+ if (t) {
64
+ if (i.shownAt === null) {
65
+ i.shownAt = now();
66
+ i.row.shows++;
67
+ }
68
+ return;
69
+ }
70
+ if (i.shownAt === null) return;
71
+ const s = now() - i.shownAt;
72
+ i.shownAt = null;
73
+ i.row.shownMs += s;
74
+ if (s > i.row.worstMs) i.row.worstMs = s;
75
+ if (s < FALLBACK_FLASH_MS) i.row.flashes++;
76
+ }
77
+
78
+ function interactionBucket(e) {
79
+ const n = formatOrigin(e);
80
+ let t = feedbackInteractions.get(n);
81
+ if (t === undefined) {
82
+ t = {
83
+ row: {
84
+ interaction: n,
85
+ dispatches: 0,
86
+ runs: 0,
87
+ selfMs: 0,
88
+ worstDispatchMs: 0,
89
+ holds: 0,
90
+ heldMs: 0,
91
+ silentMs: 0,
92
+ worstHoldMs: 0
93
+ },
94
+ dispatches: new Map
95
+ };
96
+ feedbackInteractions.set(n, t);
97
+ }
98
+ const i = e.at ?? 0;
99
+ if (!t.dispatches.has(i)) {
100
+ t.dispatches.set(i, 0);
101
+ t.row.dispatches++;
102
+ }
103
+ return t;
104
+ }
105
+
106
+ function recordFeedbackRun(e) {
107
+ if (e.interaction === undefined) return;
108
+ const n = interactionBucket(e.interaction);
109
+ n.row.runs++;
110
+ n.row.selfMs += e.selfMs;
111
+ const t = e.interaction.at ?? 0;
112
+ const i = n.dispatches.get(t) + e.selfMs;
113
+ n.dispatches.set(t, i);
114
+ if (i > n.row.worstDispatchMs) n.row.worstDispatchMs = i;
115
+ }
116
+
117
+ function recordFeedbackHold(e) {
118
+ const n = [ ...e.blockers ].sort();
119
+ const t = n.join("\0");
120
+ let i = feedbackSources.get(t);
121
+ if (i === undefined) {
122
+ i = {
123
+ row: {
124
+ sources: n,
125
+ holds: 0,
126
+ heldMs: 0,
127
+ worstMs: 0,
128
+ silent: 0,
129
+ silentMs: 0,
130
+ latestOnly: 0,
131
+ long: 0,
132
+ longMs: 0,
133
+ acknowledgedBy: [],
134
+ interactions: [],
135
+ writes: [],
136
+ actions: 0
137
+ },
138
+ acks: new Map,
139
+ interactions: new Map,
140
+ writes: new Set
141
+ };
142
+ feedbackSources.set(t, i);
143
+ }
144
+ const s = i.row;
145
+ const o = isSilentHold(e);
146
+ s.holds++;
147
+ s.heldMs += e.holdMs;
148
+ if (e.holdMs > s.worstMs) s.worstMs = e.holdMs;
149
+ if (o) {
150
+ s.silent++;
151
+ s.silentMs += e.holdMs;
152
+ } else if (e.acknowledgements.length > 0 && e.acknowledgements.every(e => e.kind === "latest")) s.latestOnly++;
153
+ if (isLongHold(e)) {
154
+ s.long++;
155
+ s.longMs += e.tailMs;
156
+ }
157
+ if (e.action) s.actions++;
158
+ for (const n of e.acknowledgements) {
159
+ const e = `${n.kind}:${n.source}`;
160
+ i.acks.set(e, (i.acks.get(e) ?? 0) + 1);
161
+ }
162
+ for (const n of e.heldWrites) i.writes.add(n.name);
163
+ if (e.interaction !== undefined) {
164
+ const n = formatOrigin(e.interaction);
165
+ i.interactions.set(n, (i.interactions.get(n) ?? 0) + 1);
166
+ const t = interactionBucket(e.interaction);
167
+ t.row.holds++;
168
+ t.row.heldMs += e.holdMs;
169
+ if (o) t.row.silentMs += e.holdMs;
170
+ if (e.holdMs > t.row.worstHoldMs) t.row.worstHoldMs = e.holdMs;
171
+ }
172
+ }
173
+
174
+ function recordFeedbackNavigation(e) {
175
+ const n = e.name ?? e.to;
176
+ if (n === undefined) return;
177
+ let t = feedbackNavigations.get(n);
178
+ if (t === undefined) {
179
+ t = {
180
+ name: n,
181
+ navigations: 0,
182
+ settledMs: 0,
183
+ worstMs: 0,
184
+ held: 0,
185
+ heldMs: 0,
186
+ silent: 0,
187
+ superseded: 0,
188
+ redirected: 0
189
+ };
190
+ feedbackNavigations.set(n, t);
191
+ }
192
+ t.navigations++;
193
+ if (e.redirects !== undefined) t.redirected++;
194
+ if (e.outcome === "superseded") {
195
+ t.superseded++;
196
+ return;
197
+ }
198
+ const i = e.settledMs;
199
+ t.settledMs += i;
200
+ if (i > t.worstMs) t.worstMs = i;
201
+ if (e.outcome === "held") {
202
+ t.held++;
203
+ t.heldMs += e.hold?.holdMs ?? i;
204
+ if (e.hold !== undefined && isSilentHold(e.hold)) t.silent++;
205
+ }
206
+ }
207
+
208
+ function rankedCounts(e, n) {
209
+ return [ ...e ].sort((e, n) => n[1] - e[1]).map(([e, t]) => ({
210
+ [n]: e,
211
+ holds: t
212
+ }));
213
+ }
214
+
215
+ registerFold({
216
+ rerun: (e, n) => recordFeedbackRun(n),
217
+ hold: recordFeedbackHold,
218
+ navigation: recordFeedbackNavigation,
219
+ flightStart(e, n) {
220
+ const t = flightBucket(e);
221
+ t.flights++;
222
+ if (n) t.abandoned++;
223
+ },
224
+ flightLanded(e, n) {
225
+ const t = flightBucket(e);
226
+ t.landed++;
227
+ t.landedMs += n;
228
+ if (n > t.worstMs) t.worstMs = n;
229
+ },
230
+ fallback: trackFallback,
231
+ reset() {
232
+ feedbackSources.clear();
233
+ feedbackInteractions.clear();
234
+ feedbackNavigations.clear();
235
+ flightStats.clear();
236
+ fallbackStats.clear();
237
+ }
238
+ });
239
+
240
+ /**
241
+ * What the user waited on, folded from holds and the interaction on each
242
+ * re-run: `sources` ranks async sources by the silent time writes spent held
243
+ * behind them (with which affordances answered, how often, and which
244
+ * interactions were held); `interactions` ranks user events by the total time
245
+ * they cost — re-run work caused (long-flush hazard) beside time held
246
+ * (silent-hold hazard). Facts at every duration; SILENT_HOLD is the
247
+ * thresholded verdict. Three more tables round out the picture: `navigations`
248
+ * ranks routes by the time spent held navigating to them (folded from settled
249
+ * navigations), `flights` counts each async source's flights and how many
250
+ * were abandoned before landing (the re-ask storm), and `fallbacks` measures
251
+ * how long each loading boundary showed its fallback and how often that was a
252
+ * flash.
253
+ */ function feedback() {
254
+ const e = [ ...feedbackNavigations.values() ].map(e => ({
255
+ ...e
256
+ })).sort((e, n) => n.heldMs - e.heldMs || n.settledMs - e.settledMs);
257
+ const n = [ ...flightStats.values() ].map(e => ({
258
+ ...e
259
+ })).sort((e, n) => n.abandoned - e.abandoned || n.flights - e.flights);
260
+ const t = [ ...fallbackStats.values() ].map(e => ({
261
+ ...e.row
262
+ })).sort((e, n) => n.flashes - e.flashes || n.shownMs - e.shownMs);
263
+ const i = [ ...feedbackSources.values() ].map(e => ({
264
+ ...e.row,
265
+ acknowledgedBy: rankedCounts(e.acks, "by"),
266
+ interactions: rankedCounts(e.interactions, "interaction"),
267
+ writes: [ ...e.writes ]
268
+ })).sort((e, n) => n.silentMs - e.silentMs || n.heldMs - e.heldMs);
269
+ // Ranked by the total time the user spent on it: held plus synchronous work.
270
+ const s = [ ...feedbackInteractions.values() ].map(e => ({
271
+ ...e.row
272
+ })).sort((e, n) => n.heldMs + n.selfMs - (e.heldMs + e.selfMs));
273
+ return {
274
+ sources: i,
275
+ interactions: s,
276
+ navigations: e,
277
+ flights: n,
278
+ fallbacks: t
279
+ };
280
+ }
281
+
282
+ export { feedback };
@@ -0,0 +1,82 @@
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
+ try {
36
+ return n();
37
+ } finally {
38
+ o.interactionEnd();
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Run `fn` as a declared unit of work — a router's navigation: every root
44
+ * write it performs is attributed to `ref` (and, through it, to the enclosing
45
+ * interaction when there is one), so the hold those writes wait in, the
46
+ * re-runs they cause and the verdicts on them all carry the route's name
47
+ * instead of a bare signal's. Same contract as `withInteraction`: the wiring,
48
+ * not the engine; `fn()` with no engine installed. Reachable as
49
+ * `OBSERVE.attribution.withOrigin`.
50
+ *
51
+ * ```ts
52
+ * OBSERVE
53
+ * ? OBSERVE.attribution.withOrigin(
54
+ * { kind: "navigation", name: match.pattern, to, from, params: match.params },
55
+ * () => setLocation(to)
56
+ * )
57
+ * : setLocation(to);
58
+ * ```
59
+ */ function withOrigin(t, n) {
60
+ const o = attrHooks;
61
+ if (o === null) return n();
62
+ o.originStart(t);
63
+ try {
64
+ return n();
65
+ } finally {
66
+ o.originEnd();
67
+ }
68
+ }
69
+
70
+ /**
71
+ * The provenance a root write performed now would carry, as the installed
72
+ * engine sees it (`AttributionHooks.currentOrigin`); `undefined` with no
73
+ * engine, or when nothing is in effect. Reachable as
74
+ * `OBSERVE.attribution.currentOrigin` — how a runtime stamps a fact of its
75
+ * own (a server-function call) with the interaction or navigation it ran
76
+ * for, so an observer joins the two by identity.
77
+ */ function currentOrigin() {
78
+ const t = attrHooks;
79
+ return t === null ? undefined : t.currentOrigin();
80
+ }
81
+
82
+ export { attrHooks, currentOrigin, setAttributionHooks, withInteraction, withOrigin };
@@ -0,0 +1,28 @@
1
+ import { nodeName, attribution } from "./attribution.js";
2
+
3
+ import { $REFRESH } from "./constants.js";
4
+
5
+ import { subjectOf } from "./dev.js";
6
+
7
+ /**
8
+ * Point queries over the engine's live state — the devtools/console view of
9
+ * one scope. Their own module so a records-only consumer never ships them;
10
+ * they read the engine's ring buffer and the graph, and register nothing.
11
+ */
12
+ /** The node behind a memo/effect accessor, or the raw node passed through. */ function nodeOf(t) {
13
+ return t?.[$REFRESH] ?? t;
14
+ }
15
+
16
+ /** Re-run history for one node — pass a memo/effect accessor or raw node. */ function why(t) {
17
+ const n = nodeOf(t);
18
+ return attribution.history().filter(t => subjectOf(t) === n);
19
+ }
20
+
21
+ /** Current dependency names of one scope — the devtools subscription view. */ function subscriptions(t) {
22
+ const n = nodeOf(t);
23
+ const o = [];
24
+ for (let t = n?.ee ?? null; t !== null; t = t.te) o.push(nodeName(t.re));
25
+ return o;
26
+ }
27
+
28
+ export { subscriptions, why };