@octane-xplat/patches 0.0.1 → 0.10.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.
@@ -0,0 +1,1699 @@
1
+ diff --git a/dist/cjs/runtime.cjs b/dist/cjs/runtime.cjs
2
+ index 6f4f11cafea50ca0de062aace3426df371a44867..dc96ffaa91081ca81f904de35ed5db9a024daa96 100644
3
+ --- a/dist/cjs/runtime.cjs
4
+ +++ b/dist/cjs/runtime.cjs
5
+ @@ -354,6 +354,7 @@ var import_stream_protocol = require("./stream-protocol.cjs");
6
+ var import_renderer_bridge = require("./renderer-bridge.cjs");
7
+ var import_context_identity = require("./context-identity.cjs");
8
+ var import_native_read_client = require("./signals/native-read-client.cjs");
9
+ +var import_local_hook = require("./signals/local-hook.cjs");
10
+ var import_native_read_collector = require("./signals/native-read-collector.cjs");
11
+ var import_native_read_retry = require("./signals/native-read-retry.cjs");
12
+ var import_transition_state = require("./signals/transition-state.cjs");
13
+ @@ -7604,6 +7605,7 @@ function nativeLocalHook(name, initialize, dispose, slot) {
14
+ }
15
+ return cell.nativeValue;
16
+ }
17
+ +(0, import_local_hook.registerLocalSignalHook)(() => CURRENT_SCOPE === null ? null : nativeLocalHook);
18
+ function useState(initial, slot) {
19
+ const s = readStateHook(initial, slot, arguments.length === 1);
20
+ return [s.value, s.setter];
21
+ diff --git a/dist/cjs/signals/client.cjs b/dist/cjs/signals/client.cjs
22
+ index 28e28444192dee7af64351cfe02d5dde96658832..af40518defca21eb88707eb76bb6fe10c1d266e7 100644
23
+ --- a/dist/cjs/signals/client.cjs
24
+ +++ b/dist/cjs/signals/client.cjs
25
+ @@ -23,13 +23,13 @@ __export(client_exports, {
26
+ });
27
+ module.exports = __toCommonJS(client_exports);
28
+ __reExport(client_exports, require("./index.cjs"), module.exports);
29
+ -var import_runtime = require("../runtime.cjs");
30
+ var import_engine = require("./engine.cjs");
31
+ +var import_local_hook = require("./local-hook.cjs");
32
+ function disposeLocalSignal(cell) {
33
+ cell.scope.dispose();
34
+ }
35
+ function useSignal$(initial, slot) {
36
+ - const cell = (0, import_runtime.nativeLocalHook)(
37
+ + const cell = (0, import_local_hook.localSignalHook)(
38
+ "useSignal$",
39
+ () => {
40
+ const value = typeof initial === "function" ? initial() : initial;
41
+ diff --git a/dist/cjs/signals/document-owner.cjs b/dist/cjs/signals/document-owner.cjs
42
+ index ea6b3040bcc3d1fe0c600894af33fece59711d4b..56a74394b0e40f0ca1dc9b1afb1b44e5a695994f 100644
43
+ --- a/dist/cjs/signals/document-owner.cjs
44
+ +++ b/dist/cjs/signals/document-owner.cjs
45
+ @@ -54,7 +54,9 @@ function enableSignalDocument(abi = 1) {
46
+ if (defaultInstalled)
47
+ return;
48
+ defaultInstalled = true;
49
+ - (0, import_owner_context.installDefaultSignalOwner)(() => typeof document === "undefined" ? null : documentSignalOwner(document));
50
+ + if (typeof document === "undefined")
51
+ + return;
52
+ + (0, import_owner_context.installDefaultSignalOwner)(() => documentSignalOwner(document));
53
+ }
54
+ function installStreamedSignalOwnerActivator(activate) {
55
+ if (streamedSignalOwnerActivator !== void 0) {
56
+ diff --git a/dist/cjs/signals/facade.cjs b/dist/cjs/signals/facade.cjs
57
+ index 9da71c1a774d4764d6e34df12211a08ae2817d5c..02db86c39e23717e0fedb83eb7faf62e4cca7780 100644
58
+ --- a/dist/cjs/signals/facade.cjs
59
+ +++ b/dist/cjs/signals/facade.cjs
60
+ @@ -283,6 +283,8 @@ class Descriptor {
61
+ this.kind = kind;
62
+ this.create = create;
63
+ this.site = site;
64
+ + // Instance declarations stay with their component across child templates and events.
65
+ + this.instanceOwner = site?.startsWith("i:") ? (0, import_owner_context.currentSignalOwner)() : null;
66
+ }
67
+ key;
68
+ kind;
69
+ @@ -292,7 +294,7 @@ class Descriptor {
70
+ cells = /* @__PURE__ */ new WeakMap();
71
+ [import_types.SIGNAL_OWNER_RESOLVE](owner) {
72
+ requireSite(this.site);
73
+ - return this.resolvedCell(resolveDescriptorOwner(this.site, owner));
74
+ + return this.resolvedCell(resolveDescriptorOwner(this.site, this.instanceOwner ?? owner));
75
+ }
76
+ resolvedCell(target) {
77
+ let cell = this.cells.get(target);
78
+ @@ -303,7 +305,7 @@ class Descriptor {
79
+ return cell;
80
+ }
81
+ resolve() {
82
+ - const token = requireOwner();
83
+ + const token = this.instanceOwner ?? requireOwner();
84
+ const owner = resolveDescriptorOwner(this.site, token);
85
+ requireSite(this.site);
86
+ return this.resolvedCell(owner);
87
+ @@ -334,7 +336,7 @@ class Descriptor {
88
+ return this.resolve().snapshot();
89
+ }
90
+ subscribe(notify) {
91
+ - const token = requireOwner();
92
+ + const token = this.instanceOwner ?? requireOwner();
93
+ const scope = resolveDescriptorOwner(this.site, token);
94
+ const run = (0, import_owner_context.captureSignalOwner)(token);
95
+ return this[import_types.SIGNAL_OWNER_RESOLVE](scope).subscribe((0, import_read_protocol.forwardNativeTransitionConsumer)(notify, () => run(notify)));
96
+ diff --git a/dist/cjs/signals/index.cjs b/dist/cjs/signals/index.cjs
97
+ index 865afcf87d84f0cc319d65145b246c9c033927aa..08dd675a49a50c1621e5d3c5a26ca22f8cdbb31d 100644
98
+ --- a/dist/cjs/signals/index.cjs
99
+ +++ b/dist/cjs/signals/index.cjs
100
+ @@ -34,6 +34,7 @@ __export(signals_exports, {
101
+ __derivedAt: () => import_derived_facade.__derivedAt,
102
+ __derivedScalarAt: () => import_facade.__derivedScalarAt,
103
+ __enableSignalDocument: () => import_document_owner.enableSignalDocument,
104
+ + __enableUniversalSignalDocument: () => import_universal_owner.enableUniversalSignalDocument,
105
+ __queryAt: () => import_query_facade.__queryAt,
106
+ __signalAt: () => import_facade.__signalAt,
107
+ __startSignalReads: () => import_facade.__startSignalReads,
108
+ @@ -65,6 +66,7 @@ module.exports = __toCommonJS(signals_exports);
109
+ var import_engine = require("./engine.cjs");
110
+ var import_control_binding = require("./control-binding.cjs");
111
+ var import_document_owner = require("./document-owner.cjs");
112
+ +var import_universal_owner = require("./universal-owner.cjs");
113
+ var import_requests = require("./requests.cjs");
114
+ var import_actions = require("./actions.cjs");
115
+ var import_derived_facade = require("./derived-facade.cjs");
116
+ @@ -92,6 +94,7 @@ var import_types = require("./types.cjs");
117
+ __derivedAt,
118
+ __derivedScalarAt,
119
+ __enableSignalDocument,
120
+ + __enableUniversalSignalDocument,
121
+ __queryAt,
122
+ __signalAt,
123
+ __startSignalReads,
124
+ diff --git a/dist/cjs/signals/local-hook.cjs b/dist/cjs/signals/local-hook.cjs
125
+ new file mode 100644
126
+ index 0000000000000000000000000000000000000000..4858595ce50e97284f18daa94c3b5ce6ab041dd4
127
+ --- /dev/null
128
+ +++ b/dist/cjs/signals/local-hook.cjs
129
+ @@ -0,0 +1,44 @@
130
+ +"use strict";
131
+ +var __defProp = Object.defineProperty;
132
+ +var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
133
+ +var __getOwnPropNames = Object.getOwnPropertyNames;
134
+ +var __hasOwnProp = Object.prototype.hasOwnProperty;
135
+ +var __export = (target, all) => {
136
+ + for (var name in all)
137
+ + __defProp(target, name, { get: all[name], enumerable: true });
138
+ +};
139
+ +var __copyProps = (to, from, except, desc) => {
140
+ + if (from && typeof from === "object" || typeof from === "function") {
141
+ + for (let key of __getOwnPropNames(from))
142
+ + if (!__hasOwnProp.call(to, key) && key !== except)
143
+ + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
144
+ + }
145
+ + return to;
146
+ +};
147
+ +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
148
+ +var local_hook_exports = {};
149
+ +__export(local_hook_exports, {
150
+ + localSignalHook: () => localSignalHook,
151
+ + registerLocalSignalHook: () => registerLocalSignalHook
152
+ +});
153
+ +module.exports = __toCommonJS(local_hook_exports);
154
+ +const probes = [];
155
+ +function registerLocalSignalHook(probe) {
156
+ + probes.push(probe);
157
+ + return () => {
158
+ + const index = probes.indexOf(probe);
159
+ + if (index !== -1) probes.splice(index, 1);
160
+ + };
161
+ +}
162
+ +function localSignalHook(name, initialize, dispose, slot) {
163
+ + for (let index = probes.length - 1; index >= 0; index--) {
164
+ + const hook = probes[index]();
165
+ + if (hook !== null) return hook(name, initialize, dispose, slot);
166
+ + }
167
+ + throw new Error(`${name} requires an active Octane component render.`);
168
+ +}
169
+ +// Annotate the CommonJS export names for ESM import in node:
170
+ +0 && (module.exports = {
171
+ + localSignalHook,
172
+ + registerLocalSignalHook
173
+ +});
174
+ diff --git a/dist/cjs/signals/universal-owner.cjs b/dist/cjs/signals/universal-owner.cjs
175
+ new file mode 100644
176
+ index 0000000000000000000000000000000000000000..00c323a8bd4453d566dbd002ab1222f03def2760
177
+ --- /dev/null
178
+ +++ b/dist/cjs/signals/universal-owner.cjs
179
+ @@ -0,0 +1,46 @@
180
+ +"use strict";
181
+ +var __defProp = Object.defineProperty;
182
+ +var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
183
+ +var __getOwnPropNames = Object.getOwnPropertyNames;
184
+ +var __hasOwnProp = Object.prototype.hasOwnProperty;
185
+ +var __export = (target, all) => {
186
+ + for (var name in all)
187
+ + __defProp(target, name, { get: all[name], enumerable: true });
188
+ +};
189
+ +var __copyProps = (to, from, except, desc) => {
190
+ + if (from && typeof from === "object" || typeof from === "function") {
191
+ + for (let key of __getOwnPropNames(from))
192
+ + if (!__hasOwnProp.call(to, key) && key !== except)
193
+ + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
194
+ + }
195
+ + return to;
196
+ +};
197
+ +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
198
+ +var universal_owner_exports = {};
199
+ +__export(universal_owner_exports, {
200
+ + enableUniversalSignalDocument: () => enableUniversalSignalDocument,
201
+ + installUniversalSignalDocumentProvider: () => installUniversalSignalDocumentProvider,
202
+ + universalSignalDocument: () => universalSignalDocument
203
+ +});
204
+ +module.exports = __toCommonJS(universal_owner_exports);
205
+ +var import_owner_context = require("./owner-context.cjs");
206
+ +let universalOwner;
207
+ +let provider;
208
+ +function installUniversalSignalDocumentProvider(next) {
209
+ + provider = next;
210
+ +}
211
+ +function universalSignalDocument() {
212
+ + return provider?.() ?? (universalOwner ??= Object.freeze({ scopeKey: "octane:universal" }));
213
+ +}
214
+ +function enableUniversalSignalDocument(abi = 1) {
215
+ + if (abi !== 1) {
216
+ + throw new TypeError("Unsupported universal signal owner ABI.");
217
+ + }
218
+ + (0, import_owner_context.installDefaultSignalOwner)(universalSignalDocument);
219
+ +}
220
+ +// Annotate the CommonJS export names for ESM import in node:
221
+ +0 && (module.exports = {
222
+ + enableUniversalSignalDocument,
223
+ + installUniversalSignalDocumentProvider,
224
+ + universalSignalDocument
225
+ +});
226
+ diff --git a/dist/compiler/compile-universal.js b/dist/compiler/compile-universal.js
227
+ index fb05864726848fec0e680cc06de3cfe88f9930de..e1ae8e2314f2cc61a7a6cbd38ae0fadea8aa08bf 100644
228
+ --- a/dist/compiler/compile-universal.js
229
+ +++ b/dist/compiler/compile-universal.js
230
+ @@ -2658,6 +2658,15 @@ function hasOwnTemplateReturn(fn) {
231
+ }
232
+
233
+ function componentRender(fn, state, force = false) {
234
+ + // Functions that are never invoked as components stay ordinary functions,
235
+ + // so an async/generator helper only fails once it is actually compiled as
236
+ + // one (the `@{ … }` form, a JSX-shaped return, or component usage).
237
+ + const component =
238
+ + fn.body?.type === 'JSXCodeBlock' ||
239
+ + (fn.body?.type !== 'BlockStatement'
240
+ + ? force || isTemplateNode(fn.body)
241
+ + : force || hasOwnTemplateReturn(fn) || state.componentNames.has(functionName(fn)));
242
+ + if (!component) return null;
243
+ if (fn.async || fn.generator) {
244
+ throw universalError(
245
+ state.filename,
246
+ @@ -2672,11 +2681,8 @@ function componentRender(fn, state, force = false) {
247
+ };
248
+ }
249
+ if (fn.body?.type !== 'BlockStatement') {
250
+ - if (!force && !isTemplateNode(fn.body)) return null;
251
+ return { setup: [], render: isTemplateNode(fn.body) ? fn.body : null, expression: fn.body };
252
+ }
253
+ - if (!force && !hasOwnTemplateReturn(fn) && !state.componentNames.has(functionName(fn)))
254
+ - return null;
255
+ return { setup: fn.body.body ?? [], render: null };
256
+ }
257
+
258
+ diff --git a/dist/compiler/compile.js b/dist/compiler/compile.js
259
+ index cdf5a08e96946bf06dd417dcfb87373e4be5f305..64a357159586e45774ff864f4478b204bb0dd7ab 100644
260
+ --- a/dist/compiler/compile.js
261
+ +++ b/dist/compiler/compile.js
262
+ @@ -2275,7 +2275,7 @@ function classifyViewTransitionOwnership(astBody, production, ownComponents) {
263
+
264
+ function hookRuntimeModulesForCompile(options, universalUnits = []) {
265
+ const modules = new Set(options?.__hookRuntimeModules || []);
266
+ - if (options?.nativeReads === true) {
267
+ + if (options?.nativeReads === true || options?.__universalSignalReads === true) {
268
+ modules.add('octane/signals/client');
269
+ modules.add('octane/signals/server');
270
+ }
271
+ @@ -10112,6 +10112,11 @@ function compileInternal(
272
+ universalRuntime: undefined,
273
+ rendererBoundaries: undefined,
274
+ rendererRegistry: undefined,
275
+ + // Universal owners observe reads at render time; the inner
276
+ + // compile must not emit the DOM native-read runtime helpers,
277
+ + // but signal hook classification still applies.
278
+ + nativeReads: false,
279
+ + __universalSignalReads: options.nativeReads === true,
280
+ __hookRuntimeModules: [...(options?.__hookRuntimeModules || []), renderer.module],
281
+ __runtimeImportRoutes: [
282
+ ...(options?.__runtimeImportRoutes ?? []),
283
+ @@ -10360,6 +10365,10 @@ function compileInternal(
284
+ autoMemo: autoMemoEnabled,
285
+ strongMemo: strongMemoEnabled,
286
+ nativeReads: options?.nativeReads === true,
287
+ + // Universal renders observe signal reads at runtime through the owner
288
+ + // environment; hook classification still needs to know the module imports
289
+ + // the signals runtime even though no DOM native-read helpers are emitted.
290
+ + signalReads: options?.nativeReads === true || options?.__universalSignalReads === true,
291
+ nativeModuleStyles: options?.nativeReads === true && hasModuleStyleMaps(ast.body),
292
+ // A split Hydrate query module is invoked as the existing server-rendered
293
+ // boundary body. Its sole component child must therefore keep the server's
294
+ @@ -11805,6 +11814,10 @@ function compileServer(
295
+ compilerNameSuffixes: null,
296
+ mode: 'server',
297
+ nativeReads: options?.nativeReads === true,
298
+ + // Universal renders observe signal reads at runtime through the owner
299
+ + // environment; hook classification still needs to know the module imports
300
+ + // the signals runtime even though no DOM native-read helpers are emitted.
301
+ + signalReads: options?.nativeReads === true || options?.__universalSignalReads === true,
302
+ nativeModuleStyles: options?.nativeReads === true && hasModuleStyleMaps(ast.body),
303
+ hmr: false, // SSR never hot-swaps in place; client/server production slot shapes stay aligned
304
+ dev: !!(options && options.dev),
305
+ @@ -18960,7 +18973,7 @@ function slotKeyedHookName(n, ctx) {
306
+ ctx.foreignImportLocals?.has(local) === true;
307
+ if (
308
+ imported !== undefined &&
309
+ - (HOOK_NAMES.has(imported) || (ctx.nativeReads && NATIVE_SIGNAL_HOOK_NAMES.has(imported)))
310
+ + (HOOK_NAMES.has(imported) || (ctx.signalReads && NATIVE_SIGNAL_HOOK_NAMES.has(imported)))
311
+ )
312
+ return imported;
313
+ if (!shadowsImport && HOOK_NAMES.has(local)) return local;
314
+ @@ -18970,7 +18983,7 @@ function slotKeyedHookName(n, ctx) {
315
+ const imported = n._octaneImportedHook ?? n._octaneHookRuntimeImportedHook;
316
+ if (
317
+ imported !== undefined &&
318
+ - (HOOK_NAMES.has(imported) || (ctx.nativeReads && NATIVE_SIGNAL_HOOK_NAMES.has(imported)))
319
+ + (HOOK_NAMES.has(imported) || (ctx.signalReads && NATIVE_SIGNAL_HOOK_NAMES.has(imported)))
320
+ ) {
321
+ return imported;
322
+ }
323
+ @@ -19817,7 +19830,7 @@ function rewriteHookCalls(node, ctx, componentName, localRoot = false) {
324
+ // enforced by rejectHookInJsLoop.
325
+ const isBuiltin =
326
+ (HOOK_NAMES.has(name) ||
327
+ - (ctx.nativeReads &&
328
+ + (ctx.signalReads &&
329
+ hookRuntimeImportedName !== undefined &&
330
+ NATIVE_SIGNAL_HOOK_NAMES.has(name))) &&
331
+ (generated ||
332
+ @@ -19946,7 +19959,7 @@ function rewriteHookCalls(node, ctx, componentName, localRoot = false) {
333
+ ) {
334
+ const name = n._octaneImportedHook ?? n._octaneHookRuntimeImportedHook;
335
+ const isBuiltin =
336
+ - HOOK_NAMES.has(name) || (ctx.nativeReads && NATIVE_SIGNAL_HOOK_NAMES.has(name));
337
+ + HOOK_NAMES.has(name) || (ctx.signalReads && NATIVE_SIGNAL_HOOK_NAMES.has(name));
338
+ const isServerUse = name === 'use' && ctx.mode === 'server';
339
+ if (isBuiltin || isServerUse) {
340
+ if (isBuiltin) {
341
+ diff --git a/dist/compiler/native-read-diagnostics.js b/dist/compiler/native-read-diagnostics.js
342
+ index 3ec74f384e5b34a8032c96646e9ba3faeee50e75..0816e98d66d5082d8d3bb337073c2123e6032c4c 100644
343
+ --- a/dist/compiler/native-read-diagnostics.js
344
+ +++ b/dist/compiler/native-read-diagnostics.js
345
+ @@ -87,9 +87,12 @@ export function assertNativeReadOptions(options) {
346
+ if (
347
+ options?.nativeReads === true &&
348
+ options.renderer?.target != null &&
349
+ - options.renderer.target !== 'dom'
350
+ + options.renderer.target !== 'dom' &&
351
+ + options.renderer.target !== 'universal'
352
+ ) {
353
+ - throw new Error('Octane native signal reads support only the DOM client and server.');
354
+ + throw new Error(
355
+ + 'Octane native signal reads support only the DOM client, DOM server, and universal renderers.',
356
+ + );
357
+ }
358
+ }
359
+
360
+ @@ -108,11 +111,11 @@ export function analyzeNativeReadDiagnostics(ast, source, filename, options = {}
361
+ filename,
362
+ rendererBoundaries: options.rendererBoundaries,
363
+ });
364
+ - const unsupported = boundaries.find(
365
+ - (boundary) =>
366
+ - boundary.childRenderer !== 'dom' &&
367
+ - options.rendererRegistry?.[boundary.childRenderer]?.target !== 'dom',
368
+ - );
369
+ + const unsupported = boundaries.find((boundary) => {
370
+ + if (boundary.childRenderer === 'dom') return false;
371
+ + const target = options.rendererRegistry?.[boundary.childRenderer]?.target;
372
+ + return target !== 'dom' && target !== 'universal';
373
+ + });
374
+ if (unsupported)
375
+ return [
376
+ nativeReadDiagnostic(
377
+ diff --git a/dist/compiler/slot-hooks.js b/dist/compiler/slot-hooks.js
378
+ index 7b2f867538a166ebaf3d1a8aa6e7d910e3b0da12..2c1d36c9edb32c483a5e7ed736e51b0ca12cde30 100644
379
+ --- a/dist/compiler/slot-hooks.js
380
+ +++ b/dist/compiler/slot-hooks.js
381
+ @@ -1182,12 +1182,12 @@ function walk(node, owner, st) {
382
+ }
383
+ const imported =
384
+ node._octaneImportedHook ??
385
+ - (st.nativeReads ? node._octaneHookRuntimeImportedHook : undefined);
386
+ + (st.signalReads ? node._octaneHookRuntimeImportedHook : undefined);
387
+ collectStateGetterEdit(node, imported, st);
388
+ if (
389
+ !st.manualSlots &&
390
+ imported &&
391
+ - (HOOK_NAMES.has(imported) || (st.nativeReads && NATIVE_SIGNAL_HOOK_NAMES.has(imported)))
392
+ + (HOOK_NAMES.has(imported) || (st.signalReads && NATIVE_SIGNAL_HOOK_NAMES.has(imported)))
393
+ ) {
394
+ const local =
395
+ node.callee?.type === 'Identifier'
396
+ @@ -1398,7 +1398,11 @@ export function slotHooks(source, id, options) {
397
+ HOOK_NAMES.has(name),
398
+ );
399
+ const manualProviders = options?.manualSlots ? findManualHookProviders(ast) : new Map();
400
+ - const nativeReadActivation = options?.nativeReads === true && importsNativeRenderer(ast);
401
+ + // Universal renderers observe reads at render time; the DOM collection
402
+ + // driver and its activation helper only apply to dom targets.
403
+ + const universalTarget = options?.renderer?.target === 'universal';
404
+ + const nativeReadActivation =
405
+ + options?.nativeReads === true && !universalTarget && importsNativeRenderer(ast);
406
+ const signalLowering = signalDeclarationSourceEdits(ast, id, source);
407
+ const pureCalls = collectPureFactoryCalls(
408
+ ast,
409
+ @@ -1487,7 +1491,8 @@ export function slotHooks(source, id, options) {
410
+ ]),
411
+ ),
412
+ manualSlots: options?.manualSlots === true,
413
+ - nativeReads: options?.nativeReads === true,
414
+ + nativeReads: options?.nativeReads === true && !universalTarget,
415
+ + signalReads: options?.nativeReads === true,
416
+ locals: importInfo.locals,
417
+ source,
418
+ inferred,
419
+ @@ -1590,7 +1595,9 @@ export function slotHooks(source, id, options) {
420
+ ? 'enableServerSignalBindings'
421
+ : nativeReadActivation
422
+ ? 'enableSignalBindings'
423
+ - : '__enableSignalDocument';
424
+ + : universalTarget
425
+ + ? '__enableUniversalSignalDocument'
426
+ + : '__enableSignalDocument';
427
+ const local = allocSlotName(st, `_$${imported}`);
428
+ let specifiers = signalHelperImports.get(request);
429
+ if (specifiers === undefined) signalHelperImports.set(request, (specifiers = []));
430
+ diff --git a/dist/node/runtime.js b/dist/node/runtime.js
431
+ index 21d10d95b6094ee07693a8b0757ca381d706b86e..2a0b4204dbcc6a0a749fcdbecfe93241d73dd768 100644
432
+ --- a/dist/node/runtime.js
433
+ +++ b/dist/node/runtime.js
434
+ @@ -162,6 +162,7 @@ import {
435
+ import { isRendererContext, registerClientRendererBridge } from "./renderer-bridge.js";
436
+ import { defineRemovedContextMembers, registerContext } from "./context-identity.js";
437
+ import { createNativeReadDriver } from "./signals/native-read-client.js";
438
+ +import { registerLocalSignalHook } from "./signals/local-hook.js";
439
+ import {
440
+ validateNativeReadWitness
441
+ } from "./signals/native-read-collector.js";
442
+ @@ -7456,6 +7457,7 @@ function nativeLocalHook(name, initialize, dispose, slot) {
443
+ }
444
+ return cell.nativeValue;
445
+ }
446
+ +registerLocalSignalHook(() => CURRENT_SCOPE === null ? null : nativeLocalHook);
447
+ function useState(initial, slot) {
448
+ const s = readStateHook(initial, slot, arguments.length === 1);
449
+ return [s.value, s.setter];
450
+ diff --git a/dist/node/signals/client.js b/dist/node/signals/client.js
451
+ index 21a39c3ce9a28d2095e276fd647fd795e939a0bf..3f85700b57cbc3084c2df776ab3e6b5365f091a8 100644
452
+ --- a/dist/node/signals/client.js
453
+ +++ b/dist/node/signals/client.js
454
+ @@ -1,11 +1,11 @@
455
+ export * from "./index.js";
456
+ -import { nativeLocalHook } from "../runtime.js";
457
+ import { createLocalScope } from "./engine.js";
458
+ +import { localSignalHook } from "./local-hook.js";
459
+ function disposeLocalSignal(cell) {
460
+ cell.scope.dispose();
461
+ }
462
+ function useSignal$(initial, slot) {
463
+ - const cell = nativeLocalHook(
464
+ + const cell = localSignalHook(
465
+ "useSignal$",
466
+ () => {
467
+ const value = typeof initial === "function" ? initial() : initial;
468
+ diff --git a/dist/node/signals/document-owner.js b/dist/node/signals/document-owner.js
469
+ index 0decc9286f8d06e78dd15eaa16d0cc53cac44220..59a94863274839675c48880bf37018fddd00ec11 100644
470
+ --- a/dist/node/signals/document-owner.js
471
+ +++ b/dist/node/signals/document-owner.js
472
+ @@ -27,7 +27,9 @@ function enableSignalDocument(abi = 1) {
473
+ if (defaultInstalled)
474
+ return;
475
+ defaultInstalled = true;
476
+ - installDefaultSignalOwner(() => typeof document === "undefined" ? null : documentSignalOwner(document));
477
+ + if (typeof document === "undefined")
478
+ + return;
479
+ + installDefaultSignalOwner(() => documentSignalOwner(document));
480
+ }
481
+ function installStreamedSignalOwnerActivator(activate) {
482
+ if (streamedSignalOwnerActivator !== void 0) {
483
+ diff --git a/dist/node/signals/facade.js b/dist/node/signals/facade.js
484
+ index 86c0233dad7013c6516912656e0e1b304351c1cb..594e697b59f19e95821ed09313835842a7afe33e 100644
485
+ --- a/dist/node/signals/facade.js
486
+ +++ b/dist/node/signals/facade.js
487
+ @@ -241,6 +241,8 @@ class Descriptor {
488
+ this.kind = kind;
489
+ this.create = create;
490
+ this.site = site;
491
+ + // Instance declarations stay with their component across child templates and events.
492
+ + this.instanceOwner = site?.startsWith("i:") ? currentSignalOwner() : null;
493
+ }
494
+ key;
495
+ kind;
496
+ @@ -250,7 +252,7 @@ class Descriptor {
497
+ cells = /* @__PURE__ */ new WeakMap();
498
+ [SIGNAL_OWNER_RESOLVE](owner) {
499
+ requireSite(this.site);
500
+ - return this.resolvedCell(resolveDescriptorOwner(this.site, owner));
501
+ + return this.resolvedCell(resolveDescriptorOwner(this.site, this.instanceOwner ?? owner));
502
+ }
503
+ resolvedCell(target) {
504
+ let cell = this.cells.get(target);
505
+ @@ -261,7 +263,7 @@ class Descriptor {
506
+ return cell;
507
+ }
508
+ resolve() {
509
+ - const token = requireOwner();
510
+ + const token = this.instanceOwner ?? requireOwner();
511
+ const owner = resolveDescriptorOwner(this.site, token);
512
+ requireSite(this.site);
513
+ return this.resolvedCell(owner);
514
+ @@ -292,7 +294,7 @@ class Descriptor {
515
+ return this.resolve().snapshot();
516
+ }
517
+ subscribe(notify) {
518
+ - const token = requireOwner();
519
+ + const token = this.instanceOwner ?? requireOwner();
520
+ const scope = resolveDescriptorOwner(this.site, token);
521
+ const run = captureSignalOwner(token);
522
+ return this[SIGNAL_OWNER_RESOLVE](scope).subscribe(forwardNativeTransitionConsumer(notify, () => run(notify)));
523
+ diff --git a/dist/node/signals/index.js b/dist/node/signals/index.js
524
+ index 8ae19e510a09e8144ae2ab66c0daf293a4a3f8f6..0bf09f5276e9e1a38ef67923b9cc2a441be2ce7f 100644
525
+ --- a/dist/node/signals/index.js
526
+ +++ b/dist/node/signals/index.js
527
+ @@ -1,6 +1,7 @@
528
+ import { createScope } from "./engine.js";
529
+ import { bindSignalControl } from "./control-binding.js";
530
+ import { enableSignalDocument } from "./document-owner.js";
531
+ +import { enableUniversalSignalDocument } from "./universal-owner.js";
532
+ import { createResource, query } from "./requests.js";
533
+ import { ActionUncertainError, action$, isActionUncertain, optimistic$ } from "./actions.js";
534
+ import { __derivedAt, derived$ } from "./derived-facade.js";
535
+ @@ -58,6 +59,7 @@ export {
536
+ __derivedAt,
537
+ __derivedScalarAt,
538
+ enableSignalDocument as __enableSignalDocument,
539
+ + enableUniversalSignalDocument as __enableUniversalSignalDocument,
540
+ __queryAt,
541
+ __signalAt,
542
+ __startSignalReads,
543
+ diff --git a/dist/node/signals/local-hook.js b/dist/node/signals/local-hook.js
544
+ new file mode 100644
545
+ index 0000000000000000000000000000000000000000..77c63f5e2d931eb8dbd384ae027ee0f09935f9ce
546
+ --- /dev/null
547
+ +++ b/dist/node/signals/local-hook.js
548
+ @@ -0,0 +1,19 @@
549
+ +const probes = [];
550
+ +function registerLocalSignalHook(probe) {
551
+ + probes.push(probe);
552
+ + return () => {
553
+ + const index = probes.indexOf(probe);
554
+ + if (index !== -1) probes.splice(index, 1);
555
+ + };
556
+ +}
557
+ +function localSignalHook(name, initialize, dispose, slot) {
558
+ + for (let index = probes.length - 1; index >= 0; index--) {
559
+ + const hook = probes[index]();
560
+ + if (hook !== null) return hook(name, initialize, dispose, slot);
561
+ + }
562
+ + throw new Error(`${name} requires an active Octane component render.`);
563
+ +}
564
+ +export {
565
+ + localSignalHook,
566
+ + registerLocalSignalHook
567
+ +};
568
+ diff --git a/dist/node/signals/universal-owner.js b/dist/node/signals/universal-owner.js
569
+ new file mode 100644
570
+ index 0000000000000000000000000000000000000000..ff5eb43e8f0ddcadbcf8e861f42788b389b4d99b
571
+ --- /dev/null
572
+ +++ b/dist/node/signals/universal-owner.js
573
+ @@ -0,0 +1,20 @@
574
+ +import { installDefaultSignalOwner } from "./owner-context.js";
575
+ +let universalOwner;
576
+ +let provider;
577
+ +function installUniversalSignalDocumentProvider(next) {
578
+ + provider = next;
579
+ +}
580
+ +function universalSignalDocument() {
581
+ + return provider?.() ?? (universalOwner ??= Object.freeze({ scopeKey: "octane:universal" }));
582
+ +}
583
+ +function enableUniversalSignalDocument(abi = 1) {
584
+ + if (abi !== 1) {
585
+ + throw new TypeError("Unsupported universal signal owner ABI.");
586
+ + }
587
+ + installDefaultSignalOwner(universalSignalDocument);
588
+ +}
589
+ +export {
590
+ + enableUniversalSignalDocument,
591
+ + installUniversalSignalDocumentProvider,
592
+ + universalSignalDocument
593
+ +};
594
+ diff --git a/dist/node/universal-core.js b/dist/node/universal-core.js
595
+ index 4dbe2f938d039d415b44821bcb3be520172bcada..735a017364cd23bdb8972a04401385f0bde99062 100644
596
+ --- a/dist/node/universal-core.js
597
+ +++ b/dist/node/universal-core.js
598
+ @@ -17,6 +17,14 @@ import {
599
+ } from "./profiling.js";
600
+ import { getRendererHostFlusher } from "./renderer-bridge.js";
601
+ import { resolveLazyDefaultProps } from "./shared-value-helpers.js";
602
+ +import { universalSignalDocument } from "./signals/universal-owner.js";
603
+ +import { registerLocalSignalHook } from "./signals/local-hook.js";
604
+ +import { retireSignalOwnerIdentity, runWithSignalOwner } from "./signals/owner-context.js";
605
+ +import {
606
+ + beginNativeWriteGuard,
607
+ + endNativeWriteGuard,
608
+ + setNativeReadObserver
609
+ +} from "./signals/read-protocol.js";
610
+ import { __methodDep } from "./method-dep.js";
611
+ const UNIVERSAL_PLAN = /* @__PURE__ */ Symbol.for("octane.universal.plan");
612
+ const UNIVERSAL_VALUE = /* @__PURE__ */ Symbol.for("octane.universal.value");
613
+ @@ -474,6 +482,133 @@ function universalComponent(renderer, component, props = null, key = NO_KEY) {
614
+ hasKey: key !== NO_KEY || normalized.hasKey
615
+ };
616
+ }
617
+ +const NORMALIZED_CHILD_KEYS = new WeakMap();
618
+ +/** Normalize deferred JSX without mounting components or consuming their context.
619
+ + * Structural owners (blocks, providers, boundaries and loops) remain renderables;
620
+ + * only transparent child groups, plan ranges, slots and JS arrays are flattened.
621
+ + */
622
+ +function universalChildEntries(children) {
623
+ + const out = [];
624
+ + const collectionKey = (child) => {
625
+ + const key = renderableKey(child);
626
+ + if (key !== null)
627
+ + return key;
628
+ + if (child?.$$kind !== UNIVERSAL_VALUE)
629
+ + return null;
630
+ + const { plan, values } = child;
631
+ + let node = plan.root;
632
+ + while (node.kind === 'range' && node.children.length === 1)
633
+ + node = node.children[0];
634
+ + if (node.kind === 'slot')
635
+ + return collectionKey(values[node.slot]);
636
+ + if (node.kind !== 'host' && node.kind !== 'component')
637
+ + return null;
638
+ + if (node.kind === 'component' && node.keySlot !== undefined)
639
+ + return normalizeUniversalKey(values[node.keySlot]);
640
+ + return normalizeUniversalKey(node.propsSlot === undefined ? node.kind === 'host' ? node.props?.key : null : normalizePropsValue(values[node.propsSlot]).key);
641
+ + };
642
+ + const visit = (child, path) => {
643
+ + if (child == null || typeof child === 'boolean')
644
+ + return;
645
+ + if (typeof child === 'object') {
646
+ + const normalizedKey = NORMALIZED_CHILD_KEYS.get(child);
647
+ + if (normalizedKey !== undefined) {
648
+ + out.push({ child, key: normalizedKey });
649
+ + return;
650
+ + }
651
+ + }
652
+ + if (Array.isArray(child)) {
653
+ + child.forEach((item, index) => {
654
+ + const key = collectionKey(item);
655
+ + visit(item, [...path, key === null ? 'index' : 'key', key ?? index]);
656
+ + });
657
+ + return;
658
+ + }
659
+ + if (child?.$$kind === UNIVERSAL_CHILDREN) {
660
+ + const children = child;
661
+ + if (CURRENT_OWNER !== null && children.renderer !== currentAttempt().root.renderer) {
662
+ + throw new Error('Universal renderer mismatch while normalizing children.');
663
+ + }
664
+ + visit(children.render(), path);
665
+ + return;
666
+ + }
667
+ + if (child?.$$kind === UNIVERSAL_LIST) {
668
+ + const list = child;
669
+ + visit(list.values.length === 0 ? list.empty : list.values, path);
670
+ + return;
671
+ + }
672
+ + if (child?.$$kind === UNIVERSAL_VALUE) {
673
+ + const value = child;
674
+ + if (CURRENT_OWNER !== null && value.plan.renderer !== currentAttempt().root.renderer) {
675
+ + throw new Error('Universal renderer mismatch while normalizing children.');
676
+ + }
677
+ + if (value.key !== null) {
678
+ + out.push({ child, key: JSON.stringify([...path, 'key', value.key]) });
679
+ + return;
680
+ + }
681
+ + const visitNode = (node, nodePath) => {
682
+ + if (node.kind === 'range') {
683
+ + node.children.forEach((item, index) => visitNode(item, [...nodePath, index]));
684
+ + }
685
+ + else if (node.kind === 'slot') {
686
+ + visit(value.values[node.slot], nodePath);
687
+ + }
688
+ + else if (node.kind === 'text') {
689
+ + visit((node.slot === undefined ? node.value : value.values[node.slot]), nodePath);
690
+ + }
691
+ + else {
692
+ + const key = node.kind === 'component'
693
+ + ? node.keySlot === undefined
694
+ + ? node.propsSlot === undefined ? null : normalizePropsValue(value.values[node.propsSlot]).key
695
+ + : value.values[node.keySlot]
696
+ + : node.kind === 'host'
697
+ + ? node.propsSlot === undefined ? node.props?.key ?? null : normalizePropsValue(value.values[node.propsSlot]).key
698
+ + : null;
699
+ + const item = universalValue(universalPlan(value.plan.renderer, node), value.values);
700
+ + out.push({ child: item, key: JSON.stringify(key == null ? nodePath : [...(node === value.plan.root ? path : nodePath.slice(0, -1)), 'key', key]) });
701
+ + }
702
+ + };
703
+ + visitNode(value.plan.root, path);
704
+ + return;
705
+ + }
706
+ + const key = renderableKey(child);
707
+ + out.push({ child, key: JSON.stringify(key === null ? path : [...path, 'key', key]) });
708
+ + };
709
+ + visit(children, []);
710
+ + return out;
711
+ +}
712
+ +/** Renderer-supported child collection operations. They never render a component
713
+ + * to discover its output. Returned entries retain their keys when mapped into
714
+ + * wrapper hosts, and are materialized normally at their eventual placement.
715
+ + */
716
+ +function normalizedChild(key, child) {
717
+ + const value = universalKey(key, child);
718
+ + NORMALIZED_CHILD_KEYS.set(value, key);
719
+ + return value;
720
+ +}
721
+ +const Children = {
722
+ + toArray(children) {
723
+ + return universalChildEntries(children).map(({ child, key }) => typeof child === 'object' ? normalizedChild(key, child) : child);
724
+ + },
725
+ + map(children, fn) {
726
+ + const out = [];
727
+ + universalChildEntries(children).forEach(({ child, key }, index) => {
728
+ + const mapped = fn(child, index);
729
+ + if (mapped == null || typeof mapped === 'boolean')
730
+ + return;
731
+ + if (Array.isArray(mapped)) {
732
+ + for (const entry of universalChildEntries(mapped)) {
733
+ + out.push(normalizedChild(JSON.stringify([key, entry.key]), entry.child));
734
+ + }
735
+ + }
736
+ + else {
737
+ + out.push(normalizedChild(key, mapped));
738
+ + }
739
+ + });
740
+ + return out;
741
+ + },
742
+ +};
743
+ +
744
+ function universalChildren(renderer, render) {
745
+ assertRendererId(renderer, "universalChildren renderer");
746
+ if (typeof render !== "function") throw new TypeError("universalChildren expected a function.");
747
+ @@ -842,6 +977,7 @@ function draftOwner(record, parent, replayPath) {
748
+ boundaryThenable: record.boundaryThenable,
749
+ isBoundary: record.isBoundary,
750
+ canHandleSuspense: record.canHandleSuspense,
751
+ + signalReads: null,
752
+ visibility: parent?.visibility ?? record.visibility,
753
+ contextStable: null
754
+ };
755
+ @@ -1012,6 +1148,7 @@ function executeOwner(owner, build, initialRenderCount = 0) {
756
+ owner.contextStable = null;
757
+ owner.needsRender = false;
758
+ owner.implicitSlot = 0;
759
+ + owner.signalReads = NO_SIGNAL_READS;
760
+ const previousOwner = CURRENT_OWNER;
761
+ const previousAttemptOwner = attempt.owner;
762
+ CURRENT_OWNER = owner;
763
+ @@ -1023,13 +1160,18 @@ function executeOwner(owner, build, initialRenderCount = 0) {
764
+ const profileFrame = typeof __OCTANE_PROFILE_ENABLED__ !== "undefined" && __OCTANE_PROFILE_ENABLED__ && component !== null ? __profileBeginRender(owner.record, component, owner.record.mounted) : null;
765
+ let didThrow = false;
766
+ let thrown;
767
+ + const signalOwner = universalSignalOwner(owner.record);
768
+ + const previousReadObserver = setNativeReadObserver(observeUniversalSignalRead);
769
+ + const writeGuard = beginNativeWriteGuard();
770
+ try {
771
+ - output = build();
772
+ + output = runWithSignalOwner(signalOwner, build);
773
+ } catch (error) {
774
+ didThrow = true;
775
+ - thrown = error;
776
+ - throw error;
777
+ + thrown = isThenable(error) ? new UniversalSuspense(error) : error;
778
+ + throw thrown;
779
+ } finally {
780
+ + endNativeWriteGuard(writeGuard);
781
+ + setNativeReadObserver(previousReadObserver);
782
+ if (typeof __OCTANE_PROFILE_ENABLED__ !== "undefined" && __OCTANE_PROFILE_ENABLED__) {
783
+ __profileEndRender(profileFrame, didThrow, thrown);
784
+ }
785
+ @@ -1196,6 +1338,8 @@ function disposeUncommittedDraft(owner) {
786
+ owner.record.componentProps = null;
787
+ owner.record.range = null;
788
+ owner.record.updates.clear();
789
+ + disposeUniversalSignalReads(owner.record);
790
+ + disposeUniversalLocalHooks(owner.hooks);
791
+ for (const hook of owner.hooks.values()) {
792
+ if (hook.kind === "effect-event") hook.cell.active = false;
793
+ }
794
+ @@ -3142,6 +3286,85 @@ function scheduleOwner(owner, slot) {
795
+ }
796
+ owner.root.scheduleOwned(owner);
797
+ }
798
+ +const UNIVERSAL_SIGNAL_OWNERS = /* @__PURE__ */ new WeakMap();
799
+ +function universalSignalOwner(record) {
800
+ + let owner = UNIVERSAL_SIGNAL_OWNERS.get(record);
801
+ + if (owner === void 0) {
802
+ + const documentOwner = universalSignalDocument();
803
+ + const identity = {
804
+ + scopeKey: documentOwner.scopeKey,
805
+ + documentOwner,
806
+ + // The identity object itself is the opaque per-instance token — the
807
+ + // same shape DOM scopes use, so identity maps never retain the record.
808
+ + instanceOwner: null,
809
+ + instanceKey: `u:${record.id}`
810
+ + };
811
+ + identity.instanceOwner = identity;
812
+ + owner = Object.freeze(identity);
813
+ + UNIVERSAL_SIGNAL_OWNERS.set(record, owner);
814
+ + }
815
+ + return owner;
816
+ +}
817
+ +const NO_SIGNAL_READS = /* @__PURE__ */ new Set();
818
+ +function observeUniversalSignalRead(source) {
819
+ + const owner = CURRENT_OWNER;
820
+ + if (owner === null) return;
821
+ + if (owner.signalReads === NO_SIGNAL_READS) owner.signalReads = /* @__PURE__ */ new Set();
822
+ + owner.signalReads.add(source);
823
+ +}
824
+ +function commitUniversalSignalReads(record, reads) {
825
+ + if (reads === null) return;
826
+ + let subscriptions = record.signalSubscriptions;
827
+ + if (subscriptions !== void 0) {
828
+ + for (const [source, unsubscribe] of subscriptions) {
829
+ + if (!reads.has(source)) {
830
+ + subscriptions.delete(source);
831
+ + unsubscribe();
832
+ + }
833
+ + }
834
+ + }
835
+ + for (const source of reads) {
836
+ + if (subscriptions?.has(source)) continue;
837
+ + if (subscriptions === void 0) {
838
+ + subscriptions = /* @__PURE__ */ new Map();
839
+ + record.signalSubscriptions = subscriptions;
840
+ + }
841
+ + subscriptions.set(
842
+ + source,
843
+ + source.subscribe(() => {
844
+ + if (!record.disposed) scheduleOwner(record);
845
+ + })
846
+ + );
847
+ + }
848
+ + if (subscriptions !== void 0 && subscriptions.size === 0) {
849
+ + record.signalSubscriptions = void 0;
850
+ + }
851
+ +}
852
+ +function disposeUniversalSignalReads(record) {
853
+ + const subscriptions = record.signalSubscriptions;
854
+ + if (subscriptions !== void 0) {
855
+ + record.signalSubscriptions = void 0;
856
+ + for (const unsubscribe of subscriptions.values()) unsubscribe();
857
+ + }
858
+ + const owner = UNIVERSAL_SIGNAL_OWNERS.get(record);
859
+ + if (owner !== void 0) retireSignalOwnerIdentity(owner);
860
+ +}
861
+ +function disposeUniversalLocalHooks(hooks) {
862
+ + for (const hook of hooks.values()) {
863
+ + if (hook.kind === "local") hook.dispose(hook.value);
864
+ + }
865
+ +}
866
+ +function universalLocalHook(_name, initialize, dispose, slot) {
867
+ + const owner = currentDraftOwner();
868
+ + const resolved = resolveHookSlot(slot);
869
+ + let cell = owner.hooks.get(resolved);
870
+ + if (cell === void 0) {
871
+ + cell = { kind: "local", value: initialize(), dispose };
872
+ + owner.hooks.set(resolved, cell);
873
+ + }
874
+ + return cell.value;
875
+ +}
876
+ +registerLocalSignalHook(() => CURRENT_OWNER === null ? null : universalLocalHook);
877
+ function currentDraftOwner() {
878
+ currentAttempt();
879
+ const owner = activateLazyLeafOwner();
880
+ @@ -4142,15 +4365,35 @@ function use(usable) {
881
+ function useImperativeHandle(ref, create, deps, slot) {
882
+ useLayoutEffect(
883
+ () => {
884
+ - const value = create();
885
+ - if (typeof ref === "function") ref(value);
886
+ - else if (ref !== null) ref.current = value;
887
+ - return () => {
888
+ - if (typeof ref === "function") ref(null);
889
+ - else if (ref !== null) ref.current = null;
890
+ + const cleanups = [];
891
+ + let value;
892
+ + let initialized = false;
893
+ + const attach = (target) => {
894
+ + if (target == null) return;
895
+ + if (Array.isArray(target)) {
896
+ + for (const entry of target) attach(entry);
897
+ + return;
898
+ + }
899
+ + if (!initialized) {
900
+ + value = create();
901
+ + initialized = true;
902
+ + }
903
+ + if (typeof target === "function") {
904
+ + const cleanup = target(value);
905
+ + cleanups.push(typeof cleanup === "function" ? cleanup : () => target(null));
906
+ + } else {
907
+ + target.current = value;
908
+ + cleanups.push(() => { target.current = null; });
909
+ + }
910
+ };
911
+ + try {
912
+ + attach(ref);
913
+ + } catch (error) {
914
+ + try { runCommitTasks(cleanups); } finally { throw error; }
915
+ + }
916
+ + return () => runCommitTasks(cleanups);
917
+ },
918
+ - deps,
919
+ + deps == null ? deps : [...deps, ref],
920
+ slot
921
+ );
922
+ }
923
+ @@ -5520,9 +5763,9 @@ class UniversalRootImpl {
924
+ }
925
+ let result;
926
+ try {
927
+ - result = event.handler(payload);
928
+ + result = runWithSignalOwner(universalSignalOwner(event.owner), () => event.handler(payload));
929
+ } catch (error) {
930
+ - if (!routeUniversalOwnerError(event.owner, error)) throw error;
931
+ + if (!routeUniversalOwnerError(event.owner, error) && !reportUniversalUncaughtError(this, error)) throw error;
932
+ }
933
+ return result;
934
+ }
935
+ @@ -6096,6 +6339,8 @@ class UniversalRootImpl {
936
+ draft.record.componentProps = null;
937
+ draft.record.range = null;
938
+ draft.record.updates.clear();
939
+ + disposeUniversalSignalReads(draft.record);
940
+ + disposeUniversalLocalHooks(draft.hooks);
941
+ for (const hook of draft.hooks.values()) {
942
+ if (hook.kind === "effect-event") hook.cell.active = false;
943
+ }
944
+ @@ -6776,6 +7021,7 @@ class UniversalRootImpl {
945
+ owner.componentProps = draftOwner2.componentProps;
946
+ owner.componentRevision = draftOwner2.componentRevision;
947
+ owner.hooks = draftOwner2.hooks;
948
+ + commitUniversalSignalReads(owner, draftOwner2.signalReads);
949
+ for (const [slot, applied] of draftOwner2.appliedUpdates) {
950
+ const queue = owner.updates.get(slot);
951
+ if (queue !== applied.queue || applied.lane) continue;
952
+ @@ -6809,6 +7055,7 @@ class UniversalRootImpl {
953
+ record.componentRevision = draft.componentRevision;
954
+ record.parent = draft.parent?.record ?? null;
955
+ record.hooks = draft.hooks;
956
+ + commitUniversalSignalReads(record, draft.signalReads);
957
+ record.effectOrder = [...draft.seenEffects];
958
+ for (let index = 0; index < draft.seenEffects.length; index++) {
959
+ draft.seenEffects[index].previous = null;
960
+ @@ -8455,6 +8702,8 @@ class UniversalRootImpl {
961
+ owner.componentProps = null;
962
+ owner.range = null;
963
+ owner.updates.clear();
964
+ + disposeUniversalSignalReads(owner);
965
+ + disposeUniversalLocalHooks(owner.hooks);
966
+ }
967
+ for (const draft of draftOwnersParentFirst) {
968
+ const record = draft.record;
969
+ @@ -8464,6 +8713,7 @@ class UniversalRootImpl {
970
+ record.parent = draft.parent?.record ?? null;
971
+ }
972
+ record.hooks = draft.hooks;
973
+ + commitUniversalSignalReads(record, draft.signalReads);
974
+ record.effectOrder = [...draft.seenEffects];
975
+ for (let index = 0; index < draft.seenEffects.length; index++) {
976
+ draft.seenEffects[index].previous = null;
977
+ @@ -8985,6 +9235,8 @@ class UniversalRootImpl {
978
+ owner.componentProps = null;
979
+ owner.range = null;
980
+ owner.updates.clear();
981
+ + disposeUniversalSignalReads(owner);
982
+ + disposeUniversalLocalHooks(owner.hooks);
983
+ }
984
+ const deactivatedRegionCells = /* @__PURE__ */ new Set();
985
+ for (const bridge of regionBridges) {
986
+ @@ -9937,6 +10189,7 @@ function createObjectDriver(renderer = "object") {
987
+ };
988
+ }
989
+ export {
990
+ + Children,
991
+ Activity,
992
+ UNIVERSAL_HMR,
993
+ UNIVERSAL_TRANSPORT_PROTOCOL_VERSION,
994
+ diff --git a/dist/node/universal-native.js b/dist/node/universal-native.js
995
+ index 55ac9b640dc8bbfd977bb2cbe0b7993c8ddb425a..3ff9a5750fb59cd91b63299aec3a3e0c030b4579 100644
996
+ --- a/dist/node/universal-native.js
997
+ +++ b/dist/node/universal-native.js
998
+ @@ -6,9 +6,11 @@ import {
999
+ import {
1000
+ universalContext
1001
+ } from "./universal-core.js";
1002
+ +import { enableUniversalSignalDocument } from "./signals/universal-owner.js";
1003
+ import { registerRendererContext, renderRendererContextProvider } from "./renderer-bridge.js";
1004
+ import { CONTEXT_TAG } from "./runtime-tags.js";
1005
+ import { registerContext } from "./context-identity.js";
1006
+ +enableUniversalSignalDocument();
1007
+ // @__NO_SIDE_EFFECTS__
1008
+ function createContext(defaultValue) {
1009
+ const context = ((props, scope) => scope === void 0 ? universalContext(context, props.value, props.children) : renderRendererContextProvider(context, props, scope));
1010
+ diff --git a/dist/node/universal.js b/dist/node/universal.js
1011
+ index 6427a0190c072e7e4629c379e42bedc03669dc6e..8049b7880824de607157091004a46c9d33a23484 100644
1012
+ --- a/dist/node/universal.js
1013
+ +++ b/dist/node/universal.js
1014
+ @@ -8,6 +8,9 @@ import {
1015
+ createUniversalHostBoundary,
1016
+ registerUniversalHostBridge
1017
+ } from "./universal-dom-boundary.js";
1018
+ +import { documentSignalOwner } from "./signals/document-owner.js";
1019
+ +import { installUniversalSignalDocumentProvider } from "./signals/universal-owner.js";
1020
+ +installUniversalSignalDocumentProvider(() => documentSignalOwner(document));
1021
+ export {
1022
+ createContext,
1023
+ createSubSlot,
1024
+ diff --git a/dist/runtime.js b/dist/runtime.js
1025
+ index ee2a2bdd0e0909921fc43ed073be0fbc7f6ec585..4f9f1b5b7782bc0e42799ddb847d22f617a4885f 100644
1026
+ --- a/dist/runtime.js
1027
+ +++ b/dist/runtime.js
1028
+ @@ -161,6 +161,7 @@ import {
1029
+ import { isRendererContext, registerClientRendererBridge } from "./renderer-bridge.js";
1030
+ import { defineRemovedContextMembers, registerContext } from "./context-identity.js";
1031
+ import { createNativeReadDriver } from "./signals/native-read-client.js";
1032
+ +import { registerLocalSignalHook } from "./signals/local-hook.js";
1033
+ import {
1034
+ validateNativeReadWitness
1035
+ } from "./signals/native-read-collector.js";
1036
+ @@ -7455,6 +7456,7 @@ function nativeLocalHook(name, initialize, dispose, slot) {
1037
+ }
1038
+ return cell.nativeValue;
1039
+ }
1040
+ +registerLocalSignalHook(() => CURRENT_SCOPE === null ? null : nativeLocalHook);
1041
+ function useState(initial, slot) {
1042
+ const s = readStateHook(initial, slot, arguments.length === 1);
1043
+ return [s.value, s.setter];
1044
+ diff --git a/dist/signals/client.d.ts b/dist/signals/client.d.ts
1045
+ index 6388c2f98517884a13af720687fb81c1f18357be..f9e98624f159cccf02a379d79201751fc07694d8 100644
1046
+ --- a/dist/signals/client.d.ts
1047
+ +++ b/dist/signals/client.d.ts
1048
+ @@ -1,4 +1,4 @@
1049
+ -/** Optional component hooks for the native-read DOM compiler mode. */
1050
+ +/** Optional component hooks for the native-read compiler mode (DOM and universal). */
1051
+ export * from './index.js';
1052
+ import type { WritableSignal } from './types.js';
1053
+ /** A writable signal owned by this compiler-assigned component hook slot. */
1054
+ diff --git a/dist/signals/client.js b/dist/signals/client.js
1055
+ index 21a39c3ce9a28d2095e276fd647fd795e939a0bf..3f85700b57cbc3084c2df776ab3e6b5365f091a8 100644
1056
+ --- a/dist/signals/client.js
1057
+ +++ b/dist/signals/client.js
1058
+ @@ -1,11 +1,11 @@
1059
+ export * from "./index.js";
1060
+ -import { nativeLocalHook } from "../runtime.js";
1061
+ import { createLocalScope } from "./engine.js";
1062
+ +import { localSignalHook } from "./local-hook.js";
1063
+ function disposeLocalSignal(cell) {
1064
+ cell.scope.dispose();
1065
+ }
1066
+ function useSignal$(initial, slot) {
1067
+ - const cell = nativeLocalHook(
1068
+ + const cell = localSignalHook(
1069
+ "useSignal$",
1070
+ () => {
1071
+ const value = typeof initial === "function" ? initial() : initial;
1072
+ diff --git a/dist/signals/document-owner.js b/dist/signals/document-owner.js
1073
+ index cd4e628f79d9fed839c089a89e711d446d2e18a4..078dcd31218022b69086238180e6b236e7d8224f 100644
1074
+ --- a/dist/signals/document-owner.js
1075
+ +++ b/dist/signals/document-owner.js
1076
+ @@ -26,7 +26,9 @@ function enableSignalDocument(abi = 1) {
1077
+ if (defaultInstalled)
1078
+ return;
1079
+ defaultInstalled = true;
1080
+ - installDefaultSignalOwner(() => typeof document === "undefined" ? null : documentSignalOwner(document));
1081
+ + if (typeof document === "undefined")
1082
+ + return;
1083
+ + installDefaultSignalOwner(() => documentSignalOwner(document));
1084
+ }
1085
+ function installStreamedSignalOwnerActivator(activate) {
1086
+ if (streamedSignalOwnerActivator !== void 0) {
1087
+ diff --git a/dist/signals/facade.js b/dist/signals/facade.js
1088
+ index 5ab4b73f2e8276b3ade5f04e8fa647efd76cb830..7b23010b7440b698ac4775b44cf16dc55625654d 100644
1089
+ --- a/dist/signals/facade.js
1090
+ +++ b/dist/signals/facade.js
1091
+ @@ -240,6 +240,8 @@ class Descriptor {
1092
+ this.kind = kind;
1093
+ this.create = create;
1094
+ this.site = site;
1095
+ + // Instance declarations stay with their component across child templates and events.
1096
+ + this.instanceOwner = site?.startsWith("i:") ? currentSignalOwner() : null;
1097
+ }
1098
+ key;
1099
+ kind;
1100
+ @@ -249,7 +251,7 @@ class Descriptor {
1101
+ cells = /* @__PURE__ */ new WeakMap();
1102
+ [SIGNAL_OWNER_RESOLVE](owner) {
1103
+ requireSite(this.site);
1104
+ - return this.resolvedCell(resolveDescriptorOwner(this.site, owner));
1105
+ + return this.resolvedCell(resolveDescriptorOwner(this.site, this.instanceOwner ?? owner));
1106
+ }
1107
+ resolvedCell(target) {
1108
+ let cell = this.cells.get(target);
1109
+ @@ -260,7 +262,7 @@ class Descriptor {
1110
+ return cell;
1111
+ }
1112
+ resolve() {
1113
+ - const token = requireOwner();
1114
+ + const token = this.instanceOwner ?? requireOwner();
1115
+ const owner = resolveDescriptorOwner(this.site, token);
1116
+ requireSite(this.site);
1117
+ return this.resolvedCell(owner);
1118
+ @@ -291,7 +293,7 @@ class Descriptor {
1119
+ return this.resolve().snapshot();
1120
+ }
1121
+ subscribe(notify) {
1122
+ - const token = requireOwner();
1123
+ + const token = this.instanceOwner ?? requireOwner();
1124
+ const scope = resolveDescriptorOwner(this.site, token);
1125
+ const run = captureSignalOwner(token);
1126
+ return this[SIGNAL_OWNER_RESOLVE](scope).subscribe(forwardNativeTransitionConsumer(notify, () => run(notify)));
1127
+ diff --git a/dist/signals/index.d.ts b/dist/signals/index.d.ts
1128
+ index 95f32a27af860c358714eeadfbdcbc18bc323d9d..98559855b07e84b61915dba198f357505d584175 100644
1129
+ --- a/dist/signals/index.d.ts
1130
+ +++ b/dist/signals/index.d.ts
1131
+ @@ -3,6 +3,7 @@ export { createScope } from './engine.js';
1132
+ export { bindSignalControl } from './control-binding.js';
1133
+ export type { SignalControlBinding } from './control-handoff.js';
1134
+ export { enableSignalDocument as __enableSignalDocument } from './document-owner.js';
1135
+ +export { enableUniversalSignalDocument as __enableUniversalSignalDocument } from './universal-owner.js';
1136
+ export { createResource, query } from './requests.js';
1137
+ export { ActionUncertainError, action$, isActionUncertain, optimistic$ } from './actions.js';
1138
+ export { __derivedAt, derived$ } from './derived-facade.js';
1139
+ diff --git a/dist/signals/index.js b/dist/signals/index.js
1140
+ index 8ae19e510a09e8144ae2ab66c0daf293a4a3f8f6..0bf09f5276e9e1a38ef67923b9cc2a441be2ce7f 100644
1141
+ --- a/dist/signals/index.js
1142
+ +++ b/dist/signals/index.js
1143
+ @@ -1,6 +1,7 @@
1144
+ import { createScope } from "./engine.js";
1145
+ import { bindSignalControl } from "./control-binding.js";
1146
+ import { enableSignalDocument } from "./document-owner.js";
1147
+ +import { enableUniversalSignalDocument } from "./universal-owner.js";
1148
+ import { createResource, query } from "./requests.js";
1149
+ import { ActionUncertainError, action$, isActionUncertain, optimistic$ } from "./actions.js";
1150
+ import { __derivedAt, derived$ } from "./derived-facade.js";
1151
+ @@ -58,6 +59,7 @@ export {
1152
+ __derivedAt,
1153
+ __derivedScalarAt,
1154
+ enableSignalDocument as __enableSignalDocument,
1155
+ + enableUniversalSignalDocument as __enableUniversalSignalDocument,
1156
+ __queryAt,
1157
+ __signalAt,
1158
+ __startSignalReads,
1159
+ diff --git a/dist/signals/local-hook.d.ts b/dist/signals/local-hook.d.ts
1160
+ new file mode 100644
1161
+ index 0000000000000000000000000000000000000000..d6c1770fc22b738e0e107b6616f60eaf676d853e
1162
+ --- /dev/null
1163
+ +++ b/dist/signals/local-hook.d.ts
1164
+ @@ -0,0 +1,16 @@
1165
+ +/**
1166
+ + * Renderer-provided slots for optional signal hooks.
1167
+ + *
1168
+ + * `useSignal$` allocates a component-local signal cell, but the cell itself
1169
+ + * lives in whichever renderer is currently rendering — DOM hook slots on the
1170
+ + * client/server, universal owner slots under `target: 'universal'` renderers.
1171
+ + * `octane/signals` cannot import either runtime (both import the signal
1172
+ + * engine), so each renderer registers a probe at module evaluation; the probe
1173
+ + * answers only while one of its own owners is executing, which also settles
1174
+ + * nesting (a universal boundary inside a DOM render probes last and wins).
1175
+ + */
1176
+ +export type LocalSignalHook = <T>(name: string, initialize: () => T, dispose: (value: T) => void, slot?: symbol | number) => T;
1177
+ +/** @internal Renderer runtimes register once, at module evaluation. */
1178
+ +export declare function registerLocalSignalHook(probe: () => LocalSignalHook | null): () => void;
1179
+ +/** @internal Dispatch to the innermost renderer that currently owns a render. */
1180
+ +export declare function localSignalHook<T>(name: string, initialize: () => T, dispose: (value: T) => void, slot?: symbol | number): T;
1181
+ diff --git a/dist/signals/local-hook.js b/dist/signals/local-hook.js
1182
+ new file mode 100644
1183
+ index 0000000000000000000000000000000000000000..77c63f5e2d931eb8dbd384ae027ee0f09935f9ce
1184
+ --- /dev/null
1185
+ +++ b/dist/signals/local-hook.js
1186
+ @@ -0,0 +1,19 @@
1187
+ +const probes = [];
1188
+ +function registerLocalSignalHook(probe) {
1189
+ + probes.push(probe);
1190
+ + return () => {
1191
+ + const index = probes.indexOf(probe);
1192
+ + if (index !== -1) probes.splice(index, 1);
1193
+ + };
1194
+ +}
1195
+ +function localSignalHook(name, initialize, dispose, slot) {
1196
+ + for (let index = probes.length - 1; index >= 0; index--) {
1197
+ + const hook = probes[index]();
1198
+ + if (hook !== null) return hook(name, initialize, dispose, slot);
1199
+ + }
1200
+ + throw new Error(`${name} requires an active Octane component render.`);
1201
+ +}
1202
+ +export {
1203
+ + localSignalHook,
1204
+ + registerLocalSignalHook
1205
+ +};
1206
+ diff --git a/dist/signals/universal-owner.d.ts b/dist/signals/universal-owner.d.ts
1207
+ new file mode 100644
1208
+ index 0000000000000000000000000000000000000000..0e57f3a79115c858355f863434c6e3423570067b
1209
+ --- /dev/null
1210
+ +++ b/dist/signals/universal-owner.d.ts
1211
+ @@ -0,0 +1,10 @@
1212
+ +import type { SignalOwner } from './types.js';
1213
+ +/**
1214
+ + * @internal A DOM runtime aliases the ambient owner so universal-boundary and
1215
+ + * DOM renders resolve the same document scope.
1216
+ + */
1217
+ +export declare function installUniversalSignalDocumentProvider(next: () => SignalOwner): void;
1218
+ +/** @internal Process-wide signal document for host-neutral renderers. */
1219
+ +export declare function universalSignalDocument(): SignalOwner;
1220
+ +/** @internal Compiler capability for ambient signal reads under universal renderers. */
1221
+ +export declare function enableUniversalSignalDocument(abi?: number): void;
1222
+ diff --git a/dist/signals/universal-owner.js b/dist/signals/universal-owner.js
1223
+ new file mode 100644
1224
+ index 0000000000000000000000000000000000000000..ff5eb43e8f0ddcadbcf8e861f42788b389b4d99b
1225
+ --- /dev/null
1226
+ +++ b/dist/signals/universal-owner.js
1227
+ @@ -0,0 +1,20 @@
1228
+ +import { installDefaultSignalOwner } from "./owner-context.js";
1229
+ +let universalOwner;
1230
+ +let provider;
1231
+ +function installUniversalSignalDocumentProvider(next) {
1232
+ + provider = next;
1233
+ +}
1234
+ +function universalSignalDocument() {
1235
+ + return provider?.() ?? (universalOwner ??= Object.freeze({ scopeKey: "octane:universal" }));
1236
+ +}
1237
+ +function enableUniversalSignalDocument(abi = 1) {
1238
+ + if (abi !== 1) {
1239
+ + throw new TypeError("Unsupported universal signal owner ABI.");
1240
+ + }
1241
+ + installDefaultSignalOwner(universalSignalDocument);
1242
+ +}
1243
+ +export {
1244
+ + enableUniversalSignalDocument,
1245
+ + installUniversalSignalDocumentProvider,
1246
+ + universalSignalDocument
1247
+ +};
1248
+ diff --git a/dist/universal-core.d.ts b/dist/universal-core.d.ts
1249
+ index 7e60feece0d3ded5f088bf4e4044516bef588663..3e4cfc19341496009be773b80562476ffd04f2d2 100644
1250
+ --- a/dist/universal-core.d.ts
1251
+ +++ b/dist/universal-core.d.ts
1252
+ @@ -764,9 +764,8 @@ export declare function lazy<C extends UniversalComponent<any>>(load: () => Prom
1253
+ default: C;
1254
+ } | C>): C;
1255
+ export declare function use<T>(usable: UniversalContext<T> | PromiseLike<T>): T;
1256
+ -export declare function useImperativeHandle<T>(ref: {
1257
+ - current: T | null;
1258
+ -} | ((value: T | null) => void) | null, create: () => T, deps?: readonly unknown[] | null, slot?: unknown): void;
1259
+ +type UniversalImperativeRef<T> = { current: T | null } | ((value: T | null) => void | (() => void)) | readonly UniversalImperativeRef<T>[] | null | undefined;
1260
+ +export declare function useImperativeHandle<T>(ref: UniversalImperativeRef<T>, create: () => T, deps?: readonly unknown[] | null, slot?: unknown): void;
1261
+ export declare function useEffectEvent<T extends (...args: any[]) => any>(fn: T, slot?: unknown): T;
1262
+ export declare function useDebugValue(): void;
1263
+ export declare function startTransition(fn: () => void | Promise<unknown>): void;
1264
+ @@ -817,3 +816,5 @@ export interface ObjectHostContainer {
1265
+ }
1266
+ export declare function createObjectContainer(renderer?: string): ObjectHostContainer;
1267
+ export declare function createObjectDriver(renderer?: string): UniversalHostDriver<ObjectHostContainer, ObjectHostInstance>;
1268
+ +
1269
+ +export declare const Children: { toArray(children: UniversalRenderable): UniversalRenderable[]; map(children: UniversalRenderable, fn: (child: UniversalRenderable, index: number) => UniversalRenderable): UniversalRenderable[]; };
1270
+ diff --git a/dist/universal-core.js b/dist/universal-core.js
1271
+ index 4dbe2f938d039d415b44821bcb3be520172bcada..735a017364cd23bdb8972a04401385f0bde99062 100644
1272
+ --- a/dist/universal-core.js
1273
+ +++ b/dist/universal-core.js
1274
+ @@ -17,6 +17,14 @@ import {
1275
+ } from "./profiling.js";
1276
+ import { getRendererHostFlusher } from "./renderer-bridge.js";
1277
+ import { resolveLazyDefaultProps } from "./shared-value-helpers.js";
1278
+ +import { universalSignalDocument } from "./signals/universal-owner.js";
1279
+ +import { registerLocalSignalHook } from "./signals/local-hook.js";
1280
+ +import { retireSignalOwnerIdentity, runWithSignalOwner } from "./signals/owner-context.js";
1281
+ +import {
1282
+ + beginNativeWriteGuard,
1283
+ + endNativeWriteGuard,
1284
+ + setNativeReadObserver
1285
+ +} from "./signals/read-protocol.js";
1286
+ import { __methodDep } from "./method-dep.js";
1287
+ const UNIVERSAL_PLAN = /* @__PURE__ */ Symbol.for("octane.universal.plan");
1288
+ const UNIVERSAL_VALUE = /* @__PURE__ */ Symbol.for("octane.universal.value");
1289
+ @@ -474,6 +482,133 @@ function universalComponent(renderer, component, props = null, key = NO_KEY) {
1290
+ hasKey: key !== NO_KEY || normalized.hasKey
1291
+ };
1292
+ }
1293
+ +const NORMALIZED_CHILD_KEYS = new WeakMap();
1294
+ +/** Normalize deferred JSX without mounting components or consuming their context.
1295
+ + * Structural owners (blocks, providers, boundaries and loops) remain renderables;
1296
+ + * only transparent child groups, plan ranges, slots and JS arrays are flattened.
1297
+ + */
1298
+ +function universalChildEntries(children) {
1299
+ + const out = [];
1300
+ + const collectionKey = (child) => {
1301
+ + const key = renderableKey(child);
1302
+ + if (key !== null)
1303
+ + return key;
1304
+ + if (child?.$$kind !== UNIVERSAL_VALUE)
1305
+ + return null;
1306
+ + const { plan, values } = child;
1307
+ + let node = plan.root;
1308
+ + while (node.kind === 'range' && node.children.length === 1)
1309
+ + node = node.children[0];
1310
+ + if (node.kind === 'slot')
1311
+ + return collectionKey(values[node.slot]);
1312
+ + if (node.kind !== 'host' && node.kind !== 'component')
1313
+ + return null;
1314
+ + if (node.kind === 'component' && node.keySlot !== undefined)
1315
+ + return normalizeUniversalKey(values[node.keySlot]);
1316
+ + return normalizeUniversalKey(node.propsSlot === undefined ? node.kind === 'host' ? node.props?.key : null : normalizePropsValue(values[node.propsSlot]).key);
1317
+ + };
1318
+ + const visit = (child, path) => {
1319
+ + if (child == null || typeof child === 'boolean')
1320
+ + return;
1321
+ + if (typeof child === 'object') {
1322
+ + const normalizedKey = NORMALIZED_CHILD_KEYS.get(child);
1323
+ + if (normalizedKey !== undefined) {
1324
+ + out.push({ child, key: normalizedKey });
1325
+ + return;
1326
+ + }
1327
+ + }
1328
+ + if (Array.isArray(child)) {
1329
+ + child.forEach((item, index) => {
1330
+ + const key = collectionKey(item);
1331
+ + visit(item, [...path, key === null ? 'index' : 'key', key ?? index]);
1332
+ + });
1333
+ + return;
1334
+ + }
1335
+ + if (child?.$$kind === UNIVERSAL_CHILDREN) {
1336
+ + const children = child;
1337
+ + if (CURRENT_OWNER !== null && children.renderer !== currentAttempt().root.renderer) {
1338
+ + throw new Error('Universal renderer mismatch while normalizing children.');
1339
+ + }
1340
+ + visit(children.render(), path);
1341
+ + return;
1342
+ + }
1343
+ + if (child?.$$kind === UNIVERSAL_LIST) {
1344
+ + const list = child;
1345
+ + visit(list.values.length === 0 ? list.empty : list.values, path);
1346
+ + return;
1347
+ + }
1348
+ + if (child?.$$kind === UNIVERSAL_VALUE) {
1349
+ + const value = child;
1350
+ + if (CURRENT_OWNER !== null && value.plan.renderer !== currentAttempt().root.renderer) {
1351
+ + throw new Error('Universal renderer mismatch while normalizing children.');
1352
+ + }
1353
+ + if (value.key !== null) {
1354
+ + out.push({ child, key: JSON.stringify([...path, 'key', value.key]) });
1355
+ + return;
1356
+ + }
1357
+ + const visitNode = (node, nodePath) => {
1358
+ + if (node.kind === 'range') {
1359
+ + node.children.forEach((item, index) => visitNode(item, [...nodePath, index]));
1360
+ + }
1361
+ + else if (node.kind === 'slot') {
1362
+ + visit(value.values[node.slot], nodePath);
1363
+ + }
1364
+ + else if (node.kind === 'text') {
1365
+ + visit((node.slot === undefined ? node.value : value.values[node.slot]), nodePath);
1366
+ + }
1367
+ + else {
1368
+ + const key = node.kind === 'component'
1369
+ + ? node.keySlot === undefined
1370
+ + ? node.propsSlot === undefined ? null : normalizePropsValue(value.values[node.propsSlot]).key
1371
+ + : value.values[node.keySlot]
1372
+ + : node.kind === 'host'
1373
+ + ? node.propsSlot === undefined ? node.props?.key ?? null : normalizePropsValue(value.values[node.propsSlot]).key
1374
+ + : null;
1375
+ + const item = universalValue(universalPlan(value.plan.renderer, node), value.values);
1376
+ + out.push({ child: item, key: JSON.stringify(key == null ? nodePath : [...(node === value.plan.root ? path : nodePath.slice(0, -1)), 'key', key]) });
1377
+ + }
1378
+ + };
1379
+ + visitNode(value.plan.root, path);
1380
+ + return;
1381
+ + }
1382
+ + const key = renderableKey(child);
1383
+ + out.push({ child, key: JSON.stringify(key === null ? path : [...path, 'key', key]) });
1384
+ + };
1385
+ + visit(children, []);
1386
+ + return out;
1387
+ +}
1388
+ +/** Renderer-supported child collection operations. They never render a component
1389
+ + * to discover its output. Returned entries retain their keys when mapped into
1390
+ + * wrapper hosts, and are materialized normally at their eventual placement.
1391
+ + */
1392
+ +function normalizedChild(key, child) {
1393
+ + const value = universalKey(key, child);
1394
+ + NORMALIZED_CHILD_KEYS.set(value, key);
1395
+ + return value;
1396
+ +}
1397
+ +const Children = {
1398
+ + toArray(children) {
1399
+ + return universalChildEntries(children).map(({ child, key }) => typeof child === 'object' ? normalizedChild(key, child) : child);
1400
+ + },
1401
+ + map(children, fn) {
1402
+ + const out = [];
1403
+ + universalChildEntries(children).forEach(({ child, key }, index) => {
1404
+ + const mapped = fn(child, index);
1405
+ + if (mapped == null || typeof mapped === 'boolean')
1406
+ + return;
1407
+ + if (Array.isArray(mapped)) {
1408
+ + for (const entry of universalChildEntries(mapped)) {
1409
+ + out.push(normalizedChild(JSON.stringify([key, entry.key]), entry.child));
1410
+ + }
1411
+ + }
1412
+ + else {
1413
+ + out.push(normalizedChild(key, mapped));
1414
+ + }
1415
+ + });
1416
+ + return out;
1417
+ + },
1418
+ +};
1419
+ +
1420
+ function universalChildren(renderer, render) {
1421
+ assertRendererId(renderer, "universalChildren renderer");
1422
+ if (typeof render !== "function") throw new TypeError("universalChildren expected a function.");
1423
+ @@ -842,6 +977,7 @@ function draftOwner(record, parent, replayPath) {
1424
+ boundaryThenable: record.boundaryThenable,
1425
+ isBoundary: record.isBoundary,
1426
+ canHandleSuspense: record.canHandleSuspense,
1427
+ + signalReads: null,
1428
+ visibility: parent?.visibility ?? record.visibility,
1429
+ contextStable: null
1430
+ };
1431
+ @@ -1012,6 +1148,7 @@ function executeOwner(owner, build, initialRenderCount = 0) {
1432
+ owner.contextStable = null;
1433
+ owner.needsRender = false;
1434
+ owner.implicitSlot = 0;
1435
+ + owner.signalReads = NO_SIGNAL_READS;
1436
+ const previousOwner = CURRENT_OWNER;
1437
+ const previousAttemptOwner = attempt.owner;
1438
+ CURRENT_OWNER = owner;
1439
+ @@ -1023,13 +1160,18 @@ function executeOwner(owner, build, initialRenderCount = 0) {
1440
+ const profileFrame = typeof __OCTANE_PROFILE_ENABLED__ !== "undefined" && __OCTANE_PROFILE_ENABLED__ && component !== null ? __profileBeginRender(owner.record, component, owner.record.mounted) : null;
1441
+ let didThrow = false;
1442
+ let thrown;
1443
+ + const signalOwner = universalSignalOwner(owner.record);
1444
+ + const previousReadObserver = setNativeReadObserver(observeUniversalSignalRead);
1445
+ + const writeGuard = beginNativeWriteGuard();
1446
+ try {
1447
+ - output = build();
1448
+ + output = runWithSignalOwner(signalOwner, build);
1449
+ } catch (error) {
1450
+ didThrow = true;
1451
+ - thrown = error;
1452
+ - throw error;
1453
+ + thrown = isThenable(error) ? new UniversalSuspense(error) : error;
1454
+ + throw thrown;
1455
+ } finally {
1456
+ + endNativeWriteGuard(writeGuard);
1457
+ + setNativeReadObserver(previousReadObserver);
1458
+ if (typeof __OCTANE_PROFILE_ENABLED__ !== "undefined" && __OCTANE_PROFILE_ENABLED__) {
1459
+ __profileEndRender(profileFrame, didThrow, thrown);
1460
+ }
1461
+ @@ -1196,6 +1338,8 @@ function disposeUncommittedDraft(owner) {
1462
+ owner.record.componentProps = null;
1463
+ owner.record.range = null;
1464
+ owner.record.updates.clear();
1465
+ + disposeUniversalSignalReads(owner.record);
1466
+ + disposeUniversalLocalHooks(owner.hooks);
1467
+ for (const hook of owner.hooks.values()) {
1468
+ if (hook.kind === "effect-event") hook.cell.active = false;
1469
+ }
1470
+ @@ -3142,6 +3286,85 @@ function scheduleOwner(owner, slot) {
1471
+ }
1472
+ owner.root.scheduleOwned(owner);
1473
+ }
1474
+ +const UNIVERSAL_SIGNAL_OWNERS = /* @__PURE__ */ new WeakMap();
1475
+ +function universalSignalOwner(record) {
1476
+ + let owner = UNIVERSAL_SIGNAL_OWNERS.get(record);
1477
+ + if (owner === void 0) {
1478
+ + const documentOwner = universalSignalDocument();
1479
+ + const identity = {
1480
+ + scopeKey: documentOwner.scopeKey,
1481
+ + documentOwner,
1482
+ + // The identity object itself is the opaque per-instance token — the
1483
+ + // same shape DOM scopes use, so identity maps never retain the record.
1484
+ + instanceOwner: null,
1485
+ + instanceKey: `u:${record.id}`
1486
+ + };
1487
+ + identity.instanceOwner = identity;
1488
+ + owner = Object.freeze(identity);
1489
+ + UNIVERSAL_SIGNAL_OWNERS.set(record, owner);
1490
+ + }
1491
+ + return owner;
1492
+ +}
1493
+ +const NO_SIGNAL_READS = /* @__PURE__ */ new Set();
1494
+ +function observeUniversalSignalRead(source) {
1495
+ + const owner = CURRENT_OWNER;
1496
+ + if (owner === null) return;
1497
+ + if (owner.signalReads === NO_SIGNAL_READS) owner.signalReads = /* @__PURE__ */ new Set();
1498
+ + owner.signalReads.add(source);
1499
+ +}
1500
+ +function commitUniversalSignalReads(record, reads) {
1501
+ + if (reads === null) return;
1502
+ + let subscriptions = record.signalSubscriptions;
1503
+ + if (subscriptions !== void 0) {
1504
+ + for (const [source, unsubscribe] of subscriptions) {
1505
+ + if (!reads.has(source)) {
1506
+ + subscriptions.delete(source);
1507
+ + unsubscribe();
1508
+ + }
1509
+ + }
1510
+ + }
1511
+ + for (const source of reads) {
1512
+ + if (subscriptions?.has(source)) continue;
1513
+ + if (subscriptions === void 0) {
1514
+ + subscriptions = /* @__PURE__ */ new Map();
1515
+ + record.signalSubscriptions = subscriptions;
1516
+ + }
1517
+ + subscriptions.set(
1518
+ + source,
1519
+ + source.subscribe(() => {
1520
+ + if (!record.disposed) scheduleOwner(record);
1521
+ + })
1522
+ + );
1523
+ + }
1524
+ + if (subscriptions !== void 0 && subscriptions.size === 0) {
1525
+ + record.signalSubscriptions = void 0;
1526
+ + }
1527
+ +}
1528
+ +function disposeUniversalSignalReads(record) {
1529
+ + const subscriptions = record.signalSubscriptions;
1530
+ + if (subscriptions !== void 0) {
1531
+ + record.signalSubscriptions = void 0;
1532
+ + for (const unsubscribe of subscriptions.values()) unsubscribe();
1533
+ + }
1534
+ + const owner = UNIVERSAL_SIGNAL_OWNERS.get(record);
1535
+ + if (owner !== void 0) retireSignalOwnerIdentity(owner);
1536
+ +}
1537
+ +function disposeUniversalLocalHooks(hooks) {
1538
+ + for (const hook of hooks.values()) {
1539
+ + if (hook.kind === "local") hook.dispose(hook.value);
1540
+ + }
1541
+ +}
1542
+ +function universalLocalHook(_name, initialize, dispose, slot) {
1543
+ + const owner = currentDraftOwner();
1544
+ + const resolved = resolveHookSlot(slot);
1545
+ + let cell = owner.hooks.get(resolved);
1546
+ + if (cell === void 0) {
1547
+ + cell = { kind: "local", value: initialize(), dispose };
1548
+ + owner.hooks.set(resolved, cell);
1549
+ + }
1550
+ + return cell.value;
1551
+ +}
1552
+ +registerLocalSignalHook(() => CURRENT_OWNER === null ? null : universalLocalHook);
1553
+ function currentDraftOwner() {
1554
+ currentAttempt();
1555
+ const owner = activateLazyLeafOwner();
1556
+ @@ -4142,15 +4365,35 @@ function use(usable) {
1557
+ function useImperativeHandle(ref, create, deps, slot) {
1558
+ useLayoutEffect(
1559
+ () => {
1560
+ - const value = create();
1561
+ - if (typeof ref === "function") ref(value);
1562
+ - else if (ref !== null) ref.current = value;
1563
+ - return () => {
1564
+ - if (typeof ref === "function") ref(null);
1565
+ - else if (ref !== null) ref.current = null;
1566
+ + const cleanups = [];
1567
+ + let value;
1568
+ + let initialized = false;
1569
+ + const attach = (target) => {
1570
+ + if (target == null) return;
1571
+ + if (Array.isArray(target)) {
1572
+ + for (const entry of target) attach(entry);
1573
+ + return;
1574
+ + }
1575
+ + if (!initialized) {
1576
+ + value = create();
1577
+ + initialized = true;
1578
+ + }
1579
+ + if (typeof target === "function") {
1580
+ + const cleanup = target(value);
1581
+ + cleanups.push(typeof cleanup === "function" ? cleanup : () => target(null));
1582
+ + } else {
1583
+ + target.current = value;
1584
+ + cleanups.push(() => { target.current = null; });
1585
+ + }
1586
+ };
1587
+ + try {
1588
+ + attach(ref);
1589
+ + } catch (error) {
1590
+ + try { runCommitTasks(cleanups); } finally { throw error; }
1591
+ + }
1592
+ + return () => runCommitTasks(cleanups);
1593
+ },
1594
+ - deps,
1595
+ + deps == null ? deps : [...deps, ref],
1596
+ slot
1597
+ );
1598
+ }
1599
+ @@ -5520,9 +5763,9 @@ class UniversalRootImpl {
1600
+ }
1601
+ let result;
1602
+ try {
1603
+ - result = event.handler(payload);
1604
+ + result = runWithSignalOwner(universalSignalOwner(event.owner), () => event.handler(payload));
1605
+ } catch (error) {
1606
+ - if (!routeUniversalOwnerError(event.owner, error)) throw error;
1607
+ + if (!routeUniversalOwnerError(event.owner, error) && !reportUniversalUncaughtError(this, error)) throw error;
1608
+ }
1609
+ return result;
1610
+ }
1611
+ @@ -6096,6 +6339,8 @@ class UniversalRootImpl {
1612
+ draft.record.componentProps = null;
1613
+ draft.record.range = null;
1614
+ draft.record.updates.clear();
1615
+ + disposeUniversalSignalReads(draft.record);
1616
+ + disposeUniversalLocalHooks(draft.hooks);
1617
+ for (const hook of draft.hooks.values()) {
1618
+ if (hook.kind === "effect-event") hook.cell.active = false;
1619
+ }
1620
+ @@ -6776,6 +7021,7 @@ class UniversalRootImpl {
1621
+ owner.componentProps = draftOwner2.componentProps;
1622
+ owner.componentRevision = draftOwner2.componentRevision;
1623
+ owner.hooks = draftOwner2.hooks;
1624
+ + commitUniversalSignalReads(owner, draftOwner2.signalReads);
1625
+ for (const [slot, applied] of draftOwner2.appliedUpdates) {
1626
+ const queue = owner.updates.get(slot);
1627
+ if (queue !== applied.queue || applied.lane) continue;
1628
+ @@ -6809,6 +7055,7 @@ class UniversalRootImpl {
1629
+ record.componentRevision = draft.componentRevision;
1630
+ record.parent = draft.parent?.record ?? null;
1631
+ record.hooks = draft.hooks;
1632
+ + commitUniversalSignalReads(record, draft.signalReads);
1633
+ record.effectOrder = [...draft.seenEffects];
1634
+ for (let index = 0; index < draft.seenEffects.length; index++) {
1635
+ draft.seenEffects[index].previous = null;
1636
+ @@ -8455,6 +8702,8 @@ class UniversalRootImpl {
1637
+ owner.componentProps = null;
1638
+ owner.range = null;
1639
+ owner.updates.clear();
1640
+ + disposeUniversalSignalReads(owner);
1641
+ + disposeUniversalLocalHooks(owner.hooks);
1642
+ }
1643
+ for (const draft of draftOwnersParentFirst) {
1644
+ const record = draft.record;
1645
+ @@ -8464,6 +8713,7 @@ class UniversalRootImpl {
1646
+ record.parent = draft.parent?.record ?? null;
1647
+ }
1648
+ record.hooks = draft.hooks;
1649
+ + commitUniversalSignalReads(record, draft.signalReads);
1650
+ record.effectOrder = [...draft.seenEffects];
1651
+ for (let index = 0; index < draft.seenEffects.length; index++) {
1652
+ draft.seenEffects[index].previous = null;
1653
+ @@ -8985,6 +9235,8 @@ class UniversalRootImpl {
1654
+ owner.componentProps = null;
1655
+ owner.range = null;
1656
+ owner.updates.clear();
1657
+ + disposeUniversalSignalReads(owner);
1658
+ + disposeUniversalLocalHooks(owner.hooks);
1659
+ }
1660
+ const deactivatedRegionCells = /* @__PURE__ */ new Set();
1661
+ for (const bridge of regionBridges) {
1662
+ @@ -9937,6 +10189,7 @@ function createObjectDriver(renderer = "object") {
1663
+ };
1664
+ }
1665
+ export {
1666
+ + Children,
1667
+ Activity,
1668
+ UNIVERSAL_HMR,
1669
+ UNIVERSAL_TRANSPORT_PROTOCOL_VERSION,
1670
+ diff --git a/dist/universal-native.js b/dist/universal-native.js
1671
+ index 55ac9b640dc8bbfd977bb2cbe0b7993c8ddb425a..3ff9a5750fb59cd91b63299aec3a3e0c030b4579 100644
1672
+ --- a/dist/universal-native.js
1673
+ +++ b/dist/universal-native.js
1674
+ @@ -6,9 +6,11 @@ import {
1675
+ import {
1676
+ universalContext
1677
+ } from "./universal-core.js";
1678
+ +import { enableUniversalSignalDocument } from "./signals/universal-owner.js";
1679
+ import { registerRendererContext, renderRendererContextProvider } from "./renderer-bridge.js";
1680
+ import { CONTEXT_TAG } from "./runtime-tags.js";
1681
+ import { registerContext } from "./context-identity.js";
1682
+ +enableUniversalSignalDocument();
1683
+ // @__NO_SIDE_EFFECTS__
1684
+ function createContext(defaultValue) {
1685
+ const context = ((props, scope) => scope === void 0 ? universalContext(context, props.value, props.children) : renderRendererContextProvider(context, props, scope));
1686
+ diff --git a/dist/universal.js b/dist/universal.js
1687
+ index 6427a0190c072e7e4629c379e42bedc03669dc6e..8049b7880824de607157091004a46c9d33a23484 100644
1688
+ --- a/dist/universal.js
1689
+ +++ b/dist/universal.js
1690
+ @@ -8,6 +8,9 @@ import {
1691
+ createUniversalHostBoundary,
1692
+ registerUniversalHostBridge
1693
+ } from "./universal-dom-boundary.js";
1694
+ +import { documentSignalOwner } from "./signals/document-owner.js";
1695
+ +import { installUniversalSignalDocumentProvider } from "./signals/universal-owner.js";
1696
+ +installUniversalSignalDocumentProvider(() => documentSignalOwner(document));
1697
+ export {
1698
+ createContext,
1699
+ createSubSlot,