@markless/router 0.3.0 → 0.3.1

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.
@@ -281,7 +281,20 @@ function fillDocumentChildren(documentShell, children) {
281
281
  return documentShell.slice(0, at) + children + documentShell.slice(at + 37);
282
282
  }
283
283
  async function renderDocumentModule(documentModule, props) {
284
- return (await documentModule?.default?.renderSsr?.(props))?.html;
284
+ const output = await documentModule?.default?.renderSsr?.(props);
285
+ assertDocumentStorageFree(output);
286
+ return output?.html;
287
+ }
288
+ function assertDocumentStorageFree(output) {
289
+ const storage = documentStorageRecords(output?.state);
290
+ if (storage.length === 0) return;
291
+ const keys = storage.map((record) => record.key).join(", ");
292
+ throw new Error(`MARKLESS_ROUTER_DOCUMENT_STORAGE_UNSUPPORTED: the document declares storage cells (${keys}), but the router serves only the document's HTML, so their state payload never reaches the browser and they can never resume. Declare storage() at module scope in a component the page renders instead.`);
293
+ }
294
+ function documentStorageRecords(state) {
295
+ if (!state || typeof state !== "object") return [];
296
+ const storage = state.storage;
297
+ return Array.isArray(storage) ? storage : [];
285
298
  }
286
299
  function insertHeadHtml(documentHtml, headHtml) {
287
300
  if (!headHtml) return documentHtml;
@@ -43,11 +43,20 @@ type MdxRenderDataChild = {
43
43
  readonly surface: MdxRenderDataSurface;
44
44
  };
45
45
  declare function createMdxRenderDataSurface(parts: ReadonlyArray<MdxRoutePart>, children: ReadonlyArray<MdxRenderDataChild>): MdxRenderDataSurface;
46
+ type MdxComputedRecord = {
47
+ readonly deriveSymbolId?: string;
48
+ readonly [key: string]: unknown;
49
+ };
50
+ type MdxStorageRecord = {
51
+ readonly graphNodeId: string;
52
+ readonly key: string;
53
+ };
46
54
  type MdxStatePayload = {
47
55
  readonly version: unknown;
48
56
  readonly cells?: readonly unknown[];
49
- readonly computed?: readonly unknown[];
57
+ readonly computed?: readonly MdxComputedRecord[];
50
58
  readonly sharedDefinitions?: readonly unknown[];
59
+ readonly storage?: readonly MdxStorageRecord[];
51
60
  };
52
61
  type MdxLocator = {
53
62
  readonly hostNodeId: string;
@@ -1,3 +1,7 @@
1
+ function protocolStateVersion(storage) {
2
+ return (storage?.length ?? 0) > 0 ? 2 : 1;
3
+ }
4
+ //#endregion
1
5
  //#region src/vite/runtime/mdx-route.ts
2
6
  function createMdxRenderDataSurface(parts, children) {
3
7
  const componentName = "MarklessMdxRoute";
@@ -125,13 +129,18 @@ async function renderMdxChild(children, component, props, child) {
125
129
  return output?.html ?? "";
126
130
  }
127
131
  function composeMdxState(children) {
128
- const childStates = children.map((child) => child.output?.state).filter(isDefined);
132
+ const childStates = children.map((child) => ({
133
+ child,
134
+ state: child.output?.state
135
+ })).filter((entry) => isDefined(entry.state));
129
136
  if (childStates.length === 0) return;
137
+ const storage = childStates.flatMap(({ state }) => state.storage ?? []);
130
138
  return {
131
- version: childStates[0].version,
132
- cells: childStates.flatMap((state) => state.cells ?? []),
133
- computed: childStates.flatMap((state) => state.computed ?? []),
134
- ...childStates.some((state) => state.sharedDefinitions?.length) ? { sharedDefinitions: childStates.flatMap((state) => state.sharedDefinitions ?? []) } : {}
139
+ version: protocolStateVersion(storage),
140
+ cells: childStates.flatMap(({ state }) => state.cells ?? []),
141
+ computed: childStates.flatMap(({ child, state }) => (state.computed ?? []).map((record) => prefixMdxComputedRecord(record, child))),
142
+ ...childStates.some(({ state }) => state.sharedDefinitions?.length) ? { sharedDefinitions: childStates.flatMap(({ state }) => state.sharedDefinitions ?? []) } : {},
143
+ ...storage.length > 0 ? { storage } : {}
135
144
  };
136
145
  }
137
146
  function composeMdxView(parts, children, initialElementOffset) {
@@ -201,6 +210,12 @@ function appendMdxChildView(context) {
201
210
  hostNodeId: context.child.hostPrefix + handle.hostNodeId
202
211
  });
203
212
  }
213
+ function prefixMdxComputedRecord(record, child) {
214
+ return {
215
+ ...record,
216
+ ...record.deriveSymbolId ? { deriveSymbolId: child.symbolPrefix + record.deriveSymbolId } : {}
217
+ };
218
+ }
204
219
  function prefixMdxSymbolRecord(record, child) {
205
220
  return {
206
221
  ...record,
package/dist/vite.js CHANGED
@@ -728,6 +728,7 @@ async function transformMdxRoute(source, id) {
728
728
  "const marklessMdxRenderData = createMdxRenderDataSurface(marklessMdxParts, []);",
729
729
  "const marklessMdxPage = {",
730
730
  " renderData: marklessMdxRenderData,",
731
+ " storageSeeds: marklessMdxStorageSeeds,",
731
732
  " renderSsr() {",
732
733
  ` return { html: ${JSON.stringify(html)} };`,
733
734
  " }",
@@ -746,9 +747,11 @@ function emitComposedMdxRoute(route, id) {
746
747
  `const marklessMdxParts = ${JSON.stringify(route.parts)};`,
747
748
  `const marklessMdxSymbolLoaders = ${renderSymbolLoaders(route.components)};`,
748
749
  `const marklessMdxRenderData = ${renderMdxRenderDataLoader(route.components, id)};`,
750
+ `const marklessMdxStorageSeeds = ${renderStorageSeeds(route.components)};`,
749
751
  "",
750
752
  "const marklessMdxPage = {",
751
753
  " renderData: marklessMdxRenderData,",
754
+ " storageSeeds: marklessMdxStorageSeeds,",
752
755
  " loadSymbol: marklessMdxLoadSymbol,",
753
756
  " async renderSsr(props = {}) {",
754
757
  " const marklessMdxChildren = [];",
@@ -1031,6 +1034,10 @@ function hastElementTags(nodes) {
1031
1034
  }
1032
1035
  return tags;
1033
1036
  }
1037
+ function renderStorageSeeds(components) {
1038
+ const localNames = [...new Set(components.map((component) => component.localName))];
1039
+ return localNames.length === 0 ? "[]" : `[${localNames.map((localName) => `...(${localName}.storageSeeds ?? [])`).join(", ")}]`;
1040
+ }
1034
1041
  function renderSymbolLoaders(components) {
1035
1042
  return `[${components.map((component) => `{ prefix: ${JSON.stringify(component.prefix)}, loadSymbol(symbolId) { return import(${JSON.stringify(`${component.specifier}?markless-symbols`)}).then((mod) => mod.loadSymbol(symbolId.slice(${component.prefix.length}))); } }`).join(", ")}]`;
1036
1043
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markless/router",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Markless router: client-side navigation with SSR streaming and build-time preload maps.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -64,11 +64,11 @@
64
64
  "pathe": "^2.0.3",
65
65
  "satteri": "0.6.3",
66
66
  "ufo": "^1.6.4",
67
- "@markless/bundler": "0.3.0",
68
- "@markless/web": "0.3.0"
67
+ "@markless/web": "0.3.1",
68
+ "@markless/bundler": "0.3.1"
69
69
  },
70
70
  "devDependencies": {
71
- "@markless/analyzer": "0.3.0"
71
+ "@markless/analyzer": "0.3.1"
72
72
  },
73
73
  "peerDependencies": {
74
74
  "vite": "8.0.16"