@funstack/static 0.0.9 → 1.0.0

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 (71) hide show
  1. package/README.md +6 -4
  2. package/dist/bin/skill-installer.mjs +5 -4
  3. package/dist/bin/skill-installer.mjs.map +1 -1
  4. package/dist/build/buildApp.mjs +1 -2
  5. package/dist/build/buildApp.mjs.map +1 -1
  6. package/dist/build/contentHash.mjs +1 -1
  7. package/dist/build/dependencyGraph.mjs +1 -1
  8. package/dist/build/dependencyGraph.mjs.map +1 -1
  9. package/dist/build/rscPath.mjs +1 -2
  10. package/dist/build/rscPath.mjs.map +1 -1
  11. package/dist/build/rscProcessor.mjs +1 -2
  12. package/dist/build/rscProcessor.mjs.map +1 -1
  13. package/dist/build/validateEntryPath.mjs +1 -1
  14. package/dist/client/entry.d.mts +1 -1
  15. package/dist/client/entry.mjs +2 -3
  16. package/dist/client/entry.mjs.map +1 -1
  17. package/dist/client/error-boundary.mjs +2 -4
  18. package/dist/client/error-boundary.mjs.map +1 -1
  19. package/dist/client/globals.mjs +2 -4
  20. package/dist/client/globals.mjs.map +1 -1
  21. package/dist/docs/GettingStarted.md +11 -5
  22. package/dist/docs/MigratingFromViteSPA.md +4 -4
  23. package/dist/docs/{learn → advanced}/MultipleEntrypoints.md +14 -42
  24. package/dist/docs/{learn → advanced}/SSR.md +3 -3
  25. package/dist/docs/api/EntryDefinition.md +2 -2
  26. package/dist/docs/api/FunstackStatic.md +6 -6
  27. package/dist/docs/index.md +5 -2
  28. package/dist/docs/learn/DeferAndActivity.md +3 -3
  29. package/dist/docs/learn/HowItWorks.md +2 -2
  30. package/dist/docs/learn/LazyServerComponents.md +3 -3
  31. package/dist/docs/learn/OptimizingPayloads.md +4 -4
  32. package/dist/docs/learn/RSC.md +3 -3
  33. package/dist/entries/client.d.mts +1 -1
  34. package/dist/entries/client.mjs +1 -2
  35. package/dist/entries/rsc-client.d.mts +0 -1
  36. package/dist/entries/rsc-client.mjs +1 -3
  37. package/dist/entries/rsc.mjs +1 -2
  38. package/dist/entries/server.mjs +1 -2
  39. package/dist/entries/ssr.mjs +1 -2
  40. package/dist/entryDefinition.mjs +1 -1
  41. package/dist/index.mjs +1 -2
  42. package/dist/plugin/getRSCEntryPoint.mjs +1 -1
  43. package/dist/plugin/getRSCEntryPoint.mjs.map +1 -1
  44. package/dist/plugin/index.mjs +1 -2
  45. package/dist/plugin/index.mjs.map +1 -1
  46. package/dist/plugin/server.mjs +1 -2
  47. package/dist/plugin/server.mjs.map +1 -1
  48. package/dist/rsc/defer.d.mts +0 -1
  49. package/dist/rsc/defer.d.mts.map +1 -1
  50. package/dist/rsc/defer.mjs +3 -7
  51. package/dist/rsc/defer.mjs.map +1 -1
  52. package/dist/rsc/entry.mjs +5 -6
  53. package/dist/rsc/entry.mjs.map +1 -1
  54. package/dist/rsc/marker.mjs +1 -1
  55. package/dist/rsc/request.mjs +1 -1
  56. package/dist/rsc/resolveEntry.mjs +1 -2
  57. package/dist/rsc/resolveEntry.mjs.map +1 -1
  58. package/dist/rsc/rscModule.mjs +1 -1
  59. package/dist/rsc-client/clientWrapper.d.mts +5 -1
  60. package/dist/rsc-client/clientWrapper.d.mts.map +1 -1
  61. package/dist/rsc-client/clientWrapper.mjs +14 -15
  62. package/dist/rsc-client/clientWrapper.mjs.map +1 -1
  63. package/dist/rsc-client/entry.mjs +2 -4
  64. package/dist/ssr/entry.d.mts.map +1 -1
  65. package/dist/ssr/entry.mjs +13 -5
  66. package/dist/ssr/entry.mjs.map +1 -1
  67. package/dist/util/basePath.mjs +1 -1
  68. package/dist/util/drainStream.mjs +1 -1
  69. package/dist/util/urlPath.mjs +1 -1
  70. package/package.json +5 -5
  71. package/dist/rsc-client/entry.d.mts +0 -1
@@ -5,9 +5,9 @@ import { jsx } from "react/jsx-runtime";
5
5
  import { RegistryContext } from "#rsc-client";
6
6
  import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
7
7
  import { renderToReadableStream } from "react-dom/server.edge";
8
+ import { prerender } from "react-dom/static";
8
9
  import { injectRSCPayload } from "rsc-html-stream/server";
9
10
  import { preload } from "react-dom";
10
-
11
11
  //#region src/ssr/entry.tsx
12
12
  async function renderHTML(rscStream, options) {
13
13
  const [rscStream1, rscStream2] = rscStream.tee();
@@ -19,7 +19,10 @@ async function renderHTML(rscStream, options) {
19
19
  as: "fetch"
20
20
  });
21
21
  return /* @__PURE__ */ jsx(RegistryContext, {
22
- value: options.deferRegistry,
22
+ value: options.deferRegistry ? {
23
+ registry: options.deferRegistry,
24
+ createFromReadableStream
25
+ } : void 0,
23
26
  children: use(payload).root
24
27
  });
25
28
  }
@@ -30,11 +33,16 @@ async function renderHTML(rscStream, options) {
30
33
  let htmlStream;
31
34
  let status;
32
35
  try {
33
- htmlStream = await renderToReadableStream(/* @__PURE__ */ jsx(SsrRoot, {}), {
36
+ if (options.build) {
37
+ const { prelude, postponed } = await prerender(/* @__PURE__ */ jsx(SsrRoot, {}), { bootstrapScriptContent });
38
+ if (postponed !== null) throw new Error("Unexpected postponed state during prerendering");
39
+ htmlStream = prelude;
40
+ } else htmlStream = await renderToReadableStream(/* @__PURE__ */ jsx(SsrRoot, {}), {
34
41
  bootstrapScriptContent,
35
42
  nonce: options?.nonce
36
43
  });
37
- } catch {
44
+ } catch (e) {
45
+ if (options.build) throw e;
38
46
  status = 500;
39
47
  htmlStream = await renderToReadableStream(/* @__PURE__ */ jsx("html", { children: /* @__PURE__ */ jsx("body", { children: /* @__PURE__ */ jsx("noscript", { children: "Internal Server Error: SSR failed" }) }) }), {
40
48
  bootstrapScriptContent: `globalThis.__NO_HYDRATE=1;` + bootstrapScriptContent,
@@ -51,7 +59,7 @@ async function renderHTML(rscStream, options) {
51
59
  status
52
60
  };
53
61
  }
54
-
55
62
  //#endregion
56
63
  export { renderHTML };
64
+
57
65
  //# sourceMappingURL=entry.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry.mjs","names":[],"sources":["../../src/ssr/entry.tsx"],"sourcesContent":["import { createFromReadableStream } from \"@vitejs/plugin-rsc/ssr\";\nimport { use } from \"react\";\nimport { renderToReadableStream } from \"react-dom/server.edge\";\nimport { injectRSCPayload } from \"rsc-html-stream/server\";\nimport type { RscPayload } from \"../rsc/entry\";\nimport { appClientManifestVar } from \"../client/globals\";\nimport { rscPayloadPlaceholder } from \"../build/rscPath\";\nimport { preload } from \"react-dom\";\nimport type { DeferRegistry } from \"../rsc/defer\";\nimport { RegistryContext } from \"#rsc-client\";\n\nexport async function renderHTML(\n rscStream: ReadableStream<Uint8Array>,\n options: {\n appEntryMarker: string;\n build: boolean;\n ssr?: boolean;\n nonce?: string;\n deferRegistry?: DeferRegistry;\n clientRscStream?: ReadableStream<Uint8Array>;\n },\n): Promise<{ stream: ReadableStream<Uint8Array>; status?: number }> {\n const [rscStream1, rscStream2] = rscStream.tee();\n\n let payload: Promise<RscPayload> | undefined;\n function SsrRoot() {\n // Tip: calling `createFromReadableStream` inside a component\n // makes `preinit`/`preload` work properly.\n payload ??= createFromReadableStream<RscPayload>(rscStream1);\n if (options.build) {\n preload(rscPayloadPlaceholder, {\n crossOrigin: \"anonymous\",\n as: \"fetch\",\n });\n }\n return (\n <RegistryContext value={options.deferRegistry}>\n {use(payload).root}\n </RegistryContext>\n );\n }\n\n let bootstrapScriptContent: string = \"\";\n if (options.build) {\n if (options.ssr) {\n // SSR on: no marker needed, client hydrates full document\n bootstrapScriptContent += `globalThis.${appClientManifestVar}={stream:\"${rscPayloadPlaceholder}\"};\\n`;\n } else {\n // SSR off: marker needed for client to find mount point\n bootstrapScriptContent += `globalThis.${appClientManifestVar}={marker:\"${options.appEntryMarker}\",stream:\"${rscPayloadPlaceholder}\"};\\n`;\n }\n }\n bootstrapScriptContent +=\n await import.meta.viteRsc.loadBootstrapScriptContent(\"index\");\n\n let htmlStream: ReadableStream<Uint8Array>;\n let status: number | undefined;\n try {\n htmlStream = await renderToReadableStream(<SsrRoot />, {\n bootstrapScriptContent,\n nonce: options?.nonce,\n });\n } catch {\n // In this case, RSC payload is still sent to client and we let client render from scratch anyway.\n // This triggers the error boundary on client side.\n status = 500;\n htmlStream = await renderToReadableStream(\n <html>\n <body>\n <noscript>Internal Server Error: SSR failed</noscript>\n </body>\n </html>,\n {\n bootstrapScriptContent:\n `globalThis.__NO_HYDRATE=1;` + bootstrapScriptContent,\n nonce: options?.nonce,\n },\n );\n }\n\n let responseStream = htmlStream;\n\n // Inject RSC payload into HTML for client consumption.\n // In dev: always inject (client reads from inline stream).\n // In build+SSR: skip (HTML already has full content, client hydrates directly).\n // In build+no-SSR: skip (client fetches RSC from separate file).\n if (!options.build) {\n const streamToInject = options.clientRscStream ?? rscStream2;\n responseStream = responseStream.pipeThrough(\n injectRSCPayload(streamToInject, {\n nonce: options?.nonce,\n }),\n );\n }\n\n return { stream: responseStream, status };\n}\n"],"mappings":";;;;;;;;;;;AAWA,eAAsB,WACpB,WACA,SAQkE;CAClE,MAAM,CAAC,YAAY,cAAc,UAAU,KAAK;CAEhD,IAAI;CACJ,SAAS,UAAU;AAGjB,cAAY,yBAAqC,WAAW;AAC5D,MAAI,QAAQ,MACV,SAAQ,uBAAuB;GAC7B,aAAa;GACb,IAAI;GACL,CAAC;AAEJ,SACE,oBAAC;GAAgB,OAAO,QAAQ;aAC7B,IAAI,QAAQ,CAAC;IACE;;CAItB,IAAI,yBAAiC;AACrC,KAAI,QAAQ,MACV,KAAI,QAAQ,IAEV,2BAA0B,cAAc,qBAAqB,YAAY,sBAAsB;KAG/F,2BAA0B,cAAc,qBAAqB,YAAY,QAAQ,eAAe,YAAY,sBAAsB;AAGtI,2BACE,MAAM,OAAO,KAAK,QAAQ,2BAA2B,QAAQ;CAE/D,IAAI;CACJ,IAAI;AACJ,KAAI;AACF,eAAa,MAAM,uBAAuB,oBAAC,YAAU,EAAE;GACrD;GACA,OAAO,SAAS;GACjB,CAAC;SACI;AAGN,WAAS;AACT,eAAa,MAAM,uBACjB,oBAAC,oBACC,oBAAC,oBACC,oBAAC,wBAAS,sCAA4C,GACjD,GACF,EACP;GACE,wBACE,+BAA+B;GACjC,OAAO,SAAS;GACjB,CACF;;CAGH,IAAI,iBAAiB;AAMrB,KAAI,CAAC,QAAQ,OAAO;EAClB,MAAM,iBAAiB,QAAQ,mBAAmB;AAClD,mBAAiB,eAAe,YAC9B,iBAAiB,gBAAgB,EAC/B,OAAO,SAAS,OACjB,CAAC,CACH;;AAGH,QAAO;EAAE,QAAQ;EAAgB;EAAQ"}
1
+ {"version":3,"file":"entry.mjs","names":[],"sources":["../../src/ssr/entry.tsx"],"sourcesContent":["import { createFromReadableStream } from \"@vitejs/plugin-rsc/ssr\";\nimport { use } from \"react\";\nimport { renderToReadableStream } from \"react-dom/server.edge\";\nimport { prerender } from \"react-dom/static\";\nimport { injectRSCPayload } from \"rsc-html-stream/server\";\nimport type { RscPayload } from \"../rsc/entry\";\nimport { appClientManifestVar } from \"../client/globals\";\nimport { rscPayloadPlaceholder } from \"../build/rscPath\";\nimport { preload } from \"react-dom\";\nimport type { DeferRegistry } from \"../rsc/defer\";\nimport { RegistryContext } from \"#rsc-client\";\n\nexport async function renderHTML(\n rscStream: ReadableStream<Uint8Array>,\n options: {\n appEntryMarker: string;\n build: boolean;\n ssr?: boolean;\n nonce?: string;\n deferRegistry?: DeferRegistry;\n clientRscStream?: ReadableStream<Uint8Array>;\n },\n): Promise<{ stream: ReadableStream<Uint8Array>; status?: number }> {\n const [rscStream1, rscStream2] = rscStream.tee();\n\n let payload: Promise<RscPayload> | undefined;\n function SsrRoot() {\n // Tip: calling `createFromReadableStream` inside a component\n // makes `preinit`/`preload` work properly.\n payload ??= createFromReadableStream<RscPayload>(rscStream1);\n if (options.build) {\n preload(rscPayloadPlaceholder, {\n crossOrigin: \"anonymous\",\n as: \"fetch\",\n });\n }\n return (\n <RegistryContext\n value={\n options.deferRegistry\n ? {\n registry: options.deferRegistry,\n createFromReadableStream,\n }\n : undefined\n }\n >\n {use(payload).root}\n </RegistryContext>\n );\n }\n\n let bootstrapScriptContent: string = \"\";\n if (options.build) {\n if (options.ssr) {\n // SSR on: no marker needed, client hydrates full document\n bootstrapScriptContent += `globalThis.${appClientManifestVar}={stream:\"${rscPayloadPlaceholder}\"};\\n`;\n } else {\n // SSR off: marker needed for client to find mount point\n bootstrapScriptContent += `globalThis.${appClientManifestVar}={marker:\"${options.appEntryMarker}\",stream:\"${rscPayloadPlaceholder}\"};\\n`;\n }\n }\n bootstrapScriptContent +=\n await import.meta.viteRsc.loadBootstrapScriptContent(\"index\");\n\n let htmlStream: ReadableStream<Uint8Array>;\n let status: number | undefined;\n try {\n if (options.build) {\n const { prelude, postponed } = await prerender(<SsrRoot />, {\n bootstrapScriptContent,\n });\n if (postponed !== null) {\n throw new Error(\"Unexpected postponed state during prerendering\");\n }\n\n htmlStream = prelude;\n } else {\n htmlStream = await renderToReadableStream(<SsrRoot />, {\n bootstrapScriptContent,\n nonce: options?.nonce,\n });\n }\n } catch (e) {\n if (options.build) {\n // In build mode, abort the build so the error is not silently swallowed.\n throw e;\n }\n // In dev mode, RSC payload is still sent to client and we let client render from scratch anyway.\n // This triggers the error boundary on client side.\n status = 500;\n htmlStream = await renderToReadableStream(\n <html>\n <body>\n <noscript>Internal Server Error: SSR failed</noscript>\n </body>\n </html>,\n {\n bootstrapScriptContent:\n `globalThis.__NO_HYDRATE=1;` + bootstrapScriptContent,\n nonce: options?.nonce,\n },\n );\n }\n\n let responseStream = htmlStream;\n\n // Inject RSC payload into HTML for client consumption.\n // In dev: always inject (client reads from inline stream).\n // In build+SSR: skip (HTML already has full content, client hydrates directly).\n // In build+no-SSR: skip (client fetches RSC from separate file).\n if (!options.build) {\n const streamToInject = options.clientRscStream ?? rscStream2;\n responseStream = responseStream.pipeThrough(\n injectRSCPayload(streamToInject, {\n nonce: options?.nonce,\n }),\n );\n }\n\n return { stream: responseStream, status };\n}\n"],"mappings":";;;;;;;;;;;AAYA,eAAsB,WACpB,WACA,SAQkE;CAClE,MAAM,CAAC,YAAY,cAAc,UAAU,KAAK;CAEhD,IAAI;CACJ,SAAS,UAAU;AAGjB,cAAY,yBAAqC,WAAW;AAC5D,MAAI,QAAQ,MACV,SAAQ,uBAAuB;GAC7B,aAAa;GACb,IAAI;GACL,CAAC;AAEJ,SACE,oBAAC,iBAAD;GACE,OACE,QAAQ,gBACJ;IACE,UAAU,QAAQ;IAClB;IACD,GACD,KAAA;aAGL,IAAI,QAAQ,CAAC;GACE,CAAA;;CAItB,IAAI,yBAAiC;AACrC,KAAI,QAAQ,MACV,KAAI,QAAQ,IAEV,2BAA0B,cAAc,qBAAqB,YAAY,sBAAsB;KAG/F,2BAA0B,cAAc,qBAAqB,YAAY,QAAQ,eAAe,YAAY,sBAAsB;AAGtI,2BACE,MAAM,OAAO,KAAK,QAAQ,2BAA2B,QAAQ;CAE/D,IAAI;CACJ,IAAI;AACJ,KAAI;AACF,MAAI,QAAQ,OAAO;GACjB,MAAM,EAAE,SAAS,cAAc,MAAM,UAAU,oBAAC,SAAD,EAAW,CAAA,EAAE,EAC1D,wBACD,CAAC;AACF,OAAI,cAAc,KAChB,OAAM,IAAI,MAAM,iDAAiD;AAGnE,gBAAa;QAEb,cAAa,MAAM,uBAAuB,oBAAC,SAAD,EAAW,CAAA,EAAE;GACrD;GACA,OAAO,SAAS;GACjB,CAAC;UAEG,GAAG;AACV,MAAI,QAAQ,MAEV,OAAM;AAIR,WAAS;AACT,eAAa,MAAM,uBACjB,oBAAC,QAAD,EAAA,UACE,oBAAC,QAAD,EAAA,UACE,oBAAC,YAAD,EAAA,UAAU,qCAA4C,CAAA,EACjD,CAAA,EACF,CAAA,EACP;GACE,wBACE,+BAA+B;GACjC,OAAO,SAAS;GACjB,CACF;;CAGH,IAAI,iBAAiB;AAMrB,KAAI,CAAC,QAAQ,OAAO;EAClB,MAAM,iBAAiB,QAAQ,mBAAmB;AAClD,mBAAiB,eAAe,YAC9B,iBAAiB,gBAAgB,EAC/B,OAAO,SAAS,OACjB,CAAC,CACH;;AAGH,QAAO;EAAE,QAAQ;EAAgB;EAAQ"}
@@ -22,7 +22,7 @@ function withBasePath(path) {
22
22
  if (base === "/") return path;
23
23
  return (base.endsWith("/") ? base.slice(0, -1) : base) + path;
24
24
  }
25
-
26
25
  //#endregion
27
26
  export { stripBasePath, withBasePath };
27
+
28
28
  //# sourceMappingURL=basePath.mjs.map
@@ -6,7 +6,7 @@ async function drainStream(stream) {
6
6
  chunks.push(decoder.decode());
7
7
  return chunks.join("");
8
8
  }
9
-
10
9
  //#endregion
11
10
  export { drainStream };
11
+
12
12
  //# sourceMappingURL=drainStream.mjs.map
@@ -16,7 +16,7 @@ function urlPathToFileCandidates(urlPath) {
16
16
  `${stripped}/index.htm`
17
17
  ];
18
18
  }
19
-
20
19
  //#endregion
21
20
  export { urlPathToFileCandidates };
21
+
22
22
  //# sourceMappingURL=urlPath.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funstack/static",
3
- "version": "0.0.9",
3
+ "version": "1.0.0",
4
4
  "description": "FUNSTACK static library",
5
5
  "type": "module",
6
6
  "repository": {
@@ -47,23 +47,23 @@
47
47
  "license": "MIT",
48
48
  "devDependencies": {
49
49
  "@playwright/test": "^1.58.2",
50
- "@types/node": "^25.2.3",
50
+ "@types/node": "^25.3.5",
51
51
  "@types/react": "^19.2.14",
52
52
  "@types/react-dom": "^19.2.3",
53
53
  "jsdom": "^28.1.0",
54
54
  "react": "^19.2.4",
55
55
  "react-dom": "^19.2.4",
56
- "tsdown": "^0.20.3",
56
+ "tsdown": "^0.21.0",
57
57
  "typescript": "^5.9.3",
58
58
  "vite": "^7.3.1",
59
59
  "vitest": "^4.0.18"
60
60
  },
61
61
  "dependencies": {
62
62
  "@funstack/skill-installer": "^1.0.0",
63
- "@vitejs/plugin-rsc": "^0.5.19",
63
+ "@vitejs/plugin-rsc": "^0.5.21",
64
64
  "react-error-boundary": "^6.1.1",
65
65
  "rsc-html-stream": "^0.0.7",
66
- "srvx": "^0.11.4"
66
+ "srvx": "^0.11.8"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "react": "^19.2.3",
@@ -1 +0,0 @@
1
- import { DeferredComponent, RegistryContext } from "./clientWrapper.mjs";