everything-dev 2.0.0-rc.7 → 2.0.0-rc.9
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/api/auth-middleware.cjs +14 -1
- package/dist/api/auth-middleware.cjs.map +1 -1
- package/dist/api/auth-middleware.d.cts +9 -0
- package/dist/api/auth-middleware.d.mts +9 -0
- package/dist/api/auth-middleware.mjs +14 -1
- package/dist/api/auth-middleware.mjs.map +1 -1
- package/dist/apps/upload/src/client.cjs +7 -1
- package/dist/apps/upload/src/client.cjs.map +1 -1
- package/dist/apps/upload/src/client.mjs +7 -1
- package/dist/apps/upload/src/client.mjs.map +1 -1
- package/dist/auth-login.cjs +24 -1
- package/dist/auth-login.cjs.map +1 -1
- package/dist/auth-login.mjs +24 -1
- package/dist/auth-login.mjs.map +1 -1
- package/dist/auth-session.cjs +4 -2
- package/dist/auth-session.cjs.map +1 -1
- package/dist/auth-session.mjs +4 -2
- package/dist/auth-session.mjs.map +1 -1
- package/dist/cdn-deploy.cjs +11 -7
- package/dist/cdn-deploy.cjs.map +1 -1
- package/dist/cdn-deploy.mjs +11 -7
- package/dist/cdn-deploy.mjs.map +1 -1
- package/dist/cli/import.cjs +1140 -0
- package/dist/cli/import.cjs.map +1 -0
- package/dist/cli/import.mjs +1126 -0
- package/dist/cli/import.mjs.map +1 -0
- package/dist/cli/init.cjs +33 -14
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +8 -2
- package/dist/cli/init.d.mts +8 -2
- package/dist/cli/init.mjs +33 -15
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli.cjs +59 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +59 -3
- package/dist/cli.mjs.map +1 -1
- package/dist/commands/import.cjs +49 -0
- package/dist/commands/import.cjs.map +1 -0
- package/dist/commands/import.mjs +46 -0
- package/dist/commands/import.mjs.map +1 -0
- package/dist/commands/keys.cjs +25 -4
- package/dist/commands/keys.cjs.map +1 -1
- package/dist/commands/keys.mjs +25 -4
- package/dist/commands/keys.mjs.map +1 -1
- package/dist/config.d.cts +1 -0
- package/dist/config.d.mts +1 -0
- package/dist/contract.cjs +89 -0
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +190 -1
- package/dist/contract.d.mts +190 -1
- package/dist/contract.meta.cjs +17 -1
- package/dist/contract.meta.cjs.map +1 -1
- package/dist/contract.meta.d.cts +27 -1
- package/dist/contract.meta.d.mts +27 -1
- package/dist/contract.meta.mjs +17 -1
- package/dist/contract.meta.mjs.map +1 -1
- package/dist/contract.mjs +84 -1
- package/dist/contract.mjs.map +1 -1
- package/dist/custody-signer.cjs +70 -0
- package/dist/custody-signer.cjs.map +1 -0
- package/dist/custody-signer.mjs +68 -0
- package/dist/custody-signer.mjs.map +1 -0
- package/dist/db/core.cjs +22 -4
- package/dist/db/core.cjs.map +1 -1
- package/dist/db/core.mjs +22 -4
- package/dist/db/core.mjs.map +1 -1
- package/dist/descriptor/schema.cjs +8 -5
- package/dist/descriptor/schema.cjs.map +1 -1
- package/dist/descriptor/schema.d.cts +1 -0
- package/dist/descriptor/schema.d.mts +1 -0
- package/dist/descriptor/schema.mjs +8 -5
- package/dist/descriptor/schema.mjs.map +1 -1
- package/dist/descriptor/serialize.cjs +1 -1
- package/dist/descriptor/serialize.cjs.map +1 -1
- package/dist/descriptor/serialize.mjs +1 -1
- package/dist/descriptor/serialize.mjs.map +1 -1
- package/dist/http-client.cjs +14 -4
- package/dist/http-client.cjs.map +1 -1
- package/dist/http-client.mjs +14 -4
- package/dist/http-client.mjs.map +1 -1
- package/dist/index.cjs +6 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/infra/planner.cjs +6 -2
- package/dist/infra/planner.cjs.map +1 -1
- package/dist/infra/planner.mjs +6 -2
- package/dist/infra/planner.mjs.map +1 -1
- package/dist/outlayer-login.cjs +54 -0
- package/dist/outlayer-login.cjs.map +1 -0
- package/dist/outlayer-login.mjs +54 -0
- package/dist/outlayer-login.mjs.map +1 -0
- package/dist/plugin.cjs +2 -0
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +74 -0
- package/dist/plugin.d.mts +74 -0
- package/dist/plugin.mjs +2 -0
- package/dist/plugin.mjs.map +1 -1
- package/dist/publish.cjs +145 -22
- package/dist/publish.cjs.map +1 -1
- package/dist/publish.mjs +147 -24
- package/dist/publish.mjs.map +1 -1
- package/dist/source-archive.cjs +51 -0
- package/dist/source-archive.cjs.map +1 -0
- package/dist/source-archive.mjs +50 -0
- package/dist/source-archive.mjs.map +1 -0
- package/dist/storage-upload.cjs +6 -1
- package/dist/storage-upload.cjs.map +1 -1
- package/dist/storage-upload.mjs +6 -1
- package/dist/storage-upload.mjs.map +1 -1
- package/dist/types.cjs +5 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +2 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +5 -1
- package/dist/types.mjs.map +1 -1
- package/dist/ui/auth.d.cts +217 -123
- package/dist/ui/auth.d.mts +217 -123
- package/dist/ui/router-client.cjs +2 -1
- package/dist/ui/router-client.cjs.map +1 -1
- package/dist/ui/router-client.mjs +2 -1
- package/dist/ui/router-client.mjs.map +1 -1
- package/dist/utils/workspace-subtree.cjs +37 -0
- package/dist/utils/workspace-subtree.cjs.map +1 -0
- package/dist/utils/workspace-subtree.mjs +36 -0
- package/dist/utils/workspace-subtree.mjs.map +1 -0
- package/dist/version-manifest-deploy.cjs +4 -2
- package/dist/version-manifest-deploy.cjs.map +1 -1
- package/dist/version-manifest-deploy.mjs +4 -2
- package/dist/version-manifest-deploy.mjs.map +1 -1
- package/dist/version-manifest-resolve.cjs +2 -1
- package/dist/version-manifest-resolve.cjs.map +1 -1
- package/dist/version-manifest-resolve.d.cts +3 -0
- package/dist/version-manifest-resolve.d.mts +3 -0
- package/dist/version-manifest-resolve.mjs +2 -1
- package/dist/version-manifest-resolve.mjs.map +1 -1
- package/package.json +11 -10
- package/skills/api-and-auth/SKILL.md +12 -9
- package/skills/api-and-auth/references/middleware.md +38 -0
- package/skills/cli-reference/SKILL.md +38 -6
- package/skills/init-upgrade/SKILL.md +76 -3
- package/skills/talk-to-the-app/SKILL.md +32 -5
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { OutlayerClient, OutlayerError, PolicyDeniedError, WalletFrozenError } from "@outlayer/sdk";
|
|
2
|
+
|
|
3
|
+
//#region src/custody-signer.ts
|
|
4
|
+
const PUBLISH_GAS = "30000000000000";
|
|
5
|
+
const MIN_GAS_BALANCE = "50000000000000000000000";
|
|
6
|
+
const OUTLAYER_HOSTS = {
|
|
7
|
+
mainnet: "https://api.outlayer.ai",
|
|
8
|
+
testnet: "https://testnet-api.outlayer.ai"
|
|
9
|
+
};
|
|
10
|
+
function custodyClient(custodyWallet) {
|
|
11
|
+
const network = custodyWallet.network;
|
|
12
|
+
return new OutlayerClient({
|
|
13
|
+
apiKey: custodyWallet.apiKey,
|
|
14
|
+
network,
|
|
15
|
+
baseUrl: OUTLAYER_HOSTS[network]
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function custodyFundUrl(custodyWallet) {
|
|
19
|
+
return `https://app.outlayer.ai/wallet/fund?to=${custodyWallet.nearAccountId}&amount=0.05&token=near`;
|
|
20
|
+
}
|
|
21
|
+
async function ensureCustodyBalance(custodyWallet) {
|
|
22
|
+
const client = custodyClient(custodyWallet);
|
|
23
|
+
try {
|
|
24
|
+
const { balance } = await client.getBalance({ chain: "near" });
|
|
25
|
+
if (BigInt(balance) < BigInt(MIN_GAS_BALANCE)) throw new Error(`The custody wallet ${custodyWallet.nearAccountId} holds ${balance} yoctoNEAR on ${custodyWallet.network}, below the gas minimum. Fund it at ${custodyFundUrl(custodyWallet)}`);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
throw custodyPublishError(error, custodyWallet);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function submitRegistryWriteCustody(opts) {
|
|
31
|
+
const client = custodyClient(opts.custodyWallet);
|
|
32
|
+
try {
|
|
33
|
+
const result = await client.call({
|
|
34
|
+
receiver_id: opts.contract,
|
|
35
|
+
method_name: "__fastdata_kv",
|
|
36
|
+
args: opts.args,
|
|
37
|
+
gas: PUBLISH_GAS,
|
|
38
|
+
deposit: "0",
|
|
39
|
+
...opts.idempotencyKey ? { idempotencyKey: opts.idempotencyKey } : {}
|
|
40
|
+
});
|
|
41
|
+
if (result.status === "failed" || result.status === "rejected") throw new Error(`The custody publish did not settle (status ${result.status}). Inspect request ${result.request_id} at the OutLayer dashboard.`);
|
|
42
|
+
if (result.status === "pending_approval") throw new Error(`The custody publish needs multisig approval (${result.approved ?? 0}/${result.required ?? "?"} so far). Approve it at the OutLayer dashboard, then re-run the deploy.`);
|
|
43
|
+
return {
|
|
44
|
+
success: true,
|
|
45
|
+
txHash: result.tx_hash ?? void 0
|
|
46
|
+
};
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if ((error instanceof OutlayerError ? error.code : "") === "onchain_tx_failed") return { success: true };
|
|
49
|
+
throw custodyPublishError(error, opts.custodyWallet);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function custodyPublishError(error, custodyWallet) {
|
|
53
|
+
if (!(error instanceof OutlayerError)) return error;
|
|
54
|
+
const code = error.code;
|
|
55
|
+
const context = custodyWallet.handoffUrl ? ` Manage the wallet at ${custodyWallet.handoffUrl}.` : "";
|
|
56
|
+
if (error instanceof PolicyDeniedError) return /* @__PURE__ */ new Error(`The custody wallet policy refused the publish.${context}`);
|
|
57
|
+
if (error instanceof WalletFrozenError) return /* @__PURE__ */ new Error(`The custody wallet is frozen.${context}`);
|
|
58
|
+
if (code === "insufficient_balance" || code === "wallet_underfunded") {
|
|
59
|
+
const detail = error.message ? ` (${error.message})` : "";
|
|
60
|
+
return /* @__PURE__ */ new Error(`The custody wallet ran out of gas for the publish${detail}. Fund it at ${custodyFundUrl(custodyWallet)}`);
|
|
61
|
+
}
|
|
62
|
+
if (code === "invalid_api_key" || code === "missing_auth") return /* @__PURE__ */ new Error(`The stored custody wallet key was rejected. Re-run \`bos login --custody\` to re-register.`);
|
|
63
|
+
return /* @__PURE__ */ new Error(`Custody publish failed (${code}): ${error.message}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
export { custodyFundUrl, ensureCustodyBalance, submitRegistryWriteCustody };
|
|
68
|
+
//# sourceMappingURL=custody-signer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custody-signer.mjs","names":[],"sources":["../src/custody-signer.ts"],"sourcesContent":["import { OutlayerClient, OutlayerError, PolicyDeniedError, WalletFrozenError } from \"@outlayer/sdk\";\nimport type { CustodyWalletRecord } from \"./auth-session\";\nimport type { RelayResult } from \"./delegate-signer\";\nimport type { NetworkId } from \"./fastkv\";\n\nconst PUBLISH_GAS = \"30000000000000\";\nconst MIN_GAS_BALANCE = \"50000000000000000000000\";\n\nconst OUTLAYER_HOSTS: Record<NetworkId, string> = {\n mainnet: \"https://api.outlayer.ai\",\n testnet: \"https://testnet-api.outlayer.ai\",\n};\n\nexport interface CustodySubmitOptions {\n contract: string;\n args: Record<string, string>;\n custodyWallet: CustodyWalletRecord;\n idempotencyKey?: string;\n}\n\nfunction custodyClient(custodyWallet: CustodyWalletRecord): OutlayerClient {\n const network = custodyWallet.network;\n return new OutlayerClient({\n apiKey: custodyWallet.apiKey,\n network,\n baseUrl: OUTLAYER_HOSTS[network],\n });\n}\n\nexport function custodyFundUrl(custodyWallet: CustodyWalletRecord): string {\n return `https://app.outlayer.ai/wallet/fund?to=${custodyWallet.nearAccountId}&amount=0.05&token=near`;\n}\n\nexport async function ensureCustodyBalance(custodyWallet: CustodyWalletRecord): Promise<void> {\n const client = custodyClient(custodyWallet);\n try {\n const { balance } = await client.getBalance({ chain: \"near\" });\n if (BigInt(balance) < BigInt(MIN_GAS_BALANCE)) {\n throw new Error(\n `The custody wallet ${custodyWallet.nearAccountId} holds ${balance} yoctoNEAR on ` +\n `${custodyWallet.network}, below the gas minimum. Fund it at ${custodyFundUrl(custodyWallet)}`,\n );\n }\n } catch (error) {\n throw custodyPublishError(error, custodyWallet);\n }\n}\n\nexport async function submitRegistryWriteCustody(opts: CustodySubmitOptions): Promise<RelayResult> {\n const client = custodyClient(opts.custodyWallet);\n try {\n const result = await client.call({\n receiver_id: opts.contract,\n method_name: \"__fastdata_kv\",\n args: opts.args,\n gas: PUBLISH_GAS,\n deposit: \"0\",\n ...(opts.idempotencyKey ? { idempotencyKey: opts.idempotencyKey } : {}),\n });\n if (result.status === \"failed\" || result.status === \"rejected\") {\n throw new Error(\n `The custody publish did not settle (status ${result.status}). Inspect request ${result.request_id} at the OutLayer dashboard.`,\n );\n }\n if (result.status === \"pending_approval\") {\n throw new Error(\n `The custody publish needs multisig approval (${result.approved ?? 0}/${result.required ?? \"?\"} so far). ` +\n \"Approve it at the OutLayer dashboard, then re-run the deploy.\",\n );\n }\n return { success: true, txHash: result.tx_hash ?? undefined };\n } catch (error) {\n const code: string = error instanceof OutlayerError ? error.code : \"\";\n if (code === \"onchain_tx_failed\") {\n return { success: true };\n }\n throw custodyPublishError(error, opts.custodyWallet);\n }\n}\n\nfunction custodyPublishError(error: unknown, custodyWallet: CustodyWalletRecord): unknown {\n if (!(error instanceof OutlayerError)) return error;\n const code: string = error.code;\n const context = custodyWallet.handoffUrl\n ? ` Manage the wallet at ${custodyWallet.handoffUrl}.`\n : \"\";\n if (error instanceof PolicyDeniedError) {\n return new Error(`The custody wallet policy refused the publish.${context}`);\n }\n if (error instanceof WalletFrozenError) {\n return new Error(`The custody wallet is frozen.${context}`);\n }\n if (code === \"insufficient_balance\" || code === \"wallet_underfunded\") {\n const detail = error.message ? ` (${error.message})` : \"\";\n return new Error(\n `The custody wallet ran out of gas for the publish${detail}. Fund it at ${custodyFundUrl(custodyWallet)}`,\n );\n }\n if (code === \"invalid_api_key\" || code === \"missing_auth\") {\n return new Error(\n `The stored custody wallet key was rejected. Re-run \\`bos login --custody\\` to re-register.`,\n );\n }\n return new Error(`Custody publish failed (${code}): ${error.message}`);\n}\n"],"mappings":";;;AAKA,MAAM,cAAc;AACpB,MAAM,kBAAkB;AAExB,MAAM,iBAA4C;CAChD,SAAS;CACT,SAAS;AACX;AASA,SAAS,cAAc,eAAoD;CACzE,MAAM,UAAU,cAAc;CAC9B,OAAO,IAAI,eAAe;EACxB,QAAQ,cAAc;EACtB;EACA,SAAS,eAAe;CAC1B,CAAC;AACH;AAEA,SAAgB,eAAe,eAA4C;CACzE,OAAO,0CAA0C,cAAc,cAAc;AAC/E;AAEA,eAAsB,qBAAqB,eAAmD;CAC5F,MAAM,SAAS,cAAc,aAAa;CAC1C,IAAI;EACF,MAAM,EAAE,YAAY,MAAM,OAAO,WAAW,EAAE,OAAO,OAAO,CAAC;EAC7D,IAAI,OAAO,OAAO,IAAI,OAAO,eAAe,GAC1C,MAAM,IAAI,MACR,sBAAsB,cAAc,cAAc,SAAS,QAAQ,gBAC9D,cAAc,QAAQ,sCAAsC,eAAe,aAAa,GAC/F;CAEJ,SAAS,OAAO;EACd,MAAM,oBAAoB,OAAO,aAAa;CAChD;AACF;AAEA,eAAsB,2BAA2B,MAAkD;CACjG,MAAM,SAAS,cAAc,KAAK,aAAa;CAC/C,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,KAAK;GAC/B,aAAa,KAAK;GAClB,aAAa;GACb,MAAM,KAAK;GACX,KAAK;GACL,SAAS;GACT,GAAI,KAAK,iBAAiB,EAAE,gBAAgB,KAAK,eAAe,IAAI,CAAC;EACvE,CAAC;EACD,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,YAClD,MAAM,IAAI,MACR,8CAA8C,OAAO,OAAO,qBAAqB,OAAO,WAAW,4BACrG;EAEF,IAAI,OAAO,WAAW,oBACpB,MAAM,IAAI,MACR,gDAAgD,OAAO,YAAY,EAAE,GAAG,OAAO,YAAY,IAAI,wEAEjG;EAEF,OAAO;GAAE,SAAS;GAAM,QAAQ,OAAO,WAAW;EAAU;CAC9D,SAAS,OAAO;EAEd,KADqB,iBAAiB,gBAAgB,MAAM,OAAO,QACtD,qBACX,OAAO,EAAE,SAAS,KAAK;EAEzB,MAAM,oBAAoB,OAAO,KAAK,aAAa;CACrD;AACF;AAEA,SAAS,oBAAoB,OAAgB,eAA6C;CACxF,IAAI,EAAE,iBAAiB,gBAAgB,OAAO;CAC9C,MAAM,OAAe,MAAM;CAC3B,MAAM,UAAU,cAAc,aAC1B,yBAAyB,cAAc,WAAW,KAClD;CACJ,IAAI,iBAAiB,mBACnB,uBAAO,IAAI,MAAM,iDAAiD,SAAS;CAE7E,IAAI,iBAAiB,mBACnB,uBAAO,IAAI,MAAM,gCAAgC,SAAS;CAE5D,IAAI,SAAS,0BAA0B,SAAS,sBAAsB;EACpE,MAAM,SAAS,MAAM,UAAU,KAAK,MAAM,QAAQ,KAAK;EACvD,uBAAO,IAAI,MACT,oDAAoD,OAAO,eAAe,eAAe,aAAa,GACxG;CACF;CACA,IAAI,SAAS,qBAAqB,SAAS,gBACzC,uBAAO,IAAI,MACT,4FACF;CAEF,uBAAO,IAAI,MAAM,2BAA2B,KAAK,KAAK,MAAM,SAAS;AACvE"}
|
package/dist/db/core.cjs
CHANGED
|
@@ -179,10 +179,28 @@ function suppressPgQueryQueueDeprecation() {
|
|
|
179
179
|
}
|
|
180
180
|
function extractExpectedTables(migrations) {
|
|
181
181
|
const tables = /* @__PURE__ */ new Set();
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
const ident = String.raw`(?:"([^"]+)"|(\w+))`;
|
|
183
|
+
const qualified = String.raw`(?:${ident}\.)?`;
|
|
184
|
+
const createRe = new RegExp(String.raw`CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?${qualified}${ident}`, "gi");
|
|
185
|
+
const dropRe = new RegExp(String.raw`DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?${qualified}${ident}`, "gi");
|
|
186
|
+
const renameRe = new RegExp(String.raw`ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?${qualified}${ident}${String.raw`\s+RENAME\s+TO\s+`}${ident}`, "gi");
|
|
187
|
+
for (const migration of migrations) for (const stmt of migration.sql) {
|
|
188
|
+
for (const match of stmt.matchAll(createRe)) {
|
|
189
|
+
const tableName = match[3] ?? match[4];
|
|
190
|
+
if (tableName) tables.add(tableName);
|
|
191
|
+
}
|
|
192
|
+
for (const match of stmt.matchAll(dropRe)) {
|
|
193
|
+
const tableName = match[3] ?? match[4];
|
|
194
|
+
if (tableName) tables.delete(tableName);
|
|
195
|
+
}
|
|
196
|
+
for (const match of stmt.matchAll(renameRe)) {
|
|
197
|
+
const from = match[3] ?? match[4];
|
|
198
|
+
const to = match[5] ?? match[6];
|
|
199
|
+
if (from && to) {
|
|
200
|
+
tables.delete(from);
|
|
201
|
+
tables.add(to);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
186
204
|
}
|
|
187
205
|
return [...tables];
|
|
188
206
|
}
|
package/dist/db/core.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.cjs","names":["join","existsSync","readFileSync","dirname"],"sources":["../../src/db/core.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nexport interface MigrationStorage {\n schema: string;\n table: string;\n slug: string;\n}\n\nconst DEFAULT_MIGRATION_JOURNAL = {\n schema: \"drizzle\",\n table: \"__drizzle_migrations\",\n} as const;\n\nconst PER_PLUGIN_ISOLATION = false;\n\nexport function normalizeSlug(name: string): string {\n const basename = name.split(\"/\").pop() ?? name;\n return basename\n .replace(/^@/, \"\")\n .replace(/[^a-zA-Z0-9_-]/g, \"_\")\n .replace(/-plugin$/i, \"\")\n .replace(/-/g, \"_\")\n .toLowerCase();\n}\n\nexport function getMigrationSlug(dir?: string): string {\n if (!dir) return normalizeSlug(process.env.npm_package_name ?? \"unknown\");\n let current = dir;\n for (let i = 0; i < 10; i++) {\n const pkgPath = join(current, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as { name?: string };\n return normalizeSlug(pkg.name ?? current);\n } catch {\n return normalizeSlug(current);\n }\n }\n const parent = dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return normalizeSlug(dir);\n}\n\nexport function getMigrationStorage(\n slug?: string,\n options?: { isolated?: boolean },\n): MigrationStorage {\n const s = normalizeSlug(slug ?? getMigrationSlug());\n const isolated = options?.isolated ?? PER_PLUGIN_ISOLATION;\n if (isolated) {\n return {\n schema: DEFAULT_MIGRATION_JOURNAL.schema,\n table: `__drizzle_migrations_${s}`,\n slug: s,\n };\n }\n return {\n schema: DEFAULT_MIGRATION_JOURNAL.schema,\n table: DEFAULT_MIGRATION_JOURNAL.table,\n slug: s,\n };\n}\n\n/**\n * Format a JavaScript string array as a PostgreSQL text array literal for use\n * with Drizzle's `sql` tag. Example return:\n * `'{\"h1\",\"h2\"}'::text[]`\n *\n * Usage: sql`WHERE col = ANY(${toSqlArray(values)})`\n *\n * Drizzle's default parameter binding does not handle array types correctly\n * with the pg driver (it emits `ANY(($1))` with a single string, which\n * Postgres rejects as a malformed array literal).\n */\nexport function toSqlArray(arr: string[]): string {\n if (arr.length === 0) return `'{}'::text[]`;\n const escaped = arr.map((v) => v.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"'));\n return `'{${escaped.map((v) => `\"${v}\"`).join(\",\")}}'::text[]`;\n}\n\nexport function pluginMigrationSlug(key: string): string {\n return normalizeSlug(key);\n}\n\nexport function getDatabaseUrlSecretName(slug: string): string {\n return `${slug.toUpperCase().replace(/-/g, \"_\")}_DATABASE_URL`;\n}\n\nexport const DUPLICATE_OBJECT_SQLSTATES: readonly string[] = [\"42710\", \"42701\", \"42P07\"];\n\n/**\n * Walk `error` and its `cause` chain (bounded), invoking `visit` on each link.\n * Postgres driver errors arrive wrapped by drizzle/Effect at varying depths,\n * so every error-shape predicate below shares this traversal.\n */\nexport function visitCauses(\n error: unknown,\n depth: number,\n visit: (link: object) => boolean,\n): boolean {\n let current: unknown = error;\n for (let i = 0; i < depth && current; i++) {\n if (typeof current === \"object\" && current !== null && visit(current)) return true;\n current = (current as { cause?: unknown })?.cause;\n }\n return false;\n}\n\n/**\n * pg_catalog unique indexes that can surface a concurrent-DDL race as SQLSTATE\n * 23505 instead of a duplicate-object class. When two connections run\n * `CREATE TABLE <name>` at the same time, the loser fails its insert into\n * `pg_type` (\"duplicate key value violates unique constraint\n * `pg_type_typname_nsp_index`\") rather than seeing 42P07, because the winner's\n * table is not yet committed. The same shape exists for schemas and other\n * relations.\n */\nconst CONCURRENT_DDL_CONSTRAINTS: ReadonlySet<string> = new Set([\n \"pg_type_typname_nsp_index\",\n \"pg_namespace_nspname_index\",\n \"pg_class_relname_nsp_index\",\n]);\n\n/**\n * Whether a SQLSTATE 23505 unique violation is the concurrent-DDL catalog\n * collision signature rather than a data-level constraint on a user table.\n * Data-level violations must keep failing loudly; only the pg_catalog indexes\n * above are treated as duplicate-DDL tolerance candidates.\n */\nexport function isConcurrentDdlUniqueViolation(error: unknown): boolean {\n return visitCauses(error, 6, (link) => {\n const { code, constraint, message } = link as {\n code?: unknown;\n constraint?: unknown;\n message?: unknown;\n };\n if (code !== \"23505\") return false;\n if (typeof constraint === \"string\" && CONCURRENT_DDL_CONSTRAINTS.has(constraint)) return true;\n if (typeof message !== \"string\") return false;\n for (const name of CONCURRENT_DDL_CONSTRAINTS) {\n if (message.includes(name)) return true;\n }\n return false;\n });\n}\n\nconst RETRYABLE_SQLSTATES: ReadonlySet<string> = new Set([\n \"42P06\",\n \"42710\",\n \"42701\",\n \"42P07\",\n \"23505\",\n \"40001\",\n \"40P01\",\n \"55P03\",\n]);\n\nconst RETRYABLE_DRIVER_CODES: ReadonlySet<string> = new Set([\n \"ECONNREFUSED\",\n \"ECONNRESET\",\n \"EPIPE\",\n \"ETIMEDOUT\",\n \"ENOTFOUND\",\n \"EAI_AGAIN\",\n]);\n\nfunction matchesRetryableStates(error: unknown, states: ReadonlySet<string>): boolean {\n return visitCauses(error, 6, (link) => {\n const code = (link as { code?: unknown }).code;\n if (typeof code !== \"string\") return false;\n return states.has(code) || code.startsWith(\"08\") || RETRYABLE_DRIVER_CODES.has(code);\n });\n}\n\n/**\n * Decide whether a failed migration-init statement (schema/journal creation)\n * is worth retrying. Covers the concurrent `CREATE SCHEMA IF NOT EXISTS`\n * race (unique violation on `pg_namespace`, duplicate-object errors),\n * serialization/deadlock errors, and transient connection failures.\n */\nexport function isRetryableMigrationError(error: unknown): boolean {\n return matchesRetryableStates(error, RETRYABLE_SQLSTATES);\n}\n\nconst RETRYABLE_STATEMENT_SQLSTATES: ReadonlySet<string> = new Set([\n \"40001\", // serialization_failure\n \"40P01\", // deadlock_detected\n \"55P03\", // lock_not_available\n]);\n\n/**\n * Decide whether a failed migration *statement* (inside its transaction) is\n * worth retrying. Deliberately narrower than `isRetryableMigrationError`:\n * duplicate-object classes are tolerated at the SAVEPOINT level, and a 23505\n * is either the concurrent-DDL catalog collision (also tolerated) or a\n * deterministic data-level violation (retrying is pointless) — so only\n * lock/serialization/transient-connection states justify re-running the\n * transaction.\n */\nexport function isRetryableMigrationExecutionError(error: unknown): boolean {\n return matchesRetryableStates(error, RETRYABLE_STATEMENT_SQLSTATES);\n}\n\nconst PG_QUERY_QUEUE_DEPRECATION = \"client.query() when the client is already executing a query\";\n\nlet pgWarningFilterInstalled = false;\n\n/**\n * Silence pg's query-queue deprecation warning (fires once per process from\n * pg-pool's internal dispatch under concurrent boot load — node-postgres#3612,\n * #3617). Node's default warning handler prints even when user listeners are\n * attached, so it is removed first; every other warning is re-printed here.\n */\nexport function suppressPgQueryQueueDeprecation(): void {\n if (pgWarningFilterInstalled) return;\n if (typeof process === \"undefined\" || typeof process.on !== \"function\") return;\n pgWarningFilterInstalled = true;\n process.removeAllListeners(\"warning\");\n process.on(\"warning\", (warning) => {\n if (\n warning.name === \"DeprecationWarning\" &&\n warning.message.includes(PG_QUERY_QUEUE_DEPRECATION)\n ) {\n return;\n }\n console.error(warning.stack || `${warning.name}: ${warning.message}`);\n });\n}\n\nexport function extractExpectedTables(migrations: { sql: string[] }[]): string[] {\n const tables = new Set<string>();\n const re = /CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(?:\"([^\"]+)\"\\.)?\"([^\"]+)\"/gi;\n for (const migration of migrations) {\n for (const stmt of migration.sql) {\n for (const match of stmt.matchAll(re)) {\n const tableName = match[2];\n if (tableName) {\n tables.add(tableName);\n }\n }\n }\n }\n return [...tables];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core.cjs","names":["join","existsSync","readFileSync","dirname"],"sources":["../../src/db/core.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nexport interface MigrationStorage {\n schema: string;\n table: string;\n slug: string;\n}\n\nconst DEFAULT_MIGRATION_JOURNAL = {\n schema: \"drizzle\",\n table: \"__drizzle_migrations\",\n} as const;\n\nconst PER_PLUGIN_ISOLATION = false;\n\nexport function normalizeSlug(name: string): string {\n const basename = name.split(\"/\").pop() ?? name;\n return basename\n .replace(/^@/, \"\")\n .replace(/[^a-zA-Z0-9_-]/g, \"_\")\n .replace(/-plugin$/i, \"\")\n .replace(/-/g, \"_\")\n .toLowerCase();\n}\n\nexport function getMigrationSlug(dir?: string): string {\n if (!dir) return normalizeSlug(process.env.npm_package_name ?? \"unknown\");\n let current = dir;\n for (let i = 0; i < 10; i++) {\n const pkgPath = join(current, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as { name?: string };\n return normalizeSlug(pkg.name ?? current);\n } catch {\n return normalizeSlug(current);\n }\n }\n const parent = dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return normalizeSlug(dir);\n}\n\nexport function getMigrationStorage(\n slug?: string,\n options?: { isolated?: boolean },\n): MigrationStorage {\n const s = normalizeSlug(slug ?? getMigrationSlug());\n const isolated = options?.isolated ?? PER_PLUGIN_ISOLATION;\n if (isolated) {\n return {\n schema: DEFAULT_MIGRATION_JOURNAL.schema,\n table: `__drizzle_migrations_${s}`,\n slug: s,\n };\n }\n return {\n schema: DEFAULT_MIGRATION_JOURNAL.schema,\n table: DEFAULT_MIGRATION_JOURNAL.table,\n slug: s,\n };\n}\n\n/**\n * Format a JavaScript string array as a PostgreSQL text array literal for use\n * with Drizzle's `sql` tag. Example return:\n * `'{\"h1\",\"h2\"}'::text[]`\n *\n * Usage: sql`WHERE col = ANY(${toSqlArray(values)})`\n *\n * Drizzle's default parameter binding does not handle array types correctly\n * with the pg driver (it emits `ANY(($1))` with a single string, which\n * Postgres rejects as a malformed array literal).\n */\nexport function toSqlArray(arr: string[]): string {\n if (arr.length === 0) return `'{}'::text[]`;\n const escaped = arr.map((v) => v.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"'));\n return `'{${escaped.map((v) => `\"${v}\"`).join(\",\")}}'::text[]`;\n}\n\nexport function pluginMigrationSlug(key: string): string {\n return normalizeSlug(key);\n}\n\nexport function getDatabaseUrlSecretName(slug: string): string {\n return `${slug.toUpperCase().replace(/-/g, \"_\")}_DATABASE_URL`;\n}\n\nexport const DUPLICATE_OBJECT_SQLSTATES: readonly string[] = [\"42710\", \"42701\", \"42P07\"];\n\n/**\n * Walk `error` and its `cause` chain (bounded), invoking `visit` on each link.\n * Postgres driver errors arrive wrapped by drizzle/Effect at varying depths,\n * so every error-shape predicate below shares this traversal.\n */\nexport function visitCauses(\n error: unknown,\n depth: number,\n visit: (link: object) => boolean,\n): boolean {\n let current: unknown = error;\n for (let i = 0; i < depth && current; i++) {\n if (typeof current === \"object\" && current !== null && visit(current)) return true;\n current = (current as { cause?: unknown })?.cause;\n }\n return false;\n}\n\n/**\n * pg_catalog unique indexes that can surface a concurrent-DDL race as SQLSTATE\n * 23505 instead of a duplicate-object class. When two connections run\n * `CREATE TABLE <name>` at the same time, the loser fails its insert into\n * `pg_type` (\"duplicate key value violates unique constraint\n * `pg_type_typname_nsp_index`\") rather than seeing 42P07, because the winner's\n * table is not yet committed. The same shape exists for schemas and other\n * relations.\n */\nconst CONCURRENT_DDL_CONSTRAINTS: ReadonlySet<string> = new Set([\n \"pg_type_typname_nsp_index\",\n \"pg_namespace_nspname_index\",\n \"pg_class_relname_nsp_index\",\n]);\n\n/**\n * Whether a SQLSTATE 23505 unique violation is the concurrent-DDL catalog\n * collision signature rather than a data-level constraint on a user table.\n * Data-level violations must keep failing loudly; only the pg_catalog indexes\n * above are treated as duplicate-DDL tolerance candidates.\n */\nexport function isConcurrentDdlUniqueViolation(error: unknown): boolean {\n return visitCauses(error, 6, (link) => {\n const { code, constraint, message } = link as {\n code?: unknown;\n constraint?: unknown;\n message?: unknown;\n };\n if (code !== \"23505\") return false;\n if (typeof constraint === \"string\" && CONCURRENT_DDL_CONSTRAINTS.has(constraint)) return true;\n if (typeof message !== \"string\") return false;\n for (const name of CONCURRENT_DDL_CONSTRAINTS) {\n if (message.includes(name)) return true;\n }\n return false;\n });\n}\n\nconst RETRYABLE_SQLSTATES: ReadonlySet<string> = new Set([\n \"42P06\",\n \"42710\",\n \"42701\",\n \"42P07\",\n \"23505\",\n \"40001\",\n \"40P01\",\n \"55P03\",\n]);\n\nconst RETRYABLE_DRIVER_CODES: ReadonlySet<string> = new Set([\n \"ECONNREFUSED\",\n \"ECONNRESET\",\n \"EPIPE\",\n \"ETIMEDOUT\",\n \"ENOTFOUND\",\n \"EAI_AGAIN\",\n]);\n\nfunction matchesRetryableStates(error: unknown, states: ReadonlySet<string>): boolean {\n return visitCauses(error, 6, (link) => {\n const code = (link as { code?: unknown }).code;\n if (typeof code !== \"string\") return false;\n return states.has(code) || code.startsWith(\"08\") || RETRYABLE_DRIVER_CODES.has(code);\n });\n}\n\n/**\n * Decide whether a failed migration-init statement (schema/journal creation)\n * is worth retrying. Covers the concurrent `CREATE SCHEMA IF NOT EXISTS`\n * race (unique violation on `pg_namespace`, duplicate-object errors),\n * serialization/deadlock errors, and transient connection failures.\n */\nexport function isRetryableMigrationError(error: unknown): boolean {\n return matchesRetryableStates(error, RETRYABLE_SQLSTATES);\n}\n\nconst RETRYABLE_STATEMENT_SQLSTATES: ReadonlySet<string> = new Set([\n \"40001\", // serialization_failure\n \"40P01\", // deadlock_detected\n \"55P03\", // lock_not_available\n]);\n\n/**\n * Decide whether a failed migration *statement* (inside its transaction) is\n * worth retrying. Deliberately narrower than `isRetryableMigrationError`:\n * duplicate-object classes are tolerated at the SAVEPOINT level, and a 23505\n * is either the concurrent-DDL catalog collision (also tolerated) or a\n * deterministic data-level violation (retrying is pointless) — so only\n * lock/serialization/transient-connection states justify re-running the\n * transaction.\n */\nexport function isRetryableMigrationExecutionError(error: unknown): boolean {\n return matchesRetryableStates(error, RETRYABLE_STATEMENT_SQLSTATES);\n}\n\nconst PG_QUERY_QUEUE_DEPRECATION = \"client.query() when the client is already executing a query\";\n\nlet pgWarningFilterInstalled = false;\n\n/**\n * Silence pg's query-queue deprecation warning (fires once per process from\n * pg-pool's internal dispatch under concurrent boot load — node-postgres#3612,\n * #3617). Node's default warning handler prints even when user listeners are\n * attached, so it is removed first; every other warning is re-printed here.\n */\nexport function suppressPgQueryQueueDeprecation(): void {\n if (pgWarningFilterInstalled) return;\n if (typeof process === \"undefined\" || typeof process.on !== \"function\") return;\n pgWarningFilterInstalled = true;\n process.removeAllListeners(\"warning\");\n process.on(\"warning\", (warning) => {\n if (\n warning.name === \"DeprecationWarning\" &&\n warning.message.includes(PG_QUERY_QUEUE_DEPRECATION)\n ) {\n return;\n }\n console.error(warning.stack || `${warning.name}: ${warning.message}`);\n });\n}\n\nexport function extractExpectedTables(migrations: { sql: string[] }[]): string[] {\n const tables = new Set<string>();\n const ident = String.raw`(?:\"([^\"]+)\"|(\\w+))`;\n const qualified = String.raw`(?:${ident}\\.)?`;\n const createRe = new RegExp(\n String.raw`CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?${qualified}${ident}`,\n \"gi\",\n );\n const dropRe = new RegExp(\n String.raw`DROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?${qualified}${ident}`,\n \"gi\",\n );\n const renameRe = new RegExp(\n String.raw`ALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?${qualified}${ident}${String.raw`\\s+RENAME\\s+TO\\s+`}${ident}`,\n \"gi\",\n );\n for (const migration of migrations) {\n for (const stmt of migration.sql) {\n for (const match of stmt.matchAll(createRe)) {\n const tableName = match[3] ?? match[4];\n if (tableName) {\n tables.add(tableName);\n }\n }\n for (const match of stmt.matchAll(dropRe)) {\n const tableName = match[3] ?? match[4];\n if (tableName) {\n tables.delete(tableName);\n }\n }\n for (const match of stmt.matchAll(renameRe)) {\n const from = match[3] ?? match[4];\n const to = match[5] ?? match[6];\n if (from && to) {\n tables.delete(from);\n tables.add(to);\n }\n }\n }\n }\n return [...tables];\n}\n"],"mappings":"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"}
|
package/dist/db/core.mjs
CHANGED
|
@@ -179,10 +179,28 @@ function suppressPgQueryQueueDeprecation() {
|
|
|
179
179
|
}
|
|
180
180
|
function extractExpectedTables(migrations) {
|
|
181
181
|
const tables = /* @__PURE__ */ new Set();
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
const ident = String.raw`(?:"([^"]+)"|(\w+))`;
|
|
183
|
+
const qualified = String.raw`(?:${ident}\.)?`;
|
|
184
|
+
const createRe = new RegExp(String.raw`CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?${qualified}${ident}`, "gi");
|
|
185
|
+
const dropRe = new RegExp(String.raw`DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?${qualified}${ident}`, "gi");
|
|
186
|
+
const renameRe = new RegExp(String.raw`ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?${qualified}${ident}${String.raw`\s+RENAME\s+TO\s+`}${ident}`, "gi");
|
|
187
|
+
for (const migration of migrations) for (const stmt of migration.sql) {
|
|
188
|
+
for (const match of stmt.matchAll(createRe)) {
|
|
189
|
+
const tableName = match[3] ?? match[4];
|
|
190
|
+
if (tableName) tables.add(tableName);
|
|
191
|
+
}
|
|
192
|
+
for (const match of stmt.matchAll(dropRe)) {
|
|
193
|
+
const tableName = match[3] ?? match[4];
|
|
194
|
+
if (tableName) tables.delete(tableName);
|
|
195
|
+
}
|
|
196
|
+
for (const match of stmt.matchAll(renameRe)) {
|
|
197
|
+
const from = match[3] ?? match[4];
|
|
198
|
+
const to = match[5] ?? match[6];
|
|
199
|
+
if (from && to) {
|
|
200
|
+
tables.delete(from);
|
|
201
|
+
tables.add(to);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
186
204
|
}
|
|
187
205
|
return [...tables];
|
|
188
206
|
}
|
package/dist/db/core.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.mjs","names":[],"sources":["../../src/db/core.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nexport interface MigrationStorage {\n schema: string;\n table: string;\n slug: string;\n}\n\nconst DEFAULT_MIGRATION_JOURNAL = {\n schema: \"drizzle\",\n table: \"__drizzle_migrations\",\n} as const;\n\nconst PER_PLUGIN_ISOLATION = false;\n\nexport function normalizeSlug(name: string): string {\n const basename = name.split(\"/\").pop() ?? name;\n return basename\n .replace(/^@/, \"\")\n .replace(/[^a-zA-Z0-9_-]/g, \"_\")\n .replace(/-plugin$/i, \"\")\n .replace(/-/g, \"_\")\n .toLowerCase();\n}\n\nexport function getMigrationSlug(dir?: string): string {\n if (!dir) return normalizeSlug(process.env.npm_package_name ?? \"unknown\");\n let current = dir;\n for (let i = 0; i < 10; i++) {\n const pkgPath = join(current, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as { name?: string };\n return normalizeSlug(pkg.name ?? current);\n } catch {\n return normalizeSlug(current);\n }\n }\n const parent = dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return normalizeSlug(dir);\n}\n\nexport function getMigrationStorage(\n slug?: string,\n options?: { isolated?: boolean },\n): MigrationStorage {\n const s = normalizeSlug(slug ?? getMigrationSlug());\n const isolated = options?.isolated ?? PER_PLUGIN_ISOLATION;\n if (isolated) {\n return {\n schema: DEFAULT_MIGRATION_JOURNAL.schema,\n table: `__drizzle_migrations_${s}`,\n slug: s,\n };\n }\n return {\n schema: DEFAULT_MIGRATION_JOURNAL.schema,\n table: DEFAULT_MIGRATION_JOURNAL.table,\n slug: s,\n };\n}\n\n/**\n * Format a JavaScript string array as a PostgreSQL text array literal for use\n * with Drizzle's `sql` tag. Example return:\n * `'{\"h1\",\"h2\"}'::text[]`\n *\n * Usage: sql`WHERE col = ANY(${toSqlArray(values)})`\n *\n * Drizzle's default parameter binding does not handle array types correctly\n * with the pg driver (it emits `ANY(($1))` with a single string, which\n * Postgres rejects as a malformed array literal).\n */\nexport function toSqlArray(arr: string[]): string {\n if (arr.length === 0) return `'{}'::text[]`;\n const escaped = arr.map((v) => v.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"'));\n return `'{${escaped.map((v) => `\"${v}\"`).join(\",\")}}'::text[]`;\n}\n\nexport function pluginMigrationSlug(key: string): string {\n return normalizeSlug(key);\n}\n\nexport function getDatabaseUrlSecretName(slug: string): string {\n return `${slug.toUpperCase().replace(/-/g, \"_\")}_DATABASE_URL`;\n}\n\nexport const DUPLICATE_OBJECT_SQLSTATES: readonly string[] = [\"42710\", \"42701\", \"42P07\"];\n\n/**\n * Walk `error` and its `cause` chain (bounded), invoking `visit` on each link.\n * Postgres driver errors arrive wrapped by drizzle/Effect at varying depths,\n * so every error-shape predicate below shares this traversal.\n */\nexport function visitCauses(\n error: unknown,\n depth: number,\n visit: (link: object) => boolean,\n): boolean {\n let current: unknown = error;\n for (let i = 0; i < depth && current; i++) {\n if (typeof current === \"object\" && current !== null && visit(current)) return true;\n current = (current as { cause?: unknown })?.cause;\n }\n return false;\n}\n\n/**\n * pg_catalog unique indexes that can surface a concurrent-DDL race as SQLSTATE\n * 23505 instead of a duplicate-object class. When two connections run\n * `CREATE TABLE <name>` at the same time, the loser fails its insert into\n * `pg_type` (\"duplicate key value violates unique constraint\n * `pg_type_typname_nsp_index`\") rather than seeing 42P07, because the winner's\n * table is not yet committed. The same shape exists for schemas and other\n * relations.\n */\nconst CONCURRENT_DDL_CONSTRAINTS: ReadonlySet<string> = new Set([\n \"pg_type_typname_nsp_index\",\n \"pg_namespace_nspname_index\",\n \"pg_class_relname_nsp_index\",\n]);\n\n/**\n * Whether a SQLSTATE 23505 unique violation is the concurrent-DDL catalog\n * collision signature rather than a data-level constraint on a user table.\n * Data-level violations must keep failing loudly; only the pg_catalog indexes\n * above are treated as duplicate-DDL tolerance candidates.\n */\nexport function isConcurrentDdlUniqueViolation(error: unknown): boolean {\n return visitCauses(error, 6, (link) => {\n const { code, constraint, message } = link as {\n code?: unknown;\n constraint?: unknown;\n message?: unknown;\n };\n if (code !== \"23505\") return false;\n if (typeof constraint === \"string\" && CONCURRENT_DDL_CONSTRAINTS.has(constraint)) return true;\n if (typeof message !== \"string\") return false;\n for (const name of CONCURRENT_DDL_CONSTRAINTS) {\n if (message.includes(name)) return true;\n }\n return false;\n });\n}\n\nconst RETRYABLE_SQLSTATES: ReadonlySet<string> = new Set([\n \"42P06\",\n \"42710\",\n \"42701\",\n \"42P07\",\n \"23505\",\n \"40001\",\n \"40P01\",\n \"55P03\",\n]);\n\nconst RETRYABLE_DRIVER_CODES: ReadonlySet<string> = new Set([\n \"ECONNREFUSED\",\n \"ECONNRESET\",\n \"EPIPE\",\n \"ETIMEDOUT\",\n \"ENOTFOUND\",\n \"EAI_AGAIN\",\n]);\n\nfunction matchesRetryableStates(error: unknown, states: ReadonlySet<string>): boolean {\n return visitCauses(error, 6, (link) => {\n const code = (link as { code?: unknown }).code;\n if (typeof code !== \"string\") return false;\n return states.has(code) || code.startsWith(\"08\") || RETRYABLE_DRIVER_CODES.has(code);\n });\n}\n\n/**\n * Decide whether a failed migration-init statement (schema/journal creation)\n * is worth retrying. Covers the concurrent `CREATE SCHEMA IF NOT EXISTS`\n * race (unique violation on `pg_namespace`, duplicate-object errors),\n * serialization/deadlock errors, and transient connection failures.\n */\nexport function isRetryableMigrationError(error: unknown): boolean {\n return matchesRetryableStates(error, RETRYABLE_SQLSTATES);\n}\n\nconst RETRYABLE_STATEMENT_SQLSTATES: ReadonlySet<string> = new Set([\n \"40001\", // serialization_failure\n \"40P01\", // deadlock_detected\n \"55P03\", // lock_not_available\n]);\n\n/**\n * Decide whether a failed migration *statement* (inside its transaction) is\n * worth retrying. Deliberately narrower than `isRetryableMigrationError`:\n * duplicate-object classes are tolerated at the SAVEPOINT level, and a 23505\n * is either the concurrent-DDL catalog collision (also tolerated) or a\n * deterministic data-level violation (retrying is pointless) — so only\n * lock/serialization/transient-connection states justify re-running the\n * transaction.\n */\nexport function isRetryableMigrationExecutionError(error: unknown): boolean {\n return matchesRetryableStates(error, RETRYABLE_STATEMENT_SQLSTATES);\n}\n\nconst PG_QUERY_QUEUE_DEPRECATION = \"client.query() when the client is already executing a query\";\n\nlet pgWarningFilterInstalled = false;\n\n/**\n * Silence pg's query-queue deprecation warning (fires once per process from\n * pg-pool's internal dispatch under concurrent boot load — node-postgres#3612,\n * #3617). Node's default warning handler prints even when user listeners are\n * attached, so it is removed first; every other warning is re-printed here.\n */\nexport function suppressPgQueryQueueDeprecation(): void {\n if (pgWarningFilterInstalled) return;\n if (typeof process === \"undefined\" || typeof process.on !== \"function\") return;\n pgWarningFilterInstalled = true;\n process.removeAllListeners(\"warning\");\n process.on(\"warning\", (warning) => {\n if (\n warning.name === \"DeprecationWarning\" &&\n warning.message.includes(PG_QUERY_QUEUE_DEPRECATION)\n ) {\n return;\n }\n console.error(warning.stack || `${warning.name}: ${warning.message}`);\n });\n}\n\nexport function extractExpectedTables(migrations: { sql: string[] }[]): string[] {\n const tables = new Set<string>();\n const re = /CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(?:\"([^\"]+)\"\\.)?\"([^\"]+)\"/gi;\n for (const migration of migrations) {\n for (const stmt of migration.sql) {\n for (const match of stmt.matchAll(re)) {\n const tableName = match[2];\n if (tableName) {\n tables.add(tableName);\n }\n }\n }\n }\n return [...tables];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core.mjs","names":[],"sources":["../../src/db/core.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nexport interface MigrationStorage {\n schema: string;\n table: string;\n slug: string;\n}\n\nconst DEFAULT_MIGRATION_JOURNAL = {\n schema: \"drizzle\",\n table: \"__drizzle_migrations\",\n} as const;\n\nconst PER_PLUGIN_ISOLATION = false;\n\nexport function normalizeSlug(name: string): string {\n const basename = name.split(\"/\").pop() ?? name;\n return basename\n .replace(/^@/, \"\")\n .replace(/[^a-zA-Z0-9_-]/g, \"_\")\n .replace(/-plugin$/i, \"\")\n .replace(/-/g, \"_\")\n .toLowerCase();\n}\n\nexport function getMigrationSlug(dir?: string): string {\n if (!dir) return normalizeSlug(process.env.npm_package_name ?? \"unknown\");\n let current = dir;\n for (let i = 0; i < 10; i++) {\n const pkgPath = join(current, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as { name?: string };\n return normalizeSlug(pkg.name ?? current);\n } catch {\n return normalizeSlug(current);\n }\n }\n const parent = dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return normalizeSlug(dir);\n}\n\nexport function getMigrationStorage(\n slug?: string,\n options?: { isolated?: boolean },\n): MigrationStorage {\n const s = normalizeSlug(slug ?? getMigrationSlug());\n const isolated = options?.isolated ?? PER_PLUGIN_ISOLATION;\n if (isolated) {\n return {\n schema: DEFAULT_MIGRATION_JOURNAL.schema,\n table: `__drizzle_migrations_${s}`,\n slug: s,\n };\n }\n return {\n schema: DEFAULT_MIGRATION_JOURNAL.schema,\n table: DEFAULT_MIGRATION_JOURNAL.table,\n slug: s,\n };\n}\n\n/**\n * Format a JavaScript string array as a PostgreSQL text array literal for use\n * with Drizzle's `sql` tag. Example return:\n * `'{\"h1\",\"h2\"}'::text[]`\n *\n * Usage: sql`WHERE col = ANY(${toSqlArray(values)})`\n *\n * Drizzle's default parameter binding does not handle array types correctly\n * with the pg driver (it emits `ANY(($1))` with a single string, which\n * Postgres rejects as a malformed array literal).\n */\nexport function toSqlArray(arr: string[]): string {\n if (arr.length === 0) return `'{}'::text[]`;\n const escaped = arr.map((v) => v.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"'));\n return `'{${escaped.map((v) => `\"${v}\"`).join(\",\")}}'::text[]`;\n}\n\nexport function pluginMigrationSlug(key: string): string {\n return normalizeSlug(key);\n}\n\nexport function getDatabaseUrlSecretName(slug: string): string {\n return `${slug.toUpperCase().replace(/-/g, \"_\")}_DATABASE_URL`;\n}\n\nexport const DUPLICATE_OBJECT_SQLSTATES: readonly string[] = [\"42710\", \"42701\", \"42P07\"];\n\n/**\n * Walk `error` and its `cause` chain (bounded), invoking `visit` on each link.\n * Postgres driver errors arrive wrapped by drizzle/Effect at varying depths,\n * so every error-shape predicate below shares this traversal.\n */\nexport function visitCauses(\n error: unknown,\n depth: number,\n visit: (link: object) => boolean,\n): boolean {\n let current: unknown = error;\n for (let i = 0; i < depth && current; i++) {\n if (typeof current === \"object\" && current !== null && visit(current)) return true;\n current = (current as { cause?: unknown })?.cause;\n }\n return false;\n}\n\n/**\n * pg_catalog unique indexes that can surface a concurrent-DDL race as SQLSTATE\n * 23505 instead of a duplicate-object class. When two connections run\n * `CREATE TABLE <name>` at the same time, the loser fails its insert into\n * `pg_type` (\"duplicate key value violates unique constraint\n * `pg_type_typname_nsp_index`\") rather than seeing 42P07, because the winner's\n * table is not yet committed. The same shape exists for schemas and other\n * relations.\n */\nconst CONCURRENT_DDL_CONSTRAINTS: ReadonlySet<string> = new Set([\n \"pg_type_typname_nsp_index\",\n \"pg_namespace_nspname_index\",\n \"pg_class_relname_nsp_index\",\n]);\n\n/**\n * Whether a SQLSTATE 23505 unique violation is the concurrent-DDL catalog\n * collision signature rather than a data-level constraint on a user table.\n * Data-level violations must keep failing loudly; only the pg_catalog indexes\n * above are treated as duplicate-DDL tolerance candidates.\n */\nexport function isConcurrentDdlUniqueViolation(error: unknown): boolean {\n return visitCauses(error, 6, (link) => {\n const { code, constraint, message } = link as {\n code?: unknown;\n constraint?: unknown;\n message?: unknown;\n };\n if (code !== \"23505\") return false;\n if (typeof constraint === \"string\" && CONCURRENT_DDL_CONSTRAINTS.has(constraint)) return true;\n if (typeof message !== \"string\") return false;\n for (const name of CONCURRENT_DDL_CONSTRAINTS) {\n if (message.includes(name)) return true;\n }\n return false;\n });\n}\n\nconst RETRYABLE_SQLSTATES: ReadonlySet<string> = new Set([\n \"42P06\",\n \"42710\",\n \"42701\",\n \"42P07\",\n \"23505\",\n \"40001\",\n \"40P01\",\n \"55P03\",\n]);\n\nconst RETRYABLE_DRIVER_CODES: ReadonlySet<string> = new Set([\n \"ECONNREFUSED\",\n \"ECONNRESET\",\n \"EPIPE\",\n \"ETIMEDOUT\",\n \"ENOTFOUND\",\n \"EAI_AGAIN\",\n]);\n\nfunction matchesRetryableStates(error: unknown, states: ReadonlySet<string>): boolean {\n return visitCauses(error, 6, (link) => {\n const code = (link as { code?: unknown }).code;\n if (typeof code !== \"string\") return false;\n return states.has(code) || code.startsWith(\"08\") || RETRYABLE_DRIVER_CODES.has(code);\n });\n}\n\n/**\n * Decide whether a failed migration-init statement (schema/journal creation)\n * is worth retrying. Covers the concurrent `CREATE SCHEMA IF NOT EXISTS`\n * race (unique violation on `pg_namespace`, duplicate-object errors),\n * serialization/deadlock errors, and transient connection failures.\n */\nexport function isRetryableMigrationError(error: unknown): boolean {\n return matchesRetryableStates(error, RETRYABLE_SQLSTATES);\n}\n\nconst RETRYABLE_STATEMENT_SQLSTATES: ReadonlySet<string> = new Set([\n \"40001\", // serialization_failure\n \"40P01\", // deadlock_detected\n \"55P03\", // lock_not_available\n]);\n\n/**\n * Decide whether a failed migration *statement* (inside its transaction) is\n * worth retrying. Deliberately narrower than `isRetryableMigrationError`:\n * duplicate-object classes are tolerated at the SAVEPOINT level, and a 23505\n * is either the concurrent-DDL catalog collision (also tolerated) or a\n * deterministic data-level violation (retrying is pointless) — so only\n * lock/serialization/transient-connection states justify re-running the\n * transaction.\n */\nexport function isRetryableMigrationExecutionError(error: unknown): boolean {\n return matchesRetryableStates(error, RETRYABLE_STATEMENT_SQLSTATES);\n}\n\nconst PG_QUERY_QUEUE_DEPRECATION = \"client.query() when the client is already executing a query\";\n\nlet pgWarningFilterInstalled = false;\n\n/**\n * Silence pg's query-queue deprecation warning (fires once per process from\n * pg-pool's internal dispatch under concurrent boot load — node-postgres#3612,\n * #3617). Node's default warning handler prints even when user listeners are\n * attached, so it is removed first; every other warning is re-printed here.\n */\nexport function suppressPgQueryQueueDeprecation(): void {\n if (pgWarningFilterInstalled) return;\n if (typeof process === \"undefined\" || typeof process.on !== \"function\") return;\n pgWarningFilterInstalled = true;\n process.removeAllListeners(\"warning\");\n process.on(\"warning\", (warning) => {\n if (\n warning.name === \"DeprecationWarning\" &&\n warning.message.includes(PG_QUERY_QUEUE_DEPRECATION)\n ) {\n return;\n }\n console.error(warning.stack || `${warning.name}: ${warning.message}`);\n });\n}\n\nexport function extractExpectedTables(migrations: { sql: string[] }[]): string[] {\n const tables = new Set<string>();\n const ident = String.raw`(?:\"([^\"]+)\"|(\\w+))`;\n const qualified = String.raw`(?:${ident}\\.)?`;\n const createRe = new RegExp(\n String.raw`CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?${qualified}${ident}`,\n \"gi\",\n );\n const dropRe = new RegExp(\n String.raw`DROP\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?${qualified}${ident}`,\n \"gi\",\n );\n const renameRe = new RegExp(\n String.raw`ALTER\\s+TABLE\\s+(?:IF\\s+EXISTS\\s+)?${qualified}${ident}${String.raw`\\s+RENAME\\s+TO\\s+`}${ident}`,\n \"gi\",\n );\n for (const migration of migrations) {\n for (const stmt of migration.sql) {\n for (const match of stmt.matchAll(createRe)) {\n const tableName = match[3] ?? match[4];\n if (tableName) {\n tables.add(tableName);\n }\n }\n for (const match of stmt.matchAll(dropRe)) {\n const tableName = match[3] ?? match[4];\n if (tableName) {\n tables.delete(tableName);\n }\n }\n for (const match of stmt.matchAll(renameRe)) {\n const from = match[3] ?? match[4];\n const to = match[5] ?? match[6];\n if (from && to) {\n tables.delete(from);\n tables.add(to);\n }\n }\n }\n }\n return [...tables];\n}\n"],"mappings":"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"}
|
|
@@ -86,11 +86,14 @@ const AppDescriptorSchema = zod.object({
|
|
|
86
86
|
railway: zod.object({ service: zod.string() }).strict().optional()
|
|
87
87
|
}).strict().optional(),
|
|
88
88
|
cdn: zod.object({ origin: zod.string().optional() }).strict().optional(),
|
|
89
|
-
publish: zod.object({
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
publish: zod.object({
|
|
90
|
+
auth: zod.enum([
|
|
91
|
+
"session",
|
|
92
|
+
"key",
|
|
93
|
+
"custody"
|
|
94
|
+
]).optional(),
|
|
95
|
+
sourcePublish: zod.boolean().optional()
|
|
96
|
+
}).strict().optional(),
|
|
94
97
|
host: HostRefSchema.optional(),
|
|
95
98
|
api: ApiRefSchema.optional(),
|
|
96
99
|
ui: UiRefSchema.optional(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.cjs","names":["z","SlotPinSchema","JsonObjectSchema","SharedDepMapSchema"],"sources":["../../src/descriptor/schema.ts"],"sourcesContent":["import * as z from \"zod\";\nimport { JsonObjectSchema, SharedDepMapSchema, SlotPinSchema } from \"../types\";\n\n/** Pipeline-owned fields — never authored; injected from the deploy map. */\nexport const PipelineFieldsSchema = z.object({\n production: z.string().optional(),\n integrity: z.string().optional(),\n ssr: z.string().optional(),\n ssrIntegrity: z.string().optional(),\n pin: SlotPinSchema.optional(),\n});\nexport type PipelineFields = z.infer<typeof PipelineFieldsSchema>;\n\nconst DevelopmentRefSchema = z.object({\n /** local workspace directory, relative to the App root — emitted as `local:<path>` */\n path: z.string().optional(),\n /** explicit development ref override (e.g. a remote dev URL) */\n development: z.string().optional(),\n});\n\n/** An attached App (today's `plugins.<id>` / `app.auth` entries). */\nexport const AttachmentRefSchema = DevelopmentRefSchema.extend({\n name: z.string().optional(),\n /** published module reference (bos://…) consumed by resolution */\n extends: z.string().optional(),\n proxy: z.string().optional(),\n variables: JsonObjectSchema.optional(),\n secrets: z.array(z.string()).optional(),\n routes: z.array(z.string()).optional(),\n shared: SharedDepMapSchema.optional(),\n connectSrc: z.array(z.string()).optional(),\n dependsOn: z.array(z.string()).optional(),\n version: z.string().optional(),\n ui: z\n .object({\n name: z.string().optional(),\n path: z.string().optional(),\n development: z.string().optional(),\n integrity: z.string().optional(),\n pin: SlotPinSchema.optional(),\n })\n .strict()\n .optional(),\n});\nexport type AttachmentRef = z.infer<typeof AttachmentRefSchema>;\nexport type AttachmentInput = AttachmentRef;\n\n/** The `app.api` slot. */\nexport const ApiRefSchema = DevelopmentRefSchema.extend({\n proxy: z.string().optional(),\n variables: JsonObjectSchema.optional(),\n secrets: z.array(z.string()).optional(),\n routes: z.array(z.string()).optional(),\n shared: SharedDepMapSchema.optional(),\n dependsOn: z.array(z.string()).optional(),\n}).strict();\nexport type ApiRef = z.infer<typeof ApiRefSchema>;\nexport type ApiInput = ApiRef;\n\n/** The `app.ui` slot — pipeline fields (production/integrity/ssr) come from the deploy map. */\nexport const UiRefSchema = DevelopmentRefSchema.strict();\nexport type UiRef = z.infer<typeof UiRefSchema>;\nexport type UiInput = UiRef;\n\n/** The `app.host` slot — production/integrity come from the deploy map. */\nexport const HostRefSchema = DevelopmentRefSchema.extend({\n secrets: z.array(z.string()).optional(),\n}).strict();\nexport type HostRef = z.infer<typeof HostRefSchema>;\nexport type HostInput = HostRef;\n\n/**\n * The authored App — pure data, the boot input. Local paths only (dev\n * authoring shape); deployment resolves them through the deploy map.\n */\nexport const AppDescriptorSchema = z\n .object({\n name: z.string(),\n /**\n * The base App this App inherits (child-wins): a registry name\n * (`\"everything.dev\"`) or an imported App descriptor value — an imported\n * parent is an inlined parent (same-repo composition; the resolved shape\n * is identical to a fetched one). The value is validated and depth-guarded\n * at resolution (flattenParent re-parses it); chains deeper than one\n * level are deferred (wayfinder ticket 08).\n */\n extends: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),\n account: z.string().optional(),\n domain: z.string().optional(),\n title: z.string().optional(),\n description: z.string().optional(),\n repository: z.string().optional(),\n testnet: z.string().optional(),\n staging: z\n .object({\n domain: z.string(),\n account: z.string().optional(),\n })\n .strict()\n .optional(),\n /** Authoring-only: the starter level this child was scaffolded with.\n * Stripped from resolved configs — publish canonicalization never\n * carries it. */\n starter: z.enum([\"simple\", \"advanced\"]).optional(),\n ci: z\n .object({\n image: z.string().optional(),\n railway: z\n .object({\n service: z.string(),\n })\n .strict()\n .optional(),\n })\n .strict()\n .optional(),\n cdn: z\n .object({\n origin: z.string().optional(),\n })\n .strict()\n .optional(),\n publish: z\n .object({\n auth: z.enum([\"session\", \"key\", \"custody\"]).optional(),\n })\n .strict()\n .optional(),\n host: HostRefSchema.optional(),\n api: ApiRefSchema.optional(),\n ui: UiRefSchema.optional(),\n /** attachment landing in the `app.auth` slot */\n auth: AttachmentRefSchema.optional(),\n plugins: z.record(z.string(), AttachmentRefSchema).optional(),\n })\n .strict();\nexport type AppDescriptor = z.infer<typeof AppDescriptorSchema>;\nexport type AppInput = AppDescriptor;\n\nexport type AppRegistry = Record<string, AppInput>;\n"],"mappings":";;;;;;;AAIA,MAAa,uBAAuBA,IAAE,OAAO;CAC3C,YAAYA,IAAE,OAAO,CAAC,CAAC,SAAS;CAChC,WAAWA,IAAE,OAAO,CAAC,CAAC,SAAS;CAC/B,KAAKA,IAAE,OAAO,CAAC,CAAC,SAAS;CACzB,cAAcA,IAAE,OAAO,CAAC,CAAC,SAAS;CAClC,KAAKC,4BAAc,SAAS;AAC9B,CAAC;AAGD,MAAM,uBAAuBD,IAAE,OAAO;;CAEpC,MAAMA,IAAE,OAAO,CAAC,CAAC,SAAS;;CAE1B,aAAaA,IAAE,OAAO,CAAC,CAAC,SAAS;AACnC,CAAC;;AAGD,MAAa,sBAAsB,qBAAqB,OAAO;CAC7D,MAAMA,IAAE,OAAO,CAAC,CAAC,SAAS;;CAE1B,SAASA,IAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,OAAOA,IAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,WAAWE,+BAAiB,SAAS;CACrC,SAASF,IAAE,MAAMA,IAAE,OAAO,CAAC,CAAC,CAAC,SAAS;CACtC,QAAQA,IAAE,MAAMA,IAAE,OAAO,CAAC,CAAC,CAAC,SAAS;CACrC,QAAQG,iCAAmB,SAAS;CACpC,YAAYH,IAAE,MAAMA,IAAE,OAAO,CAAC,CAAC,CAAC,SAAS;CACzC,WAAWA,IAAE,MAAMA,IAAE,OAAO,CAAC,CAAC,CAAC,SAAS;CACxC,SAASA,IAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,IAAIA,IACD,OAAO;EACN,MAAMA,IAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAMA,IAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,aAAaA,IAAE,OAAO,CAAC,CAAC,SAAS;EACjC,WAAWA,IAAE,OAAO,CAAC,CAAC,SAAS;EAC/B,KAAKC,4BAAc,SAAS;CAC9B,CAAC,CAAC,CACD,OAAO,CAAC,CACR,SAAS;AACd,CAAC;;AAKD,MAAa,eAAe,qBAAqB,OAAO;CACtD,OAAOD,IAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,WAAWE,+BAAiB,SAAS;CACrC,SAASF,IAAE,MAAMA,IAAE,OAAO,CAAC,CAAC,CAAC,SAAS;CACtC,QAAQA,IAAE,MAAMA,IAAE,OAAO,CAAC,CAAC,CAAC,SAAS;CACrC,QAAQG,iCAAmB,SAAS;CACpC,WAAWH,IAAE,MAAMA,IAAE,OAAO,CAAC,CAAC,CAAC,SAAS;AAC1C,CAAC,CAAC,CAAC,OAAO;;AAKV,MAAa,cAAc,qBAAqB,OAAO;;AAKvD,MAAa,gBAAgB,qBAAqB,OAAO,EACvD,SAASA,IAAE,MAAMA,IAAE,OAAO,CAAC,CAAC,CAAC,SAAS,EACxC,CAAC,CAAC,CAAC,OAAO;;;;;AAQV,MAAa,sBAAsBA,IAChC,OAAO;CACN,MAAMA,IAAE,OAAO;;;;;;;;;CASf,SAASA,IAAE,MAAM,CAACA,IAAE,OAAO,GAAGA,IAAE,OAAOA,IAAE,OAAO,GAAGA,IAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;CAC3E,SAASA,IAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,QAAQA,IAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,OAAOA,IAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,aAAaA,IAAE,OAAO,CAAC,CAAC,SAAS;CACjC,YAAYA,IAAE,OAAO,CAAC,CAAC,SAAS;CAChC,SAASA,IAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,SAASA,IACN,OAAO;EACN,QAAQA,IAAE,OAAO;EACjB,SAASA,IAAE,OAAO,CAAC,CAAC,SAAS;CAC/B,CAAC,CAAC,CACD,OAAO,CAAC,CACR,SAAS;;;;CAIZ,SAASA,IAAE,KAAK,CAAC,UAAU,UAAU,CAAC,CAAC,CAAC,SAAS;CACjD,IAAIA,IACD,OAAO;EACN,OAAOA,IAAE,OAAO,CAAC,CAAC,SAAS;EAC3B,SAASA,IACN,OAAO,EACN,SAASA,IAAE,OAAO,EACpB,CAAC,CAAC,CACD,OAAO,CAAC,CACR,SAAS;CACd,CAAC,CAAC,CACD,OAAO,CAAC,CACR,SAAS;CACZ,KAAKA,IACF,OAAO,EACN,QAAQA,IAAE,OAAO,CAAC,CAAC,SAAS,EAC9B,CAAC,CAAC,CACD,OAAO,CAAC,CACR,SAAS;CACZ,SAASA,IACN,OAAO
|
|
1
|
+
{"version":3,"file":"schema.cjs","names":["z","SlotPinSchema","JsonObjectSchema","SharedDepMapSchema"],"sources":["../../src/descriptor/schema.ts"],"sourcesContent":["import * as z from \"zod\";\nimport { JsonObjectSchema, SharedDepMapSchema, SlotPinSchema } from \"../types\";\n\n/** Pipeline-owned fields — never authored; injected from the deploy map. */\nexport const PipelineFieldsSchema = z.object({\n production: z.string().optional(),\n integrity: z.string().optional(),\n ssr: z.string().optional(),\n ssrIntegrity: z.string().optional(),\n pin: SlotPinSchema.optional(),\n});\nexport type PipelineFields = z.infer<typeof PipelineFieldsSchema>;\n\nconst DevelopmentRefSchema = z.object({\n /** local workspace directory, relative to the App root — emitted as `local:<path>` */\n path: z.string().optional(),\n /** explicit development ref override (e.g. a remote dev URL) */\n development: z.string().optional(),\n});\n\n/** An attached App (today's `plugins.<id>` / `app.auth` entries). */\nexport const AttachmentRefSchema = DevelopmentRefSchema.extend({\n name: z.string().optional(),\n /** published module reference (bos://…) consumed by resolution */\n extends: z.string().optional(),\n proxy: z.string().optional(),\n variables: JsonObjectSchema.optional(),\n secrets: z.array(z.string()).optional(),\n routes: z.array(z.string()).optional(),\n shared: SharedDepMapSchema.optional(),\n connectSrc: z.array(z.string()).optional(),\n dependsOn: z.array(z.string()).optional(),\n version: z.string().optional(),\n ui: z\n .object({\n name: z.string().optional(),\n path: z.string().optional(),\n development: z.string().optional(),\n integrity: z.string().optional(),\n pin: SlotPinSchema.optional(),\n })\n .strict()\n .optional(),\n});\nexport type AttachmentRef = z.infer<typeof AttachmentRefSchema>;\nexport type AttachmentInput = AttachmentRef;\n\n/** The `app.api` slot. */\nexport const ApiRefSchema = DevelopmentRefSchema.extend({\n proxy: z.string().optional(),\n variables: JsonObjectSchema.optional(),\n secrets: z.array(z.string()).optional(),\n routes: z.array(z.string()).optional(),\n shared: SharedDepMapSchema.optional(),\n dependsOn: z.array(z.string()).optional(),\n}).strict();\nexport type ApiRef = z.infer<typeof ApiRefSchema>;\nexport type ApiInput = ApiRef;\n\n/** The `app.ui` slot — pipeline fields (production/integrity/ssr) come from the deploy map. */\nexport const UiRefSchema = DevelopmentRefSchema.strict();\nexport type UiRef = z.infer<typeof UiRefSchema>;\nexport type UiInput = UiRef;\n\n/** The `app.host` slot — production/integrity come from the deploy map. */\nexport const HostRefSchema = DevelopmentRefSchema.extend({\n secrets: z.array(z.string()).optional(),\n}).strict();\nexport type HostRef = z.infer<typeof HostRefSchema>;\nexport type HostInput = HostRef;\n\n/**\n * The authored App — pure data, the boot input. Local paths only (dev\n * authoring shape); deployment resolves them through the deploy map.\n */\nexport const AppDescriptorSchema = z\n .object({\n name: z.string(),\n /**\n * The base App this App inherits (child-wins): a registry name\n * (`\"everything.dev\"`) or an imported App descriptor value — an imported\n * parent is an inlined parent (same-repo composition; the resolved shape\n * is identical to a fetched one). The value is validated and depth-guarded\n * at resolution (flattenParent re-parses it); chains deeper than one\n * level are deferred (wayfinder ticket 08).\n */\n extends: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),\n account: z.string().optional(),\n domain: z.string().optional(),\n title: z.string().optional(),\n description: z.string().optional(),\n repository: z.string().optional(),\n testnet: z.string().optional(),\n staging: z\n .object({\n domain: z.string(),\n account: z.string().optional(),\n })\n .strict()\n .optional(),\n /** Authoring-only: the starter level this child was scaffolded with.\n * Stripped from resolved configs — publish canonicalization never\n * carries it. */\n starter: z.enum([\"simple\", \"advanced\"]).optional(),\n ci: z\n .object({\n image: z.string().optional(),\n railway: z\n .object({\n service: z.string(),\n })\n .strict()\n .optional(),\n })\n .strict()\n .optional(),\n cdn: z\n .object({\n origin: z.string().optional(),\n })\n .strict()\n .optional(),\n publish: z\n .object({\n auth: z.enum([\"session\", \"key\", \"custody\"]).optional(),\n sourcePublish: z.boolean().optional(),\n })\n .strict()\n .optional(),\n host: HostRefSchema.optional(),\n api: ApiRefSchema.optional(),\n ui: UiRefSchema.optional(),\n /** attachment landing in the `app.auth` slot */\n auth: AttachmentRefSchema.optional(),\n plugins: z.record(z.string(), AttachmentRefSchema).optional(),\n })\n .strict();\nexport type AppDescriptor = z.infer<typeof AppDescriptorSchema>;\nexport type AppInput = AppDescriptor;\n\nexport type AppRegistry = Record<string, AppInput>;\n"],"mappings":"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"}
|
|
@@ -119,6 +119,7 @@ declare const AppDescriptorSchema: z$1.ZodObject<{
|
|
|
119
119
|
key: "key";
|
|
120
120
|
session: "session";
|
|
121
121
|
}>>;
|
|
122
|
+
sourcePublish: z$1.ZodOptional<z$1.ZodBoolean>;
|
|
122
123
|
}, z$1.core.$strict>>;
|
|
123
124
|
host: z$1.ZodOptional<z$1.ZodObject<{
|
|
124
125
|
path: z$1.ZodOptional<z$1.ZodString>;
|
|
@@ -119,6 +119,7 @@ declare const AppDescriptorSchema: z$1.ZodObject<{
|
|
|
119
119
|
key: "key";
|
|
120
120
|
session: "session";
|
|
121
121
|
}>>;
|
|
122
|
+
sourcePublish: z$1.ZodOptional<z$1.ZodBoolean>;
|
|
122
123
|
}, z$1.core.$strict>>;
|
|
123
124
|
host: z$1.ZodOptional<z$1.ZodObject<{
|
|
124
125
|
path: z$1.ZodOptional<z$1.ZodString>;
|
|
@@ -84,11 +84,14 @@ const AppDescriptorSchema = z$1.object({
|
|
|
84
84
|
railway: z$1.object({ service: z$1.string() }).strict().optional()
|
|
85
85
|
}).strict().optional(),
|
|
86
86
|
cdn: z$1.object({ origin: z$1.string().optional() }).strict().optional(),
|
|
87
|
-
publish: z$1.object({
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
publish: z$1.object({
|
|
88
|
+
auth: z$1.enum([
|
|
89
|
+
"session",
|
|
90
|
+
"key",
|
|
91
|
+
"custody"
|
|
92
|
+
]).optional(),
|
|
93
|
+
sourcePublish: z$1.boolean().optional()
|
|
94
|
+
}).strict().optional(),
|
|
92
95
|
host: HostRefSchema.optional(),
|
|
93
96
|
api: ApiRefSchema.optional(),
|
|
94
97
|
ui: UiRefSchema.optional(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.mjs","names":["z"],"sources":["../../src/descriptor/schema.ts"],"sourcesContent":["import * as z from \"zod\";\nimport { JsonObjectSchema, SharedDepMapSchema, SlotPinSchema } from \"../types\";\n\n/** Pipeline-owned fields — never authored; injected from the deploy map. */\nexport const PipelineFieldsSchema = z.object({\n production: z.string().optional(),\n integrity: z.string().optional(),\n ssr: z.string().optional(),\n ssrIntegrity: z.string().optional(),\n pin: SlotPinSchema.optional(),\n});\nexport type PipelineFields = z.infer<typeof PipelineFieldsSchema>;\n\nconst DevelopmentRefSchema = z.object({\n /** local workspace directory, relative to the App root — emitted as `local:<path>` */\n path: z.string().optional(),\n /** explicit development ref override (e.g. a remote dev URL) */\n development: z.string().optional(),\n});\n\n/** An attached App (today's `plugins.<id>` / `app.auth` entries). */\nexport const AttachmentRefSchema = DevelopmentRefSchema.extend({\n name: z.string().optional(),\n /** published module reference (bos://…) consumed by resolution */\n extends: z.string().optional(),\n proxy: z.string().optional(),\n variables: JsonObjectSchema.optional(),\n secrets: z.array(z.string()).optional(),\n routes: z.array(z.string()).optional(),\n shared: SharedDepMapSchema.optional(),\n connectSrc: z.array(z.string()).optional(),\n dependsOn: z.array(z.string()).optional(),\n version: z.string().optional(),\n ui: z\n .object({\n name: z.string().optional(),\n path: z.string().optional(),\n development: z.string().optional(),\n integrity: z.string().optional(),\n pin: SlotPinSchema.optional(),\n })\n .strict()\n .optional(),\n});\nexport type AttachmentRef = z.infer<typeof AttachmentRefSchema>;\nexport type AttachmentInput = AttachmentRef;\n\n/** The `app.api` slot. */\nexport const ApiRefSchema = DevelopmentRefSchema.extend({\n proxy: z.string().optional(),\n variables: JsonObjectSchema.optional(),\n secrets: z.array(z.string()).optional(),\n routes: z.array(z.string()).optional(),\n shared: SharedDepMapSchema.optional(),\n dependsOn: z.array(z.string()).optional(),\n}).strict();\nexport type ApiRef = z.infer<typeof ApiRefSchema>;\nexport type ApiInput = ApiRef;\n\n/** The `app.ui` slot — pipeline fields (production/integrity/ssr) come from the deploy map. */\nexport const UiRefSchema = DevelopmentRefSchema.strict();\nexport type UiRef = z.infer<typeof UiRefSchema>;\nexport type UiInput = UiRef;\n\n/** The `app.host` slot — production/integrity come from the deploy map. */\nexport const HostRefSchema = DevelopmentRefSchema.extend({\n secrets: z.array(z.string()).optional(),\n}).strict();\nexport type HostRef = z.infer<typeof HostRefSchema>;\nexport type HostInput = HostRef;\n\n/**\n * The authored App — pure data, the boot input. Local paths only (dev\n * authoring shape); deployment resolves them through the deploy map.\n */\nexport const AppDescriptorSchema = z\n .object({\n name: z.string(),\n /**\n * The base App this App inherits (child-wins): a registry name\n * (`\"everything.dev\"`) or an imported App descriptor value — an imported\n * parent is an inlined parent (same-repo composition; the resolved shape\n * is identical to a fetched one). The value is validated and depth-guarded\n * at resolution (flattenParent re-parses it); chains deeper than one\n * level are deferred (wayfinder ticket 08).\n */\n extends: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),\n account: z.string().optional(),\n domain: z.string().optional(),\n title: z.string().optional(),\n description: z.string().optional(),\n repository: z.string().optional(),\n testnet: z.string().optional(),\n staging: z\n .object({\n domain: z.string(),\n account: z.string().optional(),\n })\n .strict()\n .optional(),\n /** Authoring-only: the starter level this child was scaffolded with.\n * Stripped from resolved configs — publish canonicalization never\n * carries it. */\n starter: z.enum([\"simple\", \"advanced\"]).optional(),\n ci: z\n .object({\n image: z.string().optional(),\n railway: z\n .object({\n service: z.string(),\n })\n .strict()\n .optional(),\n })\n .strict()\n .optional(),\n cdn: z\n .object({\n origin: z.string().optional(),\n })\n .strict()\n .optional(),\n publish: z\n .object({\n auth: z.enum([\"session\", \"key\", \"custody\"]).optional(),\n })\n .strict()\n .optional(),\n host: HostRefSchema.optional(),\n api: ApiRefSchema.optional(),\n ui: UiRefSchema.optional(),\n /** attachment landing in the `app.auth` slot */\n auth: AttachmentRefSchema.optional(),\n plugins: z.record(z.string(), AttachmentRefSchema).optional(),\n })\n .strict();\nexport type AppDescriptor = z.infer<typeof AppDescriptorSchema>;\nexport type AppInput = AppDescriptor;\n\nexport type AppRegistry = Record<string, AppInput>;\n"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"schema.mjs","names":["z"],"sources":["../../src/descriptor/schema.ts"],"sourcesContent":["import * as z from \"zod\";\nimport { JsonObjectSchema, SharedDepMapSchema, SlotPinSchema } from \"../types\";\n\n/** Pipeline-owned fields — never authored; injected from the deploy map. */\nexport const PipelineFieldsSchema = z.object({\n production: z.string().optional(),\n integrity: z.string().optional(),\n ssr: z.string().optional(),\n ssrIntegrity: z.string().optional(),\n pin: SlotPinSchema.optional(),\n});\nexport type PipelineFields = z.infer<typeof PipelineFieldsSchema>;\n\nconst DevelopmentRefSchema = z.object({\n /** local workspace directory, relative to the App root — emitted as `local:<path>` */\n path: z.string().optional(),\n /** explicit development ref override (e.g. a remote dev URL) */\n development: z.string().optional(),\n});\n\n/** An attached App (today's `plugins.<id>` / `app.auth` entries). */\nexport const AttachmentRefSchema = DevelopmentRefSchema.extend({\n name: z.string().optional(),\n /** published module reference (bos://…) consumed by resolution */\n extends: z.string().optional(),\n proxy: z.string().optional(),\n variables: JsonObjectSchema.optional(),\n secrets: z.array(z.string()).optional(),\n routes: z.array(z.string()).optional(),\n shared: SharedDepMapSchema.optional(),\n connectSrc: z.array(z.string()).optional(),\n dependsOn: z.array(z.string()).optional(),\n version: z.string().optional(),\n ui: z\n .object({\n name: z.string().optional(),\n path: z.string().optional(),\n development: z.string().optional(),\n integrity: z.string().optional(),\n pin: SlotPinSchema.optional(),\n })\n .strict()\n .optional(),\n});\nexport type AttachmentRef = z.infer<typeof AttachmentRefSchema>;\nexport type AttachmentInput = AttachmentRef;\n\n/** The `app.api` slot. */\nexport const ApiRefSchema = DevelopmentRefSchema.extend({\n proxy: z.string().optional(),\n variables: JsonObjectSchema.optional(),\n secrets: z.array(z.string()).optional(),\n routes: z.array(z.string()).optional(),\n shared: SharedDepMapSchema.optional(),\n dependsOn: z.array(z.string()).optional(),\n}).strict();\nexport type ApiRef = z.infer<typeof ApiRefSchema>;\nexport type ApiInput = ApiRef;\n\n/** The `app.ui` slot — pipeline fields (production/integrity/ssr) come from the deploy map. */\nexport const UiRefSchema = DevelopmentRefSchema.strict();\nexport type UiRef = z.infer<typeof UiRefSchema>;\nexport type UiInput = UiRef;\n\n/** The `app.host` slot — production/integrity come from the deploy map. */\nexport const HostRefSchema = DevelopmentRefSchema.extend({\n secrets: z.array(z.string()).optional(),\n}).strict();\nexport type HostRef = z.infer<typeof HostRefSchema>;\nexport type HostInput = HostRef;\n\n/**\n * The authored App — pure data, the boot input. Local paths only (dev\n * authoring shape); deployment resolves them through the deploy map.\n */\nexport const AppDescriptorSchema = z\n .object({\n name: z.string(),\n /**\n * The base App this App inherits (child-wins): a registry name\n * (`\"everything.dev\"`) or an imported App descriptor value — an imported\n * parent is an inlined parent (same-repo composition; the resolved shape\n * is identical to a fetched one). The value is validated and depth-guarded\n * at resolution (flattenParent re-parses it); chains deeper than one\n * level are deferred (wayfinder ticket 08).\n */\n extends: z.union([z.string(), z.record(z.string(), z.unknown())]).optional(),\n account: z.string().optional(),\n domain: z.string().optional(),\n title: z.string().optional(),\n description: z.string().optional(),\n repository: z.string().optional(),\n testnet: z.string().optional(),\n staging: z\n .object({\n domain: z.string(),\n account: z.string().optional(),\n })\n .strict()\n .optional(),\n /** Authoring-only: the starter level this child was scaffolded with.\n * Stripped from resolved configs — publish canonicalization never\n * carries it. */\n starter: z.enum([\"simple\", \"advanced\"]).optional(),\n ci: z\n .object({\n image: z.string().optional(),\n railway: z\n .object({\n service: z.string(),\n })\n .strict()\n .optional(),\n })\n .strict()\n .optional(),\n cdn: z\n .object({\n origin: z.string().optional(),\n })\n .strict()\n .optional(),\n publish: z\n .object({\n auth: z.enum([\"session\", \"key\", \"custody\"]).optional(),\n sourcePublish: z.boolean().optional(),\n })\n .strict()\n .optional(),\n host: HostRefSchema.optional(),\n api: ApiRefSchema.optional(),\n ui: UiRefSchema.optional(),\n /** attachment landing in the `app.auth` slot */\n auth: AttachmentRefSchema.optional(),\n plugins: z.record(z.string(), AttachmentRefSchema).optional(),\n })\n .strict();\nexport type AppDescriptor = z.infer<typeof AppDescriptorSchema>;\nexport type AppInput = AppDescriptor;\n\nexport type AppRegistry = Record<string, AppInput>;\n"],"mappings":"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"}
|
|
@@ -13,7 +13,7 @@ const FIELD_COMMENTS = {
|
|
|
13
13
|
starter: "Authoring-only: the starter level this child was scaffolded with (simple | advanced).",
|
|
14
14
|
ci: "CI settings: runtime image name, Railway service.",
|
|
15
15
|
cdn: "CDN origin serving this runtime's built bundles.",
|
|
16
|
-
publish: "Publish settings: signing mode (session | key | custody).",
|
|
16
|
+
publish: "Publish settings: signing mode (session | key | custody) and sourcePublish (publish workspace source archives with each deploy).",
|
|
17
17
|
host: "Local host workspace override.",
|
|
18
18
|
api: "Local API workspace override.",
|
|
19
19
|
ui: "Local UI workspace override.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.cjs","names":["configInputToDescriptor"],"sources":["../../src/descriptor/serialize.ts"],"sourcesContent":["/**\n * The authored `bos.app.ts` source emitter — the scaffold surface of the\n * descriptor module. The descriptor is pure data (`toConfigInput` and its\n * inverse live in `./resolve`), so the scaffold emits the personalized\n * descriptor as a literal inside the `App()` call; publish/sync still\n * canonicalize the resolved config to JSON for FastKV.\n *\n * Each top-level descriptor field gets a one-line doc comment from\n * `FIELD_COMMENTS`; when a field is added to the descriptor schema, add its\n * comment line in the same commit. Fields without a comment entry are\n * emitted bare.\n */\n\nimport type { BosConfigInput } from \"../types\";\nimport { configInputToDescriptor } from \"./resolve\";\n\nconst FIELD_COMMENTS: Record<string, string> = {\n extends:\n \"The base runtime this app extends — inherit the platform, override only what you change.\",\n account: \"The NEAR account this app publishes under.\",\n domain: \"The gateway: FastKV lookup key and public ingress for this runtime.\",\n title: \"Human-readable runtime title (surfaced by the UI).\",\n description: \"Human-readable runtime description.\",\n repository: \"GitHub repository tarball source used by `bos init`/`bos sync` templates.\",\n testnet: \"Testnet identity for this runtime.\",\n staging: \"Staging environment identity.\",\n starter: \"Authoring-only: the starter level this child was scaffolded with (simple | advanced).\",\n ci: \"CI settings: runtime image name, Railway service.\",\n cdn: \"CDN origin serving this runtime's built bundles.\",\n publish
|
|
1
|
+
{"version":3,"file":"serialize.cjs","names":["configInputToDescriptor"],"sources":["../../src/descriptor/serialize.ts"],"sourcesContent":["/**\n * The authored `bos.app.ts` source emitter — the scaffold surface of the\n * descriptor module. The descriptor is pure data (`toConfigInput` and its\n * inverse live in `./resolve`), so the scaffold emits the personalized\n * descriptor as a literal inside the `App()` call; publish/sync still\n * canonicalize the resolved config to JSON for FastKV.\n *\n * Each top-level descriptor field gets a one-line doc comment from\n * `FIELD_COMMENTS`; when a field is added to the descriptor schema, add its\n * comment line in the same commit. Fields without a comment entry are\n * emitted bare.\n */\n\nimport type { BosConfigInput } from \"../types\";\nimport { configInputToDescriptor } from \"./resolve\";\n\nconst FIELD_COMMENTS: Record<string, string> = {\n extends:\n \"The base runtime this app extends — inherit the platform, override only what you change.\",\n account: \"The NEAR account this app publishes under.\",\n domain: \"The gateway: FastKV lookup key and public ingress for this runtime.\",\n title: \"Human-readable runtime title (surfaced by the UI).\",\n description: \"Human-readable runtime description.\",\n repository: \"GitHub repository tarball source used by `bos init`/`bos sync` templates.\",\n testnet: \"Testnet identity for this runtime.\",\n staging: \"Staging environment identity.\",\n starter: \"Authoring-only: the starter level this child was scaffolded with (simple | advanced).\",\n ci: \"CI settings: runtime image name, Railway service.\",\n cdn: \"CDN origin serving this runtime's built bundles.\",\n publish:\n \"Publish settings: signing mode (session | key | custody) and sourcePublish (publish workspace source archives with each deploy).\",\n host: \"Local host workspace override.\",\n api: \"Local API workspace override.\",\n ui: \"Local UI workspace override.\",\n auth: \"Auth attachment — lands in the app.auth slot (e.g. the Better-Auth + NEAR SIWN plugin).\",\n plugins:\n \"Attached plugins, keyed by registry key. `path` = local workspace, `extends` = published module.\",\n};\n\n/** The authored `bos.app.ts` source for a personalized child config. */\nexport function serializeAppDescriptorSource(input: BosConfigInput): string {\n const descriptor = configInputToDescriptor(input);\n const blocks: string[] = [];\n for (const [key, value] of Object.entries(descriptor)) {\n const comment = FIELD_COMMENTS[key];\n const body = JSON.stringify({ [key]: value }, null, 2);\n const block = [...(comment ? [` // ${comment}`] : []), ...body.split(\"\\n\").slice(1, -1)];\n blocks.push(block.join(\"\\n\"));\n }\n return `import { App } from \"everything-dev/descriptor\";\n\n// Authored app descriptor. The published config is canonicalized to JSON\n// from this file; dev-only overrides live in bos.dev.ts (never published).\nexport default App({\n${blocks.join(\",\\n\")},\n});\n`;\n}\n"],"mappings":";;;AAgBA,MAAM,iBAAyC;CAC7C,SACE;CACF,SAAS;CACT,QAAQ;CACR,OAAO;CACP,aAAa;CACb,YAAY;CACZ,SAAS;CACT,SAAS;CACT,SAAS;CACT,IAAI;CACJ,KAAK;CACL,SACE;CACF,MAAM;CACN,KAAK;CACL,IAAI;CACJ,MAAM;CACN,SACE;AACJ;;AAGA,SAAgB,6BAA6B,OAA+B;CAC1E,MAAM,aAAaA,wCAAwB,KAAK;CAChD,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,UAAU,GAAG;EACrD,MAAM,UAAU,eAAe;EAC/B,MAAM,OAAO,KAAK,UAAU,GAAG,MAAM,MAAM,GAAG,MAAM,CAAC;EACrD,MAAM,QAAQ,CAAC,GAAI,UAAU,CAAC,QAAQ,SAAS,IAAI,CAAC,GAAI,GAAG,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC;EACxF,OAAO,KAAK,MAAM,KAAK,IAAI,CAAC;CAC9B;CACA,OAAO;;;;;EAKP,OAAO,KAAK,KAAK,EAAE;;;AAGrB"}
|
|
@@ -13,7 +13,7 @@ const FIELD_COMMENTS = {
|
|
|
13
13
|
starter: "Authoring-only: the starter level this child was scaffolded with (simple | advanced).",
|
|
14
14
|
ci: "CI settings: runtime image name, Railway service.",
|
|
15
15
|
cdn: "CDN origin serving this runtime's built bundles.",
|
|
16
|
-
publish: "Publish settings: signing mode (session | key | custody).",
|
|
16
|
+
publish: "Publish settings: signing mode (session | key | custody) and sourcePublish (publish workspace source archives with each deploy).",
|
|
17
17
|
host: "Local host workspace override.",
|
|
18
18
|
api: "Local API workspace override.",
|
|
19
19
|
ui: "Local UI workspace override.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.mjs","names":[],"sources":["../../src/descriptor/serialize.ts"],"sourcesContent":["/**\n * The authored `bos.app.ts` source emitter — the scaffold surface of the\n * descriptor module. The descriptor is pure data (`toConfigInput` and its\n * inverse live in `./resolve`), so the scaffold emits the personalized\n * descriptor as a literal inside the `App()` call; publish/sync still\n * canonicalize the resolved config to JSON for FastKV.\n *\n * Each top-level descriptor field gets a one-line doc comment from\n * `FIELD_COMMENTS`; when a field is added to the descriptor schema, add its\n * comment line in the same commit. Fields without a comment entry are\n * emitted bare.\n */\n\nimport type { BosConfigInput } from \"../types\";\nimport { configInputToDescriptor } from \"./resolve\";\n\nconst FIELD_COMMENTS: Record<string, string> = {\n extends:\n \"The base runtime this app extends — inherit the platform, override only what you change.\",\n account: \"The NEAR account this app publishes under.\",\n domain: \"The gateway: FastKV lookup key and public ingress for this runtime.\",\n title: \"Human-readable runtime title (surfaced by the UI).\",\n description: \"Human-readable runtime description.\",\n repository: \"GitHub repository tarball source used by `bos init`/`bos sync` templates.\",\n testnet: \"Testnet identity for this runtime.\",\n staging: \"Staging environment identity.\",\n starter: \"Authoring-only: the starter level this child was scaffolded with (simple | advanced).\",\n ci: \"CI settings: runtime image name, Railway service.\",\n cdn: \"CDN origin serving this runtime's built bundles.\",\n publish
|
|
1
|
+
{"version":3,"file":"serialize.mjs","names":[],"sources":["../../src/descriptor/serialize.ts"],"sourcesContent":["/**\n * The authored `bos.app.ts` source emitter — the scaffold surface of the\n * descriptor module. The descriptor is pure data (`toConfigInput` and its\n * inverse live in `./resolve`), so the scaffold emits the personalized\n * descriptor as a literal inside the `App()` call; publish/sync still\n * canonicalize the resolved config to JSON for FastKV.\n *\n * Each top-level descriptor field gets a one-line doc comment from\n * `FIELD_COMMENTS`; when a field is added to the descriptor schema, add its\n * comment line in the same commit. Fields without a comment entry are\n * emitted bare.\n */\n\nimport type { BosConfigInput } from \"../types\";\nimport { configInputToDescriptor } from \"./resolve\";\n\nconst FIELD_COMMENTS: Record<string, string> = {\n extends:\n \"The base runtime this app extends — inherit the platform, override only what you change.\",\n account: \"The NEAR account this app publishes under.\",\n domain: \"The gateway: FastKV lookup key and public ingress for this runtime.\",\n title: \"Human-readable runtime title (surfaced by the UI).\",\n description: \"Human-readable runtime description.\",\n repository: \"GitHub repository tarball source used by `bos init`/`bos sync` templates.\",\n testnet: \"Testnet identity for this runtime.\",\n staging: \"Staging environment identity.\",\n starter: \"Authoring-only: the starter level this child was scaffolded with (simple | advanced).\",\n ci: \"CI settings: runtime image name, Railway service.\",\n cdn: \"CDN origin serving this runtime's built bundles.\",\n publish:\n \"Publish settings: signing mode (session | key | custody) and sourcePublish (publish workspace source archives with each deploy).\",\n host: \"Local host workspace override.\",\n api: \"Local API workspace override.\",\n ui: \"Local UI workspace override.\",\n auth: \"Auth attachment — lands in the app.auth slot (e.g. the Better-Auth + NEAR SIWN plugin).\",\n plugins:\n \"Attached plugins, keyed by registry key. `path` = local workspace, `extends` = published module.\",\n};\n\n/** The authored `bos.app.ts` source for a personalized child config. */\nexport function serializeAppDescriptorSource(input: BosConfigInput): string {\n const descriptor = configInputToDescriptor(input);\n const blocks: string[] = [];\n for (const [key, value] of Object.entries(descriptor)) {\n const comment = FIELD_COMMENTS[key];\n const body = JSON.stringify({ [key]: value }, null, 2);\n const block = [...(comment ? [` // ${comment}`] : []), ...body.split(\"\\n\").slice(1, -1)];\n blocks.push(block.join(\"\\n\"));\n }\n return `import { App } from \"everything-dev/descriptor\";\n\n// Authored app descriptor. The published config is canonicalized to JSON\n// from this file; dev-only overrides live in bos.dev.ts (never published).\nexport default App({\n${blocks.join(\",\\n\")},\n});\n`;\n}\n"],"mappings":";;;AAgBA,MAAM,iBAAyC;CAC7C,SACE;CACF,SAAS;CACT,QAAQ;CACR,OAAO;CACP,aAAa;CACb,YAAY;CACZ,SAAS;CACT,SAAS;CACT,SAAS;CACT,IAAI;CACJ,KAAK;CACL,SACE;CACF,MAAM;CACN,KAAK;CACL,IAAI;CACJ,MAAM;CACN,SACE;AACJ;;AAGA,SAAgB,6BAA6B,OAA+B;CAC1E,MAAM,aAAa,wBAAwB,KAAK;CAChD,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,UAAU,GAAG;EACrD,MAAM,UAAU,eAAe;EAC/B,MAAM,OAAO,KAAK,UAAU,GAAG,MAAM,MAAM,GAAG,MAAM,CAAC;EACrD,MAAM,QAAQ,CAAC,GAAI,UAAU,CAAC,QAAQ,SAAS,IAAI,CAAC,GAAI,GAAG,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC;EACxF,OAAO,KAAK,MAAM,KAAK,IAAI,CAAC;CAC9B;CACA,OAAO;;;;;EAKP,OAAO,KAAK,KAAK,EAAE;;;AAGrB"}
|
package/dist/http-client.cjs
CHANGED
|
@@ -72,6 +72,19 @@ var HttpFetch = class HttpFetch extends effect.Context.Service()("everything-dev
|
|
|
72
72
|
};
|
|
73
73
|
const getCache = /* @__PURE__ */ new Map();
|
|
74
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
|
+
}
|
|
75
88
|
function isCacheable(_url, options) {
|
|
76
89
|
if (options?.method && options.method !== "GET") return false;
|
|
77
90
|
if (options?.body) return false;
|
|
@@ -99,10 +112,7 @@ const fetchJsonOrNull = async (url, options) => {
|
|
|
99
112
|
}
|
|
100
113
|
try {
|
|
101
114
|
const data = await (await effect.Effect.runPromise(defaultHttpFetch.get(url, options))).json();
|
|
102
|
-
if (isCacheable(url, options))
|
|
103
|
-
data,
|
|
104
|
-
expiresAt: Date.now() + GET_CACHE_TTL_MS
|
|
105
|
-
});
|
|
115
|
+
if (isCacheable(url, options)) setCached(url, data);
|
|
106
116
|
return data;
|
|
107
117
|
} catch (error) {
|
|
108
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}`;
|
package/dist/http-client.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-client.cjs","names":["Data","Duration","Effect","Schedule","Context","Layer"],"sources":["../src/http-client.ts"],"sourcesContent":["import { Context, Data, Duration, Effect, Layer, Schedule } from \"effect\";\n\n// --- Tagged errors ---\n\nexport class FetchNetworkError extends Data.TaggedError(\"FetchNetworkError\")<{\n url: string;\n cause: unknown;\n}> {}\n\nexport class FetchTimeoutError extends Data.TaggedError(\"FetchTimeoutError\")<{\n url: string;\n}> {}\n\nexport class FetchHttpError extends Data.TaggedError(\"FetchHttpError\")<{\n url: string;\n status: number;\n statusText: string;\n}> {}\n\nexport type FetchError = FetchNetworkError | FetchTimeoutError | FetchHttpError;\n\n// --- Options ---\n\nexport interface FetchOptions {\n method?: string;\n headers?: Record<string, string>;\n body?: BodyInit;\n redirect?: RequestRedirect;\n timeout?: Duration.Input;\n}\n\nexport interface FetchWithRetryOptions extends FetchOptions {\n retries?: number;\n}\n\n// --- Defaults ---\n\nconst DEFAULT_TIMEOUT = \"10 seconds\";\nconst DEFAULT_RETRIES = 3;\n\nconst EXPONENTIAL_BASE = Duration.seconds(1);\nconst EXPONENTIAL_CAP = Duration.seconds(15);\n\ntype FetchImpl = typeof globalThis.fetch;\n\n// --- Helpers ---\n\nconst isRetryable = (error: FetchError): boolean => {\n if (error instanceof FetchNetworkError) return true;\n if (error instanceof FetchTimeoutError) return true;\n if (error instanceof FetchHttpError) return error.status >= 500;\n return false;\n};\n\nexport interface HttpFetchShape {\n /** One network attempt: no status assertion, no internal deadline. The\n * fiber's signal is composed with any caller-provided signal, so an\n * `Effect.timeout` around the call aborts the in-flight request, and a\n * caller-initiated cancellation still wins. */\n readonly attempt: (\n input: string | URL | Request,\n init?: RequestInit,\n ) => Effect.Effect<Response, FetchNetworkError>;\n /** attempt + deadline + ok assertion (non-2xx → `FetchHttpError`) + capped\n * exponential jittered retry (5xx/network/timeout retried, 4xx not). */\n readonly get: (\n url: string,\n options?: FetchWithRetryOptions,\n ) => Effect.Effect<Response, FetchError>;\n}\n\nconst makeHttpFetch = (\n base: (input: string | URL | Request, init?: RequestInit) => Promise<Response>,\n): HttpFetchShape => {\n const urlOf = (input: string | URL | Request): string =>\n typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n const attempt = (\n input: string | URL | Request,\n init?: RequestInit,\n ): Effect.Effect<Response, FetchNetworkError> =>\n Effect.tryPromise({\n try: (signal) =>\n base(input, {\n ...init,\n signal: init?.signal ? AbortSignal.any([init.signal, signal]) : signal,\n }),\n catch: (cause) => new FetchNetworkError({ url: urlOf(input), cause }),\n });\n\n const get = (\n url: string,\n options?: FetchWithRetryOptions,\n ): Effect.Effect<Response, FetchError> => {\n const timeoutMs = Duration.toMillis(options?.timeout ?? DEFAULT_TIMEOUT);\n const retries = options?.retries ?? DEFAULT_RETRIES;\n const once = attempt(url, {\n method: options?.method ?? \"GET\",\n headers: options?.headers,\n body: options?.body,\n redirect: options?.redirect,\n }).pipe(\n Effect.timeoutOrElse({\n duration: timeoutMs,\n orElse: () => Effect.fail(new FetchTimeoutError({ url })),\n }),\n Effect.filterOrFail(\n (response) => response.ok,\n (response) =>\n new FetchHttpError({ url, status: response.status, statusText: response.statusText }),\n ),\n );\n if (retries <= 0) return once;\n return once.pipe(\n Effect.retry({\n schedule: Schedule.exponential(EXPONENTIAL_BASE).pipe(\n Schedule.upTo({ duration: EXPONENTIAL_CAP, times: retries }),\n Schedule.jittered,\n ),\n while: isRetryable,\n }),\n );\n };\n\n return { attempt, get };\n};\n\nconst defaultHttpFetch = makeHttpFetch((url, init) => globalThis.fetch(url, init));\n\n/**\n * The HTTP seam for boot-path consumers (bundle resolution, federation\n * loading, orchestrator deadlines): one resilience policy — interruption-\n * backed deadlines, capped exponential jittered retry, typed errors — with\n * the base fetch injectable so the bundle fetch interceptor can point it at\n * the pre-patch `fetch` and not re-enter itself.\n */\nexport class HttpFetch extends Context.Service<HttpFetch, HttpFetchShape>()(\n \"everything-dev/http-client/HttpFetch\",\n) {\n static layerWith(options: { fetchImpl: FetchImpl }): Layer.Layer<HttpFetch> {\n return Layer.succeed(HttpFetch, makeHttpFetch(options.fetchImpl));\n }\n\n static readonly layer: Layer.Layer<HttpFetch> = Layer.succeed(HttpFetch, defaultHttpFetch);\n}\n\n// --- In-memory GET cache (per-process, prevents duplicate round trips) ---\n\nconst getCache = new Map<string, { data: unknown; expiresAt: number }>();\nconst GET_CACHE_TTL_MS = 30_000;\n\nexport function clearHttpCache(): void {\n getCache.clear();\n}\n\nfunction isCacheable(_url: string, options?: FetchWithRetryOptions): boolean {\n if (options?.method && options.method !== \"GET\") return false;\n if (options?.body) return false;\n return true;\n}\n\n// --- Promise wrappers (bridge for non-Effect code) — the ambient call-time\n// lookup means a boot fetch crossing the bundle interceptor still gains its\n// retry/deadline policy; the fiber-signal composition aborts the in-flight\n// request at the deadline. ---\n\n/** One raw fetch attempt with a deadline: returns the Response regardless of\n * status; rejects on network failure or deadline expiry. */\nexport const fetchRaw = (\n url: string,\n init?: RequestInit,\n timeout: Duration.Input = DEFAULT_TIMEOUT,\n): Promise<Response> =>\n Effect.runPromise(\n defaultHttpFetch.attempt(url, init).pipe(\n Effect.timeoutOrElse({\n duration: timeout,\n orElse: () => Effect.fail(new FetchTimeoutError({ url })),\n }),\n ),\n );\n\nexport const fetchResponse = (url: string, options?: FetchOptions): Promise<Response> =>\n Effect.runPromise(\n defaultHttpFetch\n .attempt(url, {\n method: options?.method,\n headers: options?.headers,\n body: options?.body,\n redirect: options?.redirect,\n })\n .pipe(\n Effect.timeoutOrElse({\n duration: options?.timeout ?? DEFAULT_TIMEOUT,\n orElse: () => Effect.fail(new FetchTimeoutError({ url })),\n }),\n ),\n );\n\nexport const fetchJsonOrNull = async <T>(\n url: string,\n options?: FetchWithRetryOptions,\n): Promise<T | null> => {\n if (isCacheable(url, options)) {\n const cached = getCache.get(url);\n if (cached && cached.expiresAt > Date.now()) {\n return cached.data as T;\n }\n }\n\n try {\n const res = await Effect.runPromise(defaultHttpFetch.get(url, options));\n const data = (await res.json()) as T;\n if (isCacheable(url, options)) {\n getCache.set(url, { data, expiresAt: Date.now() + GET_CACHE_TTL_MS });\n }\n return data;\n } catch (error) {\n const msg =\n error instanceof FetchNetworkError\n ? `[http] Network error: ${error.url} — ${String(error.cause)}`\n : error instanceof FetchTimeoutError\n ? `[http] Timeout: ${error.url}`\n : error instanceof FetchHttpError\n ? `[http] HTTP ${error.status}: ${error.url}`\n : `[http] Unknown error while fetching ${url}`;\n console.error(msg);\n return null;\n }\n};\n\nexport const fetchWithRetryEff = (\n url: string,\n options?: FetchWithRetryOptions,\n): Effect.Effect<Response, FetchError> => defaultHttpFetch.get(url, options);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"http-client.cjs","names":["Data","Duration","Effect","Schedule","Context","Layer"],"sources":["../src/http-client.ts"],"sourcesContent":["import { Context, Data, Duration, Effect, Layer, Schedule } from \"effect\";\n\n// --- Tagged errors ---\n\nexport class FetchNetworkError extends Data.TaggedError(\"FetchNetworkError\")<{\n url: string;\n cause: unknown;\n}> {}\n\nexport class FetchTimeoutError extends Data.TaggedError(\"FetchTimeoutError\")<{\n url: string;\n}> {}\n\nexport class FetchHttpError extends Data.TaggedError(\"FetchHttpError\")<{\n url: string;\n status: number;\n statusText: string;\n}> {}\n\nexport type FetchError = FetchNetworkError | FetchTimeoutError | FetchHttpError;\n\n// --- Options ---\n\nexport interface FetchOptions {\n method?: string;\n headers?: Record<string, string>;\n body?: BodyInit;\n redirect?: RequestRedirect;\n timeout?: Duration.Input;\n}\n\nexport interface FetchWithRetryOptions extends FetchOptions {\n retries?: number;\n}\n\n// --- Defaults ---\n\nconst DEFAULT_TIMEOUT = \"10 seconds\";\nconst DEFAULT_RETRIES = 3;\n\nconst EXPONENTIAL_BASE = Duration.seconds(1);\nconst EXPONENTIAL_CAP = Duration.seconds(15);\n\ntype FetchImpl = typeof globalThis.fetch;\n\n// --- Helpers ---\n\nconst isRetryable = (error: FetchError): boolean => {\n if (error instanceof FetchNetworkError) return true;\n if (error instanceof FetchTimeoutError) return true;\n if (error instanceof FetchHttpError) return error.status >= 500;\n return false;\n};\n\nexport interface HttpFetchShape {\n /** One network attempt: no status assertion, no internal deadline. The\n * fiber's signal is composed with any caller-provided signal, so an\n * `Effect.timeout` around the call aborts the in-flight request, and a\n * caller-initiated cancellation still wins. */\n readonly attempt: (\n input: string | URL | Request,\n init?: RequestInit,\n ) => Effect.Effect<Response, FetchNetworkError>;\n /** attempt + deadline + ok assertion (non-2xx → `FetchHttpError`) + capped\n * exponential jittered retry (5xx/network/timeout retried, 4xx not). */\n readonly get: (\n url: string,\n options?: FetchWithRetryOptions,\n ) => Effect.Effect<Response, FetchError>;\n}\n\nconst makeHttpFetch = (\n base: (input: string | URL | Request, init?: RequestInit) => Promise<Response>,\n): HttpFetchShape => {\n const urlOf = (input: string | URL | Request): string =>\n typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n\n const attempt = (\n input: string | URL | Request,\n init?: RequestInit,\n ): Effect.Effect<Response, FetchNetworkError> =>\n Effect.tryPromise({\n try: (signal) =>\n base(input, {\n ...init,\n signal: init?.signal ? AbortSignal.any([init.signal, signal]) : signal,\n }),\n catch: (cause) => new FetchNetworkError({ url: urlOf(input), cause }),\n });\n\n const get = (\n url: string,\n options?: FetchWithRetryOptions,\n ): Effect.Effect<Response, FetchError> => {\n const timeoutMs = Duration.toMillis(options?.timeout ?? DEFAULT_TIMEOUT);\n const retries = options?.retries ?? DEFAULT_RETRIES;\n const once = attempt(url, {\n method: options?.method ?? \"GET\",\n headers: options?.headers,\n body: options?.body,\n redirect: options?.redirect,\n }).pipe(\n Effect.timeoutOrElse({\n duration: timeoutMs,\n orElse: () => Effect.fail(new FetchTimeoutError({ url })),\n }),\n Effect.filterOrFail(\n (response) => response.ok,\n (response) =>\n new FetchHttpError({ url, status: response.status, statusText: response.statusText }),\n ),\n );\n if (retries <= 0) return once;\n return once.pipe(\n Effect.retry({\n schedule: Schedule.exponential(EXPONENTIAL_BASE).pipe(\n Schedule.upTo({ duration: EXPONENTIAL_CAP, times: retries }),\n Schedule.jittered,\n ),\n while: isRetryable,\n }),\n );\n };\n\n return { attempt, get };\n};\n\nconst defaultHttpFetch = makeHttpFetch((url, init) => globalThis.fetch(url, init));\n\n/**\n * The HTTP seam for boot-path consumers (bundle resolution, federation\n * loading, orchestrator deadlines): one resilience policy — interruption-\n * backed deadlines, capped exponential jittered retry, typed errors — with\n * the base fetch injectable so the bundle fetch interceptor can point it at\n * the pre-patch `fetch` and not re-enter itself.\n */\nexport class HttpFetch extends Context.Service<HttpFetch, HttpFetchShape>()(\n \"everything-dev/http-client/HttpFetch\",\n) {\n static layerWith(options: { fetchImpl: FetchImpl }): Layer.Layer<HttpFetch> {\n return Layer.succeed(HttpFetch, makeHttpFetch(options.fetchImpl));\n }\n\n static readonly layer: Layer.Layer<HttpFetch> = Layer.succeed(HttpFetch, defaultHttpFetch);\n}\n\n// --- In-memory GET cache (per-process, prevents duplicate round trips) ---\n\nconst getCache = new Map<string, { data: unknown; expiresAt: number }>();\nconst GET_CACHE_TTL_MS = 30_000;\nconst GET_CACHE_MAX_ENTRIES = 128;\n\nexport function clearHttpCache(): void {\n getCache.clear();\n}\n\nexport function httpCacheSize(): number {\n return getCache.size;\n}\n\nfunction setCached(url: string, data: unknown): void {\n const now = Date.now();\n for (const [key, entry] of getCache) {\n if (entry.expiresAt <= now) getCache.delete(key);\n }\n if (getCache.size >= GET_CACHE_MAX_ENTRIES) {\n for (const key of getCache.keys()) {\n getCache.delete(key);\n if (getCache.size < GET_CACHE_MAX_ENTRIES) break;\n }\n }\n getCache.set(url, { data, expiresAt: now + GET_CACHE_TTL_MS });\n}\n\nfunction isCacheable(_url: string, options?: FetchWithRetryOptions): boolean {\n if (options?.method && options.method !== \"GET\") return false;\n if (options?.body) return false;\n return true;\n}\n\n// --- Promise wrappers (bridge for non-Effect code) — the ambient call-time\n// lookup means a boot fetch crossing the bundle interceptor still gains its\n// retry/deadline policy; the fiber-signal composition aborts the in-flight\n// request at the deadline. ---\n\n/** One raw fetch attempt with a deadline: returns the Response regardless of\n * status; rejects on network failure or deadline expiry. */\nexport const fetchRaw = (\n url: string,\n init?: RequestInit,\n timeout: Duration.Input = DEFAULT_TIMEOUT,\n): Promise<Response> =>\n Effect.runPromise(\n defaultHttpFetch.attempt(url, init).pipe(\n Effect.timeoutOrElse({\n duration: timeout,\n orElse: () => Effect.fail(new FetchTimeoutError({ url })),\n }),\n ),\n );\n\nexport const fetchResponse = (url: string, options?: FetchOptions): Promise<Response> =>\n Effect.runPromise(\n defaultHttpFetch\n .attempt(url, {\n method: options?.method,\n headers: options?.headers,\n body: options?.body,\n redirect: options?.redirect,\n })\n .pipe(\n Effect.timeoutOrElse({\n duration: options?.timeout ?? DEFAULT_TIMEOUT,\n orElse: () => Effect.fail(new FetchTimeoutError({ url })),\n }),\n ),\n );\n\nexport const fetchJsonOrNull = async <T>(\n url: string,\n options?: FetchWithRetryOptions,\n): Promise<T | null> => {\n if (isCacheable(url, options)) {\n const cached = getCache.get(url);\n if (cached && cached.expiresAt > Date.now()) {\n return cached.data as T;\n }\n }\n\n try {\n const res = await Effect.runPromise(defaultHttpFetch.get(url, options));\n const data = (await res.json()) as T;\n if (isCacheable(url, options)) {\n setCached(url, data);\n }\n return data;\n } catch (error) {\n const msg =\n error instanceof FetchNetworkError\n ? `[http] Network error: ${error.url} — ${String(error.cause)}`\n : error instanceof FetchTimeoutError\n ? `[http] Timeout: ${error.url}`\n : error instanceof FetchHttpError\n ? `[http] HTTP ${error.status}: ${error.url}`\n : `[http] Unknown error while fetching ${url}`;\n console.error(msg);\n return null;\n }\n};\n\nexport const fetchWithRetryEff = (\n url: string,\n options?: FetchWithRetryOptions,\n): Effect.Effect<Response, FetchError> => defaultHttpFetch.get(url, options);\n"],"mappings":"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"}
|
package/dist/http-client.mjs
CHANGED
|
@@ -72,6 +72,19 @@ var HttpFetch = class HttpFetch extends Context.Service()("everything-dev/http-c
|
|
|
72
72
|
};
|
|
73
73
|
const getCache = /* @__PURE__ */ new Map();
|
|
74
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
|
+
}
|
|
75
88
|
function isCacheable(_url, options) {
|
|
76
89
|
if (options?.method && options.method !== "GET") return false;
|
|
77
90
|
if (options?.body) return false;
|
|
@@ -99,10 +112,7 @@ const fetchJsonOrNull = async (url, options) => {
|
|
|
99
112
|
}
|
|
100
113
|
try {
|
|
101
114
|
const data = await (await Effect.runPromise(defaultHttpFetch.get(url, options))).json();
|
|
102
|
-
if (isCacheable(url, options))
|
|
103
|
-
data,
|
|
104
|
-
expiresAt: Date.now() + GET_CACHE_TTL_MS
|
|
105
|
-
});
|
|
115
|
+
if (isCacheable(url, options)) setCached(url, data);
|
|
106
116
|
return data;
|
|
107
117
|
} catch (error) {
|
|
108
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}`;
|