@funstack/static 0.0.10 → 1.1.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 (70) hide show
  1. package/README.md +4 -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 +7 -8
  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 +3 -4
  10. package/dist/build/rscPath.mjs.map +1 -1
  11. package/dist/build/rscProcessor.mjs +4 -4
  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 +10 -6
  22. package/dist/docs/MigratingFromViteSPA.md +5 -5
  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 +26 -7
  27. package/dist/docs/index.md +5 -2
  28. package/dist/docs/learn/DeferAndActivity.md +3 -3
  29. package/dist/docs/learn/HowItWorks.md +3 -3
  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.mjs +1 -3
  36. package/dist/entries/rsc.mjs +1 -2
  37. package/dist/entries/server.mjs +1 -2
  38. package/dist/entries/ssr.mjs +1 -2
  39. package/dist/entryDefinition.mjs +1 -1
  40. package/dist/index.mjs +1 -2
  41. package/dist/plugin/getRSCEntryPoint.mjs +1 -1
  42. package/dist/plugin/getRSCEntryPoint.mjs.map +1 -1
  43. package/dist/plugin/index.d.mts +14 -0
  44. package/dist/plugin/index.d.mts.map +1 -1
  45. package/dist/plugin/index.mjs +6 -5
  46. package/dist/plugin/index.mjs.map +1 -1
  47. package/dist/plugin/server.mjs +1 -2
  48. package/dist/plugin/server.mjs.map +1 -1
  49. package/dist/rsc/defer.d.mts.map +1 -1
  50. package/dist/rsc/defer.mjs +3 -3
  51. package/dist/rsc/defer.mjs.map +1 -1
  52. package/dist/rsc/entry.mjs +4 -5
  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 +8 -8
  59. package/dist/rsc/rscModule.mjs.map +1 -1
  60. package/dist/rsc-client/clientWrapper.mjs +2 -3
  61. package/dist/rsc-client/clientWrapper.mjs.map +1 -1
  62. package/dist/rsc-client/entry.d.mts +1 -0
  63. package/dist/rsc-client/entry.mjs +2 -4
  64. package/dist/ssr/entry.mjs +2 -2
  65. package/dist/ssr/entry.mjs.map +1 -1
  66. package/dist/util/basePath.mjs +1 -1
  67. package/dist/util/drainStream.mjs +1 -1
  68. package/dist/util/urlPath.mjs +1 -1
  69. package/package.json +5 -5
  70. package/skills/funstack-static-knowledge/SKILL.md +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"resolveEntry.mjs","names":[],"sources":["../../src/rsc/resolveEntry.ts"],"sourcesContent":["import { createElement } from \"react\";\nimport type { EntryDefinition } from \"../entryDefinition\";\n\n/**\n * Resolves the root field of an EntryDefinition to a concrete React component.\n */\nexport async function resolveRoot(\n root: EntryDefinition[\"root\"],\n): Promise<React.ComponentType<{ children: React.ReactNode }>> {\n const module = typeof root === \"function\" ? await root() : await root;\n return module.default;\n}\n\n/**\n * Checks whether a value is an AppModule (has a `default` property that is a function).\n */\nfunction isAppModule(\n value: unknown,\n): value is { default: React.ComponentType } {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"default\" in value &&\n typeof (value as Record<string, unknown>).default === \"function\"\n );\n}\n\n/**\n * Resolves the app field of an EntryDefinition to a React node.\n */\nexport async function resolveApp(\n app: EntryDefinition[\"app\"],\n): Promise<React.ReactNode> {\n if (typeof app === \"function\") {\n // Lazy import: () => Promise<{ default: Component }>\n const module = await app();\n return createElement(module.default);\n }\n if (isAppModule(app)) {\n // Sync module object: { default: Component }\n return createElement(app.default);\n }\n // Could be a Promise<AppModule> or a ReactNode (including Promise<ReactNode>).\n // Await it and check the resolved value.\n const resolved = await app;\n if (isAppModule(resolved)) {\n return createElement(resolved.default);\n }\n // ReactNode (JSX of a server component, or a resolved ReactNode)\n return resolved;\n}\n"],"mappings":";;;;;;AAMA,eAAsB,YACpB,MAC6D;AAE7D,SADe,OAAO,SAAS,aAAa,MAAM,MAAM,GAAG,MAAM,MACnD;;;;;AAMhB,SAAS,YACP,OAC2C;AAC3C,QACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,OAAQ,MAAkC,YAAY;;;;;AAO1D,eAAsB,WACpB,KAC0B;AAC1B,KAAI,OAAO,QAAQ,WAGjB,QAAO,eADQ,MAAM,KAAK,EACE,QAAQ;AAEtC,KAAI,YAAY,IAAI,CAElB,QAAO,cAAc,IAAI,QAAQ;CAInC,MAAM,WAAW,MAAM;AACvB,KAAI,YAAY,SAAS,CACvB,QAAO,cAAc,SAAS,QAAQ;AAGxC,QAAO"}
1
+ {"version":3,"file":"resolveEntry.mjs","names":[],"sources":["../../src/rsc/resolveEntry.ts"],"sourcesContent":["import { createElement } from \"react\";\nimport type { EntryDefinition } from \"../entryDefinition\";\n\n/**\n * Resolves the root field of an EntryDefinition to a concrete React component.\n */\nexport async function resolveRoot(\n root: EntryDefinition[\"root\"],\n): Promise<React.ComponentType<{ children: React.ReactNode }>> {\n const module = typeof root === \"function\" ? await root() : await root;\n return module.default;\n}\n\n/**\n * Checks whether a value is an AppModule (has a `default` property that is a function).\n */\nfunction isAppModule(\n value: unknown,\n): value is { default: React.ComponentType } {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"default\" in value &&\n typeof (value as Record<string, unknown>).default === \"function\"\n );\n}\n\n/**\n * Resolves the app field of an EntryDefinition to a React node.\n */\nexport async function resolveApp(\n app: EntryDefinition[\"app\"],\n): Promise<React.ReactNode> {\n if (typeof app === \"function\") {\n // Lazy import: () => Promise<{ default: Component }>\n const module = await app();\n return createElement(module.default);\n }\n if (isAppModule(app)) {\n // Sync module object: { default: Component }\n return createElement(app.default);\n }\n // Could be a Promise<AppModule> or a ReactNode (including Promise<ReactNode>).\n // Await it and check the resolved value.\n const resolved = await app;\n if (isAppModule(resolved)) {\n return createElement(resolved.default);\n }\n // ReactNode (JSX of a server component, or a resolved ReactNode)\n return resolved;\n}\n"],"mappings":";;;;;AAMA,eAAsB,YACpB,MAC6D;AAE7D,SADe,OAAO,SAAS,aAAa,MAAM,MAAM,GAAG,MAAM,MACnD;;;;;AAMhB,SAAS,YACP,OAC2C;AAC3C,QACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,OAAQ,MAAkC,YAAY;;;;;AAO1D,eAAsB,WACpB,KAC0B;AAC1B,KAAI,OAAO,QAAQ,WAGjB,QAAO,eADQ,MAAM,KAAK,EACE,QAAQ;AAEtC,KAAI,YAAY,IAAI,CAElB,QAAO,cAAc,IAAI,QAAQ;CAInC,MAAM,WAAW,MAAM;AACvB,KAAI,YAAY,SAAS,CACvB,QAAO,cAAc,SAAS,QAAQ;AAGxC,QAAO"}
@@ -1,14 +1,14 @@
1
1
  //#region src/rsc/rscModule.ts
2
2
  /**
3
- * ID is prefixed with this string to form module path.
3
+ * Default directory name for RSC payload files.
4
4
  */
5
- const rscPayloadIDPrefix = "fun:rsc-payload/";
5
+ const defaultRscPayloadDir = "fun:rsc-payload";
6
6
  /**
7
- * Add prefix to raw ID to form payload ID so that the ID is
8
- * distinguishable from other possible IDs.
7
+ * Combines the RSC payload directory with a raw ID to form a
8
+ * namespaced payload ID (e.g. "fun:rsc-payload/abc123").
9
9
  */
10
- function getPayloadIDFor(rawId) {
11
- return `${rscPayloadIDPrefix}${rawId}`;
10
+ function getPayloadIDFor(rawId, rscPayloadDir = defaultRscPayloadDir) {
11
+ return `${rscPayloadDir}/${rawId}`;
12
12
  }
13
13
  const rscModulePathPrefix = "/funstack__/";
14
14
  const rscModulePathSuffix = ".txt";
@@ -19,7 +19,7 @@ function extractIDFromModulePath(modulePath) {
19
19
  if (!modulePath.startsWith(rscModulePathPrefix) || !modulePath.endsWith(rscModulePathSuffix)) return;
20
20
  return modulePath.slice(12, -4);
21
21
  }
22
-
23
22
  //#endregion
24
- export { extractIDFromModulePath, getModulePathFor, getPayloadIDFor };
23
+ export { defaultRscPayloadDir, extractIDFromModulePath, getModulePathFor, getPayloadIDFor };
24
+
25
25
  //# sourceMappingURL=rscModule.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"rscModule.mjs","names":[],"sources":["../../src/rsc/rscModule.ts"],"sourcesContent":["/**\n * ID is prefixed with this string to form module path.\n */\nconst rscPayloadIDPrefix = \"fun:rsc-payload/\";\n\n/**\n * Add prefix to raw ID to form payload ID so that the ID is\n * distinguishable from other possible IDs.\n */\nexport function getPayloadIDFor(rawId: string): string {\n return `${rscPayloadIDPrefix}${rawId}`;\n}\n\nconst rscModulePathPrefix = \"/funstack__/\";\nconst rscModulePathSuffix = \".txt\";\n\nexport function getModulePathFor(id: string): string {\n return `${rscModulePathPrefix}${id}${rscModulePathSuffix}`;\n}\n\nexport function extractIDFromModulePath(\n modulePath: string,\n): string | undefined {\n if (\n !modulePath.startsWith(rscModulePathPrefix) ||\n !modulePath.endsWith(rscModulePathSuffix)\n ) {\n return undefined;\n }\n return modulePath.slice(\n rscModulePathPrefix.length,\n -rscModulePathSuffix.length,\n );\n}\n"],"mappings":";;;;AAGA,MAAM,qBAAqB;;;;;AAM3B,SAAgB,gBAAgB,OAAuB;AACrD,QAAO,GAAG,qBAAqB;;AAGjC,MAAM,sBAAsB;AAC5B,MAAM,sBAAsB;AAE5B,SAAgB,iBAAiB,IAAoB;AACnD,QAAO,GAAG,sBAAsB,KAAK;;AAGvC,SAAgB,wBACd,YACoB;AACpB,KACE,CAAC,WAAW,WAAW,oBAAoB,IAC3C,CAAC,WAAW,SAAS,oBAAoB,CAEzC;AAEF,QAAO,WAAW,MAChB,IACA,GACD"}
1
+ {"version":3,"file":"rscModule.mjs","names":[],"sources":["../../src/rsc/rscModule.ts"],"sourcesContent":["/**\n * Default directory name for RSC payload files.\n */\nexport const defaultRscPayloadDir = \"fun:rsc-payload\";\n\n/**\n * Combines the RSC payload directory with a raw ID to form a\n * namespaced payload ID (e.g. \"fun:rsc-payload/abc123\").\n */\nexport function getPayloadIDFor(\n rawId: string,\n rscPayloadDir: string = defaultRscPayloadDir,\n): string {\n return `${rscPayloadDir}/${rawId}`;\n}\n\nconst rscModulePathPrefix = \"/funstack__/\";\nconst rscModulePathSuffix = \".txt\";\n\nexport function getModulePathFor(id: string): string {\n return `${rscModulePathPrefix}${id}${rscModulePathSuffix}`;\n}\n\nexport function extractIDFromModulePath(\n modulePath: string,\n): string | undefined {\n if (\n !modulePath.startsWith(rscModulePathPrefix) ||\n !modulePath.endsWith(rscModulePathSuffix)\n ) {\n return undefined;\n }\n return modulePath.slice(\n rscModulePathPrefix.length,\n -rscModulePathSuffix.length,\n );\n}\n"],"mappings":";;;;AAGA,MAAa,uBAAuB;;;;;AAMpC,SAAgB,gBACd,OACA,gBAAwB,sBAChB;AACR,QAAO,GAAG,cAAc,GAAG;;AAG7B,MAAM,sBAAsB;AAC5B,MAAM,sBAAsB;AAE5B,SAAgB,iBAAiB,IAAoB;AACnD,QAAO,GAAG,sBAAsB,KAAK;;AAGvC,SAAgB,wBACd,YACoB;AACpB,KACE,CAAC,WAAW,WAAW,oBAAoB,IAC3C,CAAC,WAAW,SAAS,oBAAoB,CAEzC;AAEF,QAAO,WAAW,MAChB,IACA,GACD"}
@@ -1,8 +1,7 @@
1
1
  import { getModulePathFor } from "../rsc/rscModule.mjs";
2
2
  import { withBasePath } from "../util/basePath.mjs";
3
3
  import { createFromFetch } from "@vitejs/plugin-rsc/browser";
4
- import React, { createContext, use } from "react";
5
-
4
+ import { createContext, use } from "react";
6
5
  //#region src/rsc-client/clientWrapper.tsx
7
6
  const RegistryContext = createContext(void 0);
8
7
  const DeferredComponent = ({ moduleID }) => {
@@ -38,7 +37,7 @@ function getClientRSCStream(modulePath) {
38
37
  }
39
38
  return stream;
40
39
  }
41
-
42
40
  //#endregion
43
41
  export { DeferredComponent, RegistryContext };
42
+
44
43
  //# sourceMappingURL=clientWrapper.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"clientWrapper.mjs","names":[],"sources":["../../src/rsc-client/clientWrapper.tsx"],"sourcesContent":["import React from \"react\";\nimport { createFromFetch } from \"@vitejs/plugin-rsc/browser\";\nimport { getModulePathFor } from \"../rsc/rscModule\";\nimport { createContext, use } from \"react\";\nimport type { LoadedDeferEntry, DeferRegistry } from \"../rsc/defer\";\nimport { withBasePath } from \"../util/basePath\";\n\ninterface DeferContextValue {\n registry: DeferRegistry;\n createFromReadableStream: <T>(\n stream: ReadableStream<Uint8Array>,\n ) => Promise<T>;\n}\n\nexport const RegistryContext = createContext<DeferContextValue | undefined>(\n undefined,\n);\n\ninterface DeferredComponentProps {\n moduleID: string;\n}\n\nexport const DeferredComponent: React.FC<DeferredComponentProps> = ({\n moduleID,\n}) => {\n const deferContext = use(RegistryContext);\n const modulePath = getModulePathFor(moduleID);\n if (deferContext) {\n const entry = deferContext.registry.load(moduleID);\n if (!entry) {\n throw new Error(`Module entry not found for ID '${moduleID}'`);\n }\n return getRSCStreamFromRegistry(\n entry,\n deferContext.createFromReadableStream,\n );\n }\n const stream = getClientRSCStream(withBasePath(modulePath));\n return use(stream);\n};\n\nconst moduleToStreamMap = new Map<string, Promise<React.ReactNode>>();\n\nasync function getRSCStreamFromRegistry(\n entry: LoadedDeferEntry,\n createFromReadableStream: <T>(\n stream: ReadableStream<Uint8Array>,\n ) => Promise<T>,\n): Promise<React.ReactNode> {\n switch (entry.state.state) {\n case \"streaming\": {\n return createFromReadableStream<React.ReactNode>(entry.state.stream);\n }\n case \"ready\": {\n const data = await entry.drainPromise;\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n const encoder = new TextEncoder();\n controller.enqueue(encoder.encode(data));\n controller.close();\n },\n });\n return createFromReadableStream<React.ReactNode>(stream);\n }\n case \"error\": {\n return Promise.reject(entry.state.error);\n }\n }\n}\n\nfunction getClientRSCStream(modulePath: string) {\n let stream = moduleToStreamMap.get(modulePath);\n if (!stream) {\n stream = createFromFetch<React.ReactNode>(fetch(modulePath));\n moduleToStreamMap.set(modulePath, stream);\n }\n return stream;\n}\n"],"mappings":";;;;;;AAcA,MAAa,kBAAkB,cAC7B,OACD;AAMD,MAAa,qBAAuD,EAClE,eACI;CACJ,MAAM,eAAe,IAAI,gBAAgB;CACzC,MAAM,aAAa,iBAAiB,SAAS;AAC7C,KAAI,cAAc;EAChB,MAAM,QAAQ,aAAa,SAAS,KAAK,SAAS;AAClD,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,kCAAkC,SAAS,GAAG;AAEhE,SAAO,yBACL,OACA,aAAa,yBACd;;AAGH,QAAO,IADQ,mBAAmB,aAAa,WAAW,CAAC,CACzC;;AAGpB,MAAM,oCAAoB,IAAI,KAAuC;AAErE,eAAe,yBACb,OACA,0BAG0B;AAC1B,SAAQ,MAAM,MAAM,OAApB;EACE,KAAK,YACH,QAAO,yBAA0C,MAAM,MAAM,OAAO;EAEtE,KAAK,SAAS;GACZ,MAAM,OAAO,MAAM,MAAM;AAQzB,UAAO,yBAPQ,IAAI,eAA2B,EAC5C,MAAM,YAAY;IAChB,MAAM,UAAU,IAAI,aAAa;AACjC,eAAW,QAAQ,QAAQ,OAAO,KAAK,CAAC;AACxC,eAAW,OAAO;MAErB,CAAC,CACsD;;EAE1D,KAAK,QACH,QAAO,QAAQ,OAAO,MAAM,MAAM,MAAM;;;AAK9C,SAAS,mBAAmB,YAAoB;CAC9C,IAAI,SAAS,kBAAkB,IAAI,WAAW;AAC9C,KAAI,CAAC,QAAQ;AACX,WAAS,gBAAiC,MAAM,WAAW,CAAC;AAC5D,oBAAkB,IAAI,YAAY,OAAO;;AAE3C,QAAO"}
1
+ {"version":3,"file":"clientWrapper.mjs","names":[],"sources":["../../src/rsc-client/clientWrapper.tsx"],"sourcesContent":["import React from \"react\";\nimport { createFromFetch } from \"@vitejs/plugin-rsc/browser\";\nimport { getModulePathFor } from \"../rsc/rscModule\";\nimport { createContext, use } from \"react\";\nimport type { LoadedDeferEntry, DeferRegistry } from \"../rsc/defer\";\nimport { withBasePath } from \"../util/basePath\";\n\ninterface DeferContextValue {\n registry: DeferRegistry;\n createFromReadableStream: <T>(\n stream: ReadableStream<Uint8Array>,\n ) => Promise<T>;\n}\n\nexport const RegistryContext = createContext<DeferContextValue | undefined>(\n undefined,\n);\n\ninterface DeferredComponentProps {\n moduleID: string;\n}\n\nexport const DeferredComponent: React.FC<DeferredComponentProps> = ({\n moduleID,\n}) => {\n const deferContext = use(RegistryContext);\n const modulePath = getModulePathFor(moduleID);\n if (deferContext) {\n const entry = deferContext.registry.load(moduleID);\n if (!entry) {\n throw new Error(`Module entry not found for ID '${moduleID}'`);\n }\n return getRSCStreamFromRegistry(\n entry,\n deferContext.createFromReadableStream,\n );\n }\n const stream = getClientRSCStream(withBasePath(modulePath));\n return use(stream);\n};\n\nconst moduleToStreamMap = new Map<string, Promise<React.ReactNode>>();\n\nasync function getRSCStreamFromRegistry(\n entry: LoadedDeferEntry,\n createFromReadableStream: <T>(\n stream: ReadableStream<Uint8Array>,\n ) => Promise<T>,\n): Promise<React.ReactNode> {\n switch (entry.state.state) {\n case \"streaming\": {\n return createFromReadableStream<React.ReactNode>(entry.state.stream);\n }\n case \"ready\": {\n const data = await entry.drainPromise;\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n const encoder = new TextEncoder();\n controller.enqueue(encoder.encode(data));\n controller.close();\n },\n });\n return createFromReadableStream<React.ReactNode>(stream);\n }\n case \"error\": {\n return Promise.reject(entry.state.error);\n }\n }\n}\n\nfunction getClientRSCStream(modulePath: string) {\n let stream = moduleToStreamMap.get(modulePath);\n if (!stream) {\n stream = createFromFetch<React.ReactNode>(fetch(modulePath));\n moduleToStreamMap.set(modulePath, stream);\n }\n return stream;\n}\n"],"mappings":";;;;;AAcA,MAAa,kBAAkB,cAC7B,KAAA,EACD;AAMD,MAAa,qBAAuD,EAClE,eACI;CACJ,MAAM,eAAe,IAAI,gBAAgB;CACzC,MAAM,aAAa,iBAAiB,SAAS;AAC7C,KAAI,cAAc;EAChB,MAAM,QAAQ,aAAa,SAAS,KAAK,SAAS;AAClD,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,kCAAkC,SAAS,GAAG;AAEhE,SAAO,yBACL,OACA,aAAa,yBACd;;AAGH,QAAO,IADQ,mBAAmB,aAAa,WAAW,CAAC,CACzC;;AAGpB,MAAM,oCAAoB,IAAI,KAAuC;AAErE,eAAe,yBACb,OACA,0BAG0B;AAC1B,SAAQ,MAAM,MAAM,OAApB;EACE,KAAK,YACH,QAAO,yBAA0C,MAAM,MAAM,OAAO;EAEtE,KAAK,SAAS;GACZ,MAAM,OAAO,MAAM,MAAM;AAQzB,UAAO,yBAPQ,IAAI,eAA2B,EAC5C,MAAM,YAAY;IAChB,MAAM,UAAU,IAAI,aAAa;AACjC,eAAW,QAAQ,QAAQ,OAAO,KAAK,CAAC;AACxC,eAAW,OAAO;MAErB,CAAC,CACsD;;EAE1D,KAAK,QACH,QAAO,QAAQ,OAAO,MAAM,MAAM,MAAM;;;AAK9C,SAAS,mBAAmB,YAAoB;CAC9C,IAAI,SAAS,kBAAkB,IAAI,WAAW;AAC9C,KAAI,CAAC,QAAQ;AACX,WAAS,gBAAiC,MAAM,WAAW,CAAC;AAC5D,oBAAkB,IAAI,YAAY,OAAO;;AAE3C,QAAO"}
@@ -0,0 +1 @@
1
+ import { DeferredComponent, RegistryContext } from "./clientWrapper.mjs";
@@ -1,5 +1,3 @@
1
1
  "use client";
2
-
3
- import { DeferredComponent, RegistryContext } from "./clientWrapper.mjs";
4
-
5
- export { };
2
+ import "./clientWrapper.mjs";
3
+ export {};
@@ -8,7 +8,6 @@ import { renderToReadableStream } from "react-dom/server.edge";
8
8
  import { prerender } from "react-dom/static";
9
9
  import { injectRSCPayload } from "rsc-html-stream/server";
10
10
  import { preload } from "react-dom";
11
-
12
11
  //#region src/ssr/entry.tsx
13
12
  async function renderHTML(rscStream, options) {
14
13
  const [rscStream1, rscStream2] = rscStream.tee();
@@ -43,6 +42,7 @@ async function renderHTML(rscStream, options) {
43
42
  nonce: options?.nonce
44
43
  });
45
44
  } catch (e) {
45
+ if (options.build) throw e;
46
46
  status = 500;
47
47
  htmlStream = await renderToReadableStream(/* @__PURE__ */ jsx("html", { children: /* @__PURE__ */ jsx("body", { children: /* @__PURE__ */ jsx("noscript", { children: "Internal Server Error: SSR failed" }) }) }), {
48
48
  bootstrapScriptContent: `globalThis.__NO_HYDRATE=1;` + bootstrapScriptContent,
@@ -59,7 +59,7 @@ async function renderHTML(rscStream, options) {
59
59
  status
60
60
  };
61
61
  }
62
-
63
62
  //#endregion
64
63
  export { renderHTML };
64
+
65
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 { 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 // 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":";;;;;;;;;;;;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;GACC,OACE,QAAQ,gBACJ;IACE,UAAU,QAAQ;IAClB;IACD,GACD;aAGL,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,MAAI,QAAQ,OAAO;GACjB,MAAM,EAAE,SAAS,cAAc,MAAM,UAAU,oBAAC,YAAU,EAAE,EAC1D,wBACD,CAAC;AACF,OAAI,cAAc,KAChB,OAAM,IAAI,MAAM,iDAAiD;AAGnE,gBAAa;QAEb,cAAa,MAAM,uBAAuB,oBAAC,YAAU,EAAE;GACrD;GACA,OAAO,SAAS;GACjB,CAAC;UAEG,GAAG;AAGV,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.10",
3
+ "version": "1.1.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",
@@ -16,7 +16,7 @@ FUNSTACk Static is served as a Vite plugin. See your app's `vite.config.ts` file
16
16
  ```ts
17
17
  import { defineConfig } from "vite";
18
18
  import react from "@vitejs/plugin-react";
19
- import { funstackStatic } from "@funstack/static";
19
+ import funstackStatic from "@funstack/static";
20
20
 
21
21
  export default defineConfig({
22
22
  plugins: [