everything-dev 2.0.0-rc.0 → 2.0.0-rc.4
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.
- package/dist/auth-login.cjs +1 -1
- package/dist/auth-login.mjs +1 -1
- package/dist/build.cjs +2 -2
- package/dist/build.mjs +2 -2
- package/dist/bundle-fs-resolve.cjs +9 -17
- package/dist/bundle-fs-resolve.cjs.map +1 -1
- package/dist/bundle-fs-resolve.d.cts +6 -0
- package/dist/bundle-fs-resolve.d.mts +6 -0
- package/dist/bundle-fs-resolve.mjs +9 -17
- package/dist/bundle-fs-resolve.mjs.map +1 -1
- package/dist/bundle-path.cjs +48 -0
- package/dist/bundle-path.cjs.map +1 -0
- package/dist/bundle-path.mjs +45 -0
- package/dist/bundle-path.mjs.map +1 -0
- package/dist/cli/init.cjs +13 -13
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.mjs +13 -13
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/status.cjs +69 -3
- package/dist/cli/status.cjs.map +1 -1
- package/dist/cli/status.mjs +67 -4
- package/dist/cli/status.mjs.map +1 -1
- package/dist/cli/sync.cjs +1 -1
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +1 -1
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli/upgrade.cjs +2 -2
- package/dist/cli/upgrade.cjs.map +1 -1
- package/dist/cli/upgrade.mjs +2 -2
- package/dist/cli/upgrade.mjs.map +1 -1
- package/dist/cli.cjs +5 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +5 -7
- package/dist/cli.mjs.map +1 -1
- package/dist/commands/deploy.cjs +26 -16
- package/dist/commands/deploy.cjs.map +1 -1
- package/dist/commands/deploy.mjs +26 -16
- package/dist/commands/deploy.mjs.map +1 -1
- package/dist/commands/init.cjs +1 -1
- package/dist/commands/init.cjs.map +1 -1
- package/dist/commands/init.mjs +1 -1
- package/dist/commands/init.mjs.map +1 -1
- package/dist/commands/plugins.cjs +2 -2
- package/dist/commands/plugins.cjs.map +1 -1
- package/dist/commands/plugins.mjs +2 -2
- package/dist/commands/plugins.mjs.map +1 -1
- package/dist/commands/upgrade.cjs +1 -1
- package/dist/commands/upgrade.mjs +1 -1
- package/dist/components/dev-render.cjs +1 -1
- package/dist/components/dev-render.mjs +1 -1
- package/dist/config.cjs +3 -3
- package/dist/config.cjs.map +1 -1
- package/dist/config.mjs +3 -3
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +2 -0
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +3 -0
- package/dist/contract.d.mts +3 -0
- package/dist/contract.meta.cjs +2 -1
- package/dist/contract.meta.cjs.map +1 -1
- package/dist/contract.meta.d.cts +4 -1
- package/dist/contract.meta.d.mts +4 -1
- package/dist/contract.meta.mjs +2 -1
- package/dist/contract.meta.mjs.map +1 -1
- package/dist/contract.mjs +2 -0
- package/dist/contract.mjs.map +1 -1
- package/dist/db/driver.cjs +22 -2
- package/dist/db/driver.cjs.map +1 -1
- package/dist/db/driver.d.cts +13 -2
- package/dist/db/driver.d.mts +13 -2
- package/dist/db/driver.mjs +22 -3
- package/dist/db/driver.mjs.map +1 -1
- package/dist/db/index.cjs +1 -0
- package/dist/db/index.d.cts +2 -2
- package/dist/db/index.d.mts +2 -2
- package/dist/db/index.mjs +2 -2
- package/dist/deploy-diff.cjs +1 -1
- package/dist/deploy-diff.cjs.map +1 -1
- package/dist/deploy-diff.mjs +1 -1
- package/dist/deploy-diff.mjs.map +1 -1
- package/dist/dev-program.cjs +10 -2
- package/dist/dev-program.cjs.map +1 -1
- package/dist/dev-program.mjs +10 -2
- package/dist/dev-program.mjs.map +1 -1
- package/dist/image-deploy.cjs +65 -17
- package/dist/image-deploy.cjs.map +1 -1
- package/dist/image-deploy.mjs +64 -18
- package/dist/image-deploy.mjs.map +1 -1
- package/dist/internal/manifest-normalizer.cjs.map +1 -1
- package/dist/internal/manifest-normalizer.mjs.map +1 -1
- package/dist/plugin.d.cts +902 -1
- package/dist/plugin.d.mts +902 -1
- package/dist/publish.cjs +51 -2
- package/dist/publish.cjs.map +1 -1
- package/dist/publish.mjs +51 -4
- package/dist/publish.mjs.map +1 -1
- package/dist/resolution/session.cjs.map +1 -1
- package/dist/resolution/session.d.cts +5 -5
- package/dist/resolution/session.d.mts +5 -5
- package/dist/resolution/session.mjs.map +1 -1
- package/dist/types.d.cts +31 -1
- package/dist/types.d.mts +31 -1
- package/dist/ui/auth-guards.d.cts +78 -2
- package/dist/ui/auth-guards.d.mts +78 -2
- package/dist/ui/auth.cjs.map +1 -1
- package/dist/ui/auth.d.cts +4133 -7
- package/dist/ui/auth.d.mts +4133 -7
- package/dist/ui/auth.mjs.map +1 -1
- package/dist/ui/hydrate.cjs +2 -2
- package/dist/ui/hydrate.cjs.map +1 -1
- package/dist/ui/hydrate.mjs +2 -2
- package/dist/ui/hydrate.mjs.map +1 -1
- package/dist/utils/theme.cjs +2 -2
- package/dist/utils/theme.cjs.map +1 -1
- package/dist/utils/theme.mjs +2 -2
- package/dist/utils/theme.mjs.map +1 -1
- package/dist/version-manifest-resolve.cjs +55 -10
- package/dist/version-manifest-resolve.cjs.map +1 -1
- package/dist/version-manifest-resolve.mjs +53 -10
- package/dist/version-manifest-resolve.mjs.map +1 -1
- package/dist/workspace.cjs +26 -13
- package/dist/workspace.cjs.map +1 -1
- package/dist/workspace.mjs +26 -14
- package/dist/workspace.mjs.map +1 -1
- package/package.json +11 -10
package/dist/auth-login.cjs
CHANGED
package/dist/auth-login.mjs
CHANGED
package/dist/build.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
2
|
const require_config = require('./config.cjs');
|
|
3
|
-
const require_timing = require('./cli/timing.cjs');
|
|
4
3
|
const require_run = require('./utils/run.cjs');
|
|
4
|
+
const require_theme = require('./utils/theme.cjs');
|
|
5
|
+
const require_timing = require('./cli/timing.cjs');
|
|
5
6
|
const require_workspace = require('./workspace.cjs');
|
|
6
7
|
const require_shared_deps = require('./shared-deps.cjs');
|
|
7
8
|
const require_string = require('./utils/string.cjs');
|
|
8
|
-
const require_theme = require('./utils/theme.cjs');
|
|
9
9
|
let node_process = require("node:process");
|
|
10
10
|
node_process = require_runtime.__toESM(node_process, 1);
|
|
11
11
|
let node_fs_promises = require("node:fs/promises");
|
package/dist/build.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { resolveLocalDevelopmentPath } from "./config.mjs";
|
|
2
|
-
import { formatDuration } from "./cli/timing.mjs";
|
|
3
2
|
import { run } from "./utils/run.mjs";
|
|
3
|
+
import { colors, icons } from "./utils/theme.mjs";
|
|
4
|
+
import { formatDuration } from "./cli/timing.mjs";
|
|
4
5
|
import { ensureFreshDeps, findWorkspaceRoot } from "./workspace.mjs";
|
|
5
6
|
import { syncResolvedSharedDeps } from "./shared-deps.mjs";
|
|
6
7
|
import { padRight } from "./utils/string.mjs";
|
|
7
|
-
import { colors, icons } from "./utils/theme.mjs";
|
|
8
8
|
import process from "node:process";
|
|
9
9
|
import { access, readFile } from "node:fs/promises";
|
|
10
10
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
const require_bundle_path = require('./bundle-path.cjs');
|
|
3
4
|
const require_config = require('./config.cjs');
|
|
4
5
|
const require_bundle_cache = require('./bundle-cache.cjs');
|
|
5
6
|
let node_fs = require("node:fs");
|
|
@@ -43,25 +44,16 @@ const MIME_TYPES = {
|
|
|
43
44
|
".txt": "text/plain",
|
|
44
45
|
".xml": "application/xml"
|
|
45
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Own-namespace path for a bundle URL: the shared URL→staged-path mapping,
|
|
49
|
+
* narrowed to the runtime identity — a URL whose encoded namespace differs
|
|
50
|
+
* from `namespace.account`/`namespace.gateway` must fall to the network/cache
|
|
51
|
+
* path, never serve a sibling namespace's staged bytes.
|
|
52
|
+
*/
|
|
46
53
|
function bundleUrlToLocalPath(url, namespace) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
parsed = new URL(url);
|
|
50
|
-
} catch {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
54
|
-
const prefix = `/bundles/${encodeURIComponent(namespace.account)}/${encodeURIComponent(namespace.gateway)}/`;
|
|
55
|
-
if (!parsed.pathname.startsWith(prefix)) return null;
|
|
56
|
-
let rest;
|
|
57
|
-
try {
|
|
58
|
-
rest = decodeURIComponent(parsed.pathname.slice(prefix.length));
|
|
59
|
-
} catch {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
if (!rest || rest.endsWith("/")) return null;
|
|
54
|
+
const filePath = require_bundle_path.bundleUrlToStagedPath(url, namespace.bundleDir);
|
|
55
|
+
if (!filePath) return null;
|
|
63
56
|
const nsDir = node_path.default.resolve(namespace.bundleDir, namespace.account, namespace.gateway);
|
|
64
|
-
const filePath = node_path.default.resolve(nsDir, rest);
|
|
65
57
|
if (filePath !== nsDir && !filePath.startsWith(nsDir + node_path.default.sep)) return null;
|
|
66
58
|
return filePath;
|
|
67
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle-fs-resolve.cjs","names":["Schema","path","Context","Effect","readFile","cacheControlOf","Layer","ManagedRuntime","bundleCachePath","writeBundleCache","readBundleCache","isAppDescriptorPath","readFileSync","existsSync","bundleCacheRoot"],"sources":["../src/bundle-fs-resolve.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { Context, Effect, Layer, ManagedRuntime, Schema } from \"effect\";\nimport { cacheControlOf } from \"every-plugin/build/artifact-names\";\nimport {\n bundleCachePath,\n bundleCacheRoot,\n readBundleCache,\n writeBundleCache,\n} from \"./bundle-cache\";\nimport { isAppDescriptorPath } from \"./config\";\n\n/**\n * Local-first bundle resolution (ADR 0011 amendment; unified policy, ADR 0021\n * amendment): a self-contained runtime image stages its own namespace under\n * `BOS_BUNDLE_DIR` as `bundles/<account>/<gateway>/<workspace>/…` and consumes\n * those bytes directly from disk; a miss falls through to the network, so a\n * partially-staged namespace boots over the wire instead of failing. The\n * registry tier (children, `BOS_BUNDLE_DIR` unset) keeps the network fetch —\n * the namespace guard below only ever matches the runtime's own\n * account/gateway, never another runtime's layout.\n */\n\nexport interface BundleNamespace {\n readonly bundleDir: string;\n readonly account: string;\n readonly gateway: string;\n}\n\nexport class BundleReadError extends Schema.TaggedError<BundleReadError>()(\"BundleReadError\", {\n path: Schema.String,\n cause: Schema.optional(Schema.Unknown),\n}) {}\n\nconst MIME_TYPES: Record<string, string> = {\n \".js\": \"text/javascript\",\n \".mjs\": \"text/javascript\",\n \".cjs\": \"text/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".map\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".avif\": \"image/avif\",\n \".ico\": \"image/x-icon\",\n \".html\": \"text/html\",\n \".htm\": \"text/html\",\n \".webmanifest\": \"application/manifest+json\",\n \".md\": \"text/markdown\",\n \".ts\": \"text/plain\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".otf\": \"font/otf\",\n \".eot\": \"application/vnd.ms-fontobject\",\n \".wasm\": \"application/wasm\",\n \".txt\": \"text/plain\",\n \".xml\": \"application/xml\",\n};\n\nexport function bundleUrlToLocalPath(url: string, namespace: BundleNamespace): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") return null;\n\n const prefix = `/bundles/${encodeURIComponent(namespace.account)}/${encodeURIComponent(namespace.gateway)}/`;\n if (!parsed.pathname.startsWith(prefix)) return null;\n\n let rest: string;\n try {\n rest = decodeURIComponent(parsed.pathname.slice(prefix.length));\n } catch {\n return null;\n }\n if (!rest || rest.endsWith(\"/\")) return null;\n\n // BOS_BUNDLE_DIR holds `<account>/<gateway>/<workspace>/…` directly (the\n // same layout host/src/routes/bundles.ts serves) — resolve and contain\n // within the OWN namespace, never a sibling one.\n const nsDir = path.resolve(namespace.bundleDir, namespace.account, namespace.gateway);\n const filePath = path.resolve(nsDir, rest);\n if (filePath !== nsDir && !filePath.startsWith(nsDir + path.sep)) return null;\n\n return filePath;\n}\n\nexport class BundleResolver extends Context.Service<\n BundleResolver,\n {\n /** Resolve a fetch target from the staged namespace; null = miss or not our namespace — fall through to the network. */\n readonly lookup: (input: string | URL | Request) => Effect.Effect<Response | null>;\n }\n>()(\"everything-dev/bundle-fs-resolve/BundleResolver\") {\n static layer(namespace: BundleNamespace): Layer.Layer<BundleResolver> {\n const respond = Effect.fn(\"BundleResolver.respond\")(function* (\n url: string,\n ): Effect.fn.Return<Response | null, never> {\n const filePath = bundleUrlToLocalPath(url, namespace);\n if (!filePath) return null;\n const bytes = yield* Effect.tryPromise({\n try: () => readFile(filePath),\n catch: (cause) => new BundleReadError({ path: filePath, cause }),\n }).pipe(Effect.catchTag(\"BundleReadError\", () => Effect.succeed(null)));\n if (bytes === null) return null;\n const name = path.basename(filePath);\n const contentType =\n MIME_TYPES[path.extname(name).toLowerCase()] ?? \"application/octet-stream\";\n return new Response(new Uint8Array(bytes), {\n headers: {\n \"content-type\": contentType,\n \"cache-control\": cacheControlOf(name),\n etag: `\"${Buffer.from(bytes.subarray(0, 4096)).toString(\"base64\").slice(0, 24)}\"`,\n },\n });\n });\n\n return Layer.effect(\n BundleResolver,\n Effect.succeed(\n BundleResolver.of({\n lookup: (input) => {\n const url =\n typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n return respond(url);\n },\n }),\n ),\n );\n }\n}\n\nexport interface BundleFetchHandle {\n readonly uninstall: () => Promise<void>;\n}\n\nexport interface BundleFetchOptions {\n /** Own-namespace FS resolution — requires a staged `BOS_BUNDLE_DIR`. */\n namespace?: BundleNamespace;\n /**\n * Foreign-namespace stale-if-error cache (child tier): `/bundles/…` URLs\n * outside the own namespace write through to disk on success and serve\n * the last-known-good bytes when the origin fails. Enabled implicitly\n * when `namespace` is present (deployment contexts), or explicitly via\n * `BOS_BUNDLE_CACHE_DIR`.\n */\n cacheDir?: string;\n}\n\ntype FetchImpl = typeof globalThis.fetch;\n\nconst staleResponse = (bytes: Uint8Array, url: string): Response => {\n const name = url.split(\"?\")[0].split(\"/\").pop() ?? \"\";\n const contentType = MIME_TYPES[path.extname(name).toLowerCase()] ?? \"application/octet-stream\";\n return new Response(bytes.slice().buffer, {\n status: 200,\n headers: { \"content-type\": contentType, \"x-bundle-cache\": \"stale\" },\n });\n};\n\n/**\n * Installs the resolver as a global fetch interceptor — the one seam every\n * boot-time consumer shares (config manifest discovery, contract-type\n * fetches, orchestrator host loading, MF remoteEntry/identity probes).\n * One layered resolution policy: staged disk → network → stale-if-error.\n * Own-namespace URLs resolve from the staged directory and fall through to\n * the original fetch on a miss (a partially-staged namespace boots over the\n * wire instead of failing); a miss never touches the write-through cache —\n * for the own namespace the staged disk IS the resilience artifact. Foreign\n * `/bundles/…` URLs get the network with the stale-if-error cache; every\n * other URL falls through to the original fetch untouched. Inert when\n * neither a staged namespace nor a cache is configured.\n */\nexport function installGlobalBundleFetch(options: BundleFetchOptions): BundleFetchHandle {\n const namespace = options.namespace;\n const cacheDir = options.cacheDir;\n const runtime = namespace ? ManagedRuntime.make(BundleResolver.layer(namespace)) : null;\n const original: FetchImpl = globalThis.fetch;\n const patched = (async (input: Parameters<FetchImpl>[0], init?: Parameters<FetchImpl>[1]) => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n if (namespace && bundleUrlToLocalPath(url, namespace)) {\n if (!runtime) throw new Error(\"unreachable: resolver runtime missing\");\n const response = await runtime.runPromise(\n Effect.gen(function* () {\n const resolver = yield* BundleResolver;\n return yield* resolver.lookup(input);\n }),\n );\n if (response !== null) return response;\n return original(input, init);\n }\n\n if (cacheDir !== undefined && bundleCachePath(url, { cacheDir })) {\n return (async () => {\n try {\n const response = await original(input, init);\n if (response.ok) {\n try {\n const bytes = new Uint8Array(await response.clone().arrayBuffer());\n await writeBundleCache(url, bytes, { cacheDir });\n } catch {\n // write-through is best-effort\n }\n } else {\n const cached = await readBundleCache(url, { cacheDir });\n if (cached) return staleResponse(cached, url);\n }\n return response;\n } catch (error) {\n const cached = await readBundleCache(url, { cacheDir });\n if (cached) return staleResponse(cached, url);\n throw error;\n }\n })();\n }\n\n return original(input, init);\n }) as FetchImpl;\n const originalPreconnect = (original as Partial<FetchImpl>).preconnect;\n if (originalPreconnect) {\n patched.preconnect = originalPreconnect.bind(original);\n }\n globalThis.fetch = patched;\n return {\n uninstall: async () => {\n if (globalThis.fetch === patched) {\n globalThis.fetch = original;\n }\n if (runtime) await runtime.dispose();\n },\n };\n}\n\n/**\n * Boot-time installer for the CLI: derives the own namespace from\n * `BOS_BUNDLE_DIR` plus the runtime identity (`BOS_ACCOUNT`/`BOS_GATEWAY`\n * registry env taking precedence over the bos.config.json fields), and the\n * foreign-namespace cache from `BOS_BUNDLE_CACHE_DIR`. Tier auto-detection\n * (ADR 0021): an identity whose namespace is not actually staged under the\n * bundle dir drops to the registry tier — plain network fetch — instead of\n * deterministically 404ing own-namespace URLs against a foreign image's\n * staged bytes. Never throws — a missing or malformed identity leaves the\n * default network fetch in place.\n */\nexport function installBundleFetchFromEnv(input: {\n configPath?: string | null;\n}): BundleFetchHandle | null {\n const bundleDir = process.env.BOS_BUNDLE_DIR;\n const cacheDir = process.env.BOS_BUNDLE_CACHE_DIR;\n if (!bundleDir && cacheDir === undefined) return null;\n\n let configAccount: string | undefined;\n let configGateway: string | undefined;\n const configPath = input.configPath;\n if (configPath && !isAppDescriptorPath(configPath)) {\n try {\n const parsed = JSON.parse(readFileSync(configPath, \"utf8\")) as {\n account?: string;\n domain?: string;\n };\n configAccount = parsed.account;\n configGateway = parsed.domain;\n } catch {\n // unreadable config — identity may still come from the registry env\n }\n }\n\n const account = process.env.BOS_ACCOUNT ?? configAccount;\n const gateway = process.env.BOS_GATEWAY ?? configGateway;\n if (bundleDir && (!account || !gateway)) return null;\n\n let namespace: BundleNamespace | undefined;\n if (bundleDir && account && gateway) {\n if (existsSync(path.join(bundleDir, account, gateway))) {\n namespace = { bundleDir, account, gateway };\n } else {\n console.warn(\n `[bundles] staged namespace ${account}/${gateway} not found under ${bundleDir} — registry tier (network fetch)`,\n );\n }\n }\n return installGlobalBundleFetch({\n namespace,\n cacheDir: cacheDir ?? (namespace ? bundleCacheRoot() : undefined),\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bundle-fs-resolve.cjs","names":["Schema","bundleUrlToStagedPath","path","Context","Effect","readFile","cacheControlOf","Layer","ManagedRuntime","bundleCachePath","writeBundleCache","readBundleCache","isAppDescriptorPath","readFileSync","existsSync","bundleCacheRoot"],"sources":["../src/bundle-fs-resolve.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { Context, Effect, Layer, ManagedRuntime, Schema } from \"effect\";\nimport { cacheControlOf } from \"every-plugin/build/artifact-names\";\nimport {\n bundleCachePath,\n bundleCacheRoot,\n readBundleCache,\n writeBundleCache,\n} from \"./bundle-cache\";\nimport { bundleUrlToStagedPath } from \"./bundle-path\";\nimport { isAppDescriptorPath } from \"./config\";\n\n/**\n * Local-first bundle resolution (ADR 0011 amendment; unified policy, ADR 0021\n * amendment): a self-contained runtime image stages its own namespace under\n * `BOS_BUNDLE_DIR` as `bundles/<account>/<gateway>/<workspace>/…` and consumes\n * those bytes directly from disk; a miss falls through to the network, so a\n * partially-staged namespace boots over the wire instead of failing. The\n * registry tier (children, `BOS_BUNDLE_DIR` unset) keeps the network fetch —\n * the namespace guard below only ever matches the runtime's own\n * account/gateway, never another runtime's layout.\n */\n\nexport interface BundleNamespace {\n readonly bundleDir: string;\n readonly account: string;\n readonly gateway: string;\n}\n\nexport class BundleReadError extends Schema.TaggedError<BundleReadError>()(\"BundleReadError\", {\n path: Schema.String,\n cause: Schema.optional(Schema.Unknown),\n}) {}\n\nconst MIME_TYPES: Record<string, string> = {\n \".js\": \"text/javascript\",\n \".mjs\": \"text/javascript\",\n \".cjs\": \"text/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".map\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".avif\": \"image/avif\",\n \".ico\": \"image/x-icon\",\n \".html\": \"text/html\",\n \".htm\": \"text/html\",\n \".webmanifest\": \"application/manifest+json\",\n \".md\": \"text/markdown\",\n \".ts\": \"text/plain\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".otf\": \"font/otf\",\n \".eot\": \"application/vnd.ms-fontobject\",\n \".wasm\": \"application/wasm\",\n \".txt\": \"text/plain\",\n \".xml\": \"application/xml\",\n};\n\n/**\n * Own-namespace path for a bundle URL: the shared URL→staged-path mapping,\n * narrowed to the runtime identity — a URL whose encoded namespace differs\n * from `namespace.account`/`namespace.gateway` must fall to the network/cache\n * path, never serve a sibling namespace's staged bytes.\n */\nexport function bundleUrlToLocalPath(url: string, namespace: BundleNamespace): string | null {\n const filePath = bundleUrlToStagedPath(url, namespace.bundleDir);\n if (!filePath) return null;\n\n const nsDir = path.resolve(namespace.bundleDir, namespace.account, namespace.gateway);\n if (filePath !== nsDir && !filePath.startsWith(nsDir + path.sep)) return null;\n\n return filePath;\n}\n\nexport class BundleResolver extends Context.Service<\n BundleResolver,\n {\n /** Resolve a fetch target from the staged namespace; null = miss or not our namespace — fall through to the network. */\n readonly lookup: (input: string | URL | Request) => Effect.Effect<Response | null>;\n }\n>()(\"everything-dev/bundle-fs-resolve/BundleResolver\") {\n static layer(namespace: BundleNamespace): Layer.Layer<BundleResolver> {\n const respond = Effect.fn(\"BundleResolver.respond\")(function* (\n url: string,\n ): Effect.fn.Return<Response | null, never> {\n const filePath = bundleUrlToLocalPath(url, namespace);\n if (!filePath) return null;\n const bytes = yield* Effect.tryPromise({\n try: () => readFile(filePath),\n catch: (cause) => new BundleReadError({ path: filePath, cause }),\n }).pipe(Effect.catchTag(\"BundleReadError\", () => Effect.succeed(null)));\n if (bytes === null) return null;\n const name = path.basename(filePath);\n const contentType =\n MIME_TYPES[path.extname(name).toLowerCase()] ?? \"application/octet-stream\";\n return new Response(new Uint8Array(bytes), {\n headers: {\n \"content-type\": contentType,\n \"cache-control\": cacheControlOf(name),\n etag: `\"${Buffer.from(bytes.subarray(0, 4096)).toString(\"base64\").slice(0, 24)}\"`,\n },\n });\n });\n\n return Layer.effect(\n BundleResolver,\n Effect.succeed(\n BundleResolver.of({\n lookup: (input) => {\n const url =\n typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n return respond(url);\n },\n }),\n ),\n );\n }\n}\n\nexport interface BundleFetchHandle {\n readonly uninstall: () => Promise<void>;\n}\n\nexport interface BundleFetchOptions {\n /** Own-namespace FS resolution — requires a staged `BOS_BUNDLE_DIR`. */\n namespace?: BundleNamespace;\n /**\n * Foreign-namespace stale-if-error cache (child tier): `/bundles/…` URLs\n * outside the own namespace write through to disk on success and serve\n * the last-known-good bytes when the origin fails. Enabled implicitly\n * when `namespace` is present (deployment contexts), or explicitly via\n * `BOS_BUNDLE_CACHE_DIR`.\n */\n cacheDir?: string;\n}\n\ntype FetchImpl = typeof globalThis.fetch;\n\nconst staleResponse = (bytes: Uint8Array, url: string): Response => {\n const name = url.split(\"?\")[0].split(\"/\").pop() ?? \"\";\n const contentType = MIME_TYPES[path.extname(name).toLowerCase()] ?? \"application/octet-stream\";\n return new Response(bytes.slice().buffer, {\n status: 200,\n headers: { \"content-type\": contentType, \"x-bundle-cache\": \"stale\" },\n });\n};\n\n/**\n * Installs the resolver as a global fetch interceptor — the one seam every\n * boot-time consumer shares (config manifest discovery, contract-type\n * fetches, orchestrator host loading, MF remoteEntry/identity probes).\n * One layered resolution policy: staged disk → network → stale-if-error.\n * Own-namespace URLs resolve from the staged directory and fall through to\n * the original fetch on a miss (a partially-staged namespace boots over the\n * wire instead of failing); a miss never touches the write-through cache —\n * for the own namespace the staged disk IS the resilience artifact. Foreign\n * `/bundles/…` URLs get the network with the stale-if-error cache; every\n * other URL falls through to the original fetch untouched. Inert when\n * neither a staged namespace nor a cache is configured.\n */\nexport function installGlobalBundleFetch(options: BundleFetchOptions): BundleFetchHandle {\n const namespace = options.namespace;\n const cacheDir = options.cacheDir;\n const runtime = namespace ? ManagedRuntime.make(BundleResolver.layer(namespace)) : null;\n const original: FetchImpl = globalThis.fetch;\n const patched = (async (input: Parameters<FetchImpl>[0], init?: Parameters<FetchImpl>[1]) => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n if (namespace && bundleUrlToLocalPath(url, namespace)) {\n if (!runtime) throw new Error(\"unreachable: resolver runtime missing\");\n const response = await runtime.runPromise(\n Effect.gen(function* () {\n const resolver = yield* BundleResolver;\n return yield* resolver.lookup(input);\n }),\n );\n if (response !== null) return response;\n return original(input, init);\n }\n\n if (cacheDir !== undefined && bundleCachePath(url, { cacheDir })) {\n return (async () => {\n try {\n const response = await original(input, init);\n if (response.ok) {\n try {\n const bytes = new Uint8Array(await response.clone().arrayBuffer());\n await writeBundleCache(url, bytes, { cacheDir });\n } catch {\n // write-through is best-effort\n }\n } else {\n const cached = await readBundleCache(url, { cacheDir });\n if (cached) return staleResponse(cached, url);\n }\n return response;\n } catch (error) {\n const cached = await readBundleCache(url, { cacheDir });\n if (cached) return staleResponse(cached, url);\n throw error;\n }\n })();\n }\n\n return original(input, init);\n }) as FetchImpl;\n const originalPreconnect = (original as Partial<FetchImpl>).preconnect;\n if (originalPreconnect) {\n patched.preconnect = originalPreconnect.bind(original);\n }\n globalThis.fetch = patched;\n return {\n uninstall: async () => {\n if (globalThis.fetch === patched) {\n globalThis.fetch = original;\n }\n if (runtime) await runtime.dispose();\n },\n };\n}\n\n/**\n * Boot-time installer for the CLI: derives the own namespace from\n * `BOS_BUNDLE_DIR` plus the runtime identity (`BOS_ACCOUNT`/`BOS_GATEWAY`\n * registry env taking precedence over the bos.config.json fields), and the\n * foreign-namespace cache from `BOS_BUNDLE_CACHE_DIR`. Tier auto-detection\n * (ADR 0021): an identity whose namespace is not actually staged under the\n * bundle dir drops to the registry tier — plain network fetch — instead of\n * deterministically 404ing own-namespace URLs against a foreign image's\n * staged bytes. Never throws — a missing or malformed identity leaves the\n * default network fetch in place.\n */\nexport function installBundleFetchFromEnv(input: {\n configPath?: string | null;\n}): BundleFetchHandle | null {\n const bundleDir = process.env.BOS_BUNDLE_DIR;\n const cacheDir = process.env.BOS_BUNDLE_CACHE_DIR;\n if (!bundleDir && cacheDir === undefined) return null;\n\n let configAccount: string | undefined;\n let configGateway: string | undefined;\n const configPath = input.configPath;\n if (configPath && !isAppDescriptorPath(configPath)) {\n try {\n const parsed = JSON.parse(readFileSync(configPath, \"utf8\")) as {\n account?: string;\n domain?: string;\n };\n configAccount = parsed.account;\n configGateway = parsed.domain;\n } catch {\n // unreadable config — identity may still come from the registry env\n }\n }\n\n const account = process.env.BOS_ACCOUNT ?? configAccount;\n const gateway = process.env.BOS_GATEWAY ?? configGateway;\n if (bundleDir && (!account || !gateway)) return null;\n\n let namespace: BundleNamespace | undefined;\n if (bundleDir && account && gateway) {\n if (existsSync(path.join(bundleDir, account, gateway))) {\n namespace = { bundleDir, account, gateway };\n } else {\n console.warn(\n `[bundles] staged namespace ${account}/${gateway} not found under ${bundleDir} — registry tier (network fetch)`,\n );\n }\n }\n return installGlobalBundleFetch({\n namespace,\n cacheDir: cacheDir ?? (namespace ? bundleCacheRoot() : undefined),\n });\n}\n"],"mappings":"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"}
|
|
@@ -20,6 +20,12 @@ declare const BundleReadError_base: Schema.Class<BundleReadError, Schema.TaggedS
|
|
|
20
20
|
readonly cause: Schema.optional<Schema.Unknown>;
|
|
21
21
|
}>, import("effect/Cause").YieldableError>;
|
|
22
22
|
declare class BundleReadError extends BundleReadError_base {}
|
|
23
|
+
/**
|
|
24
|
+
* Own-namespace path for a bundle URL: the shared URL→staged-path mapping,
|
|
25
|
+
* narrowed to the runtime identity — a URL whose encoded namespace differs
|
|
26
|
+
* from `namespace.account`/`namespace.gateway` must fall to the network/cache
|
|
27
|
+
* path, never serve a sibling namespace's staged bytes.
|
|
28
|
+
*/
|
|
23
29
|
declare function bundleUrlToLocalPath(url: string, namespace: BundleNamespace): string | null;
|
|
24
30
|
declare const BundleResolver_base: Context.ServiceClass<BundleResolver, "everything-dev/bundle-fs-resolve/BundleResolver", {
|
|
25
31
|
/** Resolve a fetch target from the staged namespace; null = miss or not our namespace — fall through to the network. */
|
|
@@ -20,6 +20,12 @@ declare const BundleReadError_base: Schema.Class<BundleReadError, Schema.TaggedS
|
|
|
20
20
|
readonly cause: Schema.optional<Schema.Unknown>;
|
|
21
21
|
}>, import("effect/Cause").YieldableError>;
|
|
22
22
|
declare class BundleReadError extends BundleReadError_base {}
|
|
23
|
+
/**
|
|
24
|
+
* Own-namespace path for a bundle URL: the shared URL→staged-path mapping,
|
|
25
|
+
* narrowed to the runtime identity — a URL whose encoded namespace differs
|
|
26
|
+
* from `namespace.account`/`namespace.gateway` must fall to the network/cache
|
|
27
|
+
* path, never serve a sibling namespace's staged bytes.
|
|
28
|
+
*/
|
|
23
29
|
declare function bundleUrlToLocalPath(url: string, namespace: BundleNamespace): string | null;
|
|
24
30
|
declare const BundleResolver_base: Context.ServiceClass<BundleResolver, "everything-dev/bundle-fs-resolve/BundleResolver", {
|
|
25
31
|
/** Resolve a fetch target from the staged namespace; null = miss or not our namespace — fall through to the network. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bundleUrlToStagedPath } from "./bundle-path.mjs";
|
|
1
2
|
import { isAppDescriptorPath } from "./config.mjs";
|
|
2
3
|
import { bundleCachePath, bundleCacheRoot, readBundleCache, writeBundleCache } from "./bundle-cache.mjs";
|
|
3
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -40,25 +41,16 @@ const MIME_TYPES = {
|
|
|
40
41
|
".txt": "text/plain",
|
|
41
42
|
".xml": "application/xml"
|
|
42
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Own-namespace path for a bundle URL: the shared URL→staged-path mapping,
|
|
46
|
+
* narrowed to the runtime identity — a URL whose encoded namespace differs
|
|
47
|
+
* from `namespace.account`/`namespace.gateway` must fall to the network/cache
|
|
48
|
+
* path, never serve a sibling namespace's staged bytes.
|
|
49
|
+
*/
|
|
43
50
|
function bundleUrlToLocalPath(url, namespace) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
parsed = new URL(url);
|
|
47
|
-
} catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
51
|
-
const prefix = `/bundles/${encodeURIComponent(namespace.account)}/${encodeURIComponent(namespace.gateway)}/`;
|
|
52
|
-
if (!parsed.pathname.startsWith(prefix)) return null;
|
|
53
|
-
let rest;
|
|
54
|
-
try {
|
|
55
|
-
rest = decodeURIComponent(parsed.pathname.slice(prefix.length));
|
|
56
|
-
} catch {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
if (!rest || rest.endsWith("/")) return null;
|
|
51
|
+
const filePath = bundleUrlToStagedPath(url, namespace.bundleDir);
|
|
52
|
+
if (!filePath) return null;
|
|
60
53
|
const nsDir = path.resolve(namespace.bundleDir, namespace.account, namespace.gateway);
|
|
61
|
-
const filePath = path.resolve(nsDir, rest);
|
|
62
54
|
if (filePath !== nsDir && !filePath.startsWith(nsDir + path.sep)) return null;
|
|
63
55
|
return filePath;
|
|
64
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle-fs-resolve.mjs","names":[],"sources":["../src/bundle-fs-resolve.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { Context, Effect, Layer, ManagedRuntime, Schema } from \"effect\";\nimport { cacheControlOf } from \"every-plugin/build/artifact-names\";\nimport {\n bundleCachePath,\n bundleCacheRoot,\n readBundleCache,\n writeBundleCache,\n} from \"./bundle-cache\";\nimport { isAppDescriptorPath } from \"./config\";\n\n/**\n * Local-first bundle resolution (ADR 0011 amendment; unified policy, ADR 0021\n * amendment): a self-contained runtime image stages its own namespace under\n * `BOS_BUNDLE_DIR` as `bundles/<account>/<gateway>/<workspace>/…` and consumes\n * those bytes directly from disk; a miss falls through to the network, so a\n * partially-staged namespace boots over the wire instead of failing. The\n * registry tier (children, `BOS_BUNDLE_DIR` unset) keeps the network fetch —\n * the namespace guard below only ever matches the runtime's own\n * account/gateway, never another runtime's layout.\n */\n\nexport interface BundleNamespace {\n readonly bundleDir: string;\n readonly account: string;\n readonly gateway: string;\n}\n\nexport class BundleReadError extends Schema.TaggedError<BundleReadError>()(\"BundleReadError\", {\n path: Schema.String,\n cause: Schema.optional(Schema.Unknown),\n}) {}\n\nconst MIME_TYPES: Record<string, string> = {\n \".js\": \"text/javascript\",\n \".mjs\": \"text/javascript\",\n \".cjs\": \"text/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".map\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".avif\": \"image/avif\",\n \".ico\": \"image/x-icon\",\n \".html\": \"text/html\",\n \".htm\": \"text/html\",\n \".webmanifest\": \"application/manifest+json\",\n \".md\": \"text/markdown\",\n \".ts\": \"text/plain\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".otf\": \"font/otf\",\n \".eot\": \"application/vnd.ms-fontobject\",\n \".wasm\": \"application/wasm\",\n \".txt\": \"text/plain\",\n \".xml\": \"application/xml\",\n};\n\nexport function bundleUrlToLocalPath(url: string, namespace: BundleNamespace): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") return null;\n\n const prefix = `/bundles/${encodeURIComponent(namespace.account)}/${encodeURIComponent(namespace.gateway)}/`;\n if (!parsed.pathname.startsWith(prefix)) return null;\n\n let rest: string;\n try {\n rest = decodeURIComponent(parsed.pathname.slice(prefix.length));\n } catch {\n return null;\n }\n if (!rest || rest.endsWith(\"/\")) return null;\n\n // BOS_BUNDLE_DIR holds `<account>/<gateway>/<workspace>/…` directly (the\n // same layout host/src/routes/bundles.ts serves) — resolve and contain\n // within the OWN namespace, never a sibling one.\n const nsDir = path.resolve(namespace.bundleDir, namespace.account, namespace.gateway);\n const filePath = path.resolve(nsDir, rest);\n if (filePath !== nsDir && !filePath.startsWith(nsDir + path.sep)) return null;\n\n return filePath;\n}\n\nexport class BundleResolver extends Context.Service<\n BundleResolver,\n {\n /** Resolve a fetch target from the staged namespace; null = miss or not our namespace — fall through to the network. */\n readonly lookup: (input: string | URL | Request) => Effect.Effect<Response | null>;\n }\n>()(\"everything-dev/bundle-fs-resolve/BundleResolver\") {\n static layer(namespace: BundleNamespace): Layer.Layer<BundleResolver> {\n const respond = Effect.fn(\"BundleResolver.respond\")(function* (\n url: string,\n ): Effect.fn.Return<Response | null, never> {\n const filePath = bundleUrlToLocalPath(url, namespace);\n if (!filePath) return null;\n const bytes = yield* Effect.tryPromise({\n try: () => readFile(filePath),\n catch: (cause) => new BundleReadError({ path: filePath, cause }),\n }).pipe(Effect.catchTag(\"BundleReadError\", () => Effect.succeed(null)));\n if (bytes === null) return null;\n const name = path.basename(filePath);\n const contentType =\n MIME_TYPES[path.extname(name).toLowerCase()] ?? \"application/octet-stream\";\n return new Response(new Uint8Array(bytes), {\n headers: {\n \"content-type\": contentType,\n \"cache-control\": cacheControlOf(name),\n etag: `\"${Buffer.from(bytes.subarray(0, 4096)).toString(\"base64\").slice(0, 24)}\"`,\n },\n });\n });\n\n return Layer.effect(\n BundleResolver,\n Effect.succeed(\n BundleResolver.of({\n lookup: (input) => {\n const url =\n typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n return respond(url);\n },\n }),\n ),\n );\n }\n}\n\nexport interface BundleFetchHandle {\n readonly uninstall: () => Promise<void>;\n}\n\nexport interface BundleFetchOptions {\n /** Own-namespace FS resolution — requires a staged `BOS_BUNDLE_DIR`. */\n namespace?: BundleNamespace;\n /**\n * Foreign-namespace stale-if-error cache (child tier): `/bundles/…` URLs\n * outside the own namespace write through to disk on success and serve\n * the last-known-good bytes when the origin fails. Enabled implicitly\n * when `namespace` is present (deployment contexts), or explicitly via\n * `BOS_BUNDLE_CACHE_DIR`.\n */\n cacheDir?: string;\n}\n\ntype FetchImpl = typeof globalThis.fetch;\n\nconst staleResponse = (bytes: Uint8Array, url: string): Response => {\n const name = url.split(\"?\")[0].split(\"/\").pop() ?? \"\";\n const contentType = MIME_TYPES[path.extname(name).toLowerCase()] ?? \"application/octet-stream\";\n return new Response(bytes.slice().buffer, {\n status: 200,\n headers: { \"content-type\": contentType, \"x-bundle-cache\": \"stale\" },\n });\n};\n\n/**\n * Installs the resolver as a global fetch interceptor — the one seam every\n * boot-time consumer shares (config manifest discovery, contract-type\n * fetches, orchestrator host loading, MF remoteEntry/identity probes).\n * One layered resolution policy: staged disk → network → stale-if-error.\n * Own-namespace URLs resolve from the staged directory and fall through to\n * the original fetch on a miss (a partially-staged namespace boots over the\n * wire instead of failing); a miss never touches the write-through cache —\n * for the own namespace the staged disk IS the resilience artifact. Foreign\n * `/bundles/…` URLs get the network with the stale-if-error cache; every\n * other URL falls through to the original fetch untouched. Inert when\n * neither a staged namespace nor a cache is configured.\n */\nexport function installGlobalBundleFetch(options: BundleFetchOptions): BundleFetchHandle {\n const namespace = options.namespace;\n const cacheDir = options.cacheDir;\n const runtime = namespace ? ManagedRuntime.make(BundleResolver.layer(namespace)) : null;\n const original: FetchImpl = globalThis.fetch;\n const patched = (async (input: Parameters<FetchImpl>[0], init?: Parameters<FetchImpl>[1]) => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n if (namespace && bundleUrlToLocalPath(url, namespace)) {\n if (!runtime) throw new Error(\"unreachable: resolver runtime missing\");\n const response = await runtime.runPromise(\n Effect.gen(function* () {\n const resolver = yield* BundleResolver;\n return yield* resolver.lookup(input);\n }),\n );\n if (response !== null) return response;\n return original(input, init);\n }\n\n if (cacheDir !== undefined && bundleCachePath(url, { cacheDir })) {\n return (async () => {\n try {\n const response = await original(input, init);\n if (response.ok) {\n try {\n const bytes = new Uint8Array(await response.clone().arrayBuffer());\n await writeBundleCache(url, bytes, { cacheDir });\n } catch {\n // write-through is best-effort\n }\n } else {\n const cached = await readBundleCache(url, { cacheDir });\n if (cached) return staleResponse(cached, url);\n }\n return response;\n } catch (error) {\n const cached = await readBundleCache(url, { cacheDir });\n if (cached) return staleResponse(cached, url);\n throw error;\n }\n })();\n }\n\n return original(input, init);\n }) as FetchImpl;\n const originalPreconnect = (original as Partial<FetchImpl>).preconnect;\n if (originalPreconnect) {\n patched.preconnect = originalPreconnect.bind(original);\n }\n globalThis.fetch = patched;\n return {\n uninstall: async () => {\n if (globalThis.fetch === patched) {\n globalThis.fetch = original;\n }\n if (runtime) await runtime.dispose();\n },\n };\n}\n\n/**\n * Boot-time installer for the CLI: derives the own namespace from\n * `BOS_BUNDLE_DIR` plus the runtime identity (`BOS_ACCOUNT`/`BOS_GATEWAY`\n * registry env taking precedence over the bos.config.json fields), and the\n * foreign-namespace cache from `BOS_BUNDLE_CACHE_DIR`. Tier auto-detection\n * (ADR 0021): an identity whose namespace is not actually staged under the\n * bundle dir drops to the registry tier — plain network fetch — instead of\n * deterministically 404ing own-namespace URLs against a foreign image's\n * staged bytes. Never throws — a missing or malformed identity leaves the\n * default network fetch in place.\n */\nexport function installBundleFetchFromEnv(input: {\n configPath?: string | null;\n}): BundleFetchHandle | null {\n const bundleDir = process.env.BOS_BUNDLE_DIR;\n const cacheDir = process.env.BOS_BUNDLE_CACHE_DIR;\n if (!bundleDir && cacheDir === undefined) return null;\n\n let configAccount: string | undefined;\n let configGateway: string | undefined;\n const configPath = input.configPath;\n if (configPath && !isAppDescriptorPath(configPath)) {\n try {\n const parsed = JSON.parse(readFileSync(configPath, \"utf8\")) as {\n account?: string;\n domain?: string;\n };\n configAccount = parsed.account;\n configGateway = parsed.domain;\n } catch {\n // unreadable config — identity may still come from the registry env\n }\n }\n\n const account = process.env.BOS_ACCOUNT ?? configAccount;\n const gateway = process.env.BOS_GATEWAY ?? configGateway;\n if (bundleDir && (!account || !gateway)) return null;\n\n let namespace: BundleNamespace | undefined;\n if (bundleDir && account && gateway) {\n if (existsSync(path.join(bundleDir, account, gateway))) {\n namespace = { bundleDir, account, gateway };\n } else {\n console.warn(\n `[bundles] staged namespace ${account}/${gateway} not found under ${bundleDir} — registry tier (network fetch)`,\n );\n }\n }\n return installGlobalBundleFetch({\n namespace,\n cacheDir: cacheDir ?? (namespace ? bundleCacheRoot() : undefined),\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bundle-fs-resolve.mjs","names":[],"sources":["../src/bundle-fs-resolve.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { Context, Effect, Layer, ManagedRuntime, Schema } from \"effect\";\nimport { cacheControlOf } from \"every-plugin/build/artifact-names\";\nimport {\n bundleCachePath,\n bundleCacheRoot,\n readBundleCache,\n writeBundleCache,\n} from \"./bundle-cache\";\nimport { bundleUrlToStagedPath } from \"./bundle-path\";\nimport { isAppDescriptorPath } from \"./config\";\n\n/**\n * Local-first bundle resolution (ADR 0011 amendment; unified policy, ADR 0021\n * amendment): a self-contained runtime image stages its own namespace under\n * `BOS_BUNDLE_DIR` as `bundles/<account>/<gateway>/<workspace>/…` and consumes\n * those bytes directly from disk; a miss falls through to the network, so a\n * partially-staged namespace boots over the wire instead of failing. The\n * registry tier (children, `BOS_BUNDLE_DIR` unset) keeps the network fetch —\n * the namespace guard below only ever matches the runtime's own\n * account/gateway, never another runtime's layout.\n */\n\nexport interface BundleNamespace {\n readonly bundleDir: string;\n readonly account: string;\n readonly gateway: string;\n}\n\nexport class BundleReadError extends Schema.TaggedError<BundleReadError>()(\"BundleReadError\", {\n path: Schema.String,\n cause: Schema.optional(Schema.Unknown),\n}) {}\n\nconst MIME_TYPES: Record<string, string> = {\n \".js\": \"text/javascript\",\n \".mjs\": \"text/javascript\",\n \".cjs\": \"text/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".map\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".avif\": \"image/avif\",\n \".ico\": \"image/x-icon\",\n \".html\": \"text/html\",\n \".htm\": \"text/html\",\n \".webmanifest\": \"application/manifest+json\",\n \".md\": \"text/markdown\",\n \".ts\": \"text/plain\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".otf\": \"font/otf\",\n \".eot\": \"application/vnd.ms-fontobject\",\n \".wasm\": \"application/wasm\",\n \".txt\": \"text/plain\",\n \".xml\": \"application/xml\",\n};\n\n/**\n * Own-namespace path for a bundle URL: the shared URL→staged-path mapping,\n * narrowed to the runtime identity — a URL whose encoded namespace differs\n * from `namespace.account`/`namespace.gateway` must fall to the network/cache\n * path, never serve a sibling namespace's staged bytes.\n */\nexport function bundleUrlToLocalPath(url: string, namespace: BundleNamespace): string | null {\n const filePath = bundleUrlToStagedPath(url, namespace.bundleDir);\n if (!filePath) return null;\n\n const nsDir = path.resolve(namespace.bundleDir, namespace.account, namespace.gateway);\n if (filePath !== nsDir && !filePath.startsWith(nsDir + path.sep)) return null;\n\n return filePath;\n}\n\nexport class BundleResolver extends Context.Service<\n BundleResolver,\n {\n /** Resolve a fetch target from the staged namespace; null = miss or not our namespace — fall through to the network. */\n readonly lookup: (input: string | URL | Request) => Effect.Effect<Response | null>;\n }\n>()(\"everything-dev/bundle-fs-resolve/BundleResolver\") {\n static layer(namespace: BundleNamespace): Layer.Layer<BundleResolver> {\n const respond = Effect.fn(\"BundleResolver.respond\")(function* (\n url: string,\n ): Effect.fn.Return<Response | null, never> {\n const filePath = bundleUrlToLocalPath(url, namespace);\n if (!filePath) return null;\n const bytes = yield* Effect.tryPromise({\n try: () => readFile(filePath),\n catch: (cause) => new BundleReadError({ path: filePath, cause }),\n }).pipe(Effect.catchTag(\"BundleReadError\", () => Effect.succeed(null)));\n if (bytes === null) return null;\n const name = path.basename(filePath);\n const contentType =\n MIME_TYPES[path.extname(name).toLowerCase()] ?? \"application/octet-stream\";\n return new Response(new Uint8Array(bytes), {\n headers: {\n \"content-type\": contentType,\n \"cache-control\": cacheControlOf(name),\n etag: `\"${Buffer.from(bytes.subarray(0, 4096)).toString(\"base64\").slice(0, 24)}\"`,\n },\n });\n });\n\n return Layer.effect(\n BundleResolver,\n Effect.succeed(\n BundleResolver.of({\n lookup: (input) => {\n const url =\n typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n return respond(url);\n },\n }),\n ),\n );\n }\n}\n\nexport interface BundleFetchHandle {\n readonly uninstall: () => Promise<void>;\n}\n\nexport interface BundleFetchOptions {\n /** Own-namespace FS resolution — requires a staged `BOS_BUNDLE_DIR`. */\n namespace?: BundleNamespace;\n /**\n * Foreign-namespace stale-if-error cache (child tier): `/bundles/…` URLs\n * outside the own namespace write through to disk on success and serve\n * the last-known-good bytes when the origin fails. Enabled implicitly\n * when `namespace` is present (deployment contexts), or explicitly via\n * `BOS_BUNDLE_CACHE_DIR`.\n */\n cacheDir?: string;\n}\n\ntype FetchImpl = typeof globalThis.fetch;\n\nconst staleResponse = (bytes: Uint8Array, url: string): Response => {\n const name = url.split(\"?\")[0].split(\"/\").pop() ?? \"\";\n const contentType = MIME_TYPES[path.extname(name).toLowerCase()] ?? \"application/octet-stream\";\n return new Response(bytes.slice().buffer, {\n status: 200,\n headers: { \"content-type\": contentType, \"x-bundle-cache\": \"stale\" },\n });\n};\n\n/**\n * Installs the resolver as a global fetch interceptor — the one seam every\n * boot-time consumer shares (config manifest discovery, contract-type\n * fetches, orchestrator host loading, MF remoteEntry/identity probes).\n * One layered resolution policy: staged disk → network → stale-if-error.\n * Own-namespace URLs resolve from the staged directory and fall through to\n * the original fetch on a miss (a partially-staged namespace boots over the\n * wire instead of failing); a miss never touches the write-through cache —\n * for the own namespace the staged disk IS the resilience artifact. Foreign\n * `/bundles/…` URLs get the network with the stale-if-error cache; every\n * other URL falls through to the original fetch untouched. Inert when\n * neither a staged namespace nor a cache is configured.\n */\nexport function installGlobalBundleFetch(options: BundleFetchOptions): BundleFetchHandle {\n const namespace = options.namespace;\n const cacheDir = options.cacheDir;\n const runtime = namespace ? ManagedRuntime.make(BundleResolver.layer(namespace)) : null;\n const original: FetchImpl = globalThis.fetch;\n const patched = (async (input: Parameters<FetchImpl>[0], init?: Parameters<FetchImpl>[1]) => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n if (namespace && bundleUrlToLocalPath(url, namespace)) {\n if (!runtime) throw new Error(\"unreachable: resolver runtime missing\");\n const response = await runtime.runPromise(\n Effect.gen(function* () {\n const resolver = yield* BundleResolver;\n return yield* resolver.lookup(input);\n }),\n );\n if (response !== null) return response;\n return original(input, init);\n }\n\n if (cacheDir !== undefined && bundleCachePath(url, { cacheDir })) {\n return (async () => {\n try {\n const response = await original(input, init);\n if (response.ok) {\n try {\n const bytes = new Uint8Array(await response.clone().arrayBuffer());\n await writeBundleCache(url, bytes, { cacheDir });\n } catch {\n // write-through is best-effort\n }\n } else {\n const cached = await readBundleCache(url, { cacheDir });\n if (cached) return staleResponse(cached, url);\n }\n return response;\n } catch (error) {\n const cached = await readBundleCache(url, { cacheDir });\n if (cached) return staleResponse(cached, url);\n throw error;\n }\n })();\n }\n\n return original(input, init);\n }) as FetchImpl;\n const originalPreconnect = (original as Partial<FetchImpl>).preconnect;\n if (originalPreconnect) {\n patched.preconnect = originalPreconnect.bind(original);\n }\n globalThis.fetch = patched;\n return {\n uninstall: async () => {\n if (globalThis.fetch === patched) {\n globalThis.fetch = original;\n }\n if (runtime) await runtime.dispose();\n },\n };\n}\n\n/**\n * Boot-time installer for the CLI: derives the own namespace from\n * `BOS_BUNDLE_DIR` plus the runtime identity (`BOS_ACCOUNT`/`BOS_GATEWAY`\n * registry env taking precedence over the bos.config.json fields), and the\n * foreign-namespace cache from `BOS_BUNDLE_CACHE_DIR`. Tier auto-detection\n * (ADR 0021): an identity whose namespace is not actually staged under the\n * bundle dir drops to the registry tier — plain network fetch — instead of\n * deterministically 404ing own-namespace URLs against a foreign image's\n * staged bytes. Never throws — a missing or malformed identity leaves the\n * default network fetch in place.\n */\nexport function installBundleFetchFromEnv(input: {\n configPath?: string | null;\n}): BundleFetchHandle | null {\n const bundleDir = process.env.BOS_BUNDLE_DIR;\n const cacheDir = process.env.BOS_BUNDLE_CACHE_DIR;\n if (!bundleDir && cacheDir === undefined) return null;\n\n let configAccount: string | undefined;\n let configGateway: string | undefined;\n const configPath = input.configPath;\n if (configPath && !isAppDescriptorPath(configPath)) {\n try {\n const parsed = JSON.parse(readFileSync(configPath, \"utf8\")) as {\n account?: string;\n domain?: string;\n };\n configAccount = parsed.account;\n configGateway = parsed.domain;\n } catch {\n // unreadable config — identity may still come from the registry env\n }\n }\n\n const account = process.env.BOS_ACCOUNT ?? configAccount;\n const gateway = process.env.BOS_GATEWAY ?? configGateway;\n if (bundleDir && (!account || !gateway)) return null;\n\n let namespace: BundleNamespace | undefined;\n if (bundleDir && account && gateway) {\n if (existsSync(path.join(bundleDir, account, gateway))) {\n namespace = { bundleDir, account, gateway };\n } else {\n console.warn(\n `[bundles] staged namespace ${account}/${gateway} not found under ${bundleDir} — registry tier (network fetch)`,\n );\n }\n }\n return installGlobalBundleFetch({\n namespace,\n cacheDir: cacheDir ?? (namespace ? bundleCacheRoot() : undefined),\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
|
+
let node_path = require("node:path");
|
|
3
|
+
node_path = require_runtime.__toESM(node_path, 1);
|
|
4
|
+
|
|
5
|
+
//#region src/bundle-path.ts
|
|
6
|
+
/**
|
|
7
|
+
* Maps a bundle URL onto the staged namespace layout. Bundle URLs carry their
|
|
8
|
+
* namespace in the path (`…/bundles/<account>/<gateway>/<workspace>/…`); the
|
|
9
|
+
* staged layout mirrors it under `<bundleDir>/<account>/<gateway>/…`. The
|
|
10
|
+
* namespace is taken from the URL itself — unlike the runtime-identity
|
|
11
|
+
* resolution, this answers "which staged directory do these bytes live in?"
|
|
12
|
+
* rather than "is this my namespace?" — and the result is contained within
|
|
13
|
+
* the account/gateway directory. Returns null for foreign shapes.
|
|
14
|
+
*/
|
|
15
|
+
function bundleUrlToStagedPath(url, bundleDir) {
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = new URL(url);
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
23
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
24
|
+
if (segments[0] !== "bundles" || segments.length < 3) return null;
|
|
25
|
+
let decoded;
|
|
26
|
+
try {
|
|
27
|
+
decoded = segments.slice(1).map((segment) => decodeURIComponent(segment));
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const [account, gateway, ...rest] = decoded;
|
|
32
|
+
if (!account || !gateway || rest.length === 0) return null;
|
|
33
|
+
const nsDir = node_path.default.resolve(bundleDir, account, gateway);
|
|
34
|
+
const filePath = node_path.default.resolve(nsDir, ...rest);
|
|
35
|
+
if (filePath === nsDir || !filePath.startsWith(nsDir + node_path.default.sep)) return null;
|
|
36
|
+
return filePath;
|
|
37
|
+
}
|
|
38
|
+
/** Resolve a relative config-supplied path inside `dir`, or null on escape. */
|
|
39
|
+
function resolveContained(dir, relative) {
|
|
40
|
+
const resolved = node_path.default.resolve(dir, ...relative.split("/").filter(Boolean));
|
|
41
|
+
if (resolved === dir || !resolved.startsWith(dir + node_path.default.sep)) return null;
|
|
42
|
+
return resolved;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
exports.bundleUrlToStagedPath = bundleUrlToStagedPath;
|
|
47
|
+
exports.resolveContained = resolveContained;
|
|
48
|
+
//# sourceMappingURL=bundle-path.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle-path.cjs","names":["path"],"sources":["../src/bundle-path.ts"],"sourcesContent":["import path from \"node:path\";\n\n/**\n * Maps a bundle URL onto the staged namespace layout. Bundle URLs carry their\n * namespace in the path (`…/bundles/<account>/<gateway>/<workspace>/…`); the\n * staged layout mirrors it under `<bundleDir>/<account>/<gateway>/…`. The\n * namespace is taken from the URL itself — unlike the runtime-identity\n * resolution, this answers \"which staged directory do these bytes live in?\"\n * rather than \"is this my namespace?\" — and the result is contained within\n * the account/gateway directory. Returns null for foreign shapes.\n */\nexport function bundleUrlToStagedPath(url: string, bundleDir: string): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") return null;\n\n const segments = parsed.pathname.split(\"/\").filter(Boolean);\n if (segments[0] !== \"bundles\" || segments.length < 3) return null;\n\n let decoded: string[];\n try {\n decoded = segments.slice(1).map((segment) => decodeURIComponent(segment));\n } catch {\n return null;\n }\n const [account, gateway, ...rest] = decoded;\n if (!account || !gateway || rest.length === 0) return null;\n\n const nsDir = path.resolve(bundleDir, account, gateway);\n const filePath = path.resolve(nsDir, ...rest);\n if (filePath === nsDir || !filePath.startsWith(nsDir + path.sep)) return null;\n return filePath;\n}\n\n/** Resolve a relative config-supplied path inside `dir`, or null on escape. */\nexport function resolveContained(dir: string, relative: string): string | null {\n const resolved = path.resolve(dir, ...relative.split(\"/\").filter(Boolean));\n if (resolved === dir || !resolved.startsWith(dir + path.sep)) return null;\n return resolved;\n}\n"],"mappings":";;;;;;;;;;;;;;AAWA,SAAgB,sBAAsB,KAAa,WAAkC;CACnF,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG;CACtB,QAAQ;EACN,OAAO;CACT;CACA,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAS,OAAO;CAExE,MAAM,WAAW,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO;CAC1D,IAAI,SAAS,OAAO,aAAa,SAAS,SAAS,GAAG,OAAO;CAE7D,IAAI;CACJ,IAAI;EACF,UAAU,SAAS,MAAM,CAAC,CAAC,CAAC,KAAK,YAAY,mBAAmB,OAAO,CAAC;CAC1E,QAAQ;EACN,OAAO;CACT;CACA,MAAM,CAAC,SAAS,SAAS,GAAG,QAAQ;CACpC,IAAI,CAAC,WAAW,CAAC,WAAW,KAAK,WAAW,GAAG,OAAO;CAEtD,MAAM,QAAQA,kBAAK,QAAQ,WAAW,SAAS,OAAO;CACtD,MAAM,WAAWA,kBAAK,QAAQ,OAAO,GAAG,IAAI;CAC5C,IAAI,aAAa,SAAS,CAAC,SAAS,WAAW,QAAQA,kBAAK,GAAG,GAAG,OAAO;CACzE,OAAO;AACT;;AAGA,SAAgB,iBAAiB,KAAa,UAAiC;CAC7E,MAAM,WAAWA,kBAAK,QAAQ,KAAK,GAAG,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC;CACzE,IAAI,aAAa,OAAO,CAAC,SAAS,WAAW,MAAMA,kBAAK,GAAG,GAAG,OAAO;CACrE,OAAO;AACT"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
//#region src/bundle-path.ts
|
|
4
|
+
/**
|
|
5
|
+
* Maps a bundle URL onto the staged namespace layout. Bundle URLs carry their
|
|
6
|
+
* namespace in the path (`…/bundles/<account>/<gateway>/<workspace>/…`); the
|
|
7
|
+
* staged layout mirrors it under `<bundleDir>/<account>/<gateway>/…`. The
|
|
8
|
+
* namespace is taken from the URL itself — unlike the runtime-identity
|
|
9
|
+
* resolution, this answers "which staged directory do these bytes live in?"
|
|
10
|
+
* rather than "is this my namespace?" — and the result is contained within
|
|
11
|
+
* the account/gateway directory. Returns null for foreign shapes.
|
|
12
|
+
*/
|
|
13
|
+
function bundleUrlToStagedPath(url, bundleDir) {
|
|
14
|
+
let parsed;
|
|
15
|
+
try {
|
|
16
|
+
parsed = new URL(url);
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
21
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
22
|
+
if (segments[0] !== "bundles" || segments.length < 3) return null;
|
|
23
|
+
let decoded;
|
|
24
|
+
try {
|
|
25
|
+
decoded = segments.slice(1).map((segment) => decodeURIComponent(segment));
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const [account, gateway, ...rest] = decoded;
|
|
30
|
+
if (!account || !gateway || rest.length === 0) return null;
|
|
31
|
+
const nsDir = path.resolve(bundleDir, account, gateway);
|
|
32
|
+
const filePath = path.resolve(nsDir, ...rest);
|
|
33
|
+
if (filePath === nsDir || !filePath.startsWith(nsDir + path.sep)) return null;
|
|
34
|
+
return filePath;
|
|
35
|
+
}
|
|
36
|
+
/** Resolve a relative config-supplied path inside `dir`, or null on escape. */
|
|
37
|
+
function resolveContained(dir, relative) {
|
|
38
|
+
const resolved = path.resolve(dir, ...relative.split("/").filter(Boolean));
|
|
39
|
+
if (resolved === dir || !resolved.startsWith(dir + path.sep)) return null;
|
|
40
|
+
return resolved;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { bundleUrlToStagedPath, resolveContained };
|
|
45
|
+
//# sourceMappingURL=bundle-path.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle-path.mjs","names":[],"sources":["../src/bundle-path.ts"],"sourcesContent":["import path from \"node:path\";\n\n/**\n * Maps a bundle URL onto the staged namespace layout. Bundle URLs carry their\n * namespace in the path (`…/bundles/<account>/<gateway>/<workspace>/…`); the\n * staged layout mirrors it under `<bundleDir>/<account>/<gateway>/…`. The\n * namespace is taken from the URL itself — unlike the runtime-identity\n * resolution, this answers \"which staged directory do these bytes live in?\"\n * rather than \"is this my namespace?\" — and the result is contained within\n * the account/gateway directory. Returns null for foreign shapes.\n */\nexport function bundleUrlToStagedPath(url: string, bundleDir: string): string | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") return null;\n\n const segments = parsed.pathname.split(\"/\").filter(Boolean);\n if (segments[0] !== \"bundles\" || segments.length < 3) return null;\n\n let decoded: string[];\n try {\n decoded = segments.slice(1).map((segment) => decodeURIComponent(segment));\n } catch {\n return null;\n }\n const [account, gateway, ...rest] = decoded;\n if (!account || !gateway || rest.length === 0) return null;\n\n const nsDir = path.resolve(bundleDir, account, gateway);\n const filePath = path.resolve(nsDir, ...rest);\n if (filePath === nsDir || !filePath.startsWith(nsDir + path.sep)) return null;\n return filePath;\n}\n\n/** Resolve a relative config-supplied path inside `dir`, or null on escape. */\nexport function resolveContained(dir: string, relative: string): string | null {\n const resolved = path.resolve(dir, ...relative.split(\"/\").filter(Boolean));\n if (resolved === dir || !resolved.startsWith(dir + path.sep)) return null;\n return resolved;\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAgB,sBAAsB,KAAa,WAAkC;CACnF,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG;CACtB,QAAQ;EACN,OAAO;CACT;CACA,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAS,OAAO;CAExE,MAAM,WAAW,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO;CAC1D,IAAI,SAAS,OAAO,aAAa,SAAS,SAAS,GAAG,OAAO;CAE7D,IAAI;CACJ,IAAI;EACF,UAAU,SAAS,MAAM,CAAC,CAAC,CAAC,KAAK,YAAY,mBAAmB,OAAO,CAAC;CAC1E,QAAQ;EACN,OAAO;CACT;CACA,MAAM,CAAC,SAAS,SAAS,GAAG,QAAQ;CACpC,IAAI,CAAC,WAAW,CAAC,WAAW,KAAK,WAAW,GAAG,OAAO;CAEtD,MAAM,QAAQ,KAAK,QAAQ,WAAW,SAAS,OAAO;CACtD,MAAM,WAAW,KAAK,QAAQ,OAAO,GAAG,IAAI;CAC5C,IAAI,aAAa,SAAS,CAAC,SAAS,WAAW,QAAQ,KAAK,GAAG,GAAG,OAAO;CACzE,OAAO;AACT;;AAGA,SAAgB,iBAAiB,KAAa,UAAiC;CAC7E,MAAM,WAAW,KAAK,QAAQ,KAAK,GAAG,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CAAC;CACzE,IAAI,aAAa,OAAO,CAAC,SAAS,WAAW,MAAM,KAAK,GAAG,GAAG,OAAO;CACrE,OAAO;AACT"}
|
package/dist/cli/init.cjs
CHANGED
|
@@ -14,9 +14,9 @@ const require_docker = require('../infra/docker.cjs');
|
|
|
14
14
|
const require_resolution_session = require('../resolution/session.cjs');
|
|
15
15
|
let node_fs = require("node:fs");
|
|
16
16
|
let node_path = require("node:path");
|
|
17
|
+
let execa = require("execa");
|
|
17
18
|
let node_os = require("node:os");
|
|
18
19
|
let node_stream_promises = require("node:stream/promises");
|
|
19
|
-
let execa = require("execa");
|
|
20
20
|
let glob = require("glob");
|
|
21
21
|
let tar = require("tar");
|
|
22
22
|
let yaml = require("yaml");
|
|
@@ -58,7 +58,7 @@ function writeChildWorkspaceYaml(projectDir, next) {
|
|
|
58
58
|
doc.packages = packages;
|
|
59
59
|
changed = true;
|
|
60
60
|
}
|
|
61
|
-
const catalog = { ...doc.catalog
|
|
61
|
+
const catalog = { ...doc.catalog };
|
|
62
62
|
for (const [name, version] of Object.entries(next.catalog)) if (catalog[name] !== version) {
|
|
63
63
|
catalog[name] = version;
|
|
64
64
|
changed = true;
|
|
@@ -77,7 +77,7 @@ function writeChildWorkspaceYaml(projectDir, next) {
|
|
|
77
77
|
doc.ignoreScripts = true;
|
|
78
78
|
changed = true;
|
|
79
79
|
}
|
|
80
|
-
const overrides = { ...doc.overrides
|
|
80
|
+
const overrides = { ...doc.overrides };
|
|
81
81
|
for (const key of CHILD_OVERRIDE_KEYS) if (catalog[key] && overrides[key] !== catalog[key]) {
|
|
82
82
|
overrides[key] = catalog[key];
|
|
83
83
|
changed = true;
|
|
@@ -261,7 +261,7 @@ const STARTER_SIMPLE_EXCLUSIONS = [
|
|
|
261
261
|
* `ui/src/routes/` so they compose with `copyFilteredFiles`'s ignore list.
|
|
262
262
|
*/
|
|
263
263
|
function buildStarterRouteExclusions(level, parentConfig) {
|
|
264
|
-
const excluded =
|
|
264
|
+
const excluded = new Set(STARTER_PRODUCT_EXCLUSIONS);
|
|
265
265
|
if (level === "simple") for (const entry of STARTER_SIMPLE_EXCLUSIONS) excluded.add(entry);
|
|
266
266
|
const starter = parentConfig?.starter;
|
|
267
267
|
if (starter) {
|
|
@@ -1131,8 +1131,8 @@ You don't need to wait for a PR to merge and run through CI/CD. Publish your own
|
|
|
1131
1131
|
# Output: NEAR_PRIVATE_KEY=ed25519:...
|
|
1132
1132
|
\`\`\`
|
|
1133
1133
|
Add the key to your account via near-cli-rs, then set \`NEAR_PRIVATE_KEY\` in \`.env\` or CI secrets.
|
|
1134
|
-
4. **Update \`bos.
|
|
1135
|
-
\`\`\`
|
|
1134
|
+
4. **Update \`bos.app.ts\`** — set \`account\` to your NEAR account, add \`extends\` to inherit the base platform, keep \`domain\` as the gateway:
|
|
1135
|
+
\`\`\`ts
|
|
1136
1136
|
{ "extends": "bos://<parent-account>/<parent-gateway>", "account": "<your-account>.near", "domain": "<parent-gateway>" }
|
|
1137
1137
|
\`\`\`
|
|
1138
1138
|
5. **Publish and deploy:**
|
|
@@ -1202,9 +1202,9 @@ The API plugin receives typed client factories for all other plugins via \`creat
|
|
|
1202
1202
|
|
|
1203
1203
|
### Generated Types
|
|
1204
1204
|
|
|
1205
|
-
\`api/src/lib/plugins-types.gen.ts\`, \`api/src/lib/auth-types.gen.ts\`, \`ui/src/lib/api-types.gen.ts\`, and \`ui/src/lib/auth-types.gen.ts\` are generated by \`bos types gen\` from \`bos.
|
|
1205
|
+
\`api/src/lib/plugins-types.gen.ts\`, \`api/src/lib/auth-types.gen.ts\`, \`ui/src/lib/api-types.gen.ts\`, and \`ui/src/lib/auth-types.gen.ts\` are generated by \`bos types gen\` from \`bos.app.ts\`. These files are gitignored and auto-regenerated on \`pnpm install\`, \`typecheck\`, \`bos dev\`, \`bos build\`, and bos plugin management commands.
|
|
1206
1206
|
|
|
1207
|
-
If you hand-edit \`bos.
|
|
1207
|
+
If you hand-edit \`bos.app.ts\`, run \`bos types gen\` or restart \`bos dev\` to regenerate.`);
|
|
1208
1208
|
const testCommands = [];
|
|
1209
1209
|
if (has("api") || has("host") || has("ui")) {
|
|
1210
1210
|
testCommands.push("pnpm run test # Run all tests");
|
|
@@ -1273,7 +1273,7 @@ The host exposes several surfaces for programmatic agent access:
|
|
|
1273
1273
|
|
|
1274
1274
|
### Architecture note: remotes are code bundles
|
|
1275
1275
|
|
|
1276
|
-
Remotes in \`bos.
|
|
1276
|
+
Remotes in \`bos.app.ts\` are **not hosted APIs** — they are code bundles loaded via Module Federation at runtime. The UI, API, auth, and plugins all run in the same host process. There is no remote server to call; everything is loaded in-process through Module Federation and \`every-plugin\`.`);
|
|
1277
1277
|
parts.push(`## Troubleshooting
|
|
1278
1278
|
|
|
1279
1279
|
**Process won't start:**
|
|
@@ -1284,7 +1284,7 @@ pnpm run dev # Restart
|
|
|
1284
1284
|
\`\`\`
|
|
1285
1285
|
|
|
1286
1286
|
**Module Federation errors:**
|
|
1287
|
-
- Check \`bos.
|
|
1287
|
+
- Check \`bos.app.ts\` URLs are accessible
|
|
1288
1288
|
- Verify shared dependency versions match in package.json
|
|
1289
1289
|
- Clear browser cache
|
|
1290
1290
|
|
|
@@ -1298,7 +1298,7 @@ pnpm run db:studio # Open Drizzle Studio
|
|
|
1298
1298
|
|
|
1299
1299
|
**Required files:**
|
|
1300
1300
|
- \`.env\` — Secrets (see \`.env.example\`)
|
|
1301
|
-
- \`bos.
|
|
1301
|
+
- \`bos.app.ts\` — Runtime configuration (committed)`);
|
|
1302
1302
|
return `${parts.join("\n\n")}\n`;
|
|
1303
1303
|
}
|
|
1304
1304
|
function buildChildAgentsMd(skillsBlock, opts) {
|
|
@@ -1370,7 +1370,7 @@ The \`x-api-key\` header works for \`/api/*\` (REST), \`/api/rpc/*\` (oRPC), and
|
|
|
1370
1370
|
function buildChildSkillMd(title, repository) {
|
|
1371
1371
|
return `# ${title} skill
|
|
1372
1372
|
|
|
1373
|
-
Use this when you want an agent to run, edit, and publish **${title}** — an everything.dev app composed at runtime from \`bos.
|
|
1373
|
+
Use this when you want an agent to run, edit, and publish **${title}** — an everything.dev app composed at runtime from \`bos.app.ts\`.
|
|
1374
1374
|
|
|
1375
1375
|
There are two ways to work with this app:
|
|
1376
1376
|
|
|
@@ -1420,7 +1420,7 @@ The repo ships agent workflow skills in \`.agents/skills/\` — start with \`eve
|
|
|
1420
1420
|
|
|
1421
1421
|
### Architecture note
|
|
1422
1422
|
|
|
1423
|
-
Remotes in \`bos.
|
|
1423
|
+
Remotes in \`bos.app.ts\` are **not hosted APIs** — they are code bundles loaded via Module Federation at runtime. Everything runs in the same host process.
|
|
1424
1424
|
|
|
1425
1425
|
### Run locally
|
|
1426
1426
|
|