everything-dev 2.0.0-rc.6 → 2.0.0-rc.8

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 (148) hide show
  1. package/README.md +2 -2
  2. package/dist/api/auth-middleware.cjs +14 -1
  3. package/dist/api/auth-middleware.cjs.map +1 -1
  4. package/dist/api/auth-middleware.d.cts +9 -0
  5. package/dist/api/auth-middleware.d.mts +9 -0
  6. package/dist/api/auth-middleware.mjs +14 -1
  7. package/dist/api/auth-middleware.mjs.map +1 -1
  8. package/dist/build.cjs +1 -1
  9. package/dist/build.mjs +1 -1
  10. package/dist/bundle-cache.cjs +1 -1
  11. package/dist/bundle-cache.cjs.map +1 -1
  12. package/dist/bundle-cache.mjs +1 -1
  13. package/dist/bundle-cache.mjs.map +1 -1
  14. package/dist/cli/init.cjs +22 -10
  15. package/dist/cli/init.cjs.map +1 -1
  16. package/dist/cli/init.mjs +22 -10
  17. package/dist/cli/init.mjs.map +1 -1
  18. package/dist/cli.cjs +6 -5
  19. package/dist/cli.cjs.map +1 -1
  20. package/dist/cli.mjs +5 -4
  21. package/dist/cli.mjs.map +1 -1
  22. package/dist/commands/deploy.cjs +111 -1
  23. package/dist/commands/deploy.cjs.map +1 -1
  24. package/dist/commands/deploy.mjs +112 -2
  25. package/dist/commands/deploy.mjs.map +1 -1
  26. package/dist/commands/keys.cjs +1 -1
  27. package/dist/commands/keys.cjs.map +1 -1
  28. package/dist/commands/keys.mjs +1 -1
  29. package/dist/commands/keys.mjs.map +1 -1
  30. package/dist/commands/smoke.cjs +41 -0
  31. package/dist/commands/smoke.cjs.map +1 -0
  32. package/dist/commands/smoke.mjs +41 -0
  33. package/dist/commands/smoke.mjs.map +1 -0
  34. package/dist/commands/upgrade.cjs +2 -2
  35. package/dist/commands/upgrade.cjs.map +1 -1
  36. package/dist/commands/upgrade.mjs +1 -1
  37. package/dist/commands/upgrade.mjs.map +1 -1
  38. package/dist/contract.cjs +27 -1
  39. package/dist/contract.cjs.map +1 -1
  40. package/dist/contract.d.cts +65 -1
  41. package/dist/contract.d.mts +65 -1
  42. package/dist/contract.meta.cjs +14 -2
  43. package/dist/contract.meta.cjs.map +1 -1
  44. package/dist/contract.meta.d.cts +24 -2
  45. package/dist/contract.meta.d.mts +24 -2
  46. package/dist/contract.meta.mjs +14 -2
  47. package/dist/contract.meta.mjs.map +1 -1
  48. package/dist/contract.mjs +26 -2
  49. package/dist/contract.mjs.map +1 -1
  50. package/dist/db/core.cjs +22 -4
  51. package/dist/db/core.cjs.map +1 -1
  52. package/dist/db/core.mjs +22 -4
  53. package/dist/db/core.mjs.map +1 -1
  54. package/dist/dev-log-pipeline.cjs +6 -0
  55. package/dist/dev-log-pipeline.cjs.map +1 -1
  56. package/dist/dev-log-pipeline.mjs +6 -0
  57. package/dist/dev-log-pipeline.mjs.map +1 -1
  58. package/dist/dev-program.cjs +20 -3
  59. package/dist/dev-program.cjs.map +1 -1
  60. package/dist/dev-program.mjs +20 -4
  61. package/dist/dev-program.mjs.map +1 -1
  62. package/dist/dev-session.cjs +1 -0
  63. package/dist/dev-session.cjs.map +1 -1
  64. package/dist/dev-session.mjs +1 -0
  65. package/dist/dev-session.mjs.map +1 -1
  66. package/dist/fastkv.cjs +21 -3
  67. package/dist/fastkv.cjs.map +1 -1
  68. package/dist/fastkv.d.cts +14 -1
  69. package/dist/fastkv.d.mts +14 -1
  70. package/dist/fastkv.mjs +21 -5
  71. package/dist/fastkv.mjs.map +1 -1
  72. package/dist/{mf.cjs → federation.cjs} +6 -13
  73. package/dist/federation.cjs.map +1 -0
  74. package/dist/{mf.d.cts → federation.d.cts} +2 -2
  75. package/dist/{mf.d.mts → federation.d.mts} +2 -2
  76. package/dist/{mf.mjs → federation.mjs} +6 -13
  77. package/dist/federation.mjs.map +1 -0
  78. package/dist/http-client.cjs +84 -57
  79. package/dist/http-client.cjs.map +1 -1
  80. package/dist/http-client.mjs +84 -58
  81. package/dist/http-client.mjs.map +1 -1
  82. package/dist/image-deploy.cjs +9 -0
  83. package/dist/image-deploy.cjs.map +1 -1
  84. package/dist/image-deploy.mjs +9 -1
  85. package/dist/image-deploy.mjs.map +1 -1
  86. package/dist/index.cjs +4 -0
  87. package/dist/index.d.cts +3 -3
  88. package/dist/index.d.mts +3 -3
  89. package/dist/index.mjs +3 -3
  90. package/dist/infra/planner.cjs +6 -2
  91. package/dist/infra/planner.cjs.map +1 -1
  92. package/dist/infra/planner.mjs +6 -2
  93. package/dist/infra/planner.mjs.map +1 -1
  94. package/dist/orchestrator.cjs +39 -35
  95. package/dist/orchestrator.cjs.map +1 -1
  96. package/dist/orchestrator.mjs +39 -35
  97. package/dist/orchestrator.mjs.map +1 -1
  98. package/dist/plugin.cjs +2 -0
  99. package/dist/plugin.cjs.map +1 -1
  100. package/dist/plugin.d.cts +24 -0
  101. package/dist/plugin.d.mts +24 -0
  102. package/dist/plugin.mjs +2 -0
  103. package/dist/plugin.mjs.map +1 -1
  104. package/dist/process-registry.cjs +1 -1
  105. package/dist/process-registry.mjs +1 -1
  106. package/dist/publish.cjs +3 -3
  107. package/dist/publish.cjs.map +1 -1
  108. package/dist/publish.mjs +4 -4
  109. package/dist/publish.mjs.map +1 -1
  110. package/dist/resolver.cjs +293 -0
  111. package/dist/resolver.cjs.map +1 -0
  112. package/dist/resolver.d.cts +99 -0
  113. package/dist/resolver.d.mts +99 -0
  114. package/dist/resolver.mjs +284 -0
  115. package/dist/resolver.mjs.map +1 -0
  116. package/dist/smoke.cjs +262 -0
  117. package/dist/smoke.cjs.map +1 -0
  118. package/dist/smoke.mjs +260 -0
  119. package/dist/smoke.mjs.map +1 -0
  120. package/dist/ui/auth.d.cts +217 -123
  121. package/dist/ui/auth.d.mts +217 -123
  122. package/dist/ui/router-client.cjs +2 -1
  123. package/dist/ui/router-client.cjs.map +1 -1
  124. package/dist/ui/router-client.mjs +2 -1
  125. package/dist/ui/router-client.mjs.map +1 -1
  126. package/dist/version-manifest-resolve.cjs +3 -3
  127. package/dist/version-manifest-resolve.cjs.map +1 -1
  128. package/dist/version-manifest-resolve.mjs +1 -1
  129. package/dist/version-manifest-resolve.mjs.map +1 -1
  130. package/dist/workspace.cjs +1 -1
  131. package/dist/workspace.mjs +1 -1
  132. package/package.json +18 -18
  133. package/skills/api-and-auth/SKILL.md +12 -9
  134. package/skills/api-and-auth/references/middleware.md +38 -0
  135. package/skills/cli-reference/SKILL.md +22 -6
  136. package/skills/talk-to-the-app/SKILL.md +32 -5
  137. package/dist/bundle-fs-resolve.cjs +0 -235
  138. package/dist/bundle-fs-resolve.cjs.map +0 -1
  139. package/dist/bundle-fs-resolve.d.cts +0 -82
  140. package/dist/bundle-fs-resolve.d.mts +0 -82
  141. package/dist/bundle-fs-resolve.mjs +0 -228
  142. package/dist/bundle-fs-resolve.mjs.map +0 -1
  143. package/dist/bundle-path.cjs +0 -48
  144. package/dist/bundle-path.cjs.map +0 -1
  145. package/dist/bundle-path.mjs +0 -45
  146. package/dist/bundle-path.mjs.map +0 -1
  147. package/dist/mf.cjs.map +0 -1
  148. package/dist/mf.mjs.map +0 -1
package/dist/fastkv.d.cts CHANGED
@@ -1,5 +1,11 @@
1
1
  //#region src/fastkv.d.ts
2
2
  type NetworkId = "mainnet" | "testnet";
3
+ interface FastKvEntry {
4
+ value: unknown;
5
+ }
6
+ interface FastKvListResponse {
7
+ entries?: Array<FastKvEntry | null>;
8
+ }
3
9
  declare function getFastKvBaseUrlForNetwork(network: NetworkId): string;
4
10
  declare function buildRegistryConfigUrl(accountId: string, gatewayId: string, registry?: string): string;
5
11
  declare function buildRegistryConfigUrlForNetwork(network: NetworkId, accountId: string, gatewayId: string, registry?: string): string;
@@ -11,6 +17,13 @@ declare function parseBosUrl(bosUrl: string): {
11
17
  pathSegments: string[];
12
18
  };
13
19
  declare function fetchBosConfigFromFastKv<T>(bosUrl: string, registry?: string): Promise<T>;
20
+ /** Read a published config from an already-built registry URL
21
+ * (`buildRegistryConfigUrlForNetwork`). Unlike {@link fetchBosConfigFromFastKv}
22
+ * this takes the URL directly, so the read key is exactly the one the publish
23
+ * writes, and a network failure is reported as such instead of collapsing
24
+ * into "No config found" (fetchJsonOrNull swallows errors into null). */
25
+ declare function fetchPublishedConfigFromFastKv<T>(url: string): Promise<T>;
26
+ declare function parseFastKvConfigValue<T>(payload: FastKvListResponse | null, label: string): T;
14
27
  interface ConfigHistoryEntry {
15
28
  blockHeight: number;
16
29
  blockTimestampNs: string;
@@ -74,5 +87,5 @@ interface PluginManifest {
74
87
  }
75
88
  declare function fetchRemotePluginManifest(cdnUrl: string): Promise<PluginManifest | null>;
76
89
  //#endregion
77
- export { ConfigHistoryEntry, DeployManifestEntry, NetworkId, PluginManifest, buildRegistryConfigUrl, buildRegistryConfigUrlForNetwork, fetchBosConfigFromFastKv, fetchConfigHistory, fetchDeployManifests, fetchRemotePluginManifest, getFastKvBaseUrlForNetwork, getRegistryNamespaceForAccount, getRegistryNamespaceForNetwork, parseBosUrl };
90
+ export { ConfigHistoryEntry, DeployManifestEntry, NetworkId, PluginManifest, buildRegistryConfigUrl, buildRegistryConfigUrlForNetwork, fetchBosConfigFromFastKv, fetchConfigHistory, fetchDeployManifests, fetchPublishedConfigFromFastKv, fetchRemotePluginManifest, getFastKvBaseUrlForNetwork, getRegistryNamespaceForAccount, getRegistryNamespaceForNetwork, parseBosUrl, parseFastKvConfigValue };
78
91
  //# sourceMappingURL=fastkv.d.cts.map
package/dist/fastkv.d.mts CHANGED
@@ -1,5 +1,11 @@
1
1
  //#region src/fastkv.d.ts
2
2
  type NetworkId = "mainnet" | "testnet";
3
+ interface FastKvEntry {
4
+ value: unknown;
5
+ }
6
+ interface FastKvListResponse {
7
+ entries?: Array<FastKvEntry | null>;
8
+ }
3
9
  declare function getFastKvBaseUrlForNetwork(network: NetworkId): string;
4
10
  declare function buildRegistryConfigUrl(accountId: string, gatewayId: string, registry?: string): string;
5
11
  declare function buildRegistryConfigUrlForNetwork(network: NetworkId, accountId: string, gatewayId: string, registry?: string): string;
@@ -11,6 +17,13 @@ declare function parseBosUrl(bosUrl: string): {
11
17
  pathSegments: string[];
12
18
  };
13
19
  declare function fetchBosConfigFromFastKv<T>(bosUrl: string, registry?: string): Promise<T>;
20
+ /** Read a published config from an already-built registry URL
21
+ * (`buildRegistryConfigUrlForNetwork`). Unlike {@link fetchBosConfigFromFastKv}
22
+ * this takes the URL directly, so the read key is exactly the one the publish
23
+ * writes, and a network failure is reported as such instead of collapsing
24
+ * into "No config found" (fetchJsonOrNull swallows errors into null). */
25
+ declare function fetchPublishedConfigFromFastKv<T>(url: string): Promise<T>;
26
+ declare function parseFastKvConfigValue<T>(payload: FastKvListResponse | null, label: string): T;
14
27
  interface ConfigHistoryEntry {
15
28
  blockHeight: number;
16
29
  blockTimestampNs: string;
@@ -74,5 +87,5 @@ interface PluginManifest {
74
87
  }
75
88
  declare function fetchRemotePluginManifest(cdnUrl: string): Promise<PluginManifest | null>;
76
89
  //#endregion
77
- export { ConfigHistoryEntry, DeployManifestEntry, NetworkId, PluginManifest, buildRegistryConfigUrl, buildRegistryConfigUrlForNetwork, fetchBosConfigFromFastKv, fetchConfigHistory, fetchDeployManifests, fetchRemotePluginManifest, getFastKvBaseUrlForNetwork, getRegistryNamespaceForAccount, getRegistryNamespaceForNetwork, parseBosUrl };
90
+ export { ConfigHistoryEntry, DeployManifestEntry, NetworkId, PluginManifest, buildRegistryConfigUrl, buildRegistryConfigUrlForNetwork, fetchBosConfigFromFastKv, fetchConfigHistory, fetchDeployManifests, fetchPublishedConfigFromFastKv, fetchRemotePluginManifest, getFastKvBaseUrlForNetwork, getRegistryNamespaceForAccount, getRegistryNamespaceForNetwork, parseBosUrl, parseFastKvConfigValue };
78
91
  //# sourceMappingURL=fastkv.d.mts.map
package/dist/fastkv.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { fetchJsonOrNull } from "./http-client.mjs";
1
+ import { fetchJsonOrNull, fetchRaw } from "./http-client.mjs";
2
2
 
3
3
  //#region src/fastkv.ts
4
4
  function getNetworkIdForAccount(accountId) {
@@ -52,10 +52,26 @@ function parseBosUrl(bosUrl) {
52
52
  async function fetchBosConfigFromFastKv(bosUrl, registry) {
53
53
  const { accountId, gatewayId, pathSegments } = parseBosUrl(bosUrl);
54
54
  const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId, pathSegments));
55
- const value = (await fetchJson(`${getFastKvBaseUrlForAccount(accountId)}/v0/latest/${encodeURIComponent(getRegistryNamespaceForAccount(accountId, registry))}/${encodeURIComponent(accountId)}/${key}`))?.entries?.find(Boolean)?.value;
56
- if (!value) throw new Error(`No config found for ${bosUrl}`);
55
+ return parseFastKvConfigValue(await fetchJson(`${getFastKvBaseUrlForAccount(accountId)}/v0/latest/${encodeURIComponent(getRegistryNamespaceForAccount(accountId, registry))}/${encodeURIComponent(accountId)}/${key}`), bosUrl);
56
+ }
57
+ /** Read a published config from an already-built registry URL
58
+ * (`buildRegistryConfigUrlForNetwork`). Unlike {@link fetchBosConfigFromFastKv}
59
+ * this takes the URL directly, so the read key is exactly the one the publish
60
+ * writes, and a network failure is reported as such instead of collapsing
61
+ * into "No config found" (fetchJsonOrNull swallows errors into null). */
62
+ async function fetchPublishedConfigFromFastKv(url) {
63
+ const response = await fetchRaw(url, { headers: { accept: "application/json" } });
64
+ if (!response.ok) {
65
+ if (response.status === 404) throw new Error(`No config found for ${url}`);
66
+ throw new Error(`HTTP ${response.status} fetching ${url}`);
67
+ }
68
+ return parseFastKvConfigValue(await response.json(), url);
69
+ }
70
+ function parseFastKvConfigValue(payload, label) {
71
+ const value = payload?.entries?.find(Boolean)?.value;
72
+ if (!value) throw new Error(`No config found for ${label}`);
57
73
  if (typeof value === "string") return JSON.parse(value);
58
- if (typeof value !== "object") throw new Error(`Invalid config value for ${bosUrl}`);
74
+ if (typeof value !== "object") throw new Error(`Invalid config value for ${label}`);
59
75
  return value;
60
76
  }
61
77
  const FASTKV_HISTORY_MAX_LIMIT = 200;
@@ -116,5 +132,5 @@ async function fetchJson(url, init) {
116
132
  }
117
133
 
118
134
  //#endregion
119
- export { buildRegistryConfigUrl, buildRegistryConfigUrlForNetwork, fetchBosConfigFromFastKv, fetchConfigHistory, fetchDeployManifests, fetchRemotePluginManifest, getFastKvBaseUrlForNetwork, getRegistryNamespaceForAccount, getRegistryNamespaceForNetwork, parseBosUrl };
135
+ export { buildRegistryConfigUrl, buildRegistryConfigUrlForNetwork, fetchBosConfigFromFastKv, fetchConfigHistory, fetchDeployManifests, fetchPublishedConfigFromFastKv, fetchRemotePluginManifest, getFastKvBaseUrlForNetwork, getRegistryNamespaceForAccount, getRegistryNamespaceForNetwork, parseBosUrl, parseFastKvConfigValue };
120
136
  //# sourceMappingURL=fastkv.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"fastkv.mjs","names":[],"sources":["../src/fastkv.ts"],"sourcesContent":["export type NetworkId = \"mainnet\" | \"testnet\";\n\ninterface FastKvEntry {\n value: unknown;\n}\n\ninterface FastKvListResponse {\n entries?: Array<FastKvEntry | null>;\n}\n\nimport { fetchJsonOrNull } from \"./http-client\";\n\nfunction getNetworkIdForAccount(accountId: string): NetworkId {\n return accountId.endsWith(\".testnet\") ? \"testnet\" : \"mainnet\";\n}\n\nconst FASTKV_TESTNET_URL = \"https://kv.test.fastnear.com\";\nconst FASTKV_MAINNET_URL = \"https://kv.main.fastnear.com\";\nconst REGISTRY_NAMESPACE_TESTNET = \"dev.allthethings.testnet\";\nconst REGISTRY_NAMESPACE_MAINNET = \"dev.everything.near\";\n\nexport function getFastKvBaseUrlForNetwork(network: NetworkId): string {\n return network === \"testnet\" ? FASTKV_TESTNET_URL : FASTKV_MAINNET_URL;\n}\n\nfunction getFastKvBaseUrlForAccount(accountId: string): string {\n return getNetworkIdForAccount(accountId) === \"testnet\"\n ? getFastKvBaseUrlForNetwork(\"testnet\")\n : getFastKvBaseUrlForNetwork(\"mainnet\");\n}\n\nexport function buildRegistryConfigUrl(\n accountId: string,\n gatewayId: string,\n registry?: string,\n): string {\n const baseUrl = getFastKvBaseUrlForAccount(accountId);\n const namespace = getRegistryNamespaceForAccount(accountId, registry);\n const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId));\n return `${baseUrl}/v0/latest/${encodeURIComponent(namespace)}/${encodeURIComponent(accountId)}/${key}`;\n}\n\nexport function buildRegistryConfigUrlForNetwork(\n network: NetworkId,\n accountId: string,\n gatewayId: string,\n registry?: string,\n): string {\n const baseUrl = getFastKvBaseUrlForNetwork(network);\n const namespace = getRegistryNamespaceForNetwork(network, registry);\n const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId));\n return `${baseUrl}/v0/latest/${encodeURIComponent(namespace)}/${encodeURIComponent(accountId)}/${key}`;\n}\n\nexport function getRegistryNamespaceForAccount(accountId: string, registry?: string): string {\n return registry ?? getRegistryNamespaceForNetwork(getNetworkIdForAccount(accountId));\n}\n\nexport function getRegistryNamespaceForNetwork(network: NetworkId, registry?: string): string {\n if (registry) return registry;\n return network === \"testnet\" ? REGISTRY_NAMESPACE_TESTNET : REGISTRY_NAMESPACE_MAINNET;\n}\n\nfunction getRegistryConfigKey(\n accountId: string,\n gatewayId: string,\n pathSegments: string[] = [],\n): string {\n const suffix =\n pathSegments.length > 0\n ? `/${pathSegments.map((segment) => encodeURIComponent(segment)).join(\"/\")}`\n : \"\";\n return `apps/${accountId}/${gatewayId}${suffix}/bos.config.json`;\n}\n\nexport function parseBosUrl(bosUrl: string): {\n accountId: string;\n gatewayId: string;\n pathSegments: string[];\n} {\n const strippedUrl = bosUrl.split(\"#\")[0]!;\n const match = strippedUrl.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n if (!match?.[1] || !match[2]) {\n throw new Error(`Invalid BOS URL: ${bosUrl}`);\n }\n\n const pathSegments = match[2]\n .split(\"/\")\n .filter(Boolean)\n .map((segment) => decodeURIComponent(segment));\n if (pathSegments.length === 0) {\n throw new Error(`Invalid BOS URL: ${bosUrl}`);\n }\n\n const [gatewayId, ...pathSegmentsTail] = pathSegments;\n if (!gatewayId) {\n throw new Error(`Invalid BOS URL: ${bosUrl}`);\n }\n\n return {\n accountId: match[1],\n gatewayId,\n pathSegments: pathSegmentsTail,\n };\n}\n\nexport async function fetchBosConfigFromFastKv<T>(bosUrl: string, registry?: string): Promise<T> {\n const { accountId, gatewayId, pathSegments } = parseBosUrl(bosUrl);\n const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId, pathSegments));\n const payload = await fetchJson<FastKvListResponse>(\n `${getFastKvBaseUrlForAccount(accountId)}/v0/latest/${encodeURIComponent(getRegistryNamespaceForAccount(accountId, registry))}/${encodeURIComponent(accountId)}/${key}`,\n );\n const value = payload?.entries?.find(Boolean)?.value;\n\n if (!value) {\n throw new Error(`No config found for ${bosUrl}`);\n }\n\n if (typeof value === \"string\") {\n return JSON.parse(value) as T;\n }\n\n if (typeof value !== \"object\") {\n throw new Error(`Invalid config value for ${bosUrl}`);\n }\n\n return value as T;\n}\n\nexport interface ConfigHistoryEntry {\n blockHeight: number;\n blockTimestampNs: string;\n txHash?: string;\n value: unknown;\n}\n\ninterface FastKvHistoryEntry {\n block_height?: number;\n block_timestamp?: string | number;\n tx_hash?: string;\n value?: unknown;\n}\n\ninterface FastKvHistoryResponse {\n entries?: Array<FastKvHistoryEntry | null>;\n}\n\nconst FASTKV_HISTORY_MAX_LIMIT = 200;\n\n/**\n * Publish history for the registry config key (atomic-deploys 11): the\n * exact-key variant of FastData's history endpoint, newest-first. The API\n * caps a page at 200 rows; a config key's write history fits well inside\n * that, so a single request is the whole story (the limit param is clamped).\n */\nexport async function fetchConfigHistory(opts: {\n accountId: string;\n gatewayId: string;\n registry?: string;\n limit?: number;\n}): Promise<ConfigHistoryEntry[]> {\n const key = encodeURIComponent(getRegistryConfigKey(opts.accountId, opts.gatewayId));\n const limit = Math.max(1, Math.min(opts.limit ?? 20, FASTKV_HISTORY_MAX_LIMIT));\n const url = `${getFastKvBaseUrlForAccount(opts.accountId)}/v0/history/${encodeURIComponent(getRegistryNamespaceForAccount(opts.accountId, opts.registry))}/${encodeURIComponent(opts.accountId)}/${key}?limit=${limit}`;\n const payload = await fetchJson<FastKvHistoryResponse>(url);\n const entries = (payload?.entries ?? []).filter(Boolean) as FastKvHistoryEntry[];\n\n return entries\n .map((entry) => ({\n blockHeight: entry.block_height ?? 0,\n blockTimestampNs: String(entry.block_timestamp ?? \"\"),\n ...(entry.tx_hash ? { txHash: entry.tx_hash } : {}),\n value:\n typeof entry.value === \"string\" && entry.value.length > 0\n ? (JSON.parse(entry.value) as unknown)\n : entry.value,\n }))\n .sort((a, b) => b.blockHeight - a.blockHeight);\n}\n\nexport interface DeployManifestEntry {\n key: string;\n blockHeight: number;\n blockTimestampNs: string;\n value: unknown;\n}\n\n/**\n * The per-deploy audit trail (atomic-deploys 03/12): every publish writes\n * `apps/<account>/<gateway>/manifests/<ts>.json` atomically with the pointer\n * swap — this lists that key family newest-first via the history-by-prefix\n * endpoint.\n */\nexport async function fetchDeployManifests(opts: {\n accountId: string;\n gatewayId: string;\n registry?: string;\n limit?: number;\n}): Promise<DeployManifestEntry[]> {\n const keyPrefix = encodeURIComponent(\n `${getRegistryConfigKey(opts.accountId, opts.gatewayId).replace(/bos\\.config\\.json$/, \"\")}manifests/`,\n );\n const url = `${getFastKvBaseUrlForAccount(opts.accountId)}/v0/history/${encodeURIComponent(getRegistryNamespaceForAccount(opts.accountId, opts.registry))}/${encodeURIComponent(opts.accountId)}`;\n const payload = await fetchJson<FastKvHistoryResponse>(url, {\n method: \"POST\",\n body: JSON.stringify({\n key_prefix: decodeURIComponent(keyPrefix),\n asc: false,\n limit: Math.max(1, Math.min(opts.limit ?? 20, FASTKV_HISTORY_MAX_LIMIT)),\n }),\n });\n const entries = (payload?.entries ?? []).filter(Boolean) as Array<\n FastKvHistoryEntry & { key?: string }\n >;\n\n return entries\n .map((entry) => ({\n key: entry.key ?? \"\",\n blockHeight: entry.block_height ?? 0,\n blockTimestampNs: String(entry.block_timestamp ?? \"\"),\n value:\n typeof entry.value === \"string\" && entry.value.length > 0\n ? (JSON.parse(entry.value) as unknown)\n : entry.value,\n }))\n .sort((a, b) => b.blockHeight - a.blockHeight);\n}\n\nexport interface PluginManifest {\n schemaVersion: number;\n kind: string;\n plugin: { name: string; version: string };\n runtime: { remoteEntry: string };\n contract: {\n kind: string;\n types: { path: string; exportName: string; typeName: string; sha256: string };\n };\n additionalExports?: Array<{ path: string; exports: string[]; sha256: string }>;\n}\n\nexport async function fetchRemotePluginManifest(cdnUrl: string): Promise<PluginManifest | null> {\n const baseUrl = cdnUrl.replace(/\\/$/, \"\");\n return fetchJsonOrNull<PluginManifest>(`${baseUrl}/plugin.manifest.json`, { retries: 2 });\n}\n\nasync function fetchJson<T>(url: string, init?: RequestInit): Promise<T | null> {\n const headers: Record<string, string> = {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n ...(init?.headers as Record<string, string> | undefined),\n };\n return fetchJsonOrNull<T>(url, {\n method: init?.method,\n headers,\n body: init?.body ?? undefined,\n retries: 3,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"fastkv.mjs","names":[],"sources":["../src/fastkv.ts"],"sourcesContent":["export type NetworkId = \"mainnet\" | \"testnet\";\n\ninterface FastKvEntry {\n value: unknown;\n}\n\ninterface FastKvListResponse {\n entries?: Array<FastKvEntry | null>;\n}\n\nimport { fetchJsonOrNull, fetchRaw } from \"./http-client\";\n\nfunction getNetworkIdForAccount(accountId: string): NetworkId {\n return accountId.endsWith(\".testnet\") ? \"testnet\" : \"mainnet\";\n}\n\nconst FASTKV_TESTNET_URL = \"https://kv.test.fastnear.com\";\nconst FASTKV_MAINNET_URL = \"https://kv.main.fastnear.com\";\nconst REGISTRY_NAMESPACE_TESTNET = \"dev.allthethings.testnet\";\nconst REGISTRY_NAMESPACE_MAINNET = \"dev.everything.near\";\n\nexport function getFastKvBaseUrlForNetwork(network: NetworkId): string {\n return network === \"testnet\" ? FASTKV_TESTNET_URL : FASTKV_MAINNET_URL;\n}\n\nfunction getFastKvBaseUrlForAccount(accountId: string): string {\n return getNetworkIdForAccount(accountId) === \"testnet\"\n ? getFastKvBaseUrlForNetwork(\"testnet\")\n : getFastKvBaseUrlForNetwork(\"mainnet\");\n}\n\nexport function buildRegistryConfigUrl(\n accountId: string,\n gatewayId: string,\n registry?: string,\n): string {\n const baseUrl = getFastKvBaseUrlForAccount(accountId);\n const namespace = getRegistryNamespaceForAccount(accountId, registry);\n const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId));\n return `${baseUrl}/v0/latest/${encodeURIComponent(namespace)}/${encodeURIComponent(accountId)}/${key}`;\n}\n\nexport function buildRegistryConfigUrlForNetwork(\n network: NetworkId,\n accountId: string,\n gatewayId: string,\n registry?: string,\n): string {\n const baseUrl = getFastKvBaseUrlForNetwork(network);\n const namespace = getRegistryNamespaceForNetwork(network, registry);\n const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId));\n return `${baseUrl}/v0/latest/${encodeURIComponent(namespace)}/${encodeURIComponent(accountId)}/${key}`;\n}\n\nexport function getRegistryNamespaceForAccount(accountId: string, registry?: string): string {\n return registry ?? getRegistryNamespaceForNetwork(getNetworkIdForAccount(accountId));\n}\n\nexport function getRegistryNamespaceForNetwork(network: NetworkId, registry?: string): string {\n if (registry) return registry;\n return network === \"testnet\" ? REGISTRY_NAMESPACE_TESTNET : REGISTRY_NAMESPACE_MAINNET;\n}\n\nfunction getRegistryConfigKey(\n accountId: string,\n gatewayId: string,\n pathSegments: string[] = [],\n): string {\n const suffix =\n pathSegments.length > 0\n ? `/${pathSegments.map((segment) => encodeURIComponent(segment)).join(\"/\")}`\n : \"\";\n return `apps/${accountId}/${gatewayId}${suffix}/bos.config.json`;\n}\n\nexport function parseBosUrl(bosUrl: string): {\n accountId: string;\n gatewayId: string;\n pathSegments: string[];\n} {\n const strippedUrl = bosUrl.split(\"#\")[0]!;\n const match = strippedUrl.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n if (!match?.[1] || !match[2]) {\n throw new Error(`Invalid BOS URL: ${bosUrl}`);\n }\n\n const pathSegments = match[2]\n .split(\"/\")\n .filter(Boolean)\n .map((segment) => decodeURIComponent(segment));\n if (pathSegments.length === 0) {\n throw new Error(`Invalid BOS URL: ${bosUrl}`);\n }\n\n const [gatewayId, ...pathSegmentsTail] = pathSegments;\n if (!gatewayId) {\n throw new Error(`Invalid BOS URL: ${bosUrl}`);\n }\n\n return {\n accountId: match[1],\n gatewayId,\n pathSegments: pathSegmentsTail,\n };\n}\n\nexport async function fetchBosConfigFromFastKv<T>(bosUrl: string, registry?: string): Promise<T> {\n const { accountId, gatewayId, pathSegments } = parseBosUrl(bosUrl);\n const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId, pathSegments));\n const payload = await fetchJson<FastKvListResponse>(\n `${getFastKvBaseUrlForAccount(accountId)}/v0/latest/${encodeURIComponent(getRegistryNamespaceForAccount(accountId, registry))}/${encodeURIComponent(accountId)}/${key}`,\n );\n return parseFastKvConfigValue<T>(payload, bosUrl);\n}\n\n/** Read a published config from an already-built registry URL\n * (`buildRegistryConfigUrlForNetwork`). Unlike {@link fetchBosConfigFromFastKv}\n * this takes the URL directly, so the read key is exactly the one the publish\n * writes, and a network failure is reported as such instead of collapsing\n * into \"No config found\" (fetchJsonOrNull swallows errors into null). */\nexport async function fetchPublishedConfigFromFastKv<T>(url: string): Promise<T> {\n const response = await fetchRaw(url, { headers: { accept: \"application/json\" } });\n if (!response.ok) {\n if (response.status === 404) {\n throw new Error(`No config found for ${url}`);\n }\n throw new Error(`HTTP ${response.status} fetching ${url}`);\n }\n const payload = (await response.json()) as FastKvListResponse | null;\n return parseFastKvConfigValue<T>(payload, url);\n}\n\nexport function parseFastKvConfigValue<T>(payload: FastKvListResponse | null, label: string): T {\n const value = payload?.entries?.find(Boolean)?.value;\n if (!value) {\n throw new Error(`No config found for ${label}`);\n }\n if (typeof value === \"string\") {\n return JSON.parse(value) as T;\n }\n if (typeof value !== \"object\") {\n throw new Error(`Invalid config value for ${label}`);\n }\n return value as T;\n}\n\nexport interface ConfigHistoryEntry {\n blockHeight: number;\n blockTimestampNs: string;\n txHash?: string;\n value: unknown;\n}\n\ninterface FastKvHistoryEntry {\n block_height?: number;\n block_timestamp?: string | number;\n tx_hash?: string;\n value?: unknown;\n}\n\ninterface FastKvHistoryResponse {\n entries?: Array<FastKvHistoryEntry | null>;\n}\n\nconst FASTKV_HISTORY_MAX_LIMIT = 200;\n\n/**\n * Publish history for the registry config key (atomic-deploys 11): the\n * exact-key variant of FastData's history endpoint, newest-first. The API\n * caps a page at 200 rows; a config key's write history fits well inside\n * that, so a single request is the whole story (the limit param is clamped).\n */\nexport async function fetchConfigHistory(opts: {\n accountId: string;\n gatewayId: string;\n registry?: string;\n limit?: number;\n}): Promise<ConfigHistoryEntry[]> {\n const key = encodeURIComponent(getRegistryConfigKey(opts.accountId, opts.gatewayId));\n const limit = Math.max(1, Math.min(opts.limit ?? 20, FASTKV_HISTORY_MAX_LIMIT));\n const url = `${getFastKvBaseUrlForAccount(opts.accountId)}/v0/history/${encodeURIComponent(getRegistryNamespaceForAccount(opts.accountId, opts.registry))}/${encodeURIComponent(opts.accountId)}/${key}?limit=${limit}`;\n const payload = await fetchJson<FastKvHistoryResponse>(url);\n const entries = (payload?.entries ?? []).filter(Boolean) as FastKvHistoryEntry[];\n\n return entries\n .map((entry) => ({\n blockHeight: entry.block_height ?? 0,\n blockTimestampNs: String(entry.block_timestamp ?? \"\"),\n ...(entry.tx_hash ? { txHash: entry.tx_hash } : {}),\n value:\n typeof entry.value === \"string\" && entry.value.length > 0\n ? (JSON.parse(entry.value) as unknown)\n : entry.value,\n }))\n .sort((a, b) => b.blockHeight - a.blockHeight);\n}\n\nexport interface DeployManifestEntry {\n key: string;\n blockHeight: number;\n blockTimestampNs: string;\n value: unknown;\n}\n\n/**\n * The per-deploy audit trail (atomic-deploys 03/12): every publish writes\n * `apps/<account>/<gateway>/manifests/<ts>.json` atomically with the pointer\n * swap — this lists that key family newest-first via the history-by-prefix\n * endpoint.\n */\nexport async function fetchDeployManifests(opts: {\n accountId: string;\n gatewayId: string;\n registry?: string;\n limit?: number;\n}): Promise<DeployManifestEntry[]> {\n const keyPrefix = encodeURIComponent(\n `${getRegistryConfigKey(opts.accountId, opts.gatewayId).replace(/bos\\.config\\.json$/, \"\")}manifests/`,\n );\n const url = `${getFastKvBaseUrlForAccount(opts.accountId)}/v0/history/${encodeURIComponent(getRegistryNamespaceForAccount(opts.accountId, opts.registry))}/${encodeURIComponent(opts.accountId)}`;\n const payload = await fetchJson<FastKvHistoryResponse>(url, {\n method: \"POST\",\n body: JSON.stringify({\n key_prefix: decodeURIComponent(keyPrefix),\n asc: false,\n limit: Math.max(1, Math.min(opts.limit ?? 20, FASTKV_HISTORY_MAX_LIMIT)),\n }),\n });\n const entries = (payload?.entries ?? []).filter(Boolean) as Array<\n FastKvHistoryEntry & { key?: string }\n >;\n\n return entries\n .map((entry) => ({\n key: entry.key ?? \"\",\n blockHeight: entry.block_height ?? 0,\n blockTimestampNs: String(entry.block_timestamp ?? \"\"),\n value:\n typeof entry.value === \"string\" && entry.value.length > 0\n ? (JSON.parse(entry.value) as unknown)\n : entry.value,\n }))\n .sort((a, b) => b.blockHeight - a.blockHeight);\n}\n\nexport interface PluginManifest {\n schemaVersion: number;\n kind: string;\n plugin: { name: string; version: string };\n runtime: { remoteEntry: string };\n contract: {\n kind: string;\n types: { path: string; exportName: string; typeName: string; sha256: string };\n };\n additionalExports?: Array<{ path: string; exports: string[]; sha256: string }>;\n}\n\nexport async function fetchRemotePluginManifest(cdnUrl: string): Promise<PluginManifest | null> {\n const baseUrl = cdnUrl.replace(/\\/$/, \"\");\n return fetchJsonOrNull<PluginManifest>(`${baseUrl}/plugin.manifest.json`, { retries: 2 });\n}\n\nasync function fetchJson<T>(url: string, init?: RequestInit): Promise<T | null> {\n const headers: Record<string, string> = {\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n ...(init?.headers as Record<string, string> | undefined),\n };\n return fetchJsonOrNull<T>(url, {\n method: init?.method,\n headers,\n body: init?.body ?? undefined,\n retries: 3,\n });\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_http_client = require('./http-client.cjs');
2
3
  const require_bundle_cache = require('./bundle-cache.cjs');
3
4
  const require_integrity = require('./integrity.cjs');
4
5
  let node_fs = require("node:fs");
@@ -6,23 +7,15 @@ let _module_federation_enhanced_runtime = require("@module-federation/enhanced/r
6
7
  let _module_federation_runtime_core = require("@module-federation/runtime-core");
7
8
  let every_plugin_build_rspack = require("every-plugin/build/rspack");
8
9
 
9
- //#region src/mf.ts
10
+ //#region src/federation.ts
10
11
  let mfInstance = null;
11
- function fetchWithTimeout(url, init, timeoutMs) {
12
- const controller = new AbortController();
13
- const timer = setTimeout(() => controller.abort(), timeoutMs);
14
- return fetch(url, {
15
- ...init,
16
- signal: controller.signal
17
- }).finally(() => clearTimeout(timer));
18
- }
19
12
  function patchManifestFetchForSsrPublicPath(mf) {
20
13
  if (!mf || !mf.loaderHook?.lifecycle?.fetch?.on) return;
21
14
  if (mf.__everythingDevPatchedManifestFetch === true) return;
22
15
  mf.__everythingDevPatchedManifestFetch = true;
23
16
  mf.loaderHook.lifecycle.fetch.on((url, init) => {
24
17
  if (typeof url !== "string" || !url.endsWith("/mf-manifest.json")) return;
25
- return fetchWithTimeout(url, init, 15e3).then((res) => res.json()).then((json) => {
18
+ return require_http_client.fetchRaw(url, init, "15 seconds").then((res) => res.json()).then((json) => {
26
19
  json.metaData = json.metaData ?? {};
27
20
  json.metaData.ssrPublicPath = json.metaData.ssrPublicPath ?? url.replace(/\/mf-manifest\.json$/, "/");
28
21
  if (json.metaData.publicPath === "auto" || json.metaData.publicPath === "") json.metaData.publicPath = "/";
@@ -41,7 +34,7 @@ function installIntegrityFetchHook(mf, registry, options) {
41
34
  if (typeof url !== "string") return;
42
35
  const expectedHash = registry.get(url);
43
36
  if (!expectedHash) return;
44
- return fetchWithTimeout(url, init, 15e3).then(async (res) => {
37
+ return require_http_client.fetchRaw(url, init, "15 seconds").then(async (res) => {
45
38
  const buffer = Buffer.from(await res.arrayBuffer());
46
39
  const computed = require_integrity.computeSriHash(buffer);
47
40
  if (computed !== expectedHash) {
@@ -166,7 +159,7 @@ function manifestUrl(remoteUrl) {
166
159
  return remoteUrl.endsWith("/") ? `${remoteUrl}mf-manifest.json` : `${remoteUrl}/mf-manifest.json`;
167
160
  }
168
161
  async function fetchManifest(url, timeoutMs) {
169
- const res = await fetch(manifestUrl(url), { signal: AbortSignal.timeout(timeoutMs) });
162
+ const res = await require_http_client.fetchRaw(manifestUrl(url), {}, timeoutMs);
170
163
  if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
171
164
  return res.json();
172
165
  }
@@ -301,4 +294,4 @@ exports.loadBosConfigForMfCheck = loadBosConfigForMfCheck;
301
294
  exports.loadRemoteModule = loadRemoteModule;
302
295
  exports.patchManifestFetchForSsrPublicPath = patchManifestFetchForSsrPublicPath;
303
296
  exports.registerRemote = registerRemote;
304
- //# sourceMappingURL=mf.cjs.map
297
+ //# sourceMappingURL=federation.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"federation.cjs","names":["fetchRaw","computeSriHash","readBundleCache","writeBundleCache","getInstance","createInstance","isEffectCriticalSharedDep","readFileSync"],"sources":["../src/federation.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { createInstance, getInstance } from \"@module-federation/enhanced/runtime\";\nimport { setGlobalFederationInstance } from \"@module-federation/runtime-core\";\nimport { isEffectCriticalSharedDep } from \"every-plugin/build/rspack\";\nimport { readBundleCache, writeBundleCache } from \"./bundle-cache\";\nimport { fetchRaw } from \"./http-client\";\nimport { computeSriHash, type IntegrityRegistry } from \"./integrity\";\nimport type { BosConfig, BosPluginRef } from \"./types\";\n\ntype FederationInstance = ReturnType<typeof createInstance>;\n\nlet mfInstance: FederationInstance | null = null;\n\nexport function patchManifestFetchForSsrPublicPath(mf: FederationInstance): void {\n if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) return;\n if ((mf as any).__everythingDevPatchedManifestFetch === true) return;\n (mf as any).__everythingDevPatchedManifestFetch = true;\n\n (mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {\n if (typeof url !== \"string\" || !url.endsWith(\"/mf-manifest.json\")) {\n return;\n }\n return fetchRaw(url as string, init as RequestInit, \"15 seconds\")\n .then((res) => res.json())\n .then((json: any) => {\n json.metaData = json.metaData ?? {};\n json.metaData.ssrPublicPath =\n json.metaData.ssrPublicPath ?? url.replace(/\\/mf-manifest\\.json$/, \"/\");\n if (json.metaData.publicPath === \"auto\" || json.metaData.publicPath === \"\") {\n json.metaData.publicPath = \"/\";\n }\n return new Response(JSON.stringify(json), {\n headers: { \"content-type\": \"application/json\" },\n });\n });\n });\n}\n\nexport function installIntegrityFetchHook(\n mf: FederationInstance,\n registry: IntegrityRegistry,\n options?: { cacheDir?: string },\n): void {\n if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) {\n console.warn(\"[SRI] MF lifecycle fetch hook not available, skipping integrity-in-pipeline\");\n return;\n }\n if ((mf as any).__everythingDevIntegrityHook === true) return;\n (mf as any).__everythingDevIntegrityHook = true;\n\n (mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {\n if (typeof url !== \"string\") return;\n\n const expectedHash = registry.get(url);\n if (!expectedHash) return;\n\n return fetchRaw(url as string, init as RequestInit, \"15 seconds\").then(async (res) => {\n const buffer = Buffer.from(await res.arrayBuffer());\n const computed = computeSriHash(buffer);\n\n if (computed !== expectedHash) {\n // ticket 09 (atomic-deploys): an SRI mismatch is treated like an\n // origin failure — the last-known-good bytes (which satisfied the\n // same pin when cached) serve instead, and the corrupted bytes are\n // never written into the cache.\n const cached = await readBundleCache(url as string, { cacheDir: options?.cacheDir });\n if (cached && computeSriHash(Buffer.from(cached)) === expectedHash) {\n console.warn(\n `[SRI] Origin bytes failed verification for ${url} — serving last-known-good from the bundle cache`,\n );\n return new Response(cached.slice().buffer, {\n status: 200,\n headers: { \"x-bundle-cache\": \"stale\" },\n });\n }\n console.error(\n `[SRI] Integrity check failed in MF fetch pipeline for ${url}\\n Expected: ${expectedHash}\\n Computed: ${computed}`,\n );\n return new Response(`Integrity check failed for ${url}`, {\n status: 500,\n statusText: \"Integrity Check Failed\",\n });\n }\n\n // verified: pin-checked write-through\n await writeBundleCache(url as string, new Uint8Array(buffer), {\n cacheDir: options?.cacheDir,\n }).catch(() => {\n // write-through is best-effort\n });\n\n return new Response(buffer, {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n });\n });\n}\n\nexport function getFederationInstance(): FederationInstance {\n if (mfInstance) return mfInstance;\n\n const existing = getInstance();\n if (existing) {\n mfInstance = existing as FederationInstance;\n setGlobalFederationInstance(mfInstance as any);\n patchManifestFetchForSsrPublicPath(mfInstance);\n return mfInstance;\n }\n\n mfInstance = createInstance({\n name: \"host\",\n remotes: [],\n }) as FederationInstance;\n setGlobalFederationInstance(mfInstance as any);\n patchManifestFetchForSsrPublicPath(mfInstance);\n return mfInstance;\n}\n\nexport async function registerRemote(opts: {\n name: string;\n entry: string;\n type?: \"manifest\" | \"script\";\n}): Promise<void> {\n const instance = getFederationInstance();\n\n const inferType = (): \"manifest\" | \"script\" => {\n if (opts.type) return opts.type;\n if (opts.entry.endsWith(\"/mf-manifest.json\")) return \"manifest\";\n if (/\\/remoteEntry(\\.[a-f0-9]{8,})?\\.js$/.test(opts.entry)) return \"script\";\n return typeof window === \"undefined\" ? \"script\" : \"manifest\";\n };\n\n const remoteType = inferType();\n\n instance.registerRemotes([\n {\n name: opts.name,\n entry: opts.entry,\n type: remoteType,\n },\n ]);\n}\n\nexport async function loadRemoteModule<T>(\n specifier: string,\n options?: { loadFactory?: boolean; from?: \"build\" | \"runtime\" },\n): Promise<T> {\n const instance = getFederationInstance();\n\n const isServer = typeof window === \"undefined\";\n if (isServer) {\n await (instance as any).initializeSharing?.(\"default\");\n }\n\n const mod = await instance.loadRemote<T>(specifier, options as any);\n if (!mod) {\n throw new Error(`Failed to load remote module: ${specifier}`);\n }\n return mod;\n}\n\nexport async function ensureNodeRuntimePlugin(): Promise<void> {\n const instance = getFederationInstance();\n if (typeof window !== \"undefined\") return;\n if ((instance as any).__nodeRuntimePluginLoaded) return;\n\n const mod: any = await import(\"@module-federation/node/runtimePlugin\");\n const factory = mod?.default ?? mod;\n const plugin = typeof factory === \"function\" ? factory() : null;\n if (plugin) {\n instance.registerPlugins([plugin]);\n }\n (instance as any).__nodeRuntimePluginLoaded = true;\n}\n\nexport interface FederationCompatRemote {\n role: string;\n url: string;\n reachable: boolean;\n pluginVersion: string | null;\n ok: boolean;\n reason?: string;\n}\n\nexport interface FederationCompatReport {\n ok: boolean;\n hostVersion: string | null;\n hostReachable: boolean;\n hostReason?: string;\n remotes: FederationCompatRemote[];\n}\n\nfunction parseSemver(v: unknown): { major: number; minor: number; patch: number } | null {\n const m = typeof v === \"string\" ? /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(v) : null;\n return m ? { major: +m[1]!, minor: +m[2]!, patch: +m[3]! } : null;\n}\n\nfunction satisfiesConstraint(version: string, constraint: string): boolean {\n const trimmed = constraint.trim();\n if (!trimmed) return true;\n if (![\"^\", \"~\", \">\", \"<\", \"=\", \"x\"].some((c) => trimmed.startsWith(c))) {\n return version === trimmed;\n }\n const m = /^\\s*\\^\\s*(\\d+)\\.(\\d+)\\.(\\d+)/.exec(trimmed);\n if (!m) return true;\n const v = parseSemver(version);\n if (!v) return false;\n if (v.major !== +m[1]!) return false;\n if (v.minor < +m[2]! || (v.minor === +m[2]! && v.patch < +m[3]!)) return false;\n const constraintPrerelease = trimmed.match(/-([0-9A-Za-z.-]+)/);\n const versionPrerelease = /-([0-9A-Za-z.-]+)\\s*$/.exec(version);\n if (constraintPrerelease) {\n if (!versionPrerelease) return false;\n const cPre = constraintPrerelease[1]!.split(\".\");\n const vPre = versionPrerelease[1]!.split(\".\");\n for (let i = 0; i < Math.max(cPre.length, vPre.length); i += 1) {\n const c = cPre[i] ?? \"0\";\n const p = vPre[i] ?? \"0\";\n if (c === p) continue;\n const cNum = /^\\d+$/.test(c) ? +c : null;\n const pNum = /^\\d+$/.test(p) ? +p : null;\n if (cNum !== null && pNum !== null) return cNum < pNum;\n return cNum === null ? c.localeCompare(p) < 0 : false;\n }\n return true;\n }\n if (versionPrerelease) return false;\n return true;\n}\n\nfunction manifestUrl(remoteUrl: string): string {\n return remoteUrl.endsWith(\"/\") ? `${remoteUrl}mf-manifest.json` : `${remoteUrl}/mf-manifest.json`;\n}\n\nasync function fetchManifest(url: string, timeoutMs: number): Promise<unknown> {\n const res = await fetchRaw(manifestUrl(url), {}, timeoutMs);\n if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);\n return res.json();\n}\n\nfunction listFederationRemotes(bosConfig: BosConfig): Array<{ role: string; url: string }> {\n const out: Array<{ role: string; url: string }> = [];\n for (const role of [\"api\", \"auth\"] as const) {\n const url = readProductionUrl((bosConfig?.app as Record<string, unknown> | undefined)?.[role]);\n if (url) out.push({ role, url });\n }\n for (const [id, slot] of Object.entries(bosConfig?.plugins ?? {})) {\n const url = readProductionUrl(slot);\n if (url) out.push({ role: id, url });\n }\n return out;\n}\n\nfunction readProductionUrl(entry: unknown): string | null {\n if (typeof entry === \"string\") return entry.length > 0 ? entry : null;\n if (entry && typeof (entry as BosPluginRef).production === \"string\") {\n const url = (entry as BosPluginRef).production!;\n return url.length > 0 ? url : null;\n }\n return null;\n}\n\ninterface RemoteManifest {\n metaData?: { pluginVersion?: string };\n shared?: Array<{ name: string; version?: string; requiredVersion?: string | null }>;\n}\n\nexport async function checkFederationCompat(\n bosConfig: BosConfig | null,\n opts: { timeoutMs?: number } = {},\n): Promise<FederationCompatReport> {\n const timeoutMs = opts.timeoutMs ?? 15_000;\n\n if (!bosConfig?.app?.host?.production) {\n return { ok: true, hostVersion: null, hostReachable: false, remotes: [] };\n }\n\n const hostUrl = bosConfig.app.host.production;\n let hostManifest: RemoteManifest | null = null;\n let hostReason: string | undefined;\n try {\n hostManifest = (await fetchManifest(hostUrl, timeoutMs)) as RemoteManifest;\n } catch (e) {\n hostReason = e instanceof Error ? e.message : String(e);\n }\n\n const hostVersion = hostManifest?.metaData?.pluginVersion ?? null;\n const hostShared = (hostManifest?.shared ?? []).filter(\n (s) => typeof s.requiredVersion === \"string\",\n );\n\n const remotes = listFederationRemotes(bosConfig);\n const settled = await Promise.allSettled(\n remotes.map(async ({ role, url }) => {\n let manifest: RemoteManifest | null = null;\n let reason: string | undefined;\n try {\n manifest = (await fetchManifest(url, timeoutMs)) as RemoteManifest;\n } catch (e) {\n reason = e instanceof Error ? e.message : String(e);\n }\n\n const version = manifest?.metaData?.pluginVersion ?? null;\n const versionOk = version !== null && version === hostVersion;\n\n if (!manifest) {\n return {\n role,\n url,\n reachable: false,\n pluginVersion: null,\n ok: false,\n reason: reason ?? \"unreachable\",\n } satisfies FederationCompatRemote;\n }\n\n if (!versionOk) {\n return {\n role,\n url,\n reachable: true,\n pluginVersion: version,\n ok: false,\n reason: hostVersion\n ? `expected pluginVersion=${hostVersion} (host), got ${version ?? \"missing\"} — redeploy this plugin`\n : `host manifest missing pluginVersion`,\n } satisfies FederationCompatRemote;\n }\n\n const missingShared: string[] = [];\n const mismatchedShared: string[] = [];\n for (const hostDep of hostShared) {\n const remDep = manifest.shared?.find((s) => s.name === hostDep.name);\n if (!remDep) {\n missingShared.push(`${hostDep.name}@${hostDep.requiredVersion}`);\n continue;\n }\n if (!satisfiesConstraint(remDep.version ?? \"\", hostDep.requiredVersion!)) {\n mismatchedShared.push(\n `${hostDep.name}: remote=${remDep.version ?? \"?\"} hostReq=${hostDep.requiredVersion}`,\n );\n }\n if (isEffectCriticalSharedDep(hostDep.name)) {\n const hostVersion = hostDep.version ?? \"\";\n const remoteVersion = remDep.version ?? \"\";\n if (remoteVersion && remoteVersion !== hostVersion) {\n mismatchedShared.push(\n `${hostDep.name}: exact-version skew — remote=${remoteVersion} host=${hostVersion} — bundle was built against a different prerelease build; redeploy host and plugin together`,\n );\n }\n const remoteRequired =\n typeof remDep.requiredVersion === \"string\" ? remDep.requiredVersion.trim() : \"\";\n if (\n remoteRequired.length > 0 &&\n remoteRequired[0] !== \"^\" &&\n !satisfiesConstraint(hostVersion, remoteRequired)\n ) {\n mismatchedShared.push(\n `${hostDep.name}: train skew — host=${hostVersion} vs remote requiredVersion=${remoteRequired}`,\n );\n }\n }\n }\n\n const reasons = [\n ...missingShared.map((d) => `missing shared \"${d}\" required by host`),\n ...mismatchedShared,\n ];\n return {\n role,\n url,\n reachable: true,\n pluginVersion: version,\n ok: reasons.length === 0,\n reason: reasons[0],\n } satisfies FederationCompatRemote;\n }),\n );\n\n const remoteReports = settled.map((s, i) => {\n const placeholder = {\n role: remotes[i]!.role,\n url: remotes[i]!.url,\n reachable: false,\n pluginVersion: null,\n ok: false,\n };\n return s.status === \"fulfilled\" ? s.value : { ...placeholder, reason: String(s.reason) };\n });\n\n const ok =\n hostManifest !== null &&\n remoteReports.every((r) => r.ok && r.reachable && r.pluginVersion !== null);\n\n return {\n ok,\n hostVersion,\n hostReachable: hostManifest !== null,\n hostReason,\n remotes: remoteReports,\n };\n}\n\nexport function loadBosConfigForMfCheck(configPath: string): BosConfig {\n return JSON.parse(readFileSync(configPath, \"utf8\")) as BosConfig;\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { BosConfig } from "./types.cjs";
2
2
  import { IntegrityRegistry } from "./integrity.cjs";
3
3
  import { createInstance } from "@module-federation/enhanced/runtime";
4
- //#region src/mf.d.ts
4
+ //#region src/federation.d.ts
5
5
  type FederationInstance = ReturnType<typeof createInstance>;
6
6
  declare function patchManifestFetchForSsrPublicPath(mf: FederationInstance): void;
7
7
  declare function installIntegrityFetchHook(mf: FederationInstance, registry: IntegrityRegistry, options?: {
@@ -39,4 +39,4 @@ declare function checkFederationCompat(bosConfig: BosConfig | null, opts?: {
39
39
  declare function loadBosConfigForMfCheck(configPath: string): BosConfig;
40
40
  //#endregion
41
41
  export { FederationCompatRemote, FederationCompatReport, checkFederationCompat, ensureNodeRuntimePlugin, getFederationInstance, installIntegrityFetchHook, loadBosConfigForMfCheck, loadRemoteModule, patchManifestFetchForSsrPublicPath, registerRemote };
42
- //# sourceMappingURL=mf.d.cts.map
42
+ //# sourceMappingURL=federation.d.cts.map
@@ -1,7 +1,7 @@
1
1
  import { BosConfig } from "./types.mjs";
2
2
  import { IntegrityRegistry } from "./integrity.mjs";
3
3
  import { createInstance } from "@module-federation/enhanced/runtime";
4
- //#region src/mf.d.ts
4
+ //#region src/federation.d.ts
5
5
  type FederationInstance = ReturnType<typeof createInstance>;
6
6
  declare function patchManifestFetchForSsrPublicPath(mf: FederationInstance): void;
7
7
  declare function installIntegrityFetchHook(mf: FederationInstance, registry: IntegrityRegistry, options?: {
@@ -39,4 +39,4 @@ declare function checkFederationCompat(bosConfig: BosConfig | null, opts?: {
39
39
  declare function loadBosConfigForMfCheck(configPath: string): BosConfig;
40
40
  //#endregion
41
41
  export { FederationCompatRemote, FederationCompatReport, checkFederationCompat, ensureNodeRuntimePlugin, getFederationInstance, installIntegrityFetchHook, loadBosConfigForMfCheck, loadRemoteModule, patchManifestFetchForSsrPublicPath, registerRemote };
42
- //# sourceMappingURL=mf.d.mts.map
42
+ //# sourceMappingURL=federation.d.mts.map
@@ -1,3 +1,4 @@
1
+ import { fetchRaw } from "./http-client.mjs";
1
2
  import { readBundleCache, writeBundleCache } from "./bundle-cache.mjs";
2
3
  import { computeSriHash } from "./integrity.mjs";
3
4
  import { readFileSync } from "node:fs";
@@ -5,23 +6,15 @@ import { createInstance, getInstance } from "@module-federation/enhanced/runtime
5
6
  import { setGlobalFederationInstance } from "@module-federation/runtime-core";
6
7
  import { isEffectCriticalSharedDep } from "every-plugin/build/rspack";
7
8
 
8
- //#region src/mf.ts
9
+ //#region src/federation.ts
9
10
  let mfInstance = null;
10
- function fetchWithTimeout(url, init, timeoutMs) {
11
- const controller = new AbortController();
12
- const timer = setTimeout(() => controller.abort(), timeoutMs);
13
- return fetch(url, {
14
- ...init,
15
- signal: controller.signal
16
- }).finally(() => clearTimeout(timer));
17
- }
18
11
  function patchManifestFetchForSsrPublicPath(mf) {
19
12
  if (!mf || !mf.loaderHook?.lifecycle?.fetch?.on) return;
20
13
  if (mf.__everythingDevPatchedManifestFetch === true) return;
21
14
  mf.__everythingDevPatchedManifestFetch = true;
22
15
  mf.loaderHook.lifecycle.fetch.on((url, init) => {
23
16
  if (typeof url !== "string" || !url.endsWith("/mf-manifest.json")) return;
24
- return fetchWithTimeout(url, init, 15e3).then((res) => res.json()).then((json) => {
17
+ return fetchRaw(url, init, "15 seconds").then((res) => res.json()).then((json) => {
25
18
  json.metaData = json.metaData ?? {};
26
19
  json.metaData.ssrPublicPath = json.metaData.ssrPublicPath ?? url.replace(/\/mf-manifest\.json$/, "/");
27
20
  if (json.metaData.publicPath === "auto" || json.metaData.publicPath === "") json.metaData.publicPath = "/";
@@ -40,7 +33,7 @@ function installIntegrityFetchHook(mf, registry, options) {
40
33
  if (typeof url !== "string") return;
41
34
  const expectedHash = registry.get(url);
42
35
  if (!expectedHash) return;
43
- return fetchWithTimeout(url, init, 15e3).then(async (res) => {
36
+ return fetchRaw(url, init, "15 seconds").then(async (res) => {
44
37
  const buffer = Buffer.from(await res.arrayBuffer());
45
38
  const computed = computeSriHash(buffer);
46
39
  if (computed !== expectedHash) {
@@ -165,7 +158,7 @@ function manifestUrl(remoteUrl) {
165
158
  return remoteUrl.endsWith("/") ? `${remoteUrl}mf-manifest.json` : `${remoteUrl}/mf-manifest.json`;
166
159
  }
167
160
  async function fetchManifest(url, timeoutMs) {
168
- const res = await fetch(manifestUrl(url), { signal: AbortSignal.timeout(timeoutMs) });
161
+ const res = await fetchRaw(manifestUrl(url), {}, timeoutMs);
169
162
  if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
170
163
  return res.json();
171
164
  }
@@ -293,4 +286,4 @@ function loadBosConfigForMfCheck(configPath) {
293
286
 
294
287
  //#endregion
295
288
  export { checkFederationCompat, ensureNodeRuntimePlugin, getFederationInstance, installIntegrityFetchHook, loadBosConfigForMfCheck, loadRemoteModule, patchManifestFetchForSsrPublicPath, registerRemote };
296
- //# sourceMappingURL=mf.mjs.map
289
+ //# sourceMappingURL=federation.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"federation.mjs","names":[],"sources":["../src/federation.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { createInstance, getInstance } from \"@module-federation/enhanced/runtime\";\nimport { setGlobalFederationInstance } from \"@module-federation/runtime-core\";\nimport { isEffectCriticalSharedDep } from \"every-plugin/build/rspack\";\nimport { readBundleCache, writeBundleCache } from \"./bundle-cache\";\nimport { fetchRaw } from \"./http-client\";\nimport { computeSriHash, type IntegrityRegistry } from \"./integrity\";\nimport type { BosConfig, BosPluginRef } from \"./types\";\n\ntype FederationInstance = ReturnType<typeof createInstance>;\n\nlet mfInstance: FederationInstance | null = null;\n\nexport function patchManifestFetchForSsrPublicPath(mf: FederationInstance): void {\n if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) return;\n if ((mf as any).__everythingDevPatchedManifestFetch === true) return;\n (mf as any).__everythingDevPatchedManifestFetch = true;\n\n (mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {\n if (typeof url !== \"string\" || !url.endsWith(\"/mf-manifest.json\")) {\n return;\n }\n return fetchRaw(url as string, init as RequestInit, \"15 seconds\")\n .then((res) => res.json())\n .then((json: any) => {\n json.metaData = json.metaData ?? {};\n json.metaData.ssrPublicPath =\n json.metaData.ssrPublicPath ?? url.replace(/\\/mf-manifest\\.json$/, \"/\");\n if (json.metaData.publicPath === \"auto\" || json.metaData.publicPath === \"\") {\n json.metaData.publicPath = \"/\";\n }\n return new Response(JSON.stringify(json), {\n headers: { \"content-type\": \"application/json\" },\n });\n });\n });\n}\n\nexport function installIntegrityFetchHook(\n mf: FederationInstance,\n registry: IntegrityRegistry,\n options?: { cacheDir?: string },\n): void {\n if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) {\n console.warn(\"[SRI] MF lifecycle fetch hook not available, skipping integrity-in-pipeline\");\n return;\n }\n if ((mf as any).__everythingDevIntegrityHook === true) return;\n (mf as any).__everythingDevIntegrityHook = true;\n\n (mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {\n if (typeof url !== \"string\") return;\n\n const expectedHash = registry.get(url);\n if (!expectedHash) return;\n\n return fetchRaw(url as string, init as RequestInit, \"15 seconds\").then(async (res) => {\n const buffer = Buffer.from(await res.arrayBuffer());\n const computed = computeSriHash(buffer);\n\n if (computed !== expectedHash) {\n // ticket 09 (atomic-deploys): an SRI mismatch is treated like an\n // origin failure — the last-known-good bytes (which satisfied the\n // same pin when cached) serve instead, and the corrupted bytes are\n // never written into the cache.\n const cached = await readBundleCache(url as string, { cacheDir: options?.cacheDir });\n if (cached && computeSriHash(Buffer.from(cached)) === expectedHash) {\n console.warn(\n `[SRI] Origin bytes failed verification for ${url} — serving last-known-good from the bundle cache`,\n );\n return new Response(cached.slice().buffer, {\n status: 200,\n headers: { \"x-bundle-cache\": \"stale\" },\n });\n }\n console.error(\n `[SRI] Integrity check failed in MF fetch pipeline for ${url}\\n Expected: ${expectedHash}\\n Computed: ${computed}`,\n );\n return new Response(`Integrity check failed for ${url}`, {\n status: 500,\n statusText: \"Integrity Check Failed\",\n });\n }\n\n // verified: pin-checked write-through\n await writeBundleCache(url as string, new Uint8Array(buffer), {\n cacheDir: options?.cacheDir,\n }).catch(() => {\n // write-through is best-effort\n });\n\n return new Response(buffer, {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n });\n });\n}\n\nexport function getFederationInstance(): FederationInstance {\n if (mfInstance) return mfInstance;\n\n const existing = getInstance();\n if (existing) {\n mfInstance = existing as FederationInstance;\n setGlobalFederationInstance(mfInstance as any);\n patchManifestFetchForSsrPublicPath(mfInstance);\n return mfInstance;\n }\n\n mfInstance = createInstance({\n name: \"host\",\n remotes: [],\n }) as FederationInstance;\n setGlobalFederationInstance(mfInstance as any);\n patchManifestFetchForSsrPublicPath(mfInstance);\n return mfInstance;\n}\n\nexport async function registerRemote(opts: {\n name: string;\n entry: string;\n type?: \"manifest\" | \"script\";\n}): Promise<void> {\n const instance = getFederationInstance();\n\n const inferType = (): \"manifest\" | \"script\" => {\n if (opts.type) return opts.type;\n if (opts.entry.endsWith(\"/mf-manifest.json\")) return \"manifest\";\n if (/\\/remoteEntry(\\.[a-f0-9]{8,})?\\.js$/.test(opts.entry)) return \"script\";\n return typeof window === \"undefined\" ? \"script\" : \"manifest\";\n };\n\n const remoteType = inferType();\n\n instance.registerRemotes([\n {\n name: opts.name,\n entry: opts.entry,\n type: remoteType,\n },\n ]);\n}\n\nexport async function loadRemoteModule<T>(\n specifier: string,\n options?: { loadFactory?: boolean; from?: \"build\" | \"runtime\" },\n): Promise<T> {\n const instance = getFederationInstance();\n\n const isServer = typeof window === \"undefined\";\n if (isServer) {\n await (instance as any).initializeSharing?.(\"default\");\n }\n\n const mod = await instance.loadRemote<T>(specifier, options as any);\n if (!mod) {\n throw new Error(`Failed to load remote module: ${specifier}`);\n }\n return mod;\n}\n\nexport async function ensureNodeRuntimePlugin(): Promise<void> {\n const instance = getFederationInstance();\n if (typeof window !== \"undefined\") return;\n if ((instance as any).__nodeRuntimePluginLoaded) return;\n\n const mod: any = await import(\"@module-federation/node/runtimePlugin\");\n const factory = mod?.default ?? mod;\n const plugin = typeof factory === \"function\" ? factory() : null;\n if (plugin) {\n instance.registerPlugins([plugin]);\n }\n (instance as any).__nodeRuntimePluginLoaded = true;\n}\n\nexport interface FederationCompatRemote {\n role: string;\n url: string;\n reachable: boolean;\n pluginVersion: string | null;\n ok: boolean;\n reason?: string;\n}\n\nexport interface FederationCompatReport {\n ok: boolean;\n hostVersion: string | null;\n hostReachable: boolean;\n hostReason?: string;\n remotes: FederationCompatRemote[];\n}\n\nfunction parseSemver(v: unknown): { major: number; minor: number; patch: number } | null {\n const m = typeof v === \"string\" ? /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(v) : null;\n return m ? { major: +m[1]!, minor: +m[2]!, patch: +m[3]! } : null;\n}\n\nfunction satisfiesConstraint(version: string, constraint: string): boolean {\n const trimmed = constraint.trim();\n if (!trimmed) return true;\n if (![\"^\", \"~\", \">\", \"<\", \"=\", \"x\"].some((c) => trimmed.startsWith(c))) {\n return version === trimmed;\n }\n const m = /^\\s*\\^\\s*(\\d+)\\.(\\d+)\\.(\\d+)/.exec(trimmed);\n if (!m) return true;\n const v = parseSemver(version);\n if (!v) return false;\n if (v.major !== +m[1]!) return false;\n if (v.minor < +m[2]! || (v.minor === +m[2]! && v.patch < +m[3]!)) return false;\n const constraintPrerelease = trimmed.match(/-([0-9A-Za-z.-]+)/);\n const versionPrerelease = /-([0-9A-Za-z.-]+)\\s*$/.exec(version);\n if (constraintPrerelease) {\n if (!versionPrerelease) return false;\n const cPre = constraintPrerelease[1]!.split(\".\");\n const vPre = versionPrerelease[1]!.split(\".\");\n for (let i = 0; i < Math.max(cPre.length, vPre.length); i += 1) {\n const c = cPre[i] ?? \"0\";\n const p = vPre[i] ?? \"0\";\n if (c === p) continue;\n const cNum = /^\\d+$/.test(c) ? +c : null;\n const pNum = /^\\d+$/.test(p) ? +p : null;\n if (cNum !== null && pNum !== null) return cNum < pNum;\n return cNum === null ? c.localeCompare(p) < 0 : false;\n }\n return true;\n }\n if (versionPrerelease) return false;\n return true;\n}\n\nfunction manifestUrl(remoteUrl: string): string {\n return remoteUrl.endsWith(\"/\") ? `${remoteUrl}mf-manifest.json` : `${remoteUrl}/mf-manifest.json`;\n}\n\nasync function fetchManifest(url: string, timeoutMs: number): Promise<unknown> {\n const res = await fetchRaw(manifestUrl(url), {}, timeoutMs);\n if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);\n return res.json();\n}\n\nfunction listFederationRemotes(bosConfig: BosConfig): Array<{ role: string; url: string }> {\n const out: Array<{ role: string; url: string }> = [];\n for (const role of [\"api\", \"auth\"] as const) {\n const url = readProductionUrl((bosConfig?.app as Record<string, unknown> | undefined)?.[role]);\n if (url) out.push({ role, url });\n }\n for (const [id, slot] of Object.entries(bosConfig?.plugins ?? {})) {\n const url = readProductionUrl(slot);\n if (url) out.push({ role: id, url });\n }\n return out;\n}\n\nfunction readProductionUrl(entry: unknown): string | null {\n if (typeof entry === \"string\") return entry.length > 0 ? entry : null;\n if (entry && typeof (entry as BosPluginRef).production === \"string\") {\n const url = (entry as BosPluginRef).production!;\n return url.length > 0 ? url : null;\n }\n return null;\n}\n\ninterface RemoteManifest {\n metaData?: { pluginVersion?: string };\n shared?: Array<{ name: string; version?: string; requiredVersion?: string | null }>;\n}\n\nexport async function checkFederationCompat(\n bosConfig: BosConfig | null,\n opts: { timeoutMs?: number } = {},\n): Promise<FederationCompatReport> {\n const timeoutMs = opts.timeoutMs ?? 15_000;\n\n if (!bosConfig?.app?.host?.production) {\n return { ok: true, hostVersion: null, hostReachable: false, remotes: [] };\n }\n\n const hostUrl = bosConfig.app.host.production;\n let hostManifest: RemoteManifest | null = null;\n let hostReason: string | undefined;\n try {\n hostManifest = (await fetchManifest(hostUrl, timeoutMs)) as RemoteManifest;\n } catch (e) {\n hostReason = e instanceof Error ? e.message : String(e);\n }\n\n const hostVersion = hostManifest?.metaData?.pluginVersion ?? null;\n const hostShared = (hostManifest?.shared ?? []).filter(\n (s) => typeof s.requiredVersion === \"string\",\n );\n\n const remotes = listFederationRemotes(bosConfig);\n const settled = await Promise.allSettled(\n remotes.map(async ({ role, url }) => {\n let manifest: RemoteManifest | null = null;\n let reason: string | undefined;\n try {\n manifest = (await fetchManifest(url, timeoutMs)) as RemoteManifest;\n } catch (e) {\n reason = e instanceof Error ? e.message : String(e);\n }\n\n const version = manifest?.metaData?.pluginVersion ?? null;\n const versionOk = version !== null && version === hostVersion;\n\n if (!manifest) {\n return {\n role,\n url,\n reachable: false,\n pluginVersion: null,\n ok: false,\n reason: reason ?? \"unreachable\",\n } satisfies FederationCompatRemote;\n }\n\n if (!versionOk) {\n return {\n role,\n url,\n reachable: true,\n pluginVersion: version,\n ok: false,\n reason: hostVersion\n ? `expected pluginVersion=${hostVersion} (host), got ${version ?? \"missing\"} — redeploy this plugin`\n : `host manifest missing pluginVersion`,\n } satisfies FederationCompatRemote;\n }\n\n const missingShared: string[] = [];\n const mismatchedShared: string[] = [];\n for (const hostDep of hostShared) {\n const remDep = manifest.shared?.find((s) => s.name === hostDep.name);\n if (!remDep) {\n missingShared.push(`${hostDep.name}@${hostDep.requiredVersion}`);\n continue;\n }\n if (!satisfiesConstraint(remDep.version ?? \"\", hostDep.requiredVersion!)) {\n mismatchedShared.push(\n `${hostDep.name}: remote=${remDep.version ?? \"?\"} hostReq=${hostDep.requiredVersion}`,\n );\n }\n if (isEffectCriticalSharedDep(hostDep.name)) {\n const hostVersion = hostDep.version ?? \"\";\n const remoteVersion = remDep.version ?? \"\";\n if (remoteVersion && remoteVersion !== hostVersion) {\n mismatchedShared.push(\n `${hostDep.name}: exact-version skew — remote=${remoteVersion} host=${hostVersion} — bundle was built against a different prerelease build; redeploy host and plugin together`,\n );\n }\n const remoteRequired =\n typeof remDep.requiredVersion === \"string\" ? remDep.requiredVersion.trim() : \"\";\n if (\n remoteRequired.length > 0 &&\n remoteRequired[0] !== \"^\" &&\n !satisfiesConstraint(hostVersion, remoteRequired)\n ) {\n mismatchedShared.push(\n `${hostDep.name}: train skew — host=${hostVersion} vs remote requiredVersion=${remoteRequired}`,\n );\n }\n }\n }\n\n const reasons = [\n ...missingShared.map((d) => `missing shared \"${d}\" required by host`),\n ...mismatchedShared,\n ];\n return {\n role,\n url,\n reachable: true,\n pluginVersion: version,\n ok: reasons.length === 0,\n reason: reasons[0],\n } satisfies FederationCompatRemote;\n }),\n );\n\n const remoteReports = settled.map((s, i) => {\n const placeholder = {\n role: remotes[i]!.role,\n url: remotes[i]!.url,\n reachable: false,\n pluginVersion: null,\n ok: false,\n };\n return s.status === \"fulfilled\" ? s.value : { ...placeholder, reason: String(s.reason) };\n });\n\n const ok =\n hostManifest !== null &&\n remoteReports.every((r) => r.ok && r.reachable && r.pluginVersion !== null);\n\n return {\n ok,\n hostVersion,\n hostReachable: hostManifest !== null,\n hostReason,\n remotes: remoteReports,\n };\n}\n\nexport function loadBosConfigForMfCheck(configPath: string): BosConfig {\n return JSON.parse(readFileSync(configPath, \"utf8\")) as BosConfig;\n}\n"],"mappings":"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"}
@@ -14,79 +14,105 @@ const isRetryable = (error) => {
14
14
  if (error instanceof FetchHttpError) return error.status >= 500;
15
15
  return false;
16
16
  };
17
- const runFetch = (url, init) => fetch(url, init);
18
- const fetchRawEff = (url, options) => effect.Effect.tryPromise({
19
- try: async () => {
17
+ const makeHttpFetch = (base) => {
18
+ const urlOf = (input) => typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
19
+ const attempt = (input, init) => effect.Effect.tryPromise({
20
+ try: (signal) => base(input, {
21
+ ...init,
22
+ signal: init?.signal ? AbortSignal.any([init.signal, signal]) : signal
23
+ }),
24
+ catch: (cause) => new FetchNetworkError({
25
+ url: urlOf(input),
26
+ cause
27
+ })
28
+ });
29
+ const get = (url, options) => {
20
30
  const timeoutMs = effect.Duration.toMillis(options?.timeout ?? DEFAULT_TIMEOUT);
21
- try {
22
- return await runFetch(url, {
23
- method: options?.method ?? "GET",
24
- headers: options?.headers,
25
- body: options?.body,
26
- redirect: options?.redirect,
27
- signal: AbortSignal.timeout(timeoutMs)
28
- });
29
- } catch (error) {
30
- if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) throw new FetchTimeoutError({ url });
31
- throw new FetchNetworkError({
32
- url,
33
- cause: error
34
- });
35
- }
36
- },
37
- catch: (error) => {
38
- if (error instanceof FetchNetworkError || error instanceof FetchTimeoutError) return error;
39
- return new FetchNetworkError({
31
+ const retries = options?.retries ?? DEFAULT_RETRIES;
32
+ const once = attempt(url, {
33
+ method: options?.method ?? "GET",
34
+ headers: options?.headers,
35
+ body: options?.body,
36
+ redirect: options?.redirect
37
+ }).pipe(effect.Effect.timeoutOrElse({
38
+ duration: timeoutMs,
39
+ orElse: () => effect.Effect.fail(new FetchTimeoutError({ url }))
40
+ }), effect.Effect.filterOrFail((response) => response.ok, (response) => new FetchHttpError({
40
41
  url,
41
- cause: error
42
- });
42
+ status: response.status,
43
+ statusText: response.statusText
44
+ })));
45
+ if (retries <= 0) return once;
46
+ return once.pipe(effect.Effect.retry({
47
+ schedule: effect.Schedule.exponential(EXPONENTIAL_BASE).pipe(effect.Schedule.upTo({
48
+ duration: EXPONENTIAL_CAP,
49
+ times: retries
50
+ }), effect.Schedule.jittered),
51
+ while: isRetryable
52
+ }));
53
+ };
54
+ return {
55
+ attempt,
56
+ get
57
+ };
58
+ };
59
+ const defaultHttpFetch = makeHttpFetch((url, init) => globalThis.fetch(url, init));
60
+ /**
61
+ * The HTTP seam for boot-path consumers (bundle resolution, federation
62
+ * loading, orchestrator deadlines): one resilience policy — interruption-
63
+ * backed deadlines, capped exponential jittered retry, typed errors — with
64
+ * the base fetch injectable so the bundle fetch interceptor can point it at
65
+ * the pre-patch `fetch` and not re-enter itself.
66
+ */
67
+ var HttpFetch = class HttpFetch extends effect.Context.Service()("everything-dev/http-client/HttpFetch") {
68
+ static layerWith(options) {
69
+ return effect.Layer.succeed(HttpFetch, makeHttpFetch(options.fetchImpl));
43
70
  }
44
- });
45
- const fetchEff = (url, options) => fetchRawEff(url, options).pipe(effect.Effect.filterOrFail((response) => response.ok, (response) => new FetchHttpError({
46
- url,
47
- status: response.status,
48
- statusText: response.statusText
49
- })));
50
- const retrySchedule = effect.Schedule.exponential(EXPONENTIAL_BASE).pipe(effect.Schedule.upTo({
51
- duration: EXPONENTIAL_CAP,
52
- times: DEFAULT_RETRIES
53
- }));
54
- const fetchWithRetryEff = (url, options) => {
55
- const retries = options?.retries ?? DEFAULT_RETRIES;
56
- if (retries <= 0) return fetchEff(url, options);
57
- const schedule = options?.retries !== void 0 ? effect.Schedule.exponential(EXPONENTIAL_BASE).pipe(effect.Schedule.upTo({
58
- duration: EXPONENTIAL_CAP,
59
- times: retries
60
- })) : retrySchedule;
61
- return fetchEff(url, options).pipe(effect.Effect.retry({
62
- schedule,
63
- while: isRetryable
64
- }));
71
+ static layer = effect.Layer.succeed(HttpFetch, defaultHttpFetch);
65
72
  };
66
73
  const getCache = /* @__PURE__ */ new Map();
67
74
  const GET_CACHE_TTL_MS = 3e4;
75
+ const GET_CACHE_MAX_ENTRIES = 128;
76
+ function setCached(url, data) {
77
+ const now = Date.now();
78
+ for (const [key, entry] of getCache) if (entry.expiresAt <= now) getCache.delete(key);
79
+ if (getCache.size >= GET_CACHE_MAX_ENTRIES) for (const key of getCache.keys()) {
80
+ getCache.delete(key);
81
+ if (getCache.size < GET_CACHE_MAX_ENTRIES) break;
82
+ }
83
+ getCache.set(url, {
84
+ data,
85
+ expiresAt: now + GET_CACHE_TTL_MS
86
+ });
87
+ }
68
88
  function isCacheable(_url, options) {
69
89
  if (options?.method && options.method !== "GET") return false;
70
90
  if (options?.body) return false;
71
91
  return true;
72
92
  }
73
- const fetchResponse = (url, options) => effect.Effect.runPromise(fetchRawEff(url, options));
93
+ /** One raw fetch attempt with a deadline: returns the Response regardless of
94
+ * status; rejects on network failure or deadline expiry. */
95
+ const fetchRaw = (url, init, timeout = DEFAULT_TIMEOUT) => effect.Effect.runPromise(defaultHttpFetch.attempt(url, init).pipe(effect.Effect.timeoutOrElse({
96
+ duration: timeout,
97
+ orElse: () => effect.Effect.fail(new FetchTimeoutError({ url }))
98
+ })));
99
+ const fetchResponse = (url, options) => effect.Effect.runPromise(defaultHttpFetch.attempt(url, {
100
+ method: options?.method,
101
+ headers: options?.headers,
102
+ body: options?.body,
103
+ redirect: options?.redirect
104
+ }).pipe(effect.Effect.timeoutOrElse({
105
+ duration: options?.timeout ?? DEFAULT_TIMEOUT,
106
+ orElse: () => effect.Effect.fail(new FetchTimeoutError({ url }))
107
+ })));
74
108
  const fetchJsonOrNull = async (url, options) => {
75
109
  if (isCacheable(url, options)) {
76
110
  const cached = getCache.get(url);
77
111
  if (cached && cached.expiresAt > Date.now()) return cached.data;
78
112
  }
79
- const retries = options?.retries ?? DEFAULT_RETRIES;
80
- const eff = retries > 0 ? fetchWithRetryEff(url, {
81
- ...options,
82
- retries
83
- }) : fetchEff(url, options);
84
113
  try {
85
- const data = await (await effect.Effect.runPromise(eff)).json();
86
- if (isCacheable(url, options)) getCache.set(url, {
87
- data,
88
- expiresAt: Date.now() + GET_CACHE_TTL_MS
89
- });
114
+ const data = await (await effect.Effect.runPromise(defaultHttpFetch.get(url, options))).json();
115
+ if (isCacheable(url, options)) setCached(url, data);
90
116
  return data;
91
117
  } catch (error) {
92
118
  const msg = error instanceof FetchNetworkError ? `[http] Network error: ${error.url} — ${String(error.cause)}` : error instanceof FetchTimeoutError ? `[http] Timeout: ${error.url}` : error instanceof FetchHttpError ? `[http] HTTP ${error.status}: ${error.url}` : `[http] Unknown error while fetching ${url}`;
@@ -99,7 +125,8 @@ const fetchJsonOrNull = async (url, options) => {
99
125
  exports.FetchHttpError = FetchHttpError;
100
126
  exports.FetchNetworkError = FetchNetworkError;
101
127
  exports.FetchTimeoutError = FetchTimeoutError;
128
+ exports.HttpFetch = HttpFetch;
102
129
  exports.fetchJsonOrNull = fetchJsonOrNull;
130
+ exports.fetchRaw = fetchRaw;
103
131
  exports.fetchResponse = fetchResponse;
104
- exports.fetchWithRetryEff = fetchWithRetryEff;
105
132
  //# sourceMappingURL=http-client.cjs.map