@barefootjs/hono 0.31.6 → 0.31.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"hono-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/hono-adapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,SAAS,EACd,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,MAAM,EAIX,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAElB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,UAAU,EAEf,UAAU,EAQX,MAAM,iBAAiB,CAAA;AAExB;;;;;GAKG;AACH,KAAK,aAAa,GAAG;IACnB,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAGD,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAEzB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAEzB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAuBD,qBAAa,WAAY,SAAQ,UAAW,YAAW,aAAa,CAAC,aAAa,CAAC;IACjF,IAAI,SAAS;IACb,SAAS,SAAS;IAClB,gBAAgB,SAAiC;IAmBjD,mBAAmB,QAAO,OAAO,CAAQ;IAEzC,SAAS,CAAC,SAAS,EAAE,gBAAgB,CAA0B;IAE/D,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,iBAAiB,CAAiB;IAC1C,OAAO,CAAC,sBAAsB,CAAiB;IAC/C,OAAO,CAAC,wBAAwB,CAAiB;IACjD;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB,CAAC,CAAgC;IAC9D,uFAAuF;IACvF,OAAO,CAAC,YAAY,CAAmD;IACvE;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,YAAY,CAAC,CAAU;IAC/B;;;;;;OAMG;IACH,OAAO,CAAC,aAAa,CAAC,CAAU;IAEhC,YAAY,OAAO,GAAE,kBAAuB,EAQ3C;IAED,QAAQ,CAAC,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,aAAa,CAiEzE;IAED;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;IAIvB;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,eAAe;IA4EvB;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAmB5C;IAED,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,iBAAiB;IA+QzB;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,aAAa,GAAG,MAAM,CAEpD;IAMD,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAEzF;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7B;IAED,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAEzC;IAED,eAAe,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAEjG;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAEpF;IAED,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAE7F;IAED,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAE5F;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9B;IAED,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAKnG;IAED,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAwB5F;IAED,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,MAAM,CAEtF;IAED,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM,CA2C5E;IAED,OAAO,CAAC,UAAU;IAUlB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CA2B3C;IAED,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,aAAa,GAAG,MAAM,CAOlE;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,OAAO,CAAC,qBAAqB;IAiC7B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,kBAAkB;IAsC1B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiH/B;IAED,OAAO,CAAC,oBAAoB;IAI5B;;;OAGG;IACH,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE;QAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM,CAkE5F;IAEQ,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAgB1C;IAED,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE;QAAE,uBAAuB,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM,CAkDhH;IAED,OAAO,CAAC,cAAc;IAQtB;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAY5B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAuBlC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAiBpC;IAED,OAAO,CAAC,gBAAgB;IAiCxB,OAAO,CAAC,oBAAoB;IAyB5B,OAAO,CAAC,iBAAiB;IAYzB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,0BAA0B;CAInC;AAGD,eAAO,MAAM,WAAW,aAAoB,CAAA"}
1
+ {"version":3,"file":"hono-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/hono-adapter.ts"],"names":[],"mappings":"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"}
@@ -95,7 +95,7 @@ export default ${this.componentName}` : "";
95
95
  generateImports(ir, componentCode) {
96
96
  const lines = [];
97
97
  const utilImports = [];
98
- for (const util of ["bfComment", "bfText", "bfTextEnd"]) {
98
+ for (const util of ["bfComment", "bfText", "bfTextEnd", "serializeHydrationProps"]) {
99
99
  if (new RegExp(`\\b${util}\\b`).test(componentCode)) {
100
100
  utilImports.push(util);
101
101
  }
@@ -261,7 +261,7 @@ export default ${this.componentName}` : "";
261
261
  const callerKey = p.sourceName ?? p.name;
262
262
  lines.push(` if (typeof ${propAccess} !== 'function' && !(typeof ${propAccess} === 'object' && ${propAccess} !== null && 'isEscaped' in ${propAccess})) __hydrateProps['${callerKey}'] = ${propAccess}`);
263
263
  }
264
- lines.push(` const __bfPropsJson = __bfParentProps || (Object.keys(__hydrateProps).length > 0 ? JSON.stringify(__hydrateProps) : undefined)`);
264
+ lines.push(` const __bfPropsJson = __bfParentProps || serializeHydrationProps(__hydrateProps, '${name}')`);
265
265
  } else if (hasClientInteractivity && isRootComponent) {
266
266
  lines.push("");
267
267
  lines.push(` const __bfPropsJson = __bfParentProps`);
@@ -2,13 +2,11 @@
2
2
  * Per-fixture build-time contracts for shapes the Hono adapter
3
3
  * intentionally refuses to lower. Hono's SSR runtime is JS — its
4
4
  * `acceptsTemplateCall` is broad enough to cover every adapter-specific
5
- * lowering gap, so this package's only pin is the one refusal that is
6
- * NOT adapter-specific: a method call on a host rich-typed prop (Date,
7
- * Map, …) with no catalogued lowering refuses with BF021 at the
8
- * compiler layer (`checkRichTypeMethodCalls`), ahead of and independent
9
- * of `adapter.generate()` even Hono's native JS evaluation never sees
10
- * the call (#2273). Consumed by this package's own conformance test (as
11
- * `expectedDiagnostics`) and by `bf compat` (issue-URL attribution).
5
+ * lowering gap, so the only pins here are compiler-level refusals that
6
+ * fire ahead of `adapter.generate()` and apply identically everywhere.
7
+ * `date-method-uncatalogued` stays pinned even here: hydrate-init
8
+ * re-evaluates the expression against a JSON-de-riched receiver, so a
9
+ * Hono-specific carve-out was evaluated and rejected (#2356).
12
10
  */
13
11
  import type { ConformancePins } from '@barefootjs/jsx';
14
12
  export declare const conformancePins: ConformancePins;
@@ -1 +1 @@
1
- {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eAE7B,CAAA"}
1
+ {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eAG7B,CAAA"}
package/dist/index.js CHANGED
@@ -95,7 +95,7 @@ export default ${this.componentName}` : "";
95
95
  generateImports(ir, componentCode) {
96
96
  const lines = [];
97
97
  const utilImports = [];
98
- for (const util of ["bfComment", "bfText", "bfTextEnd"]) {
98
+ for (const util of ["bfComment", "bfText", "bfTextEnd", "serializeHydrationProps"]) {
99
99
  if (new RegExp(`\\b${util}\\b`).test(componentCode)) {
100
100
  utilImports.push(util);
101
101
  }
@@ -261,7 +261,7 @@ export default ${this.componentName}` : "";
261
261
  const callerKey = p.sourceName ?? p.name;
262
262
  lines.push(` if (typeof ${propAccess} !== 'function' && !(typeof ${propAccess} === 'object' && ${propAccess} !== null && 'isEscaped' in ${propAccess})) __hydrateProps['${callerKey}'] = ${propAccess}`);
263
263
  }
264
- lines.push(` const __bfPropsJson = __bfParentProps || (Object.keys(__hydrateProps).length > 0 ? JSON.stringify(__hydrateProps) : undefined)`);
264
+ lines.push(` const __bfPropsJson = __bfParentProps || serializeHydrationProps(__hydrateProps, '${name}')`);
265
265
  } else if (hasClientInteractivity && isRootComponent) {
266
266
  lines.push("");
267
267
  lines.push(` const __bfPropsJson = __bfParentProps`);
@@ -668,7 +668,8 @@ export default ${this.componentName}` : "";
668
668
  var honoAdapter = new HonoAdapter;
669
669
  // src/conformance-pins.ts
670
670
  var conformancePins = {
671
- "date-method-uncatalogued": [{ code: "BF021", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2356" }]
671
+ "date-method-uncatalogued": [{ code: "BF021", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2356" }],
672
+ "rich-prop-client-read": [{ code: "BF049", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2648" }]
672
673
  };
673
674
  export {
674
675
  honoAdapter,
package/dist/utils.d.ts CHANGED
@@ -13,4 +13,34 @@ export declare function bfText(slotId: string): import("hono/utils/html").HtmlEs
13
13
  * Renders <!--/-->
14
14
  */
15
15
  export declare function bfTextEnd(): import("hono/utils/html").HtmlEscapedString;
16
+ /**
17
+ * Serialize a component's hydration props into the `bf-p` JSON payload,
18
+ * throwing a clear, actionable error instead of `JSON.stringify`'s opaque
19
+ * `TypeError` (for a `BigInt`) or silently succeeding with data loss (for a
20
+ * `Map`/`Set`/etc.) — the runtime backstop for #2643's compile-time check
21
+ * (`checkRichTypePropSerialization`, BF049), which can only catch a prop
22
+ * whose type is PROVABLE from `propsType`. An imported/aliased type
23
+ * (`type Timestamp = Map<…>`), or a prop typed too loosely to resolve
24
+ * statically, reaches this function uncaught by BF049, so it needs its own
25
+ * check here to avoid regressing to the pre-#2643 failure modes.
26
+ *
27
+ * Deliberately checks only TOP-LEVEL prop values, not nested shapes —
28
+ * `JSON.stringify`'s existing deep-degradation behavior for a nested rich
29
+ * value (e.g. a `RegExp` inside a plain object) is unchanged, matching a
30
+ * real, shipped, TOLERATED pattern in this repo (`site/ui`'s InputOTP demo
31
+ * passes a live `RegExp` `pattern` prop that degrades to `{}` today; the
32
+ * component's own design accounts for that). Widening this to a deep walk
33
+ * would turn that accepted degradation into a new SSR 500.
34
+ *
35
+ * The throw set is narrower than BF049's flag set, but NOT because
36
+ * `RegExp` / `Error` / `URLSearchParams` preserve more data than a `Map` or
37
+ * `Set` — `JSON.stringify` degrades `RegExp` and `Error` to `{}` exactly
38
+ * like a `Map`/`Set` (only `URLSearchParams` happens to round-trip its
39
+ * key/value pairs as a plain object). They're excluded from the runtime
40
+ * throw purely to avoid a breaking behavior change: `site/ui`'s InputOTP
41
+ * demo already ships a live `RegExp` `pattern` prop that degrades to `{}`
42
+ * today, and the component's own design tolerates that. They stay
43
+ * diagnosable at compile time (BF049) only, not a runtime hard-stop.
44
+ */
45
+ export declare function serializeHydrationProps(props: Record<string, unknown>, componentName: string): string | undefined;
16
46
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,+CAEpC;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,+CAEpC;AAED;;;GAGG;AACH,wBAAgB,SAAS,gDAExB"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,+CAEpC;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,+CAEpC;AAED;;;GAGG;AACH,wBAAgB,SAAS,gDAExB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAsCjH"}
package/dist/utils.js CHANGED
@@ -9,7 +9,22 @@ function bfText(slotId) {
9
9
  function bfTextEnd() {
10
10
  return raw("<!--/-->");
11
11
  }
12
+ function serializeHydrationProps(props, componentName) {
13
+ const keys = Object.keys(props);
14
+ if (keys.length === 0)
15
+ return;
16
+ for (const key of keys) {
17
+ const value = props[key];
18
+ const offender = typeof value === "bigint" ? "BigInt" : typeof value === "symbol" ? "Symbol" : value instanceof Map ? "Map" : value instanceof Set ? "Set" : value instanceof WeakMap ? "WeakMap" : value instanceof WeakSet ? "WeakSet" : value instanceof Promise ? "Promise" : null;
19
+ if (offender !== null) {
20
+ const consequence = offender === "BigInt" ? "" : offender === "Symbol" ? " (the prop would be silently omitted from the payload entirely)" : offender === "Promise" ? " (its eventual value would be silently unreachable — JSON.stringify can't wait for a promise to settle)" : " (its entries would be silently dropped, serializing to {})";
21
+ throw new TypeError(`[barefootjs] Cannot serialize prop '${key}' of <${componentName}> for hydration: a ${offender} does not ` + `survive the bf-p JSON boundary${consequence}. Pre-compute a JSON-safe value (string/number/array/plain ` + `object) server-side and pass that instead. See BF049 (https://github.com/piconic-ai/barefootjs/issues/2643).`);
22
+ }
23
+ }
24
+ return JSON.stringify(props);
25
+ }
12
26
  export {
27
+ serializeHydrationProps,
13
28
  bfTextEnd,
14
29
  bfText,
15
30
  bfComment
package/dist/vite.js CHANGED
@@ -433,6 +433,7 @@ var ErrorCodes = {
433
433
  COMPONENT_REQUIRED_PROP_MISSING: "BF046",
434
434
  JSX_BRANCH_LOCAL_IN_CALLBACK: "BF047",
435
435
  SIBLING_COMPONENT_NOT_COMPILED: "BF048",
436
+ RICH_TYPE_PROP_NOT_HYDRATABLE: "BF049",
436
437
  SHARED_PROGRAM_REQUIRED: "BF050",
437
438
  WRONG_PACKAGE_IMPORT: "BF051",
438
439
  BUILTIN_REQUIRES_IMPORT: "BF054",
@@ -463,6 +464,7 @@ var errorMessages = {
463
464
  [ErrorCodes.COMPONENT_REQUIRED_PROP_MISSING]: "Built-in component is missing a required prop.",
464
465
  [ErrorCodes.JSX_BRANCH_LOCAL_IN_CALLBACK]: "JSX-typed local declared inside an `if`-block cannot be referenced from a callback body (ref / event handler). " + "Render it as a child instead: `<div ref={...}>{local}</div>`.",
465
466
  [ErrorCodes.SIBLING_COMPONENT_NOT_COMPILED]: "Referenced component did not compile to a template, so this reference would throw " + "`ReferenceError` at render time. Multi-return JSX dispatch (a `switch` or `if`/`else` " + "chain across multiple JSX-returning branches) cannot compile as a component in a " + `'use client' file. Extract it to a separate non-"use client" file (where it is preserved ` + "verbatim, see #932), or rewrite it as a single-return ternary/conditional chain so the " + "component pipeline can compile it.",
467
+ [ErrorCodes.RICH_TYPE_PROP_NOT_HYDRATABLE]: "Rich-typed prop cannot cross the bf-p hydration boundary as JSON.",
466
468
  [ErrorCodes.SHARED_PROGRAM_REQUIRED]: "Shared ts.Program required for type-based reactivity classification. This source imports a Reactive<T>-branded library (e.g. @barefootjs/form) whose getters cannot be classified by regex alone. Pass `options.program` (built via `createProgramForCorpus`) so the analyzer can resolve the brand through the TypeChecker.",
467
469
  [ErrorCodes.WRONG_PACKAGE_IMPORT]: "Import from wrong package.",
468
470
  [ErrorCodes.BUILTIN_REQUIRES_IMPORT]: "Built-in <Async> / <Region> must be imported from '@barefootjs/client'. " + "The compiler recognises these tags by their import (not by tag name), " + "so an unimported tag with this name is treated as an undeclared component.",
@@ -496,6 +498,8 @@ var HOST_RICH_TYPE_NAMES = new Set([
496
498
  "BigInt",
497
499
  "Function"
498
500
  ]);
501
+ var JSON_REVIVABLE_RICH_TYPE_NAMES = new Set(["Date", "URL"]);
502
+ var JSON_UNSAFE_RICH_TYPE_NAMES = new Set([...HOST_RICH_TYPE_NAMES].filter((n) => !JSON_REVIVABLE_RICH_TYPE_NAMES.has(n)));
499
503
  function baseTypeName(raw) {
500
504
  const idx = raw.indexOf("<");
501
505
  return (idx === -1 ? raw : raw.slice(0, idx)).trim();
@@ -2449,7 +2453,7 @@ export default ${this.componentName}` : "";
2449
2453
  generateImports(ir, componentCode) {
2450
2454
  const lines = [];
2451
2455
  const utilImports = [];
2452
- for (const util of ["bfComment", "bfText", "bfTextEnd"]) {
2456
+ for (const util of ["bfComment", "bfText", "bfTextEnd", "serializeHydrationProps"]) {
2453
2457
  if (new RegExp(`\\b${util}\\b`).test(componentCode)) {
2454
2458
  utilImports.push(util);
2455
2459
  }
@@ -2615,7 +2619,7 @@ export default ${this.componentName}` : "";
2615
2619
  const callerKey = p.sourceName ?? p.name;
2616
2620
  lines.push(` if (typeof ${propAccess} !== 'function' && !(typeof ${propAccess} === 'object' && ${propAccess} !== null && 'isEscaped' in ${propAccess})) __hydrateProps['${callerKey}'] = ${propAccess}`);
2617
2621
  }
2618
- lines.push(` const __bfPropsJson = __bfParentProps || (Object.keys(__hydrateProps).length > 0 ? JSON.stringify(__hydrateProps) : undefined)`);
2622
+ lines.push(` const __bfPropsJson = __bfParentProps || serializeHydrationProps(__hydrateProps, '${name}')`);
2619
2623
  } else if (hasClientInteractivity && isRootComponent) {
2620
2624
  lines.push("");
2621
2625
  lines.push(` const __bfPropsJson = __bfParentProps`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/hono",
3
- "version": "0.31.6",
3
+ "version": "0.31.7",
4
4
  "description": "Hono integration for BarefootJS",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -122,7 +122,7 @@
122
122
  },
123
123
  "devDependencies": {
124
124
  "@barefootjs/adapter-tests": "0.1.0",
125
- "@barefootjs/vite": "0.31.6",
125
+ "@barefootjs/vite": "0.31.7",
126
126
  "@types/jsdom": "^27.0.0",
127
127
  "hono": "^4.6.0",
128
128
  "jsdom": "^27.3.0",
@@ -0,0 +1,103 @@
1
+ /**
2
+ * `serializeHydrationProps` (#2643) — the Hono-only runtime backstop for
3
+ * BF049 (`packages/jsx/src/rich-type-refusal.ts`'s
4
+ * `checkRichTypePropSerialization`). BF049 can only catch a prop whose type
5
+ * is PROVABLE from `propsType`; an imported/aliased type or a loosely-typed
6
+ * prop reaches Hono's serialization step uncaught, so this helper is the
7
+ * actual floor: it throws a clear, actionable error for a value that cannot
8
+ * survive the `bf-p` JSON boundary, instead of `JSON.stringify`'s opaque
9
+ * `TypeError` (BigInt) or silent data loss (Map/Set/…).
10
+ */
11
+
12
+ import { describe, test, expect } from 'bun:test'
13
+ import { serializeHydrationProps } from '../utils'
14
+ import { renderHonoComponent } from '../test-render'
15
+ import { HonoAdapter } from '../adapter/hono-adapter'
16
+
17
+ describe('serializeHydrationProps — unit', () => {
18
+ test('empty props return undefined', () => {
19
+ expect(serializeHydrationProps({}, 'Foo')).toBeUndefined()
20
+ })
21
+
22
+ test('a plain JSON-safe props object serializes normally', () => {
23
+ expect(serializeHydrationProps({ a: 1, b: 'x', c: [1, 2], d: null }, 'Foo')).toBe(
24
+ JSON.stringify({ a: 1, b: 'x', c: [1, 2], d: null }),
25
+ )
26
+ })
27
+
28
+ test('a Date prop passes through untouched (handled by JSON.stringify’s own toJSON)', () => {
29
+ const d = new Date('2024-01-01T00:00:00.000Z')
30
+ expect(serializeHydrationProps({ createdAt: d }, 'Foo')).toBe(JSON.stringify({ createdAt: d }))
31
+ })
32
+
33
+ test.each([
34
+ ['BigInt', { n: 123n }, 'n', 'BigInt'],
35
+ ['Symbol', { s: Symbol('x') }, 's', 'Symbol'],
36
+ ['Map', { data: new Map([['x', 1]]) }, 'data', 'Map'],
37
+ ['Set', { tags: new Set(['x']) }, 'tags', 'Set'],
38
+ ['WeakMap', { wm: new WeakMap() }, 'wm', 'WeakMap'],
39
+ ['WeakSet', { ws: new WeakSet() }, 'ws', 'WeakSet'],
40
+ ['Promise', { p: Promise.resolve(1) }, 'p', 'Promise'],
41
+ ] as const)('%s prop throws a clear TypeError naming the prop, component, and #2643', (_label, props, propName, offender) => {
42
+ expect(() => serializeHydrationProps(props, 'Foo')).toThrow(TypeError)
43
+ try {
44
+ serializeHydrationProps(props, 'Foo')
45
+ throw new Error('expected a throw')
46
+ } catch (e) {
47
+ expect((e as Error).message).toContain(`prop '${propName}'`)
48
+ expect((e as Error).message).toContain('<Foo>')
49
+ expect((e as Error).message).toContain(offender)
50
+ expect((e as Error).message).toContain('2643')
51
+ }
52
+ })
53
+
54
+ test('a nested RegExp (inside a plain object) does not throw — only top-level values are checked', () => {
55
+ // Matches the existing, shipped, TOLERATED degradation the InputOTP demo
56
+ // relies on: JSON.stringify's own deep behavior for a nested rich value
57
+ // is unchanged by this helper.
58
+ const value = { wrapper: { pattern: /^[0-9]+$/ } }
59
+ expect(() => serializeHydrationProps(value, 'Foo')).not.toThrow()
60
+ })
61
+
62
+ test('a top-level RegExp/Error/URLSearchParams degrades like JSON.stringify already did — not in the throw set', () => {
63
+ expect(() => serializeHydrationProps({ pattern: /x/ }, 'Foo')).not.toThrow()
64
+ expect(() => serializeHydrationProps({ err: new Error('x') }, 'Foo')).not.toThrow()
65
+ expect(() => serializeHydrationProps({ q: new URLSearchParams('a=1') }, 'Foo')).not.toThrow()
66
+ })
67
+ })
68
+
69
+ describe('serializeHydrationProps — integration via real Hono SSR render', () => {
70
+ // Prop typed `unknown` so BF049 has no static evidence to fire on —
71
+ // exercises the runtime backstop specifically, independent of the
72
+ // compile-time check.
73
+ const SOURCE = `
74
+ 'use client'
75
+ export function Foo({ data }: { data: unknown }) {
76
+ return <button onClick={() => console.log(data)}>go</button>
77
+ }
78
+ `
79
+
80
+ // `renderHonoComponent` routes through Hono's own default error handler
81
+ // (`app.request()`, no custom `app.onError`), which swallows a thrown
82
+ // error's message into a generic 500 body — so these integration tests
83
+ // only prove the WIRING actually fails the request end-to-end (the
84
+ // `serializeHydrationProps` call is really reached from real generated
85
+ // component code, via the real `@barefootjs/hono/utils` import). The
86
+ // exact thrown message is already pinned by the direct unit tests above.
87
+ test('a Map prop causes SSR to fail (the runtime backstop is really wired into generated code)', async () => {
88
+ await expect(
89
+ renderHonoComponent({ adapter: new HonoAdapter(), source: SOURCE, props: { data: new Map([['x', 1]]) } }),
90
+ ).rejects.toThrow(/Render failed with status 500/)
91
+ })
92
+
93
+ test('a BigInt prop causes SSR to fail (previously an opaque JSON.stringify TypeError; behavior unchanged, now via the clearer helper)', async () => {
94
+ await expect(
95
+ renderHonoComponent({ adapter: new HonoAdapter(), source: SOURCE, props: { data: 123n } }),
96
+ ).rejects.toThrow(/Render failed with status 500/)
97
+ })
98
+
99
+ test('an array prop (JSON-safe) renders successfully', async () => {
100
+ const html = await renderHonoComponent({ adapter: new HonoAdapter(), source: SOURCE, props: { data: [1, 2, 3] } })
101
+ expect(html).toContain('bf-p=')
102
+ })
103
+ })
@@ -270,9 +270,10 @@ export class HonoAdapter extends JsxAdapter implements IRNodeEmitter<HonoRenderC
270
270
  private generateImports(ir: ComponentIR, componentCode: string): string {
271
271
  const lines: string[] = []
272
272
 
273
- // Only import bfComment/bfText/bfTextEnd utilities that are actually used
273
+ // Only import bfComment/bfText/bfTextEnd/serializeHydrationProps utilities
274
+ // that are actually used
274
275
  const utilImports: string[] = []
275
- for (const util of ['bfComment', 'bfText', 'bfTextEnd']) {
276
+ for (const util of ['bfComment', 'bfText', 'bfTextEnd', 'serializeHydrationProps']) {
276
277
  if (new RegExp(`\\b${util}\\b`).test(componentCode)) {
277
278
  utilImports.push(util)
278
279
  }
@@ -618,7 +619,7 @@ export class HonoAdapter extends JsxAdapter implements IRNodeEmitter<HonoRenderC
618
619
  const callerKey = p.sourceName ?? p.name
619
620
  lines.push(` if (typeof ${propAccess} !== 'function' && !(typeof ${propAccess} === 'object' && ${propAccess} !== null && 'isEscaped' in ${propAccess})) __hydrateProps['${callerKey}'] = ${propAccess}`)
620
621
  }
621
- lines.push(` const __bfPropsJson = __bfParentProps || (Object.keys(__hydrateProps).length > 0 ? JSON.stringify(__hydrateProps) : undefined)`)
622
+ lines.push(` const __bfPropsJson = __bfParentProps || serializeHydrationProps(__hydrateProps, '${name}')`)
622
623
  } else if (hasClientInteractivity && isRootComponent) {
623
624
  // No own props, but root is a component — pass through parent's props
624
625
  lines.push('')
@@ -2,17 +2,16 @@
2
2
  * Per-fixture build-time contracts for shapes the Hono adapter
3
3
  * intentionally refuses to lower. Hono's SSR runtime is JS — its
4
4
  * `acceptsTemplateCall` is broad enough to cover every adapter-specific
5
- * lowering gap, so this package's only pin is the one refusal that is
6
- * NOT adapter-specific: a method call on a host rich-typed prop (Date,
7
- * Map, …) with no catalogued lowering refuses with BF021 at the
8
- * compiler layer (`checkRichTypeMethodCalls`), ahead of and independent
9
- * of `adapter.generate()` even Hono's native JS evaluation never sees
10
- * the call (#2273). Consumed by this package's own conformance test (as
11
- * `expectedDiagnostics`) and by `bf compat` (issue-URL attribution).
5
+ * lowering gap, so the only pins here are compiler-level refusals that
6
+ * fire ahead of `adapter.generate()` and apply identically everywhere.
7
+ * `date-method-uncatalogued` stays pinned even here: hydrate-init
8
+ * re-evaluates the expression against a JSON-de-riched receiver, so a
9
+ * Hono-specific carve-out was evaluated and rejected (#2356).
12
10
  */
13
11
 
14
12
  import type { ConformancePins } from '@barefootjs/jsx'
15
13
 
16
14
  export const conformancePins: ConformancePins = {
17
15
  'date-method-uncatalogued': [{ code: 'BF021', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2356' }],
16
+ 'rich-prop-client-read': [{ code: 'BF049', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2648' }],
18
17
  }
package/src/utils.ts CHANGED
@@ -24,3 +24,72 @@ export function bfTextEnd() {
24
24
  return raw('<!--/-->')
25
25
  }
26
26
 
27
+ /**
28
+ * Serialize a component's hydration props into the `bf-p` JSON payload,
29
+ * throwing a clear, actionable error instead of `JSON.stringify`'s opaque
30
+ * `TypeError` (for a `BigInt`) or silently succeeding with data loss (for a
31
+ * `Map`/`Set`/etc.) — the runtime backstop for #2643's compile-time check
32
+ * (`checkRichTypePropSerialization`, BF049), which can only catch a prop
33
+ * whose type is PROVABLE from `propsType`. An imported/aliased type
34
+ * (`type Timestamp = Map<…>`), or a prop typed too loosely to resolve
35
+ * statically, reaches this function uncaught by BF049, so it needs its own
36
+ * check here to avoid regressing to the pre-#2643 failure modes.
37
+ *
38
+ * Deliberately checks only TOP-LEVEL prop values, not nested shapes —
39
+ * `JSON.stringify`'s existing deep-degradation behavior for a nested rich
40
+ * value (e.g. a `RegExp` inside a plain object) is unchanged, matching a
41
+ * real, shipped, TOLERATED pattern in this repo (`site/ui`'s InputOTP demo
42
+ * passes a live `RegExp` `pattern` prop that degrades to `{}` today; the
43
+ * component's own design accounts for that). Widening this to a deep walk
44
+ * would turn that accepted degradation into a new SSR 500.
45
+ *
46
+ * The throw set is narrower than BF049's flag set, but NOT because
47
+ * `RegExp` / `Error` / `URLSearchParams` preserve more data than a `Map` or
48
+ * `Set` — `JSON.stringify` degrades `RegExp` and `Error` to `{}` exactly
49
+ * like a `Map`/`Set` (only `URLSearchParams` happens to round-trip its
50
+ * key/value pairs as a plain object). They're excluded from the runtime
51
+ * throw purely to avoid a breaking behavior change: `site/ui`'s InputOTP
52
+ * demo already ships a live `RegExp` `pattern` prop that degrades to `{}`
53
+ * today, and the component's own design tolerates that. They stay
54
+ * diagnosable at compile time (BF049) only, not a runtime hard-stop.
55
+ */
56
+ export function serializeHydrationProps(props: Record<string, unknown>, componentName: string): string | undefined {
57
+ const keys = Object.keys(props)
58
+ if (keys.length === 0) return undefined
59
+ for (const key of keys) {
60
+ const value = props[key]
61
+ const offender =
62
+ typeof value === 'bigint'
63
+ ? 'BigInt'
64
+ : typeof value === 'symbol'
65
+ ? 'Symbol'
66
+ : value instanceof Map
67
+ ? 'Map'
68
+ : value instanceof Set
69
+ ? 'Set'
70
+ : value instanceof WeakMap
71
+ ? 'WeakMap'
72
+ : value instanceof WeakSet
73
+ ? 'WeakSet'
74
+ : value instanceof Promise
75
+ ? 'Promise'
76
+ : null
77
+ if (offender !== null) {
78
+ const consequence =
79
+ offender === 'BigInt'
80
+ ? ''
81
+ : offender === 'Symbol'
82
+ ? ' (the prop would be silently omitted from the payload entirely)'
83
+ : offender === 'Promise'
84
+ ? " (its eventual value would be silently unreachable — JSON.stringify can't wait for a promise to settle)"
85
+ : ' (its entries would be silently dropped, serializing to {})'
86
+ throw new TypeError(
87
+ `[barefootjs] Cannot serialize prop '${key}' of <${componentName}> for hydration: a ${offender} does not ` +
88
+ `survive the bf-p JSON boundary${consequence}. Pre-compute a JSON-safe value (string/number/array/plain ` +
89
+ `object) server-side and pass that instead. See BF049 (https://github.com/piconic-ai/barefootjs/issues/2643).`,
90
+ )
91
+ }
92
+ }
93
+ return JSON.stringify(props)
94
+ }
95
+