@noego/wood 0.7.3 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/wood.js +9 -1
- package/dist/codegen/renderer_app_generator.cjs +14 -13
- package/dist/codegen/renderer_app_generator.cjs.map +1 -1
- package/dist/codegen/renderer_app_generator.js +14 -13
- package/dist/codegen/renderer_app_generator.js.map +1 -1
- package/dist/navigation/index.cjs +14 -0
- package/dist/navigation/index.cjs.map +1 -1
- package/dist/navigation/index.d.cts +3 -1
- package/dist/navigation/index.d.ts +3 -1
- package/dist/navigation/index.js +13 -0
- package/dist/navigation/index.js.map +1 -1
- package/dist/testing/native_browser/happy_dom_tab.cjs +3 -2
- package/dist/testing/native_browser/happy_dom_tab.cjs.map +1 -1
- package/dist/testing/native_browser/happy_dom_tab.js +3 -2
- package/dist/testing/native_browser/happy_dom_tab.js.map +1 -1
- package/dist/testing/native_browser/host_transport.cjs +18 -8
- package/dist/testing/native_browser/host_transport.cjs.map +1 -1
- package/dist/testing/native_browser/host_transport.d.cts +3 -3
- package/dist/testing/native_browser/host_transport.d.ts +3 -3
- package/dist/testing/native_browser/host_transport.js +18 -8
- package/dist/testing/native_browser/host_transport.js.map +1 -1
- package/dist/testing/native_browser/playwright_tab.cjs +4 -6
- package/dist/testing/native_browser/playwright_tab.cjs.map +1 -1
- package/dist/testing/native_browser/playwright_tab.js +4 -6
- package/dist/testing/native_browser/playwright_tab.js.map +1 -1
- package/dist/testing/native_browser/renderer_entry.cjs +5 -2
- package/dist/testing/native_browser/renderer_entry.cjs.map +1 -1
- package/dist/testing/native_browser/renderer_entry.js +5 -2
- package/dist/testing/native_browser/renderer_entry.js.map +1 -1
- package/docs/native-browser-testing.md +11 -0
- package/package.json +1 -1
|
@@ -39,14 +39,14 @@ const transport = {
|
|
|
39
39
|
if (!listeners) {
|
|
40
40
|
listeners = new Set();
|
|
41
41
|
callbacks.set(channel, listeners);
|
|
42
|
-
track(subscriptionChanges, window.__woodNativeSubscribe(channel, true));
|
|
42
|
+
track(subscriptionChanges, window.__woodNativeSubscribe(channel, true, window.__woodNativeDocumentId));
|
|
43
43
|
}
|
|
44
44
|
listeners.add(callback);
|
|
45
45
|
return () => {
|
|
46
46
|
listeners.delete(callback);
|
|
47
47
|
if (listeners.size === 0) {
|
|
48
48
|
callbacks.delete(channel);
|
|
49
|
-
track(subscriptionChanges, window.__woodNativeSubscribe(channel, false));
|
|
49
|
+
track(subscriptionChanges, window.__woodNativeSubscribe(channel, false, window.__woodNativeDocumentId));
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
},
|
|
@@ -91,6 +91,9 @@ window.__woodNativeDispose = () => closing ??= (async () => {
|
|
|
91
91
|
}
|
|
92
92
|
})();
|
|
93
93
|
window.__woodNativeReady = (async () => {
|
|
94
|
+
// A new document owns a fresh callback map. Same-document navigation keeps
|
|
95
|
+
// the existing callbacks and must not reset their host subscriptions.
|
|
96
|
+
window.__woodNativeDocumentId = await window.__woodNativeResetSubscriptions();
|
|
94
97
|
const container = getContainer();
|
|
95
98
|
if (configureRenderer) await configureRenderer(container);
|
|
96
99
|
const config = await container.get(TraceProviderConfiguration);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/testing/native_browser/renderer_entry.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport path from 'node:path';\nimport type { WoodConfigSource } from '../wood_config_source.js';\n\n/**\n * Identical renderer bootstrap for Chromium and happy-dom's isolated VM realm.\n * Production codegen owns controllers, layouts, data loaders and navigation.\n * Only the host transport differs. No components or Node globals are faked.\n */\nexport function nativeRendererEntry(source: WoodConfigSource): string {\n const operations = [...source.operations];\n const reflection = createRequire(path.join(source.configDir, 'package.json')).resolve('reflect-metadata');\n const rendererCompose = source.hookModule(source.config.renderer.compose);\n const composeImport = rendererCompose\n ? `import configureRenderer from ${JSON.stringify(rendererCompose)};`\n : 'const configureRenderer = null;';\n return `import ${JSON.stringify(reflection)};\nimport { flushSync, tick, unmount } from 'svelte';\nimport { buildTransportBridge } from '@noego/wood/transport';\nimport { getContainer, TraceProviderConfiguration, flushRendererLogs } from '@noego/wood/client';\nimport * as generated from './app.ts';\n${composeImport}\n\nconst route = window.__woodNativeRoute;\nconst callbacks = new Map();\nconst subscriptionChanges = new Set();\nconst requests = new Set();\nlet mounted;\nlet closing;\nfunction track(set, task) {\n const promise = Promise.resolve(task);\n set.add(promise);\n promise.then(() => set.delete(promise), () => set.delete(promise));\n return promise;\n}\nasync function subscriptionsReady() {\n while (subscriptionChanges.size) await Promise.all([...subscriptionChanges]);\n}\nconst transport = {\n invoke(channel, payload) {\n return track(requests, (async () => {\n await subscriptionsReady();\n return window.__woodNativeInvoke(channel, payload === undefined ? {} : payload);\n })());\n },\n subscribe(channel, callback) {\n let listeners = callbacks.get(channel);\n if (!listeners) {\n listeners = new Set();\n callbacks.set(channel, listeners);\n track(subscriptionChanges, window.__woodNativeSubscribe(channel, true));\n }\n listeners.add(callback);\n return () => {\n listeners.delete(callback);\n if (listeners.size === 0) {\n callbacks.delete(channel);\n track(subscriptionChanges, window.__woodNativeSubscribe(channel, false));\n }\n };\n },\n};\nwindow.__woodNativeEmit = (channel, payload) => {\n for (const callback of callbacks.get(channel) ?? []) callback(payload);\n};\nconst bridge = buildTransportBridge(new Map(${JSON.stringify(operations)}), transport).bridge;\nbridge.__window = { current: async () => ({ok: true, windowId: route.page.split('.')[0], defaultRoute: route.page}) };\nbridge.__woodConfig = async () => ({trace: {enabled: true, minLevel: 'debug'}});\nbridge.__trace = payload => track(requests, window.__woodNativeTrace(payload));\n// These are preload infrastructure channels, never product operation replacements.\nbridge.__logBatch = batch => track(requests, window.__woodNativeLog(batch));\nwindow.__NOEGO_WOOD_BRIDGE__ = bridge;\n\nwindow.__woodNativeFlush = async () => {\n await tick();\n flushSync();\n await subscriptionsReady();\n flushRendererLogs();\n while (requests.size) await Promise.all([...requests]);\n await tick();\n flushSync();\n};\nwindow.__woodNativeDispose = () => closing ??= (async () => {\n const errors = [];\n const attempt = async fn => {try {await fn();} catch (error) {errors.push(error);}};\n await attempt(() => mounted?.disposeNavigation?.());\n await attempt(() => mounted && unmount(mounted));\n await attempt(() => window.__woodNativeFlush());\n await attempt(() => getContainer().dispose());\n callbacks.clear();\n if (errors.length) {\n const describe = (error, depth = 0) => {\n const message = error?.message ?? String(error);\n const nested = error?.errors ?? error?.failures?.map(failure => failure.error);\n return depth < 4 && Array.isArray(nested)\n ? message + ': ' + nested.map(value => describe(value, depth + 1)).join('; ')\n : message;\n };\n throw new AggregateError(errors, 'Native Wood renderer teardown failed: ' + errors.map(error => describe(error)).join('; '));\n }\n})();\nwindow.__woodNativeReady = (async () => {\n const container = getContainer();\n if (configureRenderer) await configureRenderer(container);\n const config = await container.get(TraceProviderConfiguration);\n config.configureRuntime('renderer');\n config.configureEnabled(true);\n config.configureMinLevel('debug');\n mounted = await generated.app(document.getElementById('app'), {params: route.params, query: route.query});\n await tick();\n flushSync();\n // Subscribe before open() resolves: do not invent event replay to hide a race.\n await subscriptionsReady();\n})();\n// A host always awaits readiness. Attach a handler now so an immediate mount\n// failure cannot become an unhandled rejection before the host reads it.\nwindow.__woodNativeReady.catch(() => {});\n`;\n}\n"],"mappings":"AAAA,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AAQV,SAAS,oBAAoB,QAAkC;AACpE,QAAM,aAAa,CAAC,GAAG,OAAO,UAAU;AACxC,QAAM,aAAa,cAAc,KAAK,KAAK,OAAO,WAAW,cAAc,CAAC,EAAE,QAAQ,kBAAkB;AACxG,QAAM,kBAAkB,OAAO,WAAW,OAAO,OAAO,SAAS,OAAO;AACxE,QAAM,gBAAgB,kBAClB,iCAAiC,KAAK,UAAU,eAAe,CAAC,MAChE;AACJ,SAAO,UAAU,KAAK,UAAU,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAK3C,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8CA4C+B,KAAK,UAAU,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../../../src/testing/native_browser/renderer_entry.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport path from 'node:path';\nimport type { WoodConfigSource } from '../wood_config_source.js';\n\n/**\n * Identical renderer bootstrap for Chromium and happy-dom's isolated VM realm.\n * Production codegen owns controllers, layouts, data loaders and navigation.\n * Only the host transport differs. No components or Node globals are faked.\n */\nexport function nativeRendererEntry(source: WoodConfigSource): string {\n const operations = [...source.operations];\n const reflection = createRequire(path.join(source.configDir, 'package.json')).resolve('reflect-metadata');\n const rendererCompose = source.hookModule(source.config.renderer.compose);\n const composeImport = rendererCompose\n ? `import configureRenderer from ${JSON.stringify(rendererCompose)};`\n : 'const configureRenderer = null;';\n return `import ${JSON.stringify(reflection)};\nimport { flushSync, tick, unmount } from 'svelte';\nimport { buildTransportBridge } from '@noego/wood/transport';\nimport { getContainer, TraceProviderConfiguration, flushRendererLogs } from '@noego/wood/client';\nimport * as generated from './app.ts';\n${composeImport}\n\nconst route = window.__woodNativeRoute;\nconst callbacks = new Map();\nconst subscriptionChanges = new Set();\nconst requests = new Set();\nlet mounted;\nlet closing;\nfunction track(set, task) {\n const promise = Promise.resolve(task);\n set.add(promise);\n promise.then(() => set.delete(promise), () => set.delete(promise));\n return promise;\n}\nasync function subscriptionsReady() {\n while (subscriptionChanges.size) await Promise.all([...subscriptionChanges]);\n}\nconst transport = {\n invoke(channel, payload) {\n return track(requests, (async () => {\n await subscriptionsReady();\n return window.__woodNativeInvoke(channel, payload === undefined ? {} : payload);\n })());\n },\n subscribe(channel, callback) {\n let listeners = callbacks.get(channel);\n if (!listeners) {\n listeners = new Set();\n callbacks.set(channel, listeners);\n track(subscriptionChanges, window.__woodNativeSubscribe(channel, true, window.__woodNativeDocumentId));\n }\n listeners.add(callback);\n return () => {\n listeners.delete(callback);\n if (listeners.size === 0) {\n callbacks.delete(channel);\n track(subscriptionChanges, window.__woodNativeSubscribe(channel, false, window.__woodNativeDocumentId));\n }\n };\n },\n};\nwindow.__woodNativeEmit = (channel, payload) => {\n for (const callback of callbacks.get(channel) ?? []) callback(payload);\n};\nconst bridge = buildTransportBridge(new Map(${JSON.stringify(operations)}), transport).bridge;\nbridge.__window = { current: async () => ({ok: true, windowId: route.page.split('.')[0], defaultRoute: route.page}) };\nbridge.__woodConfig = async () => ({trace: {enabled: true, minLevel: 'debug'}});\nbridge.__trace = payload => track(requests, window.__woodNativeTrace(payload));\n// These are preload infrastructure channels, never product operation replacements.\nbridge.__logBatch = batch => track(requests, window.__woodNativeLog(batch));\nwindow.__NOEGO_WOOD_BRIDGE__ = bridge;\n\nwindow.__woodNativeFlush = async () => {\n await tick();\n flushSync();\n await subscriptionsReady();\n flushRendererLogs();\n while (requests.size) await Promise.all([...requests]);\n await tick();\n flushSync();\n};\nwindow.__woodNativeDispose = () => closing ??= (async () => {\n const errors = [];\n const attempt = async fn => {try {await fn();} catch (error) {errors.push(error);}};\n await attempt(() => mounted?.disposeNavigation?.());\n await attempt(() => mounted && unmount(mounted));\n await attempt(() => window.__woodNativeFlush());\n await attempt(() => getContainer().dispose());\n callbacks.clear();\n if (errors.length) {\n const describe = (error, depth = 0) => {\n const message = error?.message ?? String(error);\n const nested = error?.errors ?? error?.failures?.map(failure => failure.error);\n return depth < 4 && Array.isArray(nested)\n ? message + ': ' + nested.map(value => describe(value, depth + 1)).join('; ')\n : message;\n };\n throw new AggregateError(errors, 'Native Wood renderer teardown failed: ' + errors.map(error => describe(error)).join('; '));\n }\n})();\nwindow.__woodNativeReady = (async () => {\n // A new document owns a fresh callback map. Same-document navigation keeps\n // the existing callbacks and must not reset their host subscriptions.\n window.__woodNativeDocumentId = await window.__woodNativeResetSubscriptions();\n const container = getContainer();\n if (configureRenderer) await configureRenderer(container);\n const config = await container.get(TraceProviderConfiguration);\n config.configureRuntime('renderer');\n config.configureEnabled(true);\n config.configureMinLevel('debug');\n mounted = await generated.app(document.getElementById('app'), {params: route.params, query: route.query});\n await tick();\n flushSync();\n // Subscribe before open() resolves: do not invent event replay to hide a race.\n await subscriptionsReady();\n})();\n// A host always awaits readiness. Attach a handler now so an immediate mount\n// failure cannot become an unhandled rejection before the host reads it.\nwindow.__woodNativeReady.catch(() => {});\n`;\n}\n"],"mappings":"AAAA,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AAQV,SAAS,oBAAoB,QAAkC;AACpE,QAAM,aAAa,CAAC,GAAG,OAAO,UAAU;AACxC,QAAM,aAAa,cAAc,KAAK,KAAK,OAAO,WAAW,cAAc,CAAC,EAAE,QAAQ,kBAAkB;AACxG,QAAM,kBAAkB,OAAO,WAAW,OAAO,OAAO,SAAS,OAAO;AACxE,QAAM,gBAAgB,kBAClB,iCAAiC,KAAK,UAAU,eAAe,CAAC,MAChE;AACJ,SAAO,UAAU,KAAK,UAAU,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAK3C,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8CA4C+B,KAAK,UAAU,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwDxE;","names":[]}
|
|
@@ -132,6 +132,17 @@ its durable commit barrier. There is no pretend durable batch acknowledgement.
|
|
|
132
132
|
Building records application composition without opening a tab or Window.
|
|
133
133
|
First `open()` compiles the selected renderer and acquires the host. Native
|
|
134
134
|
Playwright reload remounts the renderer without duplicating subscriptions.
|
|
135
|
+
Same-document history navigation preserves live subscriptions; a new renderer
|
|
136
|
+
document resets the previous host subscriptions before mounting. Each delivery
|
|
137
|
+
is bound to that document; a queued old payload cannot replay into the new one.
|
|
138
|
+
Only Playwright's exact context-destroyed navigation error from a proven retired
|
|
139
|
+
document is discarded. Current-document and renderer callback failures remain
|
|
140
|
+
visible during settlement.
|
|
141
|
+
|
|
142
|
+
Production renderer bootstrap restores the current marked browser-history page,
|
|
143
|
+
params and query after a reload. It validates string-valued route records and
|
|
144
|
+
requires the page to exist in the current window's generated manifest. Invalid,
|
|
145
|
+
unmarked or foreign-window history falls back to the host's bootstrap route.
|
|
135
146
|
Closing an environment fences new opens, joins admitted opens, attempts all
|
|
136
147
|
cleanup steps, and reports cleanup failures without abandoning other resources.
|
|
137
148
|
Page mount errors include the route and browser diagnostics.
|