@octanejs/app-core 0.0.52 → 0.0.54

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": "@octanejs/app-core",
3
- "version": "0.0.52",
3
+ "version": "0.0.54",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -79,10 +79,10 @@
79
79
  "esbuild": "^0.28.1"
80
80
  },
81
81
  "peerDependencies": {
82
- "octane": "^0.3.0"
82
+ "octane": "^0.4.0"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@types/node": "^24.13.3",
86
- "octane": "0.3.0"
86
+ "octane": "0.4.0"
87
87
  }
88
88
  }
package/src/html.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { composeHtmlStream } from './server/html-stream.js';
2
+ export { serializeRouteData } from './server/route-data.js';
2
3
  export {
3
4
  HYDRATION_NONCE_PLACEHOLDER,
4
5
  applyHydrationNonce,
@@ -18,8 +18,8 @@
18
18
  * BUILT dist/client/index.html (hashed hydrate script already in place, so
19
19
  * nothing is injected per-request), per-route `<link rel=stylesheet/modulepreload>`
20
20
  * tags from the client manifest join the head, and render errors produce a
21
- * plain 500 (no dev stack page). Keep the two files in sync when the shape
22
- * changes.
21
+ * plain 500 (no dev stack page). Both handlers use the shared route-data
22
+ * serializer when the hydration payload shape changes.
23
23
  */
24
24
 
25
25
  import { createRouter } from './router.js';
@@ -31,6 +31,7 @@ import { runServerRequest } from './signal-owners.js';
31
31
  import { rpcIdCollision } from './rpc-registry.js';
32
32
  import { handleServerRoute } from './server-route.js';
33
33
  import { composeHtmlStream } from './html-stream.js';
34
+ import { serializeRouteData } from './route-data.js';
34
35
  import {
35
36
  applyHydrationNonce,
36
37
  getContextNonce,
@@ -253,7 +254,7 @@ function prepareIndependentHydration(manifest) {
253
254
  * Snapshot completed build authority once per handler, never from request data.
254
255
  * Legacy custom handlers without a client build retain their existing path.
255
256
  * @param {ServerManifest} manifest
256
- * @returns {ServerManifest['clientBuild'] | undefined}
257
+ * @returns {import('@octanejs/app-core/production').ClientBuildManifest | undefined}
257
258
  */
258
259
  function prepareClientBuild(manifest) {
259
260
  const build = manifest.clientBuild;
@@ -505,8 +506,7 @@ export function createHandler(manifest, deps) {
505
506
  deps,
506
507
  );
507
508
 
508
- // The hydration payload — SAME keys, SAME order as dev render-route.js, so
509
- // the data script is byte-identical between dev and production.
509
+ // Share payload ordering and script escaping with the dev handler.
510
510
  const streamedSignals =
511
511
  clientBuild == null
512
512
  ? undefined
@@ -514,7 +514,7 @@ export function createHandler(manifest, deps) {
514
514
  buildId: clientBuild.buildId,
515
515
  documentId: crypto.randomUUID(),
516
516
  };
517
- const routeData = JSON.stringify({
517
+ const routeData = serializeRouteData({
518
518
  entry: entryPath,
519
519
  exportName: exportName ?? null,
520
520
  layout: route.layout ?? null,
@@ -526,7 +526,7 @@ export function createHandler(manifest, deps) {
526
526
  clientBuild,
527
527
  streamedSignals,
528
528
  });
529
- const dataScript = `<script id="__octane_data" type="application/json"${nonceAttribute(nonce)}>${escapeScript(routeData)}</script>`;
529
+ const dataScript = `<script id="__octane_data" type="application/json"${nonceAttribute(nonce)}>${routeData}</script>`;
530
530
 
531
531
  // The page, layout, and configured root fallbacks can all contribute
532
532
  // server HTML before hydration. Their asset records also include CSS for
@@ -620,12 +620,3 @@ export function createHandler(manifest, deps) {
620
620
 
621
621
  return handler;
622
622
  }
623
-
624
- /**
625
- * Escape script content to prevent XSS in the inline JSON data block.
626
- * @param {string} str
627
- * @returns {string}
628
- */
629
- function escapeScript(str) {
630
- return str.replace(/</g, '\\u003c').replace(/>/g, '\\u003e');
631
- }
@@ -0,0 +1,25 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Serialize the common dev/production hydration payload in a stable order.
5
+ * Build and document identities are supplied by the caller, never generated or
6
+ * cached here. Escape HTML delimiters before embedding JSON in an inline script.
7
+ * @param {import('@octanejs/app-core/html').RouteHydrationData} data
8
+ * @returns {string}
9
+ */
10
+ export function serializeRouteData(data) {
11
+ return JSON.stringify({
12
+ entry: data.entry,
13
+ exportName: data.exportName ?? null,
14
+ layout: data.layout ?? null,
15
+ routeIndex: data.routeIndex,
16
+ params: data.params,
17
+ url: data.url,
18
+ preHydrate: data.preHydrate ?? null,
19
+ rootBoundary: data.rootBoundary ?? { pending: null, catch: null },
20
+ clientBuild: data.clientBuild,
21
+ streamedSignals: data.streamedSignals,
22
+ })
23
+ .replace(/</g, '\\u003c')
24
+ .replace(/>/g, '\\u003e');
25
+ }
package/types/html.d.ts CHANGED
@@ -1,4 +1,21 @@
1
1
  import type { Context } from '@octanejs/app-core';
2
+ import type { ClientBuildManifest, ServerManifest } from '@octanejs/app-core/production';
3
+
4
+ /** Request-local inputs to the script-safe dev/production hydration payload. */
5
+ export interface RouteHydrationData {
6
+ entry: string | undefined;
7
+ exportName?: string | null;
8
+ layout?: string | null;
9
+ routeIndex?: number;
10
+ params: Record<string, string>;
11
+ url: string;
12
+ preHydrate?: string | null;
13
+ rootBoundary?: ServerManifest['rootBoundaryEntries'];
14
+ clientBuild?: ClientBuildManifest;
15
+ streamedSignals?: { buildId: string; documentId: string };
16
+ }
17
+
18
+ export function serializeRouteData(data: RouteHydrationData): string;
2
19
 
3
20
  export const HYDRATION_NONCE_PLACEHOLDER: '__OCTANE_REQUEST_NONCE__';
4
21
  export function composeHtmlStream(