@jundle/cloudflare 0.0.1-13 → 0.0.1-15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/harness.js +71 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jundle/cloudflare",
3
- "version": "0.0.1-13",
3
+ "version": "0.0.1-15",
4
4
  "description": "Cloudflare Workers runtime for the jundle dev server (workerd via Miniflare).",
5
5
  "license": "ISC",
6
6
  "type": "module",
package/src/harness.js CHANGED
@@ -146,8 +146,10 @@ async function fetchJson(url) {
146
146
 
147
147
  // Pull-based invalidation (SPEC §16.11): one loopback fetch per
148
148
  // dispatch. Evicts invalidated dev-module keys (evaluated +
149
- // text) and JSON values for edited JSON files, then advances
150
- // the seen version. The dev server prunes acked batches.
149
+ // text) and JSON values for edited JSON files, and rebuilt
150
+ // dep files (dep text + evaluated deps, plus every app
151
+ // module — SPEC §16.5), then advances the seen version.
152
+ // The dev server prunes acked batches.
151
153
  // After eviction, pushes fresh SSR-variant instances for the
152
154
  // browser-env subset (see pushSsrUpdates) before the entry
153
155
  // re-imports, so the entry's decode already sees them.
@@ -168,6 +170,37 @@ async function syncVersion() {
168
170
  if (k.split("\0")[1] === file) assetValueCache.delete(k);
169
171
  }
170
172
  }
173
+ // Dep invalidation (SPEC §16.5, §16.11): an on-demand
174
+ // unification run rebuilt existing dep bundles (same
175
+ // URL, new bytes) or remapped existing specs (the old
176
+ // files are dead). Evict the changed dep text —
177
+ // bundle-dir units share the entry's `file\0rel`
178
+ // prefix — then clear every evaluated module, app and
179
+ // dep alike (mirrors NodeRunner.invalidateAll).
180
+ // Evaluated app modules may hold the old dep exports,
181
+ // the closure walk prunes cached subtrees, and dep
182
+ // modules hold state coupled to the app modules (the
183
+ // Flight client's chunk map assumes the shim's cache
184
+ // outlives it — clearing the shim without the client
185
+ // makes the client require a chunk the fresh shim
186
+ // never loaded). Only the TEXT eviction is targeted:
187
+ // unchanged dep text is still valid input, so it
188
+ // stays cached and no re-fetch is needed for it. The
189
+ // fresh text is fetched lazily by the next entry
190
+ // import, not eagerly here: a re-fetch failure after
191
+ // the version advanced would leave the dep
192
+ // permanently broken until restart.
193
+ const depFiles = data.depFiles || [];
194
+ if (depFiles.length > 0) {
195
+ for (const file of depFiles) {
196
+ for (const k of depText.keys()) {
197
+ if (k === file || k.startsWith(`${file}\0`)) depText.delete(k);
198
+ }
199
+ }
200
+ evaluated.clear();
201
+ textCache.clear();
202
+ depEvaluated.clear();
203
+ }
171
204
  await pushSsrUpdates(data.clientInvalidated || []);
172
205
  }
173
206
 
@@ -187,16 +220,50 @@ async function syncVersion() {
187
220
  // document in a worker), and surfacing it would trip the
188
221
  // hot client's first-update overlay rule and force a full
189
222
  // reload on the next update instead of HMR.
223
+ //
224
+ // The re-import can install a second (browser) shim over
225
+ // the dev shim's globals — client.browser.js imports
226
+ // ./shim.js first, so any module importing the client
227
+ // runtime installs it as a side effect — and the
228
+ // client's chunkCache skips re-loading chunks, so a
229
+ // poisoned global makes the next render require through
230
+ // an empty shim ("has not been loaded yet"). Snapshot the
231
+ // four shim globals before the re-import and restore
232
+ // them before the push lands (finally, so a failed
233
+ // import cannot leave them poisoned). A snapshot of
234
+ // undefined restores by deleting the global, not by
235
+ // assigning undefined (the shim was not installed yet).
236
+ const SHIM_GLOBALS = [
237
+ "__webpack_require__",
238
+ "__webpack_chunk_load__",
239
+ "__webpack_get_script_filename__",
240
+ "__jundleFlight",
241
+ ];
242
+ function snapshotShimGlobals() {
243
+ return SHIM_GLOBALS.map((name) => [name, globalThis[name]]);
244
+ }
245
+ function restoreShimGlobals(saved) {
246
+ for (const [name, value] of saved) {
247
+ if (value === undefined) delete globalThis[name];
248
+ else globalThis[name] = value;
249
+ }
250
+ }
190
251
  async function pushSsrUpdates(clientKeys) {
191
252
  for (const key of clientKeys) {
192
253
  const [env, file] = key.split("\0");
193
254
  const url = `/@jundle/module?env=${encodeURIComponent(env)}&file=${encodeURIComponent(file)}`;
255
+ const saved = snapshotShimGlobals();
256
+ let rec;
257
+ let ok = false;
194
258
  try {
195
- const rec = await ensureModuleByUrl(url);
196
- globalThis.__jundleFlight?.updateModule?.(url, rec.exports);
259
+ rec = await ensureModuleByUrl(url);
260
+ ok = true;
197
261
  } catch (e) {
198
262
  console.error(`[jundle runner] SSR push ${url}: ${e && e.message ? e.message : String(e)}`);
263
+ } finally {
264
+ restoreShimGlobals(saved);
199
265
  }
266
+ if (ok) globalThis.__jundleFlight?.updateModule?.(url, rec.exports);
200
267
  }
201
268
  }
202
269