every-plugin 3.0.0-rc.3 → 3.0.0-rc.5

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 (56) hide show
  1. package/dist/build/ui/generated-config.cjs +4 -0
  2. package/dist/build/ui/generated-config.cjs.map +1 -1
  3. package/dist/build/ui/generated-config.d.cts.map +1 -1
  4. package/dist/build/ui/generated-config.d.mts.map +1 -1
  5. package/dist/build/ui/generated-config.mjs +4 -1
  6. package/dist/build/ui/generated-config.mjs.map +1 -1
  7. package/dist/client.cjs +69 -0
  8. package/dist/client.cjs.map +1 -0
  9. package/dist/client.d.cts +15 -0
  10. package/dist/client.d.cts.map +1 -0
  11. package/dist/client.d.mts +15 -0
  12. package/dist/client.d.mts.map +1 -0
  13. package/dist/client.mjs +68 -0
  14. package/dist/client.mjs.map +1 -0
  15. package/dist/dev/host.cjs +149 -0
  16. package/dist/dev/host.cjs.map +1 -0
  17. package/dist/dev/host.d.cts +32 -0
  18. package/dist/dev/host.d.cts.map +1 -0
  19. package/dist/dev/host.d.mts +32 -0
  20. package/dist/dev/host.d.mts.map +1 -0
  21. package/dist/dev/host.mjs +148 -0
  22. package/dist/dev/host.mjs.map +1 -0
  23. package/dist/dev/serve.cjs +16 -111
  24. package/dist/dev/serve.cjs.map +1 -1
  25. package/dist/dev/serve.d.cts.map +1 -1
  26. package/dist/dev/serve.d.mts.map +1 -1
  27. package/dist/dev/serve.mjs +17 -112
  28. package/dist/dev/serve.mjs.map +1 -1
  29. package/dist/index.cjs +2 -0
  30. package/dist/index.d.cts +3 -2
  31. package/dist/index.d.mts +3 -2
  32. package/dist/index.mjs +2 -1
  33. package/dist/plugin.d.cts +1 -1
  34. package/dist/remote-entry.cjs +0 -1
  35. package/dist/remote-entry.cjs.map +1 -1
  36. package/dist/remote-entry.d.cts +1 -2
  37. package/dist/remote-entry.d.cts.map +1 -1
  38. package/dist/remote-entry.d.mts +1 -2
  39. package/dist/remote-entry.d.mts.map +1 -1
  40. package/dist/remote-entry.mjs +1 -1
  41. package/dist/remote-entry.mjs.map +1 -1
  42. package/dist/runtime/mf-config.cjs +1 -1
  43. package/dist/runtime/mf-config.mjs +1 -1
  44. package/dist/runtime/services/plugin-loader.service.d.cts +2 -2
  45. package/dist/types.d.cts +23 -2
  46. package/dist/types.d.cts.map +1 -1
  47. package/dist/types.d.mts +22 -1
  48. package/dist/types.d.mts.map +1 -1
  49. package/package.json +19 -2
  50. package/src/build/ui/generated-config.ts +4 -0
  51. package/src/client.ts +111 -0
  52. package/src/dev/host.ts +235 -0
  53. package/src/dev/serve.ts +22 -140
  54. package/src/index.ts +9 -0
  55. package/src/remote-entry.ts +1 -2
  56. package/src/types.ts +36 -2
@@ -19,6 +19,9 @@ function hasFolderFormUi(cwd) {
19
19
  const uiDir = node_path.default.join(cwd, UI_DIR);
20
20
  return node_fs.default.existsSync(node_path.default.join(uiDir, "src", "routes")) && !node_fs.default.existsSync(node_path.default.join(uiDir, "package.json"));
21
21
  }
22
+ function uiDistDirFor(cwd) {
23
+ return node_path.default.join(cwd, UI_DIR, "dist");
24
+ }
22
25
  function generatedUiConfig(pluginId) {
23
26
  return `import path from "node:path";
24
27
  import { createUiRsbuildConfig } from "every-plugin/build/ui";
@@ -115,4 +118,5 @@ exports.ensureGeneratedUiRsbuildConfig = ensureGeneratedUiRsbuildConfig;
115
118
  exports.hasCoreUiWorkspace = hasCoreUiWorkspace;
116
119
  exports.hasFolderFormUi = hasFolderFormUi;
117
120
  exports.pluginLayoutKey = pluginLayoutKey;
121
+ exports.uiDistDirFor = uiDistDirFor;
118
122
  //# sourceMappingURL=generated-config.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"generated-config.cjs","names":["path","fs","findBosConfigPath"],"sources":["../../../src/build/ui/generated-config.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { findBosConfigPath } from \"../rspack/compose\";\n\nconst UI_DIR = \"ui\";\nconst GENERATED_CONFIG_DIR = \".every-plugin\";\nconst GENERATED_UI_CONFIG = \"ui.rsbuild.config.generated.mjs\";\n\n/**\n * Folder-form UI source: a `ui/` directory with route files but no own\n * package.json — the plugin workspace owns the build (single-workspace\n * plugin, one dev process, one deploy train). Workspace-form ui sources\n * (own package.json + scripts) are NOT managed here.\n */\nexport function hasFolderFormUi(cwd: string): boolean {\n const uiDir = path.join(cwd, UI_DIR);\n return (\n fs.existsSync(path.join(uiDir, \"src\", \"routes\")) &&\n !fs.existsSync(path.join(uiDir, \"package.json\"))\n );\n}\n\nfunction generatedUiConfig(pluginId: string): string {\n return `import path from \"node:path\";\nimport { createUiRsbuildConfig } from \"every-plugin/build/ui\";\nimport pkg from \"../package.json\";\n\nconst workspaceRoot = path.resolve(import.meta.dirname, \"..\", \"ui\");\n\nexport default createUiRsbuildConfig({\n workspaceRoot,\n pkg,\n role: \"consumer\",\n manifestName: ${JSON.stringify(pluginId)},\n devPort: Number(process.env.BOS_UI_PORT) || 0,\n webEntry: \"./ui/src/routeConfig.gen.ts\",\n webExposes: { \"./routeConfig\": \"./ui/src/routeConfig.gen.ts\" },\n nodeEntry: \"./ui/src/routeConfig.gen.ts\",\n nodeExposes: { \"./routeConfig\": \"./ui/src/routeConfig.gen.ts\" },\n routesDirectory: \"./ui/src/routes\",\n});\n`;\n}\n\n/**\n * Canonical config-layout derivation: the composition key for a plugin\n * workspace — the `<key>` of the `plugins/<key>` directory relative to the\n * nearest `bos.config.json`. The dev server and the generated ui config\n * both key off this; no consumer keeps a private copy.\n */\nexport function pluginLayoutKey(cwd: string): string | null {\n const bosConfigPath = findBosConfigPath(cwd);\n const relativeWorkspace = bosConfigPath\n ? path.relative(path.dirname(bosConfigPath), cwd)\n : undefined;\n const [workspaceGroup, layoutKey] = relativeWorkspace?.split(path.sep) ?? [];\n return workspaceGroup === \"plugins\" && layoutKey ? layoutKey : null;\n}\n\n/**\n * Synthesize the rsbuild config for a folder-form ui source (the plugin\n * build contract, not per-plugin config — mirrors the rspack generated\n * config). Returns the config file path, or null when the workspace has no\n * folder-form ui source.\n */\nexport function ensureGeneratedUiRsbuildConfig(cwd: string): string | null {\n if (!hasFolderFormUi(cwd)) return null;\n // The manifest's name derives ONLY from the plugins/<key> layout key\n // (ADR 0008 §2) — a non-derivable key fails the build loudly, never the\n // container-name fallback that mis-keyed manifests.\n const pluginId = pluginLayoutKey(cwd);\n if (!pluginId) {\n throw new Error(\n `[every-plugin] cannot derive the ui's composition key from ${cwd} — no authored config (bos.app.ts / bos.config.json) found above a plugins/<key> directory. The manifest name is the layout key; the container name is never a fallback.`,\n );\n }\n const outDir = path.join(cwd, GENERATED_CONFIG_DIR);\n fs.mkdirSync(outDir, { recursive: true });\n const outPath = path.join(outDir, GENERATED_UI_CONFIG);\n const next = generatedUiConfig(pluginId);\n if (!fs.existsSync(outPath) || fs.readFileSync(outPath, \"utf8\") !== next) {\n fs.writeFileSync(outPath, next);\n }\n return outPath;\n}\n\nconst CORE_UI_MARKERS = {\n routes: \"src/routes\",\n contract: \"src/contract.ts\",\n};\n\n/**\n * Workspace-form core ui: the workspace IS the ui (own package.json, route\n * tree) and is not plugin-shaped — the /api counterpart of the plugin\n * workspace form. The entry stub is NOT a marker: it is generated\n * (ADR 0023), so a fresh clone has no bootstrap stubs until the first\n * generation pass.\n */\nexport function hasCoreUiWorkspace(cwd: string = process.cwd()): boolean {\n return (\n fs.existsSync(path.join(cwd, CORE_UI_MARKERS.routes)) &&\n !fs.existsSync(path.join(cwd, CORE_UI_MARKERS.contract)) &&\n !fs.existsSync(path.join(cwd, \"plugin.dev.ts\"))\n );\n}\n\nfunction generatedCoreUiConfig(): string {\n return `import { defineConfig } from \"@rsbuild/core\";\nimport { readAuthoredConfigInput } from \"everything-dev/config\";\nimport { createCoreUiRsbuildConfig } from \"every-plugin/build/ui\";\n\nexport default defineConfig(async () =>\n createCoreUiRsbuildConfig(await readAuthoredConfigInput()),\n);\n`;\n}\n\n/**\n * Synthesize the rsbuild config for a workspace-form core ui with no local\n * `rsbuild.config.ts` — the /api generated-config model. A local file is an\n * override and wins untouched. Returns the workspace-relative config path,\n * or null when the workspace is not a core ui (or has a local config).\n */\nexport function ensureGeneratedCoreUiRsbuildConfig(cwd: string = process.cwd()): string | null {\n if (!hasCoreUiWorkspace(cwd)) return null;\n if (fs.existsSync(path.join(cwd, \"rsbuild.config.ts\"))) return null;\n const outDir = path.join(cwd, GENERATED_CONFIG_DIR);\n fs.mkdirSync(outDir, { recursive: true });\n const outPath = path.join(outDir, GENERATED_UI_CONFIG);\n fs.writeFileSync(outPath, generatedCoreUiConfig());\n console.log(\"[every-plugin] rsbuild.config.ts not found — using the core ui build factory.\");\n return path.join(GENERATED_CONFIG_DIR, GENERATED_UI_CONFIG);\n}\n"],"mappings":";;;;;;;;AAIA,MAAM,SAAS;AACf,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;;;;;;;AAQ5B,SAAgB,gBAAgB,KAAsB;CACpD,MAAM,QAAQA,kBAAK,KAAK,KAAK,MAAM;CACnC,OACEC,gBAAG,WAAWD,kBAAK,KAAK,OAAO,OAAO,QAAQ,CAAC,KAC/C,CAACC,gBAAG,WAAWD,kBAAK,KAAK,OAAO,cAAc,CAAC;AAEnD;AAEA,SAAS,kBAAkB,UAA0B;CACnD,OAAO;;;;;;;;;;kBAUS,KAAK,UAAU,QAAQ,EAAE;;;;;;;;;AAS3C;;;;;;;AAQA,SAAgB,gBAAgB,KAA4B;CAC1D,MAAM,gBAAgBE,kCAAkB,GAAG;CAI3C,MAAM,CAAC,gBAAgB,cAHG,gBACtBF,kBAAK,SAASA,kBAAK,QAAQ,aAAa,GAAG,GAAG,IAC9C,OACiD,EAAE,MAAMA,kBAAK,GAAG,KAAK,CAAC;CAC3E,OAAO,mBAAmB,aAAa,YAAY,YAAY;AACjE;;;;;;;AAQA,SAAgB,+BAA+B,KAA4B;CACzE,IAAI,CAAC,gBAAgB,GAAG,GAAG,OAAO;CAIlC,MAAM,WAAW,gBAAgB,GAAG;CACpC,IAAI,CAAC,UACH,MAAM,IAAI,MACR,8DAA8D,IAAI,yKACpE;CAEF,MAAM,SAASA,kBAAK,KAAK,KAAK,oBAAoB;CAClD,gBAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CACxC,MAAM,UAAUA,kBAAK,KAAK,QAAQ,mBAAmB;CACrD,MAAM,OAAO,kBAAkB,QAAQ;CACvC,IAAI,CAACC,gBAAG,WAAW,OAAO,KAAKA,gBAAG,aAAa,SAAS,MAAM,MAAM,MAClE,gBAAG,cAAc,SAAS,IAAI;CAEhC,OAAO;AACT;AAEA,MAAM,kBAAkB;CACtB,QAAQ;CACR,UAAU;AACZ;;;;;;;;AASA,SAAgB,mBAAmB,MAAc,QAAQ,IAAI,GAAY;CACvE,OACEA,gBAAG,WAAWD,kBAAK,KAAK,KAAK,gBAAgB,MAAM,CAAC,KACpD,CAACC,gBAAG,WAAWD,kBAAK,KAAK,KAAK,gBAAgB,QAAQ,CAAC,KACvD,CAACC,gBAAG,WAAWD,kBAAK,KAAK,KAAK,eAAe,CAAC;AAElD;AAEA,SAAS,wBAAgC;CACvC,OAAO;;;;;;;;AAQT;;;;;;;AAQA,SAAgB,mCAAmC,MAAc,QAAQ,IAAI,GAAkB;CAC7F,IAAI,CAAC,mBAAmB,GAAG,GAAG,OAAO;CACrC,IAAIC,gBAAG,WAAWD,kBAAK,KAAK,KAAK,mBAAmB,CAAC,GAAG,OAAO;CAC/D,MAAM,SAASA,kBAAK,KAAK,KAAK,oBAAoB;CAClD,gBAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CACxC,MAAM,UAAUA,kBAAK,KAAK,QAAQ,mBAAmB;CACrD,gBAAG,cAAc,SAAS,sBAAsB,CAAC;CACjD,QAAQ,IAAI,+EAA+E;CAC3F,OAAOA,kBAAK,KAAK,sBAAsB,mBAAmB;AAC5D"}
1
+ {"version":3,"file":"generated-config.cjs","names":["path","fs","findBosConfigPath"],"sources":["../../../src/build/ui/generated-config.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { findBosConfigPath } from \"../rspack/compose\";\n\nconst UI_DIR = \"ui\";\nconst GENERATED_CONFIG_DIR = \".every-plugin\";\nconst GENERATED_UI_CONFIG = \"ui.rsbuild.config.generated.mjs\";\n\n/**\n * Folder-form UI source: a `ui/` directory with route files but no own\n * package.json — the plugin workspace owns the build (single-workspace\n * plugin, one dev process, one deploy train). Workspace-form ui sources\n * (own package.json + scripts) are NOT managed here.\n */\nexport function hasFolderFormUi(cwd: string): boolean {\n const uiDir = path.join(cwd, UI_DIR);\n return (\n fs.existsSync(path.join(uiDir, \"src\", \"routes\")) &&\n !fs.existsSync(path.join(uiDir, \"package.json\"))\n );\n}\n\nexport function uiDistDirFor(cwd: string): string {\n return path.join(cwd, UI_DIR, \"dist\");\n}\n\nfunction generatedUiConfig(pluginId: string): string {\n return `import path from \"node:path\";\nimport { createUiRsbuildConfig } from \"every-plugin/build/ui\";\nimport pkg from \"../package.json\";\n\nconst workspaceRoot = path.resolve(import.meta.dirname, \"..\", \"ui\");\n\nexport default createUiRsbuildConfig({\n workspaceRoot,\n pkg,\n role: \"consumer\",\n manifestName: ${JSON.stringify(pluginId)},\n devPort: Number(process.env.BOS_UI_PORT) || 0,\n webEntry: \"./ui/src/routeConfig.gen.ts\",\n webExposes: { \"./routeConfig\": \"./ui/src/routeConfig.gen.ts\" },\n nodeEntry: \"./ui/src/routeConfig.gen.ts\",\n nodeExposes: { \"./routeConfig\": \"./ui/src/routeConfig.gen.ts\" },\n routesDirectory: \"./ui/src/routes\",\n});\n`;\n}\n\n/**\n * Canonical config-layout derivation: the composition key for a plugin\n * workspace — the `<key>` of the `plugins/<key>` directory relative to the\n * nearest `bos.config.json`. The dev server and the generated ui config\n * both key off this; no consumer keeps a private copy.\n */\nexport function pluginLayoutKey(cwd: string): string | null {\n const bosConfigPath = findBosConfigPath(cwd);\n const relativeWorkspace = bosConfigPath\n ? path.relative(path.dirname(bosConfigPath), cwd)\n : undefined;\n const [workspaceGroup, layoutKey] = relativeWorkspace?.split(path.sep) ?? [];\n return workspaceGroup === \"plugins\" && layoutKey ? layoutKey : null;\n}\n\n/**\n * Synthesize the rsbuild config for a folder-form ui source (the plugin\n * build contract, not per-plugin config — mirrors the rspack generated\n * config). Returns the config file path, or null when the workspace has no\n * folder-form ui source.\n */\nexport function ensureGeneratedUiRsbuildConfig(cwd: string): string | null {\n if (!hasFolderFormUi(cwd)) return null;\n // The manifest's name derives ONLY from the plugins/<key> layout key\n // (ADR 0008 §2) — a non-derivable key fails the build loudly, never the\n // container-name fallback that mis-keyed manifests.\n const pluginId = pluginLayoutKey(cwd);\n if (!pluginId) {\n throw new Error(\n `[every-plugin] cannot derive the ui's composition key from ${cwd} — no authored config (bos.app.ts / bos.config.json) found above a plugins/<key> directory. The manifest name is the layout key; the container name is never a fallback.`,\n );\n }\n const outDir = path.join(cwd, GENERATED_CONFIG_DIR);\n fs.mkdirSync(outDir, { recursive: true });\n const outPath = path.join(outDir, GENERATED_UI_CONFIG);\n const next = generatedUiConfig(pluginId);\n if (!fs.existsSync(outPath) || fs.readFileSync(outPath, \"utf8\") !== next) {\n fs.writeFileSync(outPath, next);\n }\n return outPath;\n}\n\nconst CORE_UI_MARKERS = {\n routes: \"src/routes\",\n contract: \"src/contract.ts\",\n};\n\n/**\n * Workspace-form core ui: the workspace IS the ui (own package.json, route\n * tree) and is not plugin-shaped — the /api counterpart of the plugin\n * workspace form. The entry stub is NOT a marker: it is generated\n * (ADR 0023), so a fresh clone has no bootstrap stubs until the first\n * generation pass.\n */\nexport function hasCoreUiWorkspace(cwd: string = process.cwd()): boolean {\n return (\n fs.existsSync(path.join(cwd, CORE_UI_MARKERS.routes)) &&\n !fs.existsSync(path.join(cwd, CORE_UI_MARKERS.contract)) &&\n !fs.existsSync(path.join(cwd, \"plugin.dev.ts\"))\n );\n}\n\nfunction generatedCoreUiConfig(): string {\n return `import { defineConfig } from \"@rsbuild/core\";\nimport { readAuthoredConfigInput } from \"everything-dev/config\";\nimport { createCoreUiRsbuildConfig } from \"every-plugin/build/ui\";\n\nexport default defineConfig(async () =>\n createCoreUiRsbuildConfig(await readAuthoredConfigInput()),\n);\n`;\n}\n\n/**\n * Synthesize the rsbuild config for a workspace-form core ui with no local\n * `rsbuild.config.ts` — the /api generated-config model. A local file is an\n * override and wins untouched. Returns the workspace-relative config path,\n * or null when the workspace is not a core ui (or has a local config).\n */\nexport function ensureGeneratedCoreUiRsbuildConfig(cwd: string = process.cwd()): string | null {\n if (!hasCoreUiWorkspace(cwd)) return null;\n if (fs.existsSync(path.join(cwd, \"rsbuild.config.ts\"))) return null;\n const outDir = path.join(cwd, GENERATED_CONFIG_DIR);\n fs.mkdirSync(outDir, { recursive: true });\n const outPath = path.join(outDir, GENERATED_UI_CONFIG);\n fs.writeFileSync(outPath, generatedCoreUiConfig());\n console.log(\"[every-plugin] rsbuild.config.ts not found — using the core ui build factory.\");\n return path.join(GENERATED_CONFIG_DIR, GENERATED_UI_CONFIG);\n}\n"],"mappings":";;;;;;;;AAIA,MAAM,SAAS;AACf,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;;;;;;;AAQ5B,SAAgB,gBAAgB,KAAsB;CACpD,MAAM,QAAQA,kBAAK,KAAK,KAAK,MAAM;CACnC,OACEC,gBAAG,WAAWD,kBAAK,KAAK,OAAO,OAAO,QAAQ,CAAC,KAC/C,CAACC,gBAAG,WAAWD,kBAAK,KAAK,OAAO,cAAc,CAAC;AAEnD;AAEA,SAAgB,aAAa,KAAqB;CAChD,OAAOA,kBAAK,KAAK,KAAK,QAAQ,MAAM;AACtC;AAEA,SAAS,kBAAkB,UAA0B;CACnD,OAAO;;;;;;;;;;kBAUS,KAAK,UAAU,QAAQ,EAAE;;;;;;;;;AAS3C;;;;;;;AAQA,SAAgB,gBAAgB,KAA4B;CAC1D,MAAM,gBAAgBE,kCAAkB,GAAG;CAI3C,MAAM,CAAC,gBAAgB,cAHG,gBACtBF,kBAAK,SAASA,kBAAK,QAAQ,aAAa,GAAG,GAAG,IAC9C,OACiD,EAAE,MAAMA,kBAAK,GAAG,KAAK,CAAC;CAC3E,OAAO,mBAAmB,aAAa,YAAY,YAAY;AACjE;;;;;;;AAQA,SAAgB,+BAA+B,KAA4B;CACzE,IAAI,CAAC,gBAAgB,GAAG,GAAG,OAAO;CAIlC,MAAM,WAAW,gBAAgB,GAAG;CACpC,IAAI,CAAC,UACH,MAAM,IAAI,MACR,8DAA8D,IAAI,yKACpE;CAEF,MAAM,SAASA,kBAAK,KAAK,KAAK,oBAAoB;CAClD,gBAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CACxC,MAAM,UAAUA,kBAAK,KAAK,QAAQ,mBAAmB;CACrD,MAAM,OAAO,kBAAkB,QAAQ;CACvC,IAAI,CAACC,gBAAG,WAAW,OAAO,KAAKA,gBAAG,aAAa,SAAS,MAAM,MAAM,MAClE,gBAAG,cAAc,SAAS,IAAI;CAEhC,OAAO;AACT;AAEA,MAAM,kBAAkB;CACtB,QAAQ;CACR,UAAU;AACZ;;;;;;;;AASA,SAAgB,mBAAmB,MAAc,QAAQ,IAAI,GAAY;CACvE,OACEA,gBAAG,WAAWD,kBAAK,KAAK,KAAK,gBAAgB,MAAM,CAAC,KACpD,CAACC,gBAAG,WAAWD,kBAAK,KAAK,KAAK,gBAAgB,QAAQ,CAAC,KACvD,CAACC,gBAAG,WAAWD,kBAAK,KAAK,KAAK,eAAe,CAAC;AAElD;AAEA,SAAS,wBAAgC;CACvC,OAAO;;;;;;;;AAQT;;;;;;;AAQA,SAAgB,mCAAmC,MAAc,QAAQ,IAAI,GAAkB;CAC7F,IAAI,CAAC,mBAAmB,GAAG,GAAG,OAAO;CACrC,IAAIC,gBAAG,WAAWD,kBAAK,KAAK,KAAK,mBAAmB,CAAC,GAAG,OAAO;CAC/D,MAAM,SAASA,kBAAK,KAAK,KAAK,oBAAoB;CAClD,gBAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CACxC,MAAM,UAAUA,kBAAK,KAAK,QAAQ,mBAAmB;CACrD,gBAAG,cAAc,SAAS,sBAAsB,CAAC;CACjD,QAAQ,IAAI,+EAA+E;CAC3F,OAAOA,kBAAK,KAAK,sBAAsB,mBAAmB;AAC5D"}
@@ -1 +1 @@
1
- {"version":3,"file":"generated-config.d.cts","names":[],"sources":["../../../src/build/ui/generated-config.ts"],"mappings":";;;;;;;iBAcgB,gBAAgB;;;;;;;iBAmDhB,+BAA+B;;;;;;;;iBAiC/B,mBAAmB;;;;;;;iBAyBnB,mCAAmC"}
1
+ {"version":3,"file":"generated-config.d.cts","names":[],"sources":["../../../src/build/ui/generated-config.ts"],"mappings":";;;;;;;iBAcgB,gBAAgB;;;;;;;iBAuDhB,+BAA+B;;;;;;;;iBAiC/B,mBAAmB;;;;;;;iBAyBnB,mCAAmC"}
@@ -1 +1 @@
1
- {"version":3,"file":"generated-config.d.mts","names":[],"sources":["../../../src/build/ui/generated-config.ts"],"mappings":";;;;;;;iBAcgB,gBAAgB;;;;;;;iBAmDhB,+BAA+B;;;;;;;;iBAiC/B,mBAAmB;;;;;;;iBAyBnB,mCAAmC"}
1
+ {"version":3,"file":"generated-config.d.mts","names":[],"sources":["../../../src/build/ui/generated-config.ts"],"mappings":";;;;;;;iBAcgB,gBAAgB;;;;;;;iBAuDhB,+BAA+B;;;;;;;;iBAiC/B,mBAAmB;;;;;;;iBAyBnB,mCAAmC"}
@@ -16,6 +16,9 @@ function hasFolderFormUi(cwd) {
16
16
  const uiDir = path.join(cwd, UI_DIR);
17
17
  return fs.existsSync(path.join(uiDir, "src", "routes")) && !fs.existsSync(path.join(uiDir, "package.json"));
18
18
  }
19
+ function uiDistDirFor(cwd) {
20
+ return path.join(cwd, UI_DIR, "dist");
21
+ }
19
22
  function generatedUiConfig(pluginId) {
20
23
  return `import path from "node:path";
21
24
  import { createUiRsbuildConfig } from "every-plugin/build/ui";
@@ -107,5 +110,5 @@ function ensureGeneratedCoreUiRsbuildConfig(cwd = process.cwd()) {
107
110
  }
108
111
 
109
112
  //#endregion
110
- export { ensureGeneratedCoreUiRsbuildConfig, ensureGeneratedUiRsbuildConfig, hasCoreUiWorkspace, hasFolderFormUi, pluginLayoutKey };
113
+ export { ensureGeneratedCoreUiRsbuildConfig, ensureGeneratedUiRsbuildConfig, hasCoreUiWorkspace, hasFolderFormUi, pluginLayoutKey, uiDistDirFor };
111
114
  //# sourceMappingURL=generated-config.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"generated-config.mjs","names":[],"sources":["../../../src/build/ui/generated-config.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { findBosConfigPath } from \"../rspack/compose\";\n\nconst UI_DIR = \"ui\";\nconst GENERATED_CONFIG_DIR = \".every-plugin\";\nconst GENERATED_UI_CONFIG = \"ui.rsbuild.config.generated.mjs\";\n\n/**\n * Folder-form UI source: a `ui/` directory with route files but no own\n * package.json — the plugin workspace owns the build (single-workspace\n * plugin, one dev process, one deploy train). Workspace-form ui sources\n * (own package.json + scripts) are NOT managed here.\n */\nexport function hasFolderFormUi(cwd: string): boolean {\n const uiDir = path.join(cwd, UI_DIR);\n return (\n fs.existsSync(path.join(uiDir, \"src\", \"routes\")) &&\n !fs.existsSync(path.join(uiDir, \"package.json\"))\n );\n}\n\nfunction generatedUiConfig(pluginId: string): string {\n return `import path from \"node:path\";\nimport { createUiRsbuildConfig } from \"every-plugin/build/ui\";\nimport pkg from \"../package.json\";\n\nconst workspaceRoot = path.resolve(import.meta.dirname, \"..\", \"ui\");\n\nexport default createUiRsbuildConfig({\n workspaceRoot,\n pkg,\n role: \"consumer\",\n manifestName: ${JSON.stringify(pluginId)},\n devPort: Number(process.env.BOS_UI_PORT) || 0,\n webEntry: \"./ui/src/routeConfig.gen.ts\",\n webExposes: { \"./routeConfig\": \"./ui/src/routeConfig.gen.ts\" },\n nodeEntry: \"./ui/src/routeConfig.gen.ts\",\n nodeExposes: { \"./routeConfig\": \"./ui/src/routeConfig.gen.ts\" },\n routesDirectory: \"./ui/src/routes\",\n});\n`;\n}\n\n/**\n * Canonical config-layout derivation: the composition key for a plugin\n * workspace — the `<key>` of the `plugins/<key>` directory relative to the\n * nearest `bos.config.json`. The dev server and the generated ui config\n * both key off this; no consumer keeps a private copy.\n */\nexport function pluginLayoutKey(cwd: string): string | null {\n const bosConfigPath = findBosConfigPath(cwd);\n const relativeWorkspace = bosConfigPath\n ? path.relative(path.dirname(bosConfigPath), cwd)\n : undefined;\n const [workspaceGroup, layoutKey] = relativeWorkspace?.split(path.sep) ?? [];\n return workspaceGroup === \"plugins\" && layoutKey ? layoutKey : null;\n}\n\n/**\n * Synthesize the rsbuild config for a folder-form ui source (the plugin\n * build contract, not per-plugin config — mirrors the rspack generated\n * config). Returns the config file path, or null when the workspace has no\n * folder-form ui source.\n */\nexport function ensureGeneratedUiRsbuildConfig(cwd: string): string | null {\n if (!hasFolderFormUi(cwd)) return null;\n // The manifest's name derives ONLY from the plugins/<key> layout key\n // (ADR 0008 §2) — a non-derivable key fails the build loudly, never the\n // container-name fallback that mis-keyed manifests.\n const pluginId = pluginLayoutKey(cwd);\n if (!pluginId) {\n throw new Error(\n `[every-plugin] cannot derive the ui's composition key from ${cwd} — no authored config (bos.app.ts / bos.config.json) found above a plugins/<key> directory. The manifest name is the layout key; the container name is never a fallback.`,\n );\n }\n const outDir = path.join(cwd, GENERATED_CONFIG_DIR);\n fs.mkdirSync(outDir, { recursive: true });\n const outPath = path.join(outDir, GENERATED_UI_CONFIG);\n const next = generatedUiConfig(pluginId);\n if (!fs.existsSync(outPath) || fs.readFileSync(outPath, \"utf8\") !== next) {\n fs.writeFileSync(outPath, next);\n }\n return outPath;\n}\n\nconst CORE_UI_MARKERS = {\n routes: \"src/routes\",\n contract: \"src/contract.ts\",\n};\n\n/**\n * Workspace-form core ui: the workspace IS the ui (own package.json, route\n * tree) and is not plugin-shaped — the /api counterpart of the plugin\n * workspace form. The entry stub is NOT a marker: it is generated\n * (ADR 0023), so a fresh clone has no bootstrap stubs until the first\n * generation pass.\n */\nexport function hasCoreUiWorkspace(cwd: string = process.cwd()): boolean {\n return (\n fs.existsSync(path.join(cwd, CORE_UI_MARKERS.routes)) &&\n !fs.existsSync(path.join(cwd, CORE_UI_MARKERS.contract)) &&\n !fs.existsSync(path.join(cwd, \"plugin.dev.ts\"))\n );\n}\n\nfunction generatedCoreUiConfig(): string {\n return `import { defineConfig } from \"@rsbuild/core\";\nimport { readAuthoredConfigInput } from \"everything-dev/config\";\nimport { createCoreUiRsbuildConfig } from \"every-plugin/build/ui\";\n\nexport default defineConfig(async () =>\n createCoreUiRsbuildConfig(await readAuthoredConfigInput()),\n);\n`;\n}\n\n/**\n * Synthesize the rsbuild config for a workspace-form core ui with no local\n * `rsbuild.config.ts` — the /api generated-config model. A local file is an\n * override and wins untouched. Returns the workspace-relative config path,\n * or null when the workspace is not a core ui (or has a local config).\n */\nexport function ensureGeneratedCoreUiRsbuildConfig(cwd: string = process.cwd()): string | null {\n if (!hasCoreUiWorkspace(cwd)) return null;\n if (fs.existsSync(path.join(cwd, \"rsbuild.config.ts\"))) return null;\n const outDir = path.join(cwd, GENERATED_CONFIG_DIR);\n fs.mkdirSync(outDir, { recursive: true });\n const outPath = path.join(outDir, GENERATED_UI_CONFIG);\n fs.writeFileSync(outPath, generatedCoreUiConfig());\n console.log(\"[every-plugin] rsbuild.config.ts not found — using the core ui build factory.\");\n return path.join(GENERATED_CONFIG_DIR, GENERATED_UI_CONFIG);\n}\n"],"mappings":";;;;;AAIA,MAAM,SAAS;AACf,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;;;;;;;AAQ5B,SAAgB,gBAAgB,KAAsB;CACpD,MAAM,QAAQ,KAAK,KAAK,KAAK,MAAM;CACnC,OACE,GAAG,WAAW,KAAK,KAAK,OAAO,OAAO,QAAQ,CAAC,KAC/C,CAAC,GAAG,WAAW,KAAK,KAAK,OAAO,cAAc,CAAC;AAEnD;AAEA,SAAS,kBAAkB,UAA0B;CACnD,OAAO;;;;;;;;;;kBAUS,KAAK,UAAU,QAAQ,EAAE;;;;;;;;;AAS3C;;;;;;;AAQA,SAAgB,gBAAgB,KAA4B;CAC1D,MAAM,gBAAgB,kBAAkB,GAAG;CAI3C,MAAM,CAAC,gBAAgB,cAHG,gBACtB,KAAK,SAAS,KAAK,QAAQ,aAAa,GAAG,GAAG,IAC9C,OACiD,EAAE,MAAM,KAAK,GAAG,KAAK,CAAC;CAC3E,OAAO,mBAAmB,aAAa,YAAY,YAAY;AACjE;;;;;;;AAQA,SAAgB,+BAA+B,KAA4B;CACzE,IAAI,CAAC,gBAAgB,GAAG,GAAG,OAAO;CAIlC,MAAM,WAAW,gBAAgB,GAAG;CACpC,IAAI,CAAC,UACH,MAAM,IAAI,MACR,8DAA8D,IAAI,yKACpE;CAEF,MAAM,SAAS,KAAK,KAAK,KAAK,oBAAoB;CAClD,GAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CACxC,MAAM,UAAU,KAAK,KAAK,QAAQ,mBAAmB;CACrD,MAAM,OAAO,kBAAkB,QAAQ;CACvC,IAAI,CAAC,GAAG,WAAW,OAAO,KAAK,GAAG,aAAa,SAAS,MAAM,MAAM,MAClE,GAAG,cAAc,SAAS,IAAI;CAEhC,OAAO;AACT;AAEA,MAAM,kBAAkB;CACtB,QAAQ;CACR,UAAU;AACZ;;;;;;;;AASA,SAAgB,mBAAmB,MAAc,QAAQ,IAAI,GAAY;CACvE,OACE,GAAG,WAAW,KAAK,KAAK,KAAK,gBAAgB,MAAM,CAAC,KACpD,CAAC,GAAG,WAAW,KAAK,KAAK,KAAK,gBAAgB,QAAQ,CAAC,KACvD,CAAC,GAAG,WAAW,KAAK,KAAK,KAAK,eAAe,CAAC;AAElD;AAEA,SAAS,wBAAgC;CACvC,OAAO;;;;;;;;AAQT;;;;;;;AAQA,SAAgB,mCAAmC,MAAc,QAAQ,IAAI,GAAkB;CAC7F,IAAI,CAAC,mBAAmB,GAAG,GAAG,OAAO;CACrC,IAAI,GAAG,WAAW,KAAK,KAAK,KAAK,mBAAmB,CAAC,GAAG,OAAO;CAC/D,MAAM,SAAS,KAAK,KAAK,KAAK,oBAAoB;CAClD,GAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CACxC,MAAM,UAAU,KAAK,KAAK,QAAQ,mBAAmB;CACrD,GAAG,cAAc,SAAS,sBAAsB,CAAC;CACjD,QAAQ,IAAI,+EAA+E;CAC3F,OAAO,KAAK,KAAK,sBAAsB,mBAAmB;AAC5D"}
1
+ {"version":3,"file":"generated-config.mjs","names":[],"sources":["../../../src/build/ui/generated-config.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { findBosConfigPath } from \"../rspack/compose\";\n\nconst UI_DIR = \"ui\";\nconst GENERATED_CONFIG_DIR = \".every-plugin\";\nconst GENERATED_UI_CONFIG = \"ui.rsbuild.config.generated.mjs\";\n\n/**\n * Folder-form UI source: a `ui/` directory with route files but no own\n * package.json — the plugin workspace owns the build (single-workspace\n * plugin, one dev process, one deploy train). Workspace-form ui sources\n * (own package.json + scripts) are NOT managed here.\n */\nexport function hasFolderFormUi(cwd: string): boolean {\n const uiDir = path.join(cwd, UI_DIR);\n return (\n fs.existsSync(path.join(uiDir, \"src\", \"routes\")) &&\n !fs.existsSync(path.join(uiDir, \"package.json\"))\n );\n}\n\nexport function uiDistDirFor(cwd: string): string {\n return path.join(cwd, UI_DIR, \"dist\");\n}\n\nfunction generatedUiConfig(pluginId: string): string {\n return `import path from \"node:path\";\nimport { createUiRsbuildConfig } from \"every-plugin/build/ui\";\nimport pkg from \"../package.json\";\n\nconst workspaceRoot = path.resolve(import.meta.dirname, \"..\", \"ui\");\n\nexport default createUiRsbuildConfig({\n workspaceRoot,\n pkg,\n role: \"consumer\",\n manifestName: ${JSON.stringify(pluginId)},\n devPort: Number(process.env.BOS_UI_PORT) || 0,\n webEntry: \"./ui/src/routeConfig.gen.ts\",\n webExposes: { \"./routeConfig\": \"./ui/src/routeConfig.gen.ts\" },\n nodeEntry: \"./ui/src/routeConfig.gen.ts\",\n nodeExposes: { \"./routeConfig\": \"./ui/src/routeConfig.gen.ts\" },\n routesDirectory: \"./ui/src/routes\",\n});\n`;\n}\n\n/**\n * Canonical config-layout derivation: the composition key for a plugin\n * workspace — the `<key>` of the `plugins/<key>` directory relative to the\n * nearest `bos.config.json`. The dev server and the generated ui config\n * both key off this; no consumer keeps a private copy.\n */\nexport function pluginLayoutKey(cwd: string): string | null {\n const bosConfigPath = findBosConfigPath(cwd);\n const relativeWorkspace = bosConfigPath\n ? path.relative(path.dirname(bosConfigPath), cwd)\n : undefined;\n const [workspaceGroup, layoutKey] = relativeWorkspace?.split(path.sep) ?? [];\n return workspaceGroup === \"plugins\" && layoutKey ? layoutKey : null;\n}\n\n/**\n * Synthesize the rsbuild config for a folder-form ui source (the plugin\n * build contract, not per-plugin config — mirrors the rspack generated\n * config). Returns the config file path, or null when the workspace has no\n * folder-form ui source.\n */\nexport function ensureGeneratedUiRsbuildConfig(cwd: string): string | null {\n if (!hasFolderFormUi(cwd)) return null;\n // The manifest's name derives ONLY from the plugins/<key> layout key\n // (ADR 0008 §2) — a non-derivable key fails the build loudly, never the\n // container-name fallback that mis-keyed manifests.\n const pluginId = pluginLayoutKey(cwd);\n if (!pluginId) {\n throw new Error(\n `[every-plugin] cannot derive the ui's composition key from ${cwd} — no authored config (bos.app.ts / bos.config.json) found above a plugins/<key> directory. The manifest name is the layout key; the container name is never a fallback.`,\n );\n }\n const outDir = path.join(cwd, GENERATED_CONFIG_DIR);\n fs.mkdirSync(outDir, { recursive: true });\n const outPath = path.join(outDir, GENERATED_UI_CONFIG);\n const next = generatedUiConfig(pluginId);\n if (!fs.existsSync(outPath) || fs.readFileSync(outPath, \"utf8\") !== next) {\n fs.writeFileSync(outPath, next);\n }\n return outPath;\n}\n\nconst CORE_UI_MARKERS = {\n routes: \"src/routes\",\n contract: \"src/contract.ts\",\n};\n\n/**\n * Workspace-form core ui: the workspace IS the ui (own package.json, route\n * tree) and is not plugin-shaped — the /api counterpart of the plugin\n * workspace form. The entry stub is NOT a marker: it is generated\n * (ADR 0023), so a fresh clone has no bootstrap stubs until the first\n * generation pass.\n */\nexport function hasCoreUiWorkspace(cwd: string = process.cwd()): boolean {\n return (\n fs.existsSync(path.join(cwd, CORE_UI_MARKERS.routes)) &&\n !fs.existsSync(path.join(cwd, CORE_UI_MARKERS.contract)) &&\n !fs.existsSync(path.join(cwd, \"plugin.dev.ts\"))\n );\n}\n\nfunction generatedCoreUiConfig(): string {\n return `import { defineConfig } from \"@rsbuild/core\";\nimport { readAuthoredConfigInput } from \"everything-dev/config\";\nimport { createCoreUiRsbuildConfig } from \"every-plugin/build/ui\";\n\nexport default defineConfig(async () =>\n createCoreUiRsbuildConfig(await readAuthoredConfigInput()),\n);\n`;\n}\n\n/**\n * Synthesize the rsbuild config for a workspace-form core ui with no local\n * `rsbuild.config.ts` — the /api generated-config model. A local file is an\n * override and wins untouched. Returns the workspace-relative config path,\n * or null when the workspace is not a core ui (or has a local config).\n */\nexport function ensureGeneratedCoreUiRsbuildConfig(cwd: string = process.cwd()): string | null {\n if (!hasCoreUiWorkspace(cwd)) return null;\n if (fs.existsSync(path.join(cwd, \"rsbuild.config.ts\"))) return null;\n const outDir = path.join(cwd, GENERATED_CONFIG_DIR);\n fs.mkdirSync(outDir, { recursive: true });\n const outPath = path.join(outDir, GENERATED_UI_CONFIG);\n fs.writeFileSync(outPath, generatedCoreUiConfig());\n console.log(\"[every-plugin] rsbuild.config.ts not found — using the core ui build factory.\");\n return path.join(GENERATED_CONFIG_DIR, GENERATED_UI_CONFIG);\n}\n"],"mappings":";;;;;AAIA,MAAM,SAAS;AACf,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;;;;;;;AAQ5B,SAAgB,gBAAgB,KAAsB;CACpD,MAAM,QAAQ,KAAK,KAAK,KAAK,MAAM;CACnC,OACE,GAAG,WAAW,KAAK,KAAK,OAAO,OAAO,QAAQ,CAAC,KAC/C,CAAC,GAAG,WAAW,KAAK,KAAK,OAAO,cAAc,CAAC;AAEnD;AAEA,SAAgB,aAAa,KAAqB;CAChD,OAAO,KAAK,KAAK,KAAK,QAAQ,MAAM;AACtC;AAEA,SAAS,kBAAkB,UAA0B;CACnD,OAAO;;;;;;;;;;kBAUS,KAAK,UAAU,QAAQ,EAAE;;;;;;;;;AAS3C;;;;;;;AAQA,SAAgB,gBAAgB,KAA4B;CAC1D,MAAM,gBAAgB,kBAAkB,GAAG;CAI3C,MAAM,CAAC,gBAAgB,cAHG,gBACtB,KAAK,SAAS,KAAK,QAAQ,aAAa,GAAG,GAAG,IAC9C,OACiD,EAAE,MAAM,KAAK,GAAG,KAAK,CAAC;CAC3E,OAAO,mBAAmB,aAAa,YAAY,YAAY;AACjE;;;;;;;AAQA,SAAgB,+BAA+B,KAA4B;CACzE,IAAI,CAAC,gBAAgB,GAAG,GAAG,OAAO;CAIlC,MAAM,WAAW,gBAAgB,GAAG;CACpC,IAAI,CAAC,UACH,MAAM,IAAI,MACR,8DAA8D,IAAI,yKACpE;CAEF,MAAM,SAAS,KAAK,KAAK,KAAK,oBAAoB;CAClD,GAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CACxC,MAAM,UAAU,KAAK,KAAK,QAAQ,mBAAmB;CACrD,MAAM,OAAO,kBAAkB,QAAQ;CACvC,IAAI,CAAC,GAAG,WAAW,OAAO,KAAK,GAAG,aAAa,SAAS,MAAM,MAAM,MAClE,GAAG,cAAc,SAAS,IAAI;CAEhC,OAAO;AACT;AAEA,MAAM,kBAAkB;CACtB,QAAQ;CACR,UAAU;AACZ;;;;;;;;AASA,SAAgB,mBAAmB,MAAc,QAAQ,IAAI,GAAY;CACvE,OACE,GAAG,WAAW,KAAK,KAAK,KAAK,gBAAgB,MAAM,CAAC,KACpD,CAAC,GAAG,WAAW,KAAK,KAAK,KAAK,gBAAgB,QAAQ,CAAC,KACvD,CAAC,GAAG,WAAW,KAAK,KAAK,KAAK,eAAe,CAAC;AAElD;AAEA,SAAS,wBAAgC;CACvC,OAAO;;;;;;;;AAQT;;;;;;;AAQA,SAAgB,mCAAmC,MAAc,QAAQ,IAAI,GAAkB;CAC7F,IAAI,CAAC,mBAAmB,GAAG,GAAG,OAAO;CACrC,IAAI,GAAG,WAAW,KAAK,KAAK,KAAK,mBAAmB,CAAC,GAAG,OAAO;CAC/D,MAAM,SAAS,KAAK,KAAK,KAAK,oBAAoB;CAClD,GAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CACxC,MAAM,UAAU,KAAK,KAAK,QAAQ,mBAAmB;CACrD,GAAG,cAAc,SAAS,sBAAsB,CAAC;CACjD,QAAQ,IAAI,+EAA+E;CAC3F,OAAO,KAAK,KAAK,sBAAsB,mBAAmB;AAC5D"}
@@ -0,0 +1,69 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let _orpc_client = require("@orpc/client");
3
+ let _orpc_client_fetch = require("@orpc/client/fetch");
4
+
5
+ //#region src/client.ts
6
+ const RATE_LIMIT_MAX_RETRIES = 3;
7
+ const RATE_LIMIT_RETRY_CAP_MS = 2e3;
8
+ const browserClients = /* @__PURE__ */ new Map();
9
+ async function fetchWithRateLimitRetry(fetchUrl, options) {
10
+ let response = await fetch(fetchUrl, options);
11
+ for (let attempt = 1; attempt <= RATE_LIMIT_MAX_RETRIES && response.status === 429; attempt++) {
12
+ const retryAfterSeconds = Number(response.headers.get("retry-after"));
13
+ const delay = Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0 ? Math.min(retryAfterSeconds * 1e3, RATE_LIMIT_RETRY_CAP_MS) : 100 * attempt;
14
+ await new Promise((resolve) => setTimeout(resolve, delay));
15
+ response = await fetch(fetchUrl, options);
16
+ }
17
+ return response;
18
+ }
19
+ function createRpcLink(config, headers) {
20
+ return new _orpc_client_fetch.RPCLink({
21
+ origin: config.hostUrl,
22
+ url: config.path,
23
+ interceptors: [(0, _orpc_client.onError)((error) => {
24
+ if (typeof globalThis.window === "undefined") return;
25
+ if (error && typeof error === "object" && "message" in error) {
26
+ const message = String(error.message).toLowerCase();
27
+ if (message.includes("fetch") || message.includes("network") || message.includes("failed to fetch")) import("sonner").then(({ toast }) => {
28
+ const message = config.connectionError?.() ?? {
29
+ title: "Unable to connect to API",
30
+ description: "The API is currently unavailable. Please try again later."
31
+ };
32
+ toast.error(message.title, {
33
+ id: "api-connection-error",
34
+ description: message.description
35
+ });
36
+ }).catch(() => {});
37
+ }
38
+ })],
39
+ fetch(fetchUrl, options) {
40
+ return fetchWithRateLimitRetry(fetchUrl, {
41
+ ...options,
42
+ credentials: "include",
43
+ headers: headers ? {
44
+ ...Object.fromEntries(headers),
45
+ ...options?.headers
46
+ } : options?.headers
47
+ });
48
+ }
49
+ });
50
+ }
51
+ function buildRpcClient(config, headers) {
52
+ if (!config.hostUrl) throw new Error("Missing runtime host URL");
53
+ return (0, _orpc_client.createORPCClient)(createRpcLink(config, headers));
54
+ }
55
+ function createRpcClient(config, headers) {
56
+ if (typeof globalThis.window !== "undefined" && !headers) {
57
+ const key = `${config.hostUrl}${config.path}`;
58
+ const memoized = browserClients.get(key);
59
+ if (memoized) return memoized;
60
+ const client = buildRpcClient(config, headers);
61
+ browserClients.set(key, client);
62
+ return client;
63
+ }
64
+ return buildRpcClient(config, headers);
65
+ }
66
+
67
+ //#endregion
68
+ exports.createRpcClient = createRpcClient;
69
+ //# sourceMappingURL=client.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.cjs","names":["RPCLink","onError","createORPCClient"],"sources":["../src/client.ts"],"sourcesContent":["import { createORPCClient, onError } from \"@orpc/client\";\nimport { RPCLink } from \"@orpc/client/fetch\";\nimport type { ContractRouterClient, RouterContract } from \"@orpc/contract\";\n\nexport type RpcConnectionErrorCopy = () => { title: string; description: string };\n\nexport interface RpcClientConfig {\n hostUrl: string;\n path: `/${string}`;\n connectionError?: RpcConnectionErrorCopy;\n}\n\nconst RATE_LIMIT_MAX_RETRIES = 3;\nconst RATE_LIMIT_RETRY_CAP_MS = 2_000;\nconst browserClients = new Map<string, unknown>();\ntype FetchInput = Parameters<typeof fetch>[0];\n\nasync function fetchWithRateLimitRetry(\n fetchUrl: FetchInput,\n options?: RequestInit,\n): Promise<Response> {\n let response = await fetch(fetchUrl, options);\n for (let attempt = 1; attempt <= RATE_LIMIT_MAX_RETRIES && response.status === 429; attempt++) {\n const retryAfterSeconds = Number(response.headers.get(\"retry-after\"));\n const delay =\n Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0\n ? Math.min(retryAfterSeconds * 1000, RATE_LIMIT_RETRY_CAP_MS)\n : 100 * attempt;\n await new Promise((resolve) => setTimeout(resolve, delay));\n response = await fetch(fetchUrl, options);\n }\n\n return response;\n}\n\nfunction createRpcLink(config: RpcClientConfig, headers?: Headers) {\n return new RPCLink({\n origin: config.hostUrl,\n url: config.path,\n interceptors: [\n onError((error: unknown) => {\n const browserWindow = (globalThis as { window?: unknown }).window;\n if (typeof browserWindow === \"undefined\") {\n return;\n }\n\n if (error && typeof error === \"object\" && \"message\" in error) {\n const message = String(error.message).toLowerCase();\n if (\n message.includes(\"fetch\") ||\n message.includes(\"network\") ||\n message.includes(\"failed to fetch\")\n ) {\n void import(\"sonner\")\n .then(({ toast }) => {\n const message = config.connectionError?.() ?? {\n title: \"Unable to connect to API\",\n description: \"The API is currently unavailable. Please try again later.\",\n };\n toast.error(message.title, {\n id: \"api-connection-error\",\n description: message.description,\n });\n })\n .catch(() => {});\n }\n }\n }),\n ],\n fetch(fetchUrl: FetchInput, options?: RequestInit) {\n return fetchWithRateLimitRetry(fetchUrl, {\n ...options,\n credentials: \"include\",\n headers: headers\n ? { ...Object.fromEntries(headers), ...(options?.headers as Record<string, string>) }\n : options?.headers,\n });\n },\n });\n}\n\nfunction buildRpcClient<T extends RouterContract>(\n config: RpcClientConfig,\n headers?: Headers,\n): ContractRouterClient<T> {\n if (!config.hostUrl) {\n throw new Error(\"Missing runtime host URL\");\n }\n\n return createORPCClient(createRpcLink(config, headers)) as ContractRouterClient<T>;\n}\n\nexport function createRpcClient<T extends RouterContract = RouterContract>(\n config: RpcClientConfig,\n headers?: Headers,\n): ContractRouterClient<T> {\n const browserWindow = (globalThis as { window?: unknown }).window;\n if (typeof browserWindow !== \"undefined\" && !headers) {\n const key = `${config.hostUrl}${config.path}`;\n const memoized = browserClients.get(key);\n if (memoized) {\n return memoized as ContractRouterClient<T>;\n }\n\n const client = buildRpcClient<T>(config, headers);\n browserClients.set(key, client);\n return client;\n }\n\n return buildRpcClient<T>(config, headers);\n}\n"],"mappings":";;;;;AAYA,MAAM,yBAAyB;AAC/B,MAAM,0BAA0B;AAChC,MAAM,iCAAiB,IAAI,IAAqB;AAGhD,eAAe,wBACb,UACA,SACmB;CACnB,IAAI,WAAW,MAAM,MAAM,UAAU,OAAO;CAC5C,KAAK,IAAI,UAAU,GAAG,WAAW,0BAA0B,SAAS,WAAW,KAAK,WAAW;EAC7F,MAAM,oBAAoB,OAAO,SAAS,QAAQ,IAAI,aAAa,CAAC;EACpE,MAAM,QACJ,OAAO,SAAS,iBAAiB,KAAK,oBAAoB,IACtD,KAAK,IAAI,oBAAoB,KAAM,uBAAuB,IAC1D,MAAM;EACZ,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,KAAK,CAAC;EACzD,WAAW,MAAM,MAAM,UAAU,OAAO;CAC1C;CAEA,OAAO;AACT;AAEA,SAAS,cAAc,QAAyB,SAAmB;CACjE,OAAO,IAAIA,2BAAQ;EACjB,QAAQ,OAAO;EACf,KAAK,OAAO;EACZ,cAAc,KACZC,uBAAS,UAAmB;GAE1B,IAAI,OADmB,WAAoC,WAC9B,aAC3B;GAGF,IAAI,SAAS,OAAO,UAAU,YAAY,aAAa,OAAO;IAC5D,MAAM,UAAU,OAAO,MAAM,OAAO,CAAC,CAAC,YAAY;IAClD,IACE,QAAQ,SAAS,OAAO,KACxB,QAAQ,SAAS,SAAS,KAC1B,QAAQ,SAAS,iBAAiB,GAElC,AAAK,OAAO,SAAS,CAClB,MAAM,EAAE,YAAY;KACnB,MAAM,UAAU,OAAO,kBAAkB,KAAK;MAC5C,OAAO;MACP,aAAa;KACf;KACA,MAAM,MAAM,QAAQ,OAAO;MACzB,IAAI;MACJ,aAAa,QAAQ;KACvB,CAAC;IACH,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;GAErB;EACF,CAAC,CACH;EACA,MAAM,UAAsB,SAAuB;GACjD,OAAO,wBAAwB,UAAU;IACvC,GAAG;IACH,aAAa;IACb,SAAS,UACL;KAAE,GAAG,OAAO,YAAY,OAAO;KAAG,GAAI,SAAS;IAAmC,IAClF,SAAS;GACf,CAAC;EACH;CACF,CAAC;AACH;AAEA,SAAS,eACP,QACA,SACyB;CACzB,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MAAM,0BAA0B;CAG5C,WAAOC,+BAAiB,cAAc,QAAQ,OAAO,CAAC;AACxD;AAEA,SAAgB,gBACd,QACA,SACyB;CAEzB,IAAI,OADmB,WAAoC,WAC9B,eAAe,CAAC,SAAS;EACpD,MAAM,MAAM,GAAG,OAAO,UAAU,OAAO;EACvC,MAAM,WAAW,eAAe,IAAI,GAAG;EACvC,IAAI,UACF,OAAO;EAGT,MAAM,SAAS,eAAkB,QAAQ,OAAO;EAChD,eAAe,IAAI,KAAK,MAAM;EAC9B,OAAO;CACT;CAEA,OAAO,eAAkB,QAAQ,OAAO;AAC1C"}
@@ -0,0 +1,15 @@
1
+ import { ContractRouterClient, RouterContract } from "@orpc/contract";
2
+ //#region src/client.d.ts
3
+ type RpcConnectionErrorCopy = () => {
4
+ title: string;
5
+ description: string;
6
+ };
7
+ interface RpcClientConfig {
8
+ hostUrl: string;
9
+ path: `/${string}`;
10
+ connectionError?: RpcConnectionErrorCopy;
11
+ }
12
+ declare function createRpcClient<T extends RouterContract = RouterContract>(config: RpcClientConfig, headers?: Headers): ContractRouterClient<T>;
13
+ //#endregion
14
+ export { RpcClientConfig, RpcConnectionErrorCopy, createRpcClient };
15
+ //# sourceMappingURL=client.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.cts","names":[],"sources":["../src/client.ts"],"mappings":";;KAIY;EAAiC;EAAe;;UAE3C;EACf;EACA;EACA,kBAAkB;;iBAmFJ,gBAAgB,UAAU,iBAAiB,gBACzD,QAAQ,iBACR,UAAU,UACT,qBAAqB"}
@@ -0,0 +1,15 @@
1
+ import { ContractRouterClient, RouterContract } from "@orpc/contract";
2
+ //#region src/client.d.ts
3
+ type RpcConnectionErrorCopy = () => {
4
+ title: string;
5
+ description: string;
6
+ };
7
+ interface RpcClientConfig {
8
+ hostUrl: string;
9
+ path: `/${string}`;
10
+ connectionError?: RpcConnectionErrorCopy;
11
+ }
12
+ declare function createRpcClient<T extends RouterContract = RouterContract>(config: RpcClientConfig, headers?: Headers): ContractRouterClient<T>;
13
+ //#endregion
14
+ export { RpcClientConfig, RpcConnectionErrorCopy, createRpcClient };
15
+ //# sourceMappingURL=client.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;KAIY;EAAiC;EAAe;;UAE3C;EACf;EACA;EACA,kBAAkB;;iBAmFJ,gBAAgB,UAAU,iBAAiB,gBACzD,QAAQ,iBACR,UAAU,UACT,qBAAqB"}
@@ -0,0 +1,68 @@
1
+ import { createORPCClient, onError } from "@orpc/client";
2
+ import { RPCLink } from "@orpc/client/fetch";
3
+
4
+ //#region src/client.ts
5
+ const RATE_LIMIT_MAX_RETRIES = 3;
6
+ const RATE_LIMIT_RETRY_CAP_MS = 2e3;
7
+ const browserClients = /* @__PURE__ */ new Map();
8
+ async function fetchWithRateLimitRetry(fetchUrl, options) {
9
+ let response = await fetch(fetchUrl, options);
10
+ for (let attempt = 1; attempt <= RATE_LIMIT_MAX_RETRIES && response.status === 429; attempt++) {
11
+ const retryAfterSeconds = Number(response.headers.get("retry-after"));
12
+ const delay = Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0 ? Math.min(retryAfterSeconds * 1e3, RATE_LIMIT_RETRY_CAP_MS) : 100 * attempt;
13
+ await new Promise((resolve) => setTimeout(resolve, delay));
14
+ response = await fetch(fetchUrl, options);
15
+ }
16
+ return response;
17
+ }
18
+ function createRpcLink(config, headers) {
19
+ return new RPCLink({
20
+ origin: config.hostUrl,
21
+ url: config.path,
22
+ interceptors: [onError((error) => {
23
+ if (typeof globalThis.window === "undefined") return;
24
+ if (error && typeof error === "object" && "message" in error) {
25
+ const message = String(error.message).toLowerCase();
26
+ if (message.includes("fetch") || message.includes("network") || message.includes("failed to fetch")) import("sonner").then(({ toast }) => {
27
+ const message = config.connectionError?.() ?? {
28
+ title: "Unable to connect to API",
29
+ description: "The API is currently unavailable. Please try again later."
30
+ };
31
+ toast.error(message.title, {
32
+ id: "api-connection-error",
33
+ description: message.description
34
+ });
35
+ }).catch(() => {});
36
+ }
37
+ })],
38
+ fetch(fetchUrl, options) {
39
+ return fetchWithRateLimitRetry(fetchUrl, {
40
+ ...options,
41
+ credentials: "include",
42
+ headers: headers ? {
43
+ ...Object.fromEntries(headers),
44
+ ...options?.headers
45
+ } : options?.headers
46
+ });
47
+ }
48
+ });
49
+ }
50
+ function buildRpcClient(config, headers) {
51
+ if (!config.hostUrl) throw new Error("Missing runtime host URL");
52
+ return createORPCClient(createRpcLink(config, headers));
53
+ }
54
+ function createRpcClient(config, headers) {
55
+ if (typeof globalThis.window !== "undefined" && !headers) {
56
+ const key = `${config.hostUrl}${config.path}`;
57
+ const memoized = browserClients.get(key);
58
+ if (memoized) return memoized;
59
+ const client = buildRpcClient(config, headers);
60
+ browserClients.set(key, client);
61
+ return client;
62
+ }
63
+ return buildRpcClient(config, headers);
64
+ }
65
+
66
+ //#endregion
67
+ export { createRpcClient };
68
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import { createORPCClient, onError } from \"@orpc/client\";\nimport { RPCLink } from \"@orpc/client/fetch\";\nimport type { ContractRouterClient, RouterContract } from \"@orpc/contract\";\n\nexport type RpcConnectionErrorCopy = () => { title: string; description: string };\n\nexport interface RpcClientConfig {\n hostUrl: string;\n path: `/${string}`;\n connectionError?: RpcConnectionErrorCopy;\n}\n\nconst RATE_LIMIT_MAX_RETRIES = 3;\nconst RATE_LIMIT_RETRY_CAP_MS = 2_000;\nconst browserClients = new Map<string, unknown>();\ntype FetchInput = Parameters<typeof fetch>[0];\n\nasync function fetchWithRateLimitRetry(\n fetchUrl: FetchInput,\n options?: RequestInit,\n): Promise<Response> {\n let response = await fetch(fetchUrl, options);\n for (let attempt = 1; attempt <= RATE_LIMIT_MAX_RETRIES && response.status === 429; attempt++) {\n const retryAfterSeconds = Number(response.headers.get(\"retry-after\"));\n const delay =\n Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0\n ? Math.min(retryAfterSeconds * 1000, RATE_LIMIT_RETRY_CAP_MS)\n : 100 * attempt;\n await new Promise((resolve) => setTimeout(resolve, delay));\n response = await fetch(fetchUrl, options);\n }\n\n return response;\n}\n\nfunction createRpcLink(config: RpcClientConfig, headers?: Headers) {\n return new RPCLink({\n origin: config.hostUrl,\n url: config.path,\n interceptors: [\n onError((error: unknown) => {\n const browserWindow = (globalThis as { window?: unknown }).window;\n if (typeof browserWindow === \"undefined\") {\n return;\n }\n\n if (error && typeof error === \"object\" && \"message\" in error) {\n const message = String(error.message).toLowerCase();\n if (\n message.includes(\"fetch\") ||\n message.includes(\"network\") ||\n message.includes(\"failed to fetch\")\n ) {\n void import(\"sonner\")\n .then(({ toast }) => {\n const message = config.connectionError?.() ?? {\n title: \"Unable to connect to API\",\n description: \"The API is currently unavailable. Please try again later.\",\n };\n toast.error(message.title, {\n id: \"api-connection-error\",\n description: message.description,\n });\n })\n .catch(() => {});\n }\n }\n }),\n ],\n fetch(fetchUrl: FetchInput, options?: RequestInit) {\n return fetchWithRateLimitRetry(fetchUrl, {\n ...options,\n credentials: \"include\",\n headers: headers\n ? { ...Object.fromEntries(headers), ...(options?.headers as Record<string, string>) }\n : options?.headers,\n });\n },\n });\n}\n\nfunction buildRpcClient<T extends RouterContract>(\n config: RpcClientConfig,\n headers?: Headers,\n): ContractRouterClient<T> {\n if (!config.hostUrl) {\n throw new Error(\"Missing runtime host URL\");\n }\n\n return createORPCClient(createRpcLink(config, headers)) as ContractRouterClient<T>;\n}\n\nexport function createRpcClient<T extends RouterContract = RouterContract>(\n config: RpcClientConfig,\n headers?: Headers,\n): ContractRouterClient<T> {\n const browserWindow = (globalThis as { window?: unknown }).window;\n if (typeof browserWindow !== \"undefined\" && !headers) {\n const key = `${config.hostUrl}${config.path}`;\n const memoized = browserClients.get(key);\n if (memoized) {\n return memoized as ContractRouterClient<T>;\n }\n\n const client = buildRpcClient<T>(config, headers);\n browserClients.set(key, client);\n return client;\n }\n\n return buildRpcClient<T>(config, headers);\n}\n"],"mappings":";;;;AAYA,MAAM,yBAAyB;AAC/B,MAAM,0BAA0B;AAChC,MAAM,iCAAiB,IAAI,IAAqB;AAGhD,eAAe,wBACb,UACA,SACmB;CACnB,IAAI,WAAW,MAAM,MAAM,UAAU,OAAO;CAC5C,KAAK,IAAI,UAAU,GAAG,WAAW,0BAA0B,SAAS,WAAW,KAAK,WAAW;EAC7F,MAAM,oBAAoB,OAAO,SAAS,QAAQ,IAAI,aAAa,CAAC;EACpE,MAAM,QACJ,OAAO,SAAS,iBAAiB,KAAK,oBAAoB,IACtD,KAAK,IAAI,oBAAoB,KAAM,uBAAuB,IAC1D,MAAM;EACZ,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,KAAK,CAAC;EACzD,WAAW,MAAM,MAAM,UAAU,OAAO;CAC1C;CAEA,OAAO;AACT;AAEA,SAAS,cAAc,QAAyB,SAAmB;CACjE,OAAO,IAAI,QAAQ;EACjB,QAAQ,OAAO;EACf,KAAK,OAAO;EACZ,cAAc,CACZ,SAAS,UAAmB;GAE1B,IAAI,OADmB,WAAoC,WAC9B,aAC3B;GAGF,IAAI,SAAS,OAAO,UAAU,YAAY,aAAa,OAAO;IAC5D,MAAM,UAAU,OAAO,MAAM,OAAO,CAAC,CAAC,YAAY;IAClD,IACE,QAAQ,SAAS,OAAO,KACxB,QAAQ,SAAS,SAAS,KAC1B,QAAQ,SAAS,iBAAiB,GAElC,AAAK,OAAO,SAAS,CAClB,MAAM,EAAE,YAAY;KACnB,MAAM,UAAU,OAAO,kBAAkB,KAAK;MAC5C,OAAO;MACP,aAAa;KACf;KACA,MAAM,MAAM,QAAQ,OAAO;MACzB,IAAI;MACJ,aAAa,QAAQ;KACvB,CAAC;IACH,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;GAErB;EACF,CAAC,CACH;EACA,MAAM,UAAsB,SAAuB;GACjD,OAAO,wBAAwB,UAAU;IACvC,GAAG;IACH,aAAa;IACb,SAAS,UACL;KAAE,GAAG,OAAO,YAAY,OAAO;KAAG,GAAI,SAAS;IAAmC,IAClF,SAAS;GACf,CAAC;EACH;CACF,CAAC;AACH;AAEA,SAAS,eACP,QACA,SACyB;CACzB,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MAAM,0BAA0B;CAG5C,OAAO,iBAAiB,cAAc,QAAQ,OAAO,CAAC;AACxD;AAEA,SAAgB,gBACd,QACA,SACyB;CAEzB,IAAI,OADmB,WAAoC,WAC9B,eAAe,CAAC,SAAS;EACpD,MAAM,MAAM,GAAG,OAAO,UAAU,OAAO;EACvC,MAAM,WAAW,eAAe,IAAI,GAAG;EACvC,IAAI,UACF,OAAO;EAGT,MAAM,SAAS,eAAkB,QAAQ,OAAO;EAChD,eAAe,IAAI,KAAK,MAAM;EAC9B,OAAO;CACT;CAEA,OAAO,eAAkB,QAAQ,OAAO;AAC1C"}
@@ -0,0 +1,149 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let node_http = require("node:http");
3
+
4
+ //#region src/dev/host.ts
5
+ const corsHeaders = {
6
+ "Access-Control-Allow-Origin": "*",
7
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
8
+ "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
9
+ };
10
+ const applyCorsHeaders = (res) => {
11
+ Object.entries(corsHeaders).forEach(([key, value]) => {
12
+ res.setHeader(key, value);
13
+ });
14
+ };
15
+ const sendJson = (res, status, body) => {
16
+ res.statusCode = status;
17
+ res.setHeader("content-type", "application/json; charset=utf-8");
18
+ res.end(JSON.stringify(body));
19
+ };
20
+ const sendText = (res, status, text) => {
21
+ res.statusCode = status;
22
+ res.setHeader("content-type", "text/plain; charset=utf-8");
23
+ res.end(text);
24
+ };
25
+ const pathnameOf = (req) => ((req.url ?? "/").split("?")[0] ?? "/").replace(/\/+$/, "") || "/";
26
+ const toWebRequest = (req, port) => {
27
+ const url = `http://localhost:${port}${req.url}`;
28
+ return new Request(url, {
29
+ method: req.method,
30
+ headers: req.headers,
31
+ body: req.method !== "GET" && req.method !== "HEAD" ? req : void 0,
32
+ duplex: req.method !== "GET" && req.method !== "HEAD" ? "half" : void 0
33
+ });
34
+ };
35
+ const buildHostContext = (effectContext, webRequest, cachedRawBodyRef) => {
36
+ const rawClone = webRequest.method === "GET" || webRequest.method === "HEAD" ? null : webRequest.clone();
37
+ return {
38
+ reqHeaders: webRequest.headers,
39
+ "effect/context": effectContext,
40
+ getRawBody: async () => {
41
+ if (cachedRawBodyRef.value !== null) return cachedRawBodyRef.value;
42
+ if (!rawClone) {
43
+ cachedRawBodyRef.value = "";
44
+ return cachedRawBodyRef.value;
45
+ }
46
+ cachedRawBodyRef.value = await rawClone.text();
47
+ return cachedRawBodyRef.value;
48
+ }
49
+ };
50
+ };
51
+ const handleOrpc = async (req, res, handler, prefix, errorLabel, effectContext, port) => {
52
+ try {
53
+ const webRequest = toWebRequest(req, port);
54
+ const result = await handler.handle(webRequest, {
55
+ prefix,
56
+ context: buildHostContext(effectContext, webRequest, { value: null })
57
+ });
58
+ if (result.response) {
59
+ res.statusCode = result.response.status;
60
+ result.response.headers.forEach((value, key) => {
61
+ res.setHeader(key, value);
62
+ });
63
+ const text = await result.response.text();
64
+ if (!res.getHeader("content-length")) res.setHeader("content-length", Buffer.byteLength(text).toString());
65
+ res.end(text);
66
+ } else sendText(res, 404, "Not Found");
67
+ } catch (error) {
68
+ console.error(`${errorLabel} error:`, error);
69
+ sendJson(res, 500, { error: error.message });
70
+ }
71
+ };
72
+ async function startDirectModuleHost(options) {
73
+ const port = options.port ?? 0;
74
+ let resolvedPort = port;
75
+ const rpcPrefix = options.rpcPrefix ?? "/api/rpc";
76
+ const apiPrefix = options.apiPrefix ?? "/api";
77
+ const staticHandler = options.staticHandler ?? null;
78
+ const resolveEffectContext = typeof options.effectContext === "function" ? options.effectContext : () => options.effectContext;
79
+ const server = (0, node_http.createServer)((req, res) => {
80
+ applyCorsHeaders(res);
81
+ const pathname = pathnameOf(req);
82
+ if (req.method === "OPTIONS") {
83
+ res.statusCode = 200;
84
+ res.end();
85
+ return;
86
+ }
87
+ if (pathname === "/" && (req.method === "GET" || req.method === "HEAD")) {
88
+ sendJson(res, 200, {
89
+ ok: true,
90
+ module: options.name,
91
+ version: options.version,
92
+ status: options.handlers.rpc ? "ready" : "loading",
93
+ endpoints: {
94
+ health: "/health",
95
+ docs: apiPrefix,
96
+ rpc: rpcPrefix
97
+ }
98
+ });
99
+ return;
100
+ }
101
+ if (pathname === "/health" && (req.method === "GET" || req.method === "HEAD")) {
102
+ sendText(res, 200, "OK");
103
+ return;
104
+ }
105
+ if (pathname.startsWith(`${rpcPrefix}/`)) {
106
+ if (!options.handlers.rpc) {
107
+ sendJson(res, 503, { error: "Plugin still loading..." });
108
+ return;
109
+ }
110
+ handleOrpc(req, res, options.handlers.rpc, rpcPrefix, "RPC", resolveEffectContext(), resolvedPort).catch(() => {});
111
+ return;
112
+ }
113
+ if (pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`)) {
114
+ if (!options.handlers.api) {
115
+ sendJson(res, 503, { error: "Plugin still loading..." });
116
+ return;
117
+ }
118
+ handleOrpc(req, res, options.handlers.api, apiPrefix, "OpenAPI", resolveEffectContext(), resolvedPort).catch(() => {});
119
+ return;
120
+ }
121
+ if (staticHandler) {
122
+ staticHandler(req, res, () => {
123
+ sendText(res, 404, "Not Found");
124
+ });
125
+ return;
126
+ }
127
+ sendText(res, 404, "Not Found");
128
+ });
129
+ await new Promise((resolve, reject) => {
130
+ server.once("error", reject);
131
+ server.listen(port, () => resolve());
132
+ });
133
+ const address = server.address();
134
+ if (!address || typeof address === "string") {
135
+ await new Promise((resolve) => server.close(() => resolve()));
136
+ throw new Error("Failed to resolve host port");
137
+ }
138
+ resolvedPort = address.port;
139
+ return {
140
+ port: resolvedPort,
141
+ close: async () => {
142
+ await new Promise((resolve) => server.close(() => resolve()));
143
+ }
144
+ };
145
+ }
146
+
147
+ //#endregion
148
+ exports.startDirectModuleHost = startDirectModuleHost;
149
+ //# sourceMappingURL=host.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.cjs","names":["createServer"],"sources":["../../src/dev/host.ts"],"sourcesContent":["import type http from \"node:http\";\nimport { createServer } from \"node:http\";\n\nconst corsHeaders = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, PATCH, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"X-Requested-With, content-type, Authorization\",\n};\n\nconst applyCorsHeaders = (res: http.ServerResponse) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst sendJson = (res: http.ServerResponse, status: number, body: unknown) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify(body));\n};\n\nconst sendText = (res: http.ServerResponse, status: number, text: string) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(text);\n};\n\nconst pathnameOf = (req: http.IncomingMessage): string =>\n ((req.url ?? \"/\").split(\"?\")[0] ?? \"/\").replace(/\\/+$/, \"\") || \"/\";\n\nconst toWebRequest = (req: http.IncomingMessage, port: number): Request => {\n const url = `http://localhost:${port}${req.url}`;\n return new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n};\n\nconst buildHostContext = (\n effectContext: unknown,\n webRequest: Request,\n cachedRawBodyRef: { value: string | null },\n) => {\n const rawClone =\n webRequest.method === \"GET\" || webRequest.method === \"HEAD\" ? null : webRequest.clone();\n\n return {\n reqHeaders: webRequest.headers,\n \"effect/context\": effectContext,\n getRawBody: async (): Promise<string> => {\n if (cachedRawBodyRef.value !== null) return cachedRawBodyRef.value;\n if (!rawClone) {\n cachedRawBodyRef.value = \"\";\n return cachedRawBodyRef.value;\n }\n cachedRawBodyRef.value = await rawClone.text();\n return cachedRawBodyRef.value;\n },\n };\n};\n\nexport interface DirectModuleHostHandler {\n handle(\n request: Request,\n options: { prefix: string; context: unknown },\n ): Promise<{ response?: Response }>;\n}\n\nconst handleOrpc = async (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n handler: DirectModuleHostHandler,\n prefix: string,\n errorLabel: string,\n effectContext: unknown,\n port: number,\n) => {\n try {\n const webRequest = toWebRequest(req, port);\n const cachedRawBodyRef = { value: null as string | null };\n const result = await handler.handle(webRequest, {\n prefix,\n context: buildHostContext(effectContext, webRequest, cachedRawBodyRef),\n });\n\n if (result.response) {\n res.statusCode = result.response.status;\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n if (!res.getHeader(\"content-length\")) {\n res.setHeader(\"content-length\", Buffer.byteLength(text).toString());\n }\n res.end(text);\n } else {\n sendText(res, 404, \"Not Found\");\n }\n } catch (error) {\n console.error(`${errorLabel} error:`, error);\n sendJson(res, 500, { error: (error as Error).message });\n }\n};\n\nexport interface DirectModuleHostHandlers {\n rpc: DirectModuleHostHandler | null;\n api: DirectModuleHostHandler | null;\n}\n\nexport interface DirectModuleHostOptions {\n name: string;\n handlers: DirectModuleHostHandlers;\n effectContext: unknown;\n port?: number;\n version?: string;\n rpcPrefix?: `/${string}`;\n apiPrefix?: `/${string}`;\n staticHandler?:\n | ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void)\n | null;\n}\n\nexport interface DirectModuleHostHandle {\n port: number;\n close: () => Promise<void>;\n}\n\nexport async function startDirectModuleHost(\n options: DirectModuleHostOptions,\n): Promise<DirectModuleHostHandle> {\n const port = options.port ?? 0;\n let resolvedPort = port;\n const rpcPrefix = options.rpcPrefix ?? \"/api/rpc\";\n const apiPrefix = options.apiPrefix ?? \"/api\";\n const staticHandler = options.staticHandler ?? null;\n const resolveEffectContext: () => unknown =\n typeof options.effectContext === \"function\"\n ? (options.effectContext as () => unknown)\n : () => options.effectContext;\n\n const server = createServer((req, res) => {\n applyCorsHeaders(res);\n const pathname = pathnameOf(req);\n\n if (req.method === \"OPTIONS\") {\n res.statusCode = 200;\n res.end();\n return;\n }\n\n if (pathname === \"/\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendJson(res, 200, {\n ok: true,\n module: options.name,\n version: options.version,\n status: options.handlers.rpc ? \"ready\" : \"loading\",\n endpoints: {\n health: \"/health\",\n docs: apiPrefix,\n rpc: rpcPrefix,\n },\n });\n return;\n }\n\n if (pathname === \"/health\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendText(res, 200, \"OK\");\n return;\n }\n\n if (pathname.startsWith(`${rpcPrefix}/`)) {\n if (!options.handlers.rpc) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(\n req,\n res,\n options.handlers.rpc,\n rpcPrefix,\n \"RPC\",\n resolveEffectContext(),\n resolvedPort,\n ).catch(() => {});\n return;\n }\n\n if (pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`)) {\n if (!options.handlers.api) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(\n req,\n res,\n options.handlers.api,\n apiPrefix,\n \"OpenAPI\",\n resolveEffectContext(),\n resolvedPort,\n ).catch(() => {});\n return;\n }\n\n if (staticHandler) {\n staticHandler(req, res, () => {\n sendText(res, 404, \"Not Found\");\n });\n return;\n }\n\n sendText(res, 404, \"Not Found\");\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, () => resolve());\n });\n\n const address = server.address();\n if (!address || typeof address === \"string\") {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n throw new Error(\"Failed to resolve host port\");\n }\n resolvedPort = address.port;\n\n return {\n port: resolvedPort,\n close: async () => {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import http from "node:http";
2
+ //#region src/dev/host.d.ts
3
+ interface DirectModuleHostHandler {
4
+ handle(request: Request, options: {
5
+ prefix: string;
6
+ context: unknown;
7
+ }): Promise<{
8
+ response?: Response;
9
+ }>;
10
+ }
11
+ interface DirectModuleHostHandlers {
12
+ rpc: DirectModuleHostHandler | null;
13
+ api: DirectModuleHostHandler | null;
14
+ }
15
+ interface DirectModuleHostOptions {
16
+ name: string;
17
+ handlers: DirectModuleHostHandlers;
18
+ effectContext: unknown;
19
+ port?: number;
20
+ version?: string;
21
+ rpcPrefix?: `/${string}`;
22
+ apiPrefix?: `/${string}`;
23
+ staticHandler?: ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void) | null;
24
+ }
25
+ interface DirectModuleHostHandle {
26
+ port: number;
27
+ close: () => Promise<void>;
28
+ }
29
+ declare function startDirectModuleHost(options: DirectModuleHostOptions): Promise<DirectModuleHostHandle>;
30
+ //#endregion
31
+ export { DirectModuleHostHandle, DirectModuleHostHandler, DirectModuleHostHandlers, DirectModuleHostOptions, startDirectModuleHost };
32
+ //# sourceMappingURL=host.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.cts","names":[],"sources":["../../src/dev/host.ts"],"mappings":";;UA+DiB;EACf,OACE,SAAS,SACT;IAAW;IAAgB;MAC1B;IAAU,WAAW;;;UAuCT;EACf,KAAK;EACL,KAAK;;UAGU;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA,kBACM,KAAK,KAAK,iBAAiB,KAAK,KAAK,gBAAgB;;UAI5C;EACf;EACA,aAAa;;iBAGO,sBACpB,SAAS,0BACR,QAAQ"}
@@ -0,0 +1,32 @@
1
+ import http from "node:http";
2
+ //#region src/dev/host.d.ts
3
+ interface DirectModuleHostHandler {
4
+ handle(request: Request, options: {
5
+ prefix: string;
6
+ context: unknown;
7
+ }): Promise<{
8
+ response?: Response;
9
+ }>;
10
+ }
11
+ interface DirectModuleHostHandlers {
12
+ rpc: DirectModuleHostHandler | null;
13
+ api: DirectModuleHostHandler | null;
14
+ }
15
+ interface DirectModuleHostOptions {
16
+ name: string;
17
+ handlers: DirectModuleHostHandlers;
18
+ effectContext: unknown;
19
+ port?: number;
20
+ version?: string;
21
+ rpcPrefix?: `/${string}`;
22
+ apiPrefix?: `/${string}`;
23
+ staticHandler?: ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void) | null;
24
+ }
25
+ interface DirectModuleHostHandle {
26
+ port: number;
27
+ close: () => Promise<void>;
28
+ }
29
+ declare function startDirectModuleHost(options: DirectModuleHostOptions): Promise<DirectModuleHostHandle>;
30
+ //#endregion
31
+ export { DirectModuleHostHandle, DirectModuleHostHandler, DirectModuleHostHandlers, DirectModuleHostOptions, startDirectModuleHost };
32
+ //# sourceMappingURL=host.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.mts","names":[],"sources":["../../src/dev/host.ts"],"mappings":";;UA+DiB;EACf,OACE,SAAS,SACT;IAAW;IAAgB;MAC1B;IAAU,WAAW;;;UAuCT;EACf,KAAK;EACL,KAAK;;UAGU;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA,kBACM,KAAK,KAAK,iBAAiB,KAAK,KAAK,gBAAgB;;UAI5C;EACf;EACA,aAAa;;iBAGO,sBACpB,SAAS,0BACR,QAAQ"}