@webjsdev/core 0.7.20 → 0.7.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webjsdev/core",
3
- "version": "0.7.20",
3
+ "version": "0.7.21",
4
4
  "type": "module",
5
5
  "description": "webjs core runtime - html/css tags, WebComponent base, isomorphic renderers",
6
6
  "types": "./index.d.ts",
@@ -361,11 +361,15 @@ const SSR_HTMLELEMENT_METHODS = new Set([
361
361
  */
362
362
  function browserMemberHint(e) {
363
363
  const msg = e && typeof (/** @type any */ (e).message) === 'string' ? /** @type any */ (e).message : '';
364
- let m = /^(\w+) is not defined$/.exec(msg);
364
+ // Match on a word boundary, NOT end-of-string: V8 (Node) ends the message at
365
+ // "is not defined" / "is not a function", but JSC (Bun) appends a detail
366
+ // clause (e.g. ". (In '({}).querySelector(\"p\")', '...' is undefined)"), so an
367
+ // anchored `$` would miss the Bun message and drop the actionable hint.
368
+ let m = /^(\w+) is not defined\b/.exec(msg);
365
369
  if (e instanceof ReferenceError && m && SSR_BROWSER_GLOBALS.has(m[1])) {
366
370
  return `\`${m[1]}\` is a browser-only global and is undefined during SSR.`;
367
371
  }
368
- m = /\.(\w+) is not a function$/.exec(msg);
372
+ m = /\.(\w+) is not a function\b/.exec(msg);
369
373
  if (e instanceof TypeError && m && SSR_HTMLELEMENT_METHODS.has(m[1])) {
370
374
  return `\`${m[1]}\` is an HTMLElement method that does not exist on the server-side component instance during SSR.`;
371
375
  }
package/src/serialize.js CHANGED
@@ -142,6 +142,12 @@ function newEncodeCtx() {
142
142
  emitted: new Set(), // ids already emitted as the canonical encoding
143
143
  nextId: 0,
144
144
  blobBytes: new Map(), // Blob → Uint8Array (precomputed during countRefs)
145
+ // FormData → its entries captured ONCE. Bun returns a FRESH Blob/File
146
+ // identity on every FormData.entries()/get() call (Node returns a stable
147
+ // one), so countRefs and encode must iterate the SAME captured array or the
148
+ // identity-keyed blobBytes lookup misses on Bun (crash). Captured here so
149
+ // both passes see the same Blob objects on every runtime.
150
+ fdEntries: new Map(),
145
151
  };
146
152
  }
147
153
 
@@ -164,7 +170,11 @@ async function countRefs(value, ctx) {
164
170
  continue;
165
171
  }
166
172
  if (typeof FormData !== 'undefined' && v instanceof FormData) {
167
- for (const [, val] of v.entries()) {
173
+ // Capture entries ONCE (see fdEntries note) and traverse THOSE Blob
174
+ // objects, so encode reuses the exact instances whose bytes we precompute.
175
+ const captured = ctx.fdEntries.get(v) || [...v.entries()];
176
+ ctx.fdEntries.set(v, captured);
177
+ for (const [, val] of captured) {
168
178
  if (typeof val !== 'string') stack.push(val);
169
179
  }
170
180
  continue;
@@ -233,8 +243,12 @@ function encodeOne(v, ctx) {
233
243
  out = { [TAG]: 'Blob', v: bytesToB64(bytes), t: v.type || '' };
234
244
  }
235
245
  } else if (typeof FormData !== 'undefined' && v instanceof FormData) {
246
+ // Reuse the entries captured in countRefs so the Blob/File instances match
247
+ // the ones whose bytes were precomputed (Bun yields fresh identities per
248
+ // entries() call; see the fdEntries note in newEncodeCtx).
249
+ const captured = ctx.fdEntries.get(v) || [...v.entries()];
236
250
  const entries = [];
237
- for (const [k, val] of v.entries()) entries.push([k, encodeOne(val, ctx)]);
251
+ for (const [k, val] of captured) entries.push([k, encodeOne(val, ctx)]);
238
252
  out = { [TAG]: 'FD', v: entries };
239
253
  } else if (ArrayBuffer.isView(v) || v instanceof ArrayBuffer) {
240
254
  out = encodeBinary(v);