naro-cli 0.28.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -1
- package/dist/{chunk-UJGDKNDM.js → chunk-ITTYPT3A.js} +398 -101
- package/dist/deploy.js +1 -1
- package/dist/functions/deploy.d.ts +7 -0
- package/dist/functions/retry.d.ts +16 -1
- package/dist/index.js +316 -53
- package/dist/{stack-PDV3NPWC.js → stack-DFLT7AVX.js} +41 -22
- package/package.json +8 -9
|
@@ -306,6 +306,9 @@ function sanitizeRoutes(value) {
|
|
|
306
306
|
return JSON.stringify(routes).length > MAX_ROUTE_LIST_BYTES ? void 0 : routes;
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
+
// ../../packages/core/src/static-assets.ts
|
|
310
|
+
var IMMUTABLE_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
311
|
+
|
|
309
312
|
// ../../packages/core/src/artifact-manifest.ts
|
|
310
313
|
var MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|
311
314
|
var MAX_ARTIFACT_BYTES = 64 * 1024 * 1024;
|
|
@@ -1746,7 +1749,12 @@ var CAPABILITIES = [
|
|
|
1746
1749
|
id: "function-get",
|
|
1747
1750
|
domain: "functions",
|
|
1748
1751
|
title: "\uD568\uC218 \uC0C1\uC138",
|
|
1749
|
-
|
|
1752
|
+
// `deploy` is a name the rule allows; the batch deploy's static segment
|
|
1753
|
+
// answers that one path for it (functions/deploy/route.ts).
|
|
1754
|
+
routes: [
|
|
1755
|
+
"GET projects/[projectId]/backend/functions/[name]",
|
|
1756
|
+
"GET projects/[projectId]/backend/functions/deploy"
|
|
1757
|
+
],
|
|
1750
1758
|
ui: { kind: "built" },
|
|
1751
1759
|
cli: { kind: "built", commands: ["naro functions versions"] },
|
|
1752
1760
|
mcp: { kind: "built", tools: ["get_function"] }
|
|
@@ -1755,7 +1763,10 @@ var CAPABILITIES = [
|
|
|
1755
1763
|
id: "function-update",
|
|
1756
1764
|
domain: "functions",
|
|
1757
1765
|
title: "\uD568\uC218 verify_jwt \uC124\uC815",
|
|
1758
|
-
routes: [
|
|
1766
|
+
routes: [
|
|
1767
|
+
"PATCH projects/[projectId]/backend/functions/[name]",
|
|
1768
|
+
"PATCH projects/[projectId]/backend/functions/deploy"
|
|
1769
|
+
],
|
|
1759
1770
|
ui: { kind: "built" },
|
|
1760
1771
|
cli: { kind: "built", commands: ["naro functions update"] },
|
|
1761
1772
|
mcp: { kind: "built", tools: ["update_function"] }
|
|
@@ -1764,7 +1775,10 @@ var CAPABILITIES = [
|
|
|
1764
1775
|
id: "function-delete",
|
|
1765
1776
|
domain: "functions",
|
|
1766
1777
|
title: "\uD568\uC218 \uC0AD\uC81C",
|
|
1767
|
-
routes: [
|
|
1778
|
+
routes: [
|
|
1779
|
+
"DELETE projects/[projectId]/backend/functions/[name]",
|
|
1780
|
+
"DELETE projects/[projectId]/backend/functions/deploy"
|
|
1781
|
+
],
|
|
1768
1782
|
ui: { kind: "built" },
|
|
1769
1783
|
cli: { kind: "built", commands: ["naro functions delete"] },
|
|
1770
1784
|
mcp: { kind: "built", tools: ["delete_function"] }
|
|
@@ -1787,10 +1801,14 @@ var CAPABILITIES = [
|
|
|
1787
1801
|
domain: "functions",
|
|
1788
1802
|
title: "\uC800\uC7A5\uB41C \uBC84\uC804 \uBC30\uD3EC",
|
|
1789
1803
|
routes: [
|
|
1790
|
-
"POST projects/[projectId]/backend/functions/[name]/versions/[versionId]/deploy"
|
|
1804
|
+
"POST projects/[projectId]/backend/functions/[name]/versions/[versionId]/deploy",
|
|
1805
|
+
// Many functions' versions in one upload of the project's worker
|
|
1806
|
+
// (design 2026-11-08 G4): naro functions deploy with several names,
|
|
1807
|
+
// deploy_function with `names` (Task 8).
|
|
1808
|
+
"POST projects/[projectId]/backend/functions/deploy"
|
|
1791
1809
|
],
|
|
1792
1810
|
ui: { kind: "built" },
|
|
1793
|
-
// naro functions deploy가 올린 버전을 배포한다(client.functions.deploy).
|
|
1811
|
+
// naro functions deploy가 올린 버전을 배포한다(client.functions.deploy, 여럿이면 deployMany).
|
|
1794
1812
|
cli: { kind: "built" },
|
|
1795
1813
|
// deploy_function이 naro-cli의 deployFunctions로 올린 버전을 배포한다.
|
|
1796
1814
|
mcp: { kind: "built" }
|
|
@@ -2511,12 +2529,22 @@ var DEPLOYMENT_ERROR_CODES = [
|
|
|
2511
2529
|
"DATABASE_FULL",
|
|
2512
2530
|
/** Usage limits spec 2026-10-18 §6: a hobby team created its 100 deployments this UTC month. */
|
|
2513
2531
|
"USAGE_LIMIT_REACHED",
|
|
2514
|
-
/**
|
|
2532
|
+
/** Hobby: another limit (requests, D1, Storage, Functions, Realtime) is reached, so new builds wait (spec 2026-11-07-hobby-any-limit-blocks-build). */
|
|
2533
|
+
"BUILDS_PAUSED",
|
|
2534
|
+
/** A hobby team has its 20 projects; no project can be added. */
|
|
2515
2535
|
"PROJECT_LIMIT_REACHED",
|
|
2516
2536
|
/** A hobby team has its 1 member; no member or invite can be added. */
|
|
2517
2537
|
"MEMBER_LIMIT_REACHED",
|
|
2518
|
-
/**
|
|
2538
|
+
/**
|
|
2539
|
+
* Custom domains were not part of the Hobby plan. No longer answered since
|
|
2540
|
+
* 202611070030 (one domain per hobby project); kept so an older client
|
|
2541
|
+
* still names it.
|
|
2542
|
+
*/
|
|
2519
2543
|
"PLAN_DOMAINS_NOT_INCLUDED",
|
|
2544
|
+
/** A hobby project has its 1 custom domain (with its www form); no other can be added. */
|
|
2545
|
+
"PLAN_DOMAIN_LIMIT_REACHED",
|
|
2546
|
+
/** A hobby team has its 5 projects with a database; no other project can turn its database or backend on. */
|
|
2547
|
+
"BACKEND_LIMIT_REACHED",
|
|
2520
2548
|
/** Named environments (spec 2026-10-18-named-environments §15): the name is not lowercase letters, digits and single hyphens, or is over 20 characters. */
|
|
2521
2549
|
"ENVIRONMENT_NAME_INVALID",
|
|
2522
2550
|
/** `production` and `preview` are reserved names. */
|
|
@@ -17567,7 +17595,7 @@ var REQUESTS = {
|
|
|
17567
17595
|
subject100: (team) => `${team} has reached its monthly request limit`,
|
|
17568
17596
|
usage: (team, used, limit, month) => `${team} has used ${used} of the ${limit} requests the Hobby plan includes for ${month}.`,
|
|
17569
17597
|
warning: "When it reaches the limit, requests past it may be blocked until the month ends (UTC).",
|
|
17570
|
-
blocked: "Sites on this team
|
|
17598
|
+
blocked: "Sites on this team are blocked (HTTP 403 or 503) until the month ends (UTC).",
|
|
17571
17599
|
running: "Sites keep running for now.",
|
|
17572
17600
|
button: "View usage",
|
|
17573
17601
|
footer: (team) => `You're receiving this because you're an owner or billing member of ${team} on Naro.`
|
|
@@ -17577,7 +17605,7 @@ var REQUESTS = {
|
|
|
17577
17605
|
subject100: (team) => `${team} \uD300\uC774 \uC774\uBC88 \uB2EC \uC694\uCCAD \uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4`,
|
|
17578
17606
|
usage: (team, used, limit, month) => `${team} \uD300\uC774 ${month} Hobby \uD50C\uB79C\uC5D0 \uD3EC\uD568\uB41C \uC694\uCCAD ${limit}\uAC74 \uC911 ${used}\uAC74\uC744 \uC37C\uC2B5\uB2C8\uB2E4.`,
|
|
17579
17607
|
warning: "\uD55C\uB3C4\uC5D0 \uB3C4\uB2EC\uD558\uBA74 \uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uADF8 \uB4A4 \uC694\uCCAD\uC774 \uB9C9\uD790 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
17580
|
-
blocked: "\uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uC774 \uD300\uC758 \uC0AC\uC774\uD2B8\uB294
|
|
17608
|
+
blocked: "\uC774\uBC88 \uB2EC\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0(UTC) \uC774 \uD300\uC758 \uC0AC\uC774\uD2B8\uB294 \uC694\uCCAD\uC744 \uB9C9\uC2B5\uB2C8\uB2E4(HTTP 403 \uB610\uB294 503).",
|
|
17581
17609
|
running: "\uC9C0\uAE08\uC740 \uC0AC\uC774\uD2B8\uAC00 \uACC4\uC18D \uB3D9\uC791\uD569\uB2C8\uB2E4.",
|
|
17582
17610
|
button: "\uC0AC\uC6A9\uB7C9 \uBCF4\uAE30",
|
|
17583
17611
|
footer: (team) => `Naro\uC5D0\uC11C ${team} \uD300\uC758 owner \uB610\uB294 billing \uC5ED\uD560\uC774\uB77C \uC774 \uBA54\uC77C\uC744 \uBC1B\uC558\uC2B5\uB2C8\uB2E4.`
|
|
@@ -17587,7 +17615,7 @@ var REQUESTS = {
|
|
|
17587
17615
|
subject100: (team) => `${team} \u30C1\u30FC\u30E0\u304C\u4ECA\u6708\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F`,
|
|
17588
17616
|
usage: (team, used, limit, month) => `${team} \u30C1\u30FC\u30E0\u306F ${month} \u306E Hobby \u30D7\u30E9\u30F3\u306B\u542B\u307E\u308C\u308B ${limit} \u30EA\u30AF\u30A8\u30B9\u30C8\u306E\u3046\u3061 ${used} \u3092\u4F7F\u7528\u3057\u307E\u3057\u305F\u3002`,
|
|
17589
17617
|
warning: "\u4E0A\u9650\u306B\u9054\u3059\u308B\u3068\u3001\u6708\u672B(UTC)\u307E\u3067\u4EE5\u964D\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30D6\u30ED\u30C3\u30AF\u3055\u308C\u308B\u5834\u5408\u304C\u3042\u308A\u307E\u3059\u3002",
|
|
17590
|
-
blocked: "\u6708\u672B(UTC)\u307E\u3067\u3001\u3053\u306E\u30C1\u30FC\u30E0\u306E\u30B5\u30A4\u30C8\
|
|
17618
|
+
blocked: "\u6708\u672B(UTC)\u307E\u3067\u3001\u3053\u306E\u30C1\u30FC\u30E0\u306E\u30B5\u30A4\u30C8\u306F\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u62D2\u5426\u3057\u307E\u3059(HTTP 403 \u307E\u305F\u306F 503)\u3002",
|
|
17591
17619
|
running: "\u73FE\u5728\u3001\u30B5\u30A4\u30C8\u306F\u5F15\u304D\u7D9A\u304D\u52D5\u4F5C\u3057\u3066\u3044\u307E\u3059\u3002",
|
|
17592
17620
|
button: "\u4F7F\u7528\u91CF\u3092\u898B\u308B",
|
|
17593
17621
|
footer: (team) => `Naro \u3067 ${team} \u30C1\u30FC\u30E0\u306E owner \u307E\u305F\u306F billing \u30ED\u30FC\u30EB\u306E\u305F\u3081\u3001\u3053\u306E\u30E1\u30FC\u30EB\u3092\u304A\u9001\u308A\u3057\u3066\u3044\u307E\u3059\u3002`
|
|
@@ -17739,6 +17767,63 @@ async function wrapNextConfig(appRoot, file2) {
|
|
|
17739
17767
|
);
|
|
17740
17768
|
return { wrapper: plan.wrapper, project: project2 };
|
|
17741
17769
|
}
|
|
17770
|
+
function assetPrefixWrapperSource(projectSpecifier, typescript) {
|
|
17771
|
+
return `${typescript ? "// @ts-nocheck\n" : ""}// Written by Naro for this build only. The project's own next.config is
|
|
17772
|
+
// ${projectSpecifier}, unchanged; this wrapper serves _next/static from
|
|
17773
|
+
// Naro's CDN unless the project sets its own assetPrefix.
|
|
17774
|
+
import projectConfig from ${JSON.stringify(projectSpecifier)};
|
|
17775
|
+
|
|
17776
|
+
export default async function naroNextConfig(phase, context) {
|
|
17777
|
+
const config =
|
|
17778
|
+
typeof projectConfig === "function"
|
|
17779
|
+
? await projectConfig(phase, context)
|
|
17780
|
+
: await projectConfig;
|
|
17781
|
+
const rest = { ...(config ?? {}) };
|
|
17782
|
+
return {
|
|
17783
|
+
...rest,
|
|
17784
|
+
assetPrefix: rest.assetPrefix || process.env.NARO_STATIC_ASSET_PREFIX,
|
|
17785
|
+
};
|
|
17786
|
+
}
|
|
17787
|
+
`;
|
|
17788
|
+
}
|
|
17789
|
+
var ASSET_PREFIX_ONLY_CONFIG = `// Written by Naro for this build only: the project has no next.config.
|
|
17790
|
+
export default { assetPrefix: process.env.NARO_STATIC_ASSET_PREFIX };
|
|
17791
|
+
`;
|
|
17792
|
+
var CONFIG_LOOKUP = [
|
|
17793
|
+
"next.config.js",
|
|
17794
|
+
"next.config.mjs",
|
|
17795
|
+
"next.config.ts",
|
|
17796
|
+
"next.config.mts",
|
|
17797
|
+
"next.config.cjs"
|
|
17798
|
+
];
|
|
17799
|
+
async function wrapNextConfigForAssetPrefix(appRoot, prefix) {
|
|
17800
|
+
if (!prefix) return "skipped";
|
|
17801
|
+
let file2;
|
|
17802
|
+
for (const name of CONFIG_LOOKUP)
|
|
17803
|
+
if (await exists(join2(appRoot, name))) {
|
|
17804
|
+
file2 = name;
|
|
17805
|
+
break;
|
|
17806
|
+
}
|
|
17807
|
+
if (!file2) {
|
|
17808
|
+
await writeFile2(join2(appRoot, "next.config.mjs"), ASSET_PREFIX_ONLY_CONFIG);
|
|
17809
|
+
return "created";
|
|
17810
|
+
}
|
|
17811
|
+
const plan = WRAPPABLE[file2];
|
|
17812
|
+
if (!plan) return "skipped";
|
|
17813
|
+
const project2 = `${PROJECT_CONFIG_BASENAME}.${file2.slice("next.config.".length)}`;
|
|
17814
|
+
if (await exists(join2(appRoot, project2))) return "skipped";
|
|
17815
|
+
await rename(join2(appRoot, file2), join2(appRoot, project2));
|
|
17816
|
+
try {
|
|
17817
|
+
await writeFile2(
|
|
17818
|
+
join2(appRoot, plan.wrapper),
|
|
17819
|
+
assetPrefixWrapperSource(plan.specifier, plan.typescript)
|
|
17820
|
+
);
|
|
17821
|
+
} catch (error51) {
|
|
17822
|
+
await rename(join2(appRoot, project2), join2(appRoot, file2));
|
|
17823
|
+
throw error51;
|
|
17824
|
+
}
|
|
17825
|
+
return "wrapped";
|
|
17826
|
+
}
|
|
17742
17827
|
|
|
17743
17828
|
// ../../packages/next-adapter/src/index.ts
|
|
17744
17829
|
function nextAdapterPath(base2 = import.meta.url) {
|
|
@@ -19201,6 +19286,54 @@ async function readStaticArtifact(appRoot, outputDirectory, identity, options, o
|
|
|
19201
19286
|
};
|
|
19202
19287
|
}
|
|
19203
19288
|
|
|
19289
|
+
// ../../packages/build/src/static-cdn.ts
|
|
19290
|
+
var STATIC_ASSET_PREFIX_ENV = "NARO_STATIC_ASSET_PREFIX";
|
|
19291
|
+
var STATIC_ASSETS_ON_CDN_NOTE = "Static assets (_next/static) are served from static.naro.sh.";
|
|
19292
|
+
var STATIC_ASSETS_ON_WORKER_NOTE = "Static assets stay on the Worker: the build did not use Naro's asset prefix (the project sets its own assetPrefix or a basePath, or it is not a Next.js build).";
|
|
19293
|
+
function buildEnvWithStaticPrefix(env, prefix) {
|
|
19294
|
+
const result = { ...env };
|
|
19295
|
+
delete result[STATIC_ASSET_PREFIX_ENV];
|
|
19296
|
+
if (prefix) result[STATIC_ASSET_PREFIX_ENV] = prefix;
|
|
19297
|
+
return result;
|
|
19298
|
+
}
|
|
19299
|
+
function contains(file2, needle) {
|
|
19300
|
+
return Buffer.from(
|
|
19301
|
+
file2.data.buffer,
|
|
19302
|
+
file2.data.byteOffset,
|
|
19303
|
+
file2.data.byteLength
|
|
19304
|
+
).includes(needle);
|
|
19305
|
+
}
|
|
19306
|
+
function withStaticAssetPrefix(artifact, prefix) {
|
|
19307
|
+
if (!prefix) return { artifact, note: null };
|
|
19308
|
+
const needle = Buffer.from(prefix, "utf8");
|
|
19309
|
+
const files = [artifact.entrypoint, ...artifact.modules, ...artifact.assets];
|
|
19310
|
+
if (!files.some((file2) => contains(file2, needle)))
|
|
19311
|
+
return { artifact, note: STATIC_ASSETS_ON_WORKER_NOTE };
|
|
19312
|
+
return {
|
|
19313
|
+
artifact: { ...artifact, staticAssetPrefix: prefix },
|
|
19314
|
+
note: STATIC_ASSETS_ON_CDN_NOTE
|
|
19315
|
+
};
|
|
19316
|
+
}
|
|
19317
|
+
var IMMUTABLE_HEADERS_FILE = "_headers";
|
|
19318
|
+
var IMMUTABLE_HEADERS = `/_next/static/*
|
|
19319
|
+
Cache-Control: ${IMMUTABLE_CACHE_CONTROL}
|
|
19320
|
+
`;
|
|
19321
|
+
function withImmutableHeaders(artifact) {
|
|
19322
|
+
if (artifact.assets.some((asset) => asset.name === IMMUTABLE_HEADERS_FILE))
|
|
19323
|
+
return artifact;
|
|
19324
|
+
return {
|
|
19325
|
+
...artifact,
|
|
19326
|
+
assets: [
|
|
19327
|
+
...artifact.assets,
|
|
19328
|
+
{
|
|
19329
|
+
name: IMMUTABLE_HEADERS_FILE,
|
|
19330
|
+
data: new TextEncoder().encode(IMMUTABLE_HEADERS),
|
|
19331
|
+
contentType: "text/plain"
|
|
19332
|
+
}
|
|
19333
|
+
]
|
|
19334
|
+
};
|
|
19335
|
+
}
|
|
19336
|
+
|
|
19204
19337
|
// ../../packages/build/src/strategy/fingerprint.ts
|
|
19205
19338
|
import { createHash as createHash3 } from "crypto";
|
|
19206
19339
|
import { readFile as readFile9 } from "fs/promises";
|
|
@@ -20352,6 +20485,17 @@ function createVinextCloudflareStrategy(options) {
|
|
|
20352
20485
|
);
|
|
20353
20486
|
if (!added.ok) return failed("prepare", describeFailure(added));
|
|
20354
20487
|
}
|
|
20488
|
+
try {
|
|
20489
|
+
await wrapNextConfigForAssetPrefix(
|
|
20490
|
+
fork.appRoot,
|
|
20491
|
+
context.env[STATIC_ASSET_PREFIX_ENV]
|
|
20492
|
+
);
|
|
20493
|
+
} catch (error51) {
|
|
20494
|
+
await warn(
|
|
20495
|
+
observer,
|
|
20496
|
+
`Could not point static assets at static.naro.sh: ${describeError(error51)}`
|
|
20497
|
+
);
|
|
20498
|
+
}
|
|
20355
20499
|
const projectBin = adopt ? null : await findProjectBin(fork.appRoot, fork.checkout, "vinext");
|
|
20356
20500
|
const build = await runStep(
|
|
20357
20501
|
context,
|
|
@@ -20760,7 +20904,7 @@ var LocalBuildExecutor = class {
|
|
|
20760
20904
|
input.rootDirectory
|
|
20761
20905
|
);
|
|
20762
20906
|
await assertAppRootInsideCheckout(checkout, appRoot);
|
|
20763
|
-
const stacks = await import("./stack-
|
|
20907
|
+
const stacks = await import("./stack-DFLT7AVX.js");
|
|
20764
20908
|
const stack = await (this.dependencies.detectStack ?? stacks.detectStack)(
|
|
20765
20909
|
appRoot
|
|
20766
20910
|
).catch(() => null);
|
|
@@ -20920,7 +21064,7 @@ var LocalBuildExecutor = class {
|
|
|
20920
21064
|
const context = {
|
|
20921
21065
|
identity,
|
|
20922
21066
|
branch: input.branch,
|
|
20923
|
-
env: input.env
|
|
21067
|
+
env: buildEnvWithStaticPrefix(input.env, input.staticAssetPrefix),
|
|
20924
21068
|
// The toolchain add has to use the store the install linked
|
|
20925
21069
|
// node_modules from (withSharedCache).
|
|
20926
21070
|
packageManager: cachePorts ? withSharedCache(packageManager, pmCacheDir) : packageManager,
|
|
@@ -20976,7 +21120,10 @@ var LocalBuildExecutor = class {
|
|
|
20976
21120
|
args: ["run", resolved.buildScript],
|
|
20977
21121
|
cwd: appRoot,
|
|
20978
21122
|
env: {
|
|
20979
|
-
|
|
21123
|
+
// Static presets never get the asset prefix (spec
|
|
21124
|
+
// 2026-11-07-static-asset-cdn §3): a Vite base or CRA
|
|
21125
|
+
// PUBLIC_URL also moves the router and public/ files.
|
|
21126
|
+
...buildEnvWithStaticPrefix(input.env, void 0),
|
|
20980
21127
|
NPM_CONFIG_USERCONFIG: packageManagerConfigPath
|
|
20981
21128
|
},
|
|
20982
21129
|
timeoutMs: 15 * 6e4,
|
|
@@ -21010,6 +21157,14 @@ var LocalBuildExecutor = class {
|
|
|
21010
21157
|
appRoot,
|
|
21011
21158
|
observer
|
|
21012
21159
|
);
|
|
21160
|
+
const cdn = withStaticAssetPrefix(artifact, input.staticAssetPrefix);
|
|
21161
|
+
if (cdn.note)
|
|
21162
|
+
await observer.onLog({
|
|
21163
|
+
level: "INFO",
|
|
21164
|
+
stage: "DEPLOYING",
|
|
21165
|
+
message: cdn.note
|
|
21166
|
+
});
|
|
21167
|
+
artifact = withImmutableHeaders(cdn.artifact);
|
|
21013
21168
|
return {
|
|
21014
21169
|
gitCommitSha,
|
|
21015
21170
|
artifact,
|
|
@@ -21194,7 +21349,16 @@ async function errorFromResponse(response, context) {
|
|
|
21194
21349
|
}
|
|
21195
21350
|
|
|
21196
21351
|
// ../../packages/sdk/src/session.ts
|
|
21197
|
-
import {
|
|
21352
|
+
import { randomBytes } from "crypto";
|
|
21353
|
+
import {
|
|
21354
|
+
chmodSync,
|
|
21355
|
+
mkdirSync,
|
|
21356
|
+
readdirSync,
|
|
21357
|
+
readFileSync,
|
|
21358
|
+
renameSync,
|
|
21359
|
+
rmSync,
|
|
21360
|
+
writeFileSync
|
|
21361
|
+
} from "fs";
|
|
21198
21362
|
import { homedir } from "os";
|
|
21199
21363
|
import { join as join18 } from "path";
|
|
21200
21364
|
function credentialsDir(home = homedir()) {
|
|
@@ -21204,10 +21368,18 @@ function credentialsFile(home = homedir()) {
|
|
|
21204
21368
|
return join18(credentialsDir(home), "credentials.json");
|
|
21205
21369
|
}
|
|
21206
21370
|
function saveSession(session, home = homedir()) {
|
|
21207
|
-
|
|
21208
|
-
|
|
21209
|
-
|
|
21210
|
-
|
|
21371
|
+
const dir = credentialsDir(home);
|
|
21372
|
+
const file2 = credentialsFile(home);
|
|
21373
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
21374
|
+
chmodSync(dir, 448);
|
|
21375
|
+
const tmp = `${file2}.${randomBytes(6).toString("hex")}.tmp`;
|
|
21376
|
+
try {
|
|
21377
|
+
writeFileSync(tmp, JSON.stringify(session, null, 2), { mode: 384 });
|
|
21378
|
+
renameSync(tmp, file2);
|
|
21379
|
+
} catch (error51) {
|
|
21380
|
+
rmSync(tmp, { force: true });
|
|
21381
|
+
throw error51;
|
|
21382
|
+
}
|
|
21211
21383
|
}
|
|
21212
21384
|
function loadStoredSession(home = homedir()) {
|
|
21213
21385
|
try {
|
|
@@ -21219,7 +21391,19 @@ function loadStoredSession(home = homedir()) {
|
|
|
21219
21391
|
}
|
|
21220
21392
|
}
|
|
21221
21393
|
function clearSession(home = homedir()) {
|
|
21394
|
+
const dir = credentialsDir(home);
|
|
21222
21395
|
rmSync(credentialsFile(home), { force: true });
|
|
21396
|
+
let names;
|
|
21397
|
+
try {
|
|
21398
|
+
names = readdirSync(dir);
|
|
21399
|
+
} catch {
|
|
21400
|
+
return;
|
|
21401
|
+
}
|
|
21402
|
+
for (const name of names) {
|
|
21403
|
+
if (/^credentials\.json\.[0-9a-f]+\.tmp$/.test(name)) {
|
|
21404
|
+
rmSync(join18(dir, name), { force: true });
|
|
21405
|
+
}
|
|
21406
|
+
}
|
|
21223
21407
|
}
|
|
21224
21408
|
function isExpired(session, skewSeconds = 60) {
|
|
21225
21409
|
return session.expires_at * 1e3 <= Date.now() + skewSeconds * 1e3;
|
|
@@ -21394,7 +21578,20 @@ async function startCallbackServer() {
|
|
|
21394
21578
|
resolveCode = resolve16;
|
|
21395
21579
|
rejectCode = reject;
|
|
21396
21580
|
});
|
|
21581
|
+
let port = 0;
|
|
21582
|
+
let settled = false;
|
|
21397
21583
|
const server = createServer2((req, res) => {
|
|
21584
|
+
const host = (req.headers.host ?? "").toLowerCase();
|
|
21585
|
+
if (host !== `localhost:${String(port)}` && host !== `127.0.0.1:${String(port)}`) {
|
|
21586
|
+
res.statusCode = 404;
|
|
21587
|
+
res.end("Not found");
|
|
21588
|
+
return;
|
|
21589
|
+
}
|
|
21590
|
+
if (settled) {
|
|
21591
|
+
res.statusCode = 410;
|
|
21592
|
+
res.end("Gone");
|
|
21593
|
+
return;
|
|
21594
|
+
}
|
|
21398
21595
|
const url2 = new URL(req.url ?? "/", "http://localhost");
|
|
21399
21596
|
if (url2.pathname !== "/callback") {
|
|
21400
21597
|
res.statusCode = 404;
|
|
@@ -21405,6 +21602,7 @@ async function startCallbackServer() {
|
|
|
21405
21602
|
const code = url2.searchParams.get("code");
|
|
21406
21603
|
if (error51) {
|
|
21407
21604
|
const description = url2.searchParams.get("error_description");
|
|
21605
|
+
settled = true;
|
|
21408
21606
|
sendPage(res, 200, failurePage(description ?? error51));
|
|
21409
21607
|
rejectCode(new Error(`GitHub authorization failed: ${error51}`));
|
|
21410
21608
|
return;
|
|
@@ -21417,11 +21615,12 @@ async function startCallbackServer() {
|
|
|
21417
21615
|
);
|
|
21418
21616
|
return;
|
|
21419
21617
|
}
|
|
21618
|
+
settled = true;
|
|
21420
21619
|
sendPage(res, 200, successPage());
|
|
21421
21620
|
resolveCode(code);
|
|
21422
21621
|
});
|
|
21423
21622
|
await new Promise((resolve16) => server.listen(0, "127.0.0.1", resolve16));
|
|
21424
|
-
|
|
21623
|
+
port = server.address().port;
|
|
21425
21624
|
return {
|
|
21426
21625
|
port,
|
|
21427
21626
|
waitForCode(timeoutMs) {
|
|
@@ -21627,10 +21826,15 @@ async function resolveAuth(options = {}) {
|
|
|
21627
21826
|
|
|
21628
21827
|
// ../../packages/sdk/src/browser.ts
|
|
21629
21828
|
import { spawn as spawn2 } from "child_process";
|
|
21829
|
+
function browserCommand(url2, platform = process.platform) {
|
|
21830
|
+
if (platform === "darwin") return { command: "open", args: [url2] };
|
|
21831
|
+
if (platform === "win32") {
|
|
21832
|
+
return { command: "rundll32", args: ["url.dll,FileProtocolHandler", url2] };
|
|
21833
|
+
}
|
|
21834
|
+
return { command: "xdg-open", args: [url2] };
|
|
21835
|
+
}
|
|
21630
21836
|
function openBrowser(url2) {
|
|
21631
|
-
const
|
|
21632
|
-
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
21633
|
-
const args = platform === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
21837
|
+
const { command, args } = browserCommand(url2);
|
|
21634
21838
|
const child = spawn2(command, args, { stdio: "ignore", detached: true });
|
|
21635
21839
|
child.on("error", () => {
|
|
21636
21840
|
});
|
|
@@ -24119,6 +24323,28 @@ var FunctionsApi = class {
|
|
|
24119
24323
|
);
|
|
24120
24324
|
return body.function;
|
|
24121
24325
|
}
|
|
24326
|
+
/**
|
|
24327
|
+
* Puts several functions' versions live in one upload of the project's
|
|
24328
|
+
* worker (design 2026-11-08 G4): 1 to 100 `{ name, versionId }`, each
|
|
24329
|
+
* function once. One refusal refuses them all and nothing goes live —
|
|
24330
|
+
* `deploy`'s refusals, `VALIDATION_FAILED` for the list itself (`Send 1 to
|
|
24331
|
+
* 100 versions.`, a name listed twice), and `FUNCTIONS_TOO_LARGE` (413:
|
|
24332
|
+
* the project's live bundles together past 64 MiB). The answer is each
|
|
24333
|
+
* function's new version, in the order asked. Two minutes, or
|
|
24334
|
+
* `options.timeoutMs`; a timeout here does not say whether they went
|
|
24335
|
+
* live — `list` does.
|
|
24336
|
+
*/
|
|
24337
|
+
async deployMany(projectId, versions, options = {}) {
|
|
24338
|
+
return this.client.request(
|
|
24339
|
+
this.path(projectId, "/deploy"),
|
|
24340
|
+
{
|
|
24341
|
+
method: "POST",
|
|
24342
|
+
body: { versions },
|
|
24343
|
+
context: "Deploy functions",
|
|
24344
|
+
timeoutMs: options.timeoutMs ?? PROVISION_TIMEOUT_MS
|
|
24345
|
+
}
|
|
24346
|
+
);
|
|
24347
|
+
}
|
|
24122
24348
|
/**
|
|
24123
24349
|
* Puts a stored version live again: the one numbered `version`, which must
|
|
24124
24350
|
* have been live before (`VERSION_NOT_FOUND` otherwise — put a version that
|
|
@@ -24386,7 +24612,7 @@ function serializeDotenv(vars) {
|
|
|
24386
24612
|
}
|
|
24387
24613
|
|
|
24388
24614
|
// ../../packages/sdk/src/env-scan.ts
|
|
24389
|
-
import { existsSync as existsSync2, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
|
|
24615
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync } from "fs";
|
|
24390
24616
|
import { join as join20 } from "path";
|
|
24391
24617
|
var SOURCE_EXTENSIONS3 = /* @__PURE__ */ new Set([
|
|
24392
24618
|
".ts",
|
|
@@ -24435,7 +24661,7 @@ function collect(dir, out) {
|
|
|
24435
24661
|
if (out.length >= MAX_FILES) return;
|
|
24436
24662
|
let entries;
|
|
24437
24663
|
try {
|
|
24438
|
-
entries =
|
|
24664
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
24439
24665
|
} catch {
|
|
24440
24666
|
return;
|
|
24441
24667
|
}
|
|
@@ -33432,12 +33658,32 @@ function generateTypes(tables, options = {}) {
|
|
|
33432
33658
|
// ../../packages/sdk/src/link.ts
|
|
33433
33659
|
import {
|
|
33434
33660
|
existsSync as existsSync4,
|
|
33661
|
+
lstatSync,
|
|
33435
33662
|
mkdirSync as mkdirSync2,
|
|
33436
33663
|
readFileSync as readFileSync5,
|
|
33437
33664
|
rmSync as rmSync2,
|
|
33438
33665
|
writeFileSync as writeFileSync2
|
|
33439
33666
|
} from "fs";
|
|
33440
33667
|
import { join as join22 } from "path";
|
|
33668
|
+
function isSymbolicLink(path) {
|
|
33669
|
+
try {
|
|
33670
|
+
return lstatSync(path).isSymbolicLink();
|
|
33671
|
+
} catch {
|
|
33672
|
+
return false;
|
|
33673
|
+
}
|
|
33674
|
+
}
|
|
33675
|
+
function refuseLink(path, shown, before) {
|
|
33676
|
+
if (isSymbolicLink(path))
|
|
33677
|
+
throw new NaroError(
|
|
33678
|
+
"VALIDATION_FAILED",
|
|
33679
|
+
`${shown} is a symbolic link. Remove it before ${before}.`
|
|
33680
|
+
);
|
|
33681
|
+
}
|
|
33682
|
+
function assertProjectLinkWritable(dir, before = "linking the project") {
|
|
33683
|
+
refuseLink(linkDir(dir), ".naro", before);
|
|
33684
|
+
refuseLink(linkFile(dir), ".naro/project.json", before);
|
|
33685
|
+
refuseLink(join22(linkDir(dir), "README.txt"), ".naro/README.txt", before);
|
|
33686
|
+
}
|
|
33441
33687
|
function linkDir(dir) {
|
|
33442
33688
|
return join22(dir, ".naro");
|
|
33443
33689
|
}
|
|
@@ -33460,6 +33706,7 @@ function readProjectLink(dir) {
|
|
|
33460
33706
|
}
|
|
33461
33707
|
}
|
|
33462
33708
|
function writeProjectLink(dir, link) {
|
|
33709
|
+
assertProjectLinkWritable(dir);
|
|
33463
33710
|
mkdirSync2(linkDir(dir), { recursive: true });
|
|
33464
33711
|
writeFileSync2(linkFile(dir), `${JSON.stringify(link, null, 2)}
|
|
33465
33712
|
`);
|
|
@@ -33475,6 +33722,7 @@ function removeProjectLink(dir) {
|
|
|
33475
33722
|
}
|
|
33476
33723
|
function ensureGitignoreHasNaro(dir) {
|
|
33477
33724
|
const gitignorePath = join22(dir, ".gitignore");
|
|
33725
|
+
if (isSymbolicLink(gitignorePath)) return false;
|
|
33478
33726
|
const hasGitignore = existsSync4(gitignorePath);
|
|
33479
33727
|
if (!hasGitignore && !existsSync4(join22(dir, ".git"))) return false;
|
|
33480
33728
|
const current = hasGitignore ? readFileSync5(gitignorePath, "utf8") : "";
|
|
@@ -33815,7 +34063,7 @@ async function repairLedger(client, projectId, key, status, ledgerIsEmpty) {
|
|
|
33815
34063
|
}
|
|
33816
34064
|
|
|
33817
34065
|
// ../../packages/sdk/src/migrations.ts
|
|
33818
|
-
import { existsSync as existsSync5, readdirSync as
|
|
34066
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3 } from "fs";
|
|
33819
34067
|
import { resolve as resolve9 } from "path";
|
|
33820
34068
|
function resolveMigrationsDir(input) {
|
|
33821
34069
|
if (input.dir) return resolve9(input.baseDir, input.dir);
|
|
@@ -33835,7 +34083,7 @@ function sortMigrationNames(names) {
|
|
|
33835
34083
|
function listMigrationFiles(dir) {
|
|
33836
34084
|
if (!existsSync5(dir)) return [];
|
|
33837
34085
|
return sortMigrationNames(
|
|
33838
|
-
|
|
34086
|
+
readdirSync3(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".sql")).map((entry) => entry.name)
|
|
33839
34087
|
);
|
|
33840
34088
|
}
|
|
33841
34089
|
function nextMigrationFileName(existing, name) {
|
|
@@ -34236,11 +34484,13 @@ function summarizeTeamUsage(report, currentMonth) {
|
|
|
34236
34484
|
};
|
|
34237
34485
|
const meters = USAGE_METRICS.map((metric) => {
|
|
34238
34486
|
const limit = report.limits?.[metric] ?? null;
|
|
34487
|
+
const perProject = metric === "domains" ? report.limits?.domainsPerProject ?? null : null;
|
|
34239
34488
|
return {
|
|
34240
34489
|
metric,
|
|
34241
34490
|
used: used[metric],
|
|
34242
34491
|
limit,
|
|
34243
|
-
percent: limit ? Math.floor(used[metric] * 100 / limit) : null
|
|
34492
|
+
percent: limit ? Math.floor(used[metric] * 100 / limit) : null,
|
|
34493
|
+
perProject
|
|
34244
34494
|
};
|
|
34245
34495
|
});
|
|
34246
34496
|
return {
|
|
@@ -35276,8 +35526,6 @@ var BUNDLE_MESSAGES = {
|
|
|
35276
35526
|
wasm: (file2) => `WebAssembly modules are not supported in functions yet: ${file2}.`,
|
|
35277
35527
|
naroJsMissing: 'Cannot find "naro-js/functions". Install naro-js 0.7.0 or later: npm install naro-js@latest.',
|
|
35278
35528
|
tooLarge: (name, size, inputs) => `Function ${name} bundles to ${bytes(size)} bytes; the limit is 67,108,864 (64 MiB). The biggest inputs: ${inputs}.`,
|
|
35279
|
-
// Rounded up: a size over the limit never reads as the limit (10.0 MB).
|
|
35280
|
-
tooLargeCompressed: (name, size) => `Function ${name} is ${(Math.ceil(size / 1048576 * 10) / 10).toFixed(1)} MB after compression; Cloudflare accepts 10 MB.`,
|
|
35281
35529
|
otherBundle: (name, text3, location2) => `Function ${name} did not bundle: ${text3}${location2 ? ` (${location2})` : ""}`,
|
|
35282
35530
|
noHandler: (name) => `Function ${name} did not register a handler. Call Deno.serve(handler) or export default { fetch }.`,
|
|
35283
35531
|
failedToStart: (name, line) => `Function ${name} failed to start: ${line}`,
|
|
@@ -35287,12 +35535,11 @@ var BUNDLE_MESSAGES = {
|
|
|
35287
35535
|
};
|
|
35288
35536
|
|
|
35289
35537
|
// ../../packages/functions-bundler/src/discover.ts
|
|
35290
|
-
import { existsSync as existsSync7, readdirSync as
|
|
35538
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync2 } from "fs";
|
|
35291
35539
|
import { join as join27, relative as relative8, resolve as resolve12, sep as sep6 } from "path";
|
|
35292
35540
|
|
|
35293
35541
|
// ../../packages/functions-bundler/src/limits.ts
|
|
35294
35542
|
var FUNCTION_BUNDLE_MAX_BYTES = 67108864;
|
|
35295
|
-
var FUNCTION_COMPRESSED_BUNDLE_MAX_BYTES = 10485760;
|
|
35296
35543
|
var ENTRY_FILES = [
|
|
35297
35544
|
"index.ts",
|
|
35298
35545
|
"index.js",
|
|
@@ -35319,7 +35566,7 @@ function functionsFolder(options) {
|
|
|
35319
35566
|
function listFunctionNames(folder) {
|
|
35320
35567
|
let entries;
|
|
35321
35568
|
try {
|
|
35322
|
-
entries =
|
|
35569
|
+
entries = readdirSync4(folder.dir, { withFileTypes: true });
|
|
35323
35570
|
} catch {
|
|
35324
35571
|
if (!folder.explicit && existsSync7(join27(folder.root, "supabase", "functions")))
|
|
35325
35572
|
throw new FunctionBundleError(
|
|
@@ -36052,25 +36299,18 @@ function denoImportsPlugin(options) {
|
|
|
36052
36299
|
// ../../packages/functions-bundler/src/bundle.ts
|
|
36053
36300
|
import { createHash as createHash4 } from "crypto";
|
|
36054
36301
|
import { join as join30, relative as relative10, sep as sep9 } from "path";
|
|
36055
|
-
import { gzipSync } from "zlib";
|
|
36056
36302
|
|
|
36057
36303
|
// ../../packages/functions-runtime/dist/index.js
|
|
36058
36304
|
var NARO_SHIM_MODULE = "naro-shim.js";
|
|
36059
36305
|
var NARO_USER_MODULE = "user.js";
|
|
36060
36306
|
var NARO_BOOT_MODULE = "naro-boot.js";
|
|
36061
|
-
var NARO_ENTRY_SOURCE = '// src/naro-entry.ts\nimport "./naro-shim.js";\nimport * as user from "./user.js";\n\n// src/context.ts\nvar CONTEXT_HEADER = "x-naro-context";\nvar MAX_CONTEXT_LENGTH = 4096;\nvar BASE64URL = /^[A-Za-z0-9_-]+$/;\nvar TRACE_ID_RE = /^[A-Za-z0-9-]{1,64}$/;\nvar MAX_HOP = 1e3;\nvar ROLES = /* @__PURE__ */ new Set([\n "anon",\n "authenticated",\n "service"\n]);\nfunction fieldsOf(value) {\n if (typeof value !== "object" || value === null || Array.isArray(value))\n return null;\n return value;\n}\nfunction own(fields, key) {\n return Object.hasOwn(fields, key) ? fields[key] : void 0;\n}\nfunction nonEmptyText(value) {\n return typeof value === "string" && value !== "";\n}\nvar MAX_USER_ID_LENGTH = 256;\nfunction headerSafeId(value) {\n if (!nonEmptyText(value) || value.length > MAX_USER_ID_LENGTH) return false;\n if (value.trim() !== value) return false;\n for (let index = 0; index < value.length; index += 1) {\n const code = value.charCodeAt(index);\n if (code < 32 || code >= 127 && code <= 159) return false;\n if (code >= 55296 && code <= 57343) {\n const next = value.charCodeAt(index + 1);\n if (code >= 56320 || !(next >= 56320 && next <= 57343)) return false;\n index += 1;\n }\n }\n return true;\n}\nfunction userOf(value) {\n const fields = fieldsOf(value);\n if (fields === null) return null;\n const id = own(fields, "id");\n const email = own(fields, "email");\n const emailVerified = own(fields, "emailVerified");\n if (!headerSafeId(id)) return null;\n if (email !== null && typeof email !== "string") return null;\n if (typeof emailVerified !== "boolean") return null;\n return { id, email, emailVerified };\n}\nfunction contextOf(value, projectId, name) {\n const fields = fieldsOf(value);\n if (fields === null) return null;\n if (own(fields, "v") !== 1) return null;\n if (own(fields, "projectId") !== projectId) return null;\n if (own(fields, "function") !== name) return null;\n const traceId = own(fields, "traceId");\n if (typeof traceId !== "string" || !TRACE_ID_RE.test(traceId)) return null;\n const hop = own(fields, "hop");\n if (typeof hop !== "number" || !Number.isInteger(hop) || hop < 0 || hop > MAX_HOP)\n return null;\n const role = own(fields, "role");\n if (role !== null && !ROLES.has(role)) return null;\n const sentUser = own(fields, "user");\n let user2 = null;\n if (role === "authenticated") {\n user2 = userOf(sentUser);\n if (user2 === null) return null;\n } else if (sentUser !== null) return null;\n return {\n v: 1,\n projectId,\n function: name,\n role,\n user: user2,\n traceId,\n hop\n };\n}\nfunction bytesOf(value) {\n if (!BASE64URL.test(value) || value.length % 4 === 1) return null;\n const base64 = value.replace(/-/g, "+").replace(/_/g, "/");\n let binary;\n try {\n binary = atob(base64 + "=".repeat((4 - base64.length % 4) % 4));\n } catch {\n return null;\n }\n const again = btoa(binary).replace(/\\+/g, "-").replace(/\\//g, "_").replace(/=+$/, "");\n if (again !== value) return null;\n return Uint8Array.from(binary, (char) => char.charCodeAt(0));\n}\nfunction decodeContext(value, bindings) {\n const projectId = bindings.NARO_PROJECT_ID;\n const name = bindings.NARO_FUNCTION_NAME;\n if (!nonEmptyText(projectId) || !nonEmptyText(name)) return null;\n if (typeof value !== "string" || value.length > MAX_CONTEXT_LENGTH)\n return null;\n const bytes = bytesOf(value);\n if (bytes === null) return null;\n let parsed;\n try {\n parsed = JSON.parse(\n new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes)\n );\n } catch {\n return null;\n }\n return contextOf(parsed, projectId, name);\n}\n\n// src/rejections.ts\nfunction loggedRejection(promise, name) {\n return Promise.resolve(promise).then(\n () => void 0,\n (error) => {\n console.error(`naro functions: ${name} waitUntil rejected`, error);\n }\n );\n}\n\n// src/respond.ts\nvar FUNCTION_EXPOSE_HEADERS = [\n "x-relay-error",\n "x-naro-trace-id",\n "retry-after",\n "x-retry-after"\n];\nvar WRAPPER_ERRORS = {\n FUNCTION_ERROR: "The function threw an error. See `naro functions logs {name}`.",\n FUNCTION_NO_HANDLER: "The function has no handler. Call Deno.serve(handler) or export default { fetch }.",\n FUNCTION_MISROUTED: "This function was reached without the Naro gateway."\n};\nfunction appendExposeHeaders(value) {\n const listed = (value ?? "").split(",").map((part) => part.trim()).filter((part) => part !== "");\n const seen = new Set(listed.map((part) => part.toLowerCase()));\n for (const name of FUNCTION_EXPOSE_HEADERS)\n if (!seen.has(name)) listed.push(name);\n return listed.join(", ");\n}\nfunction wrapperError(code, name, traceId) {\n const headers = new Headers({\n "access-control-allow-origin": "*",\n "access-control-expose-headers": appendExposeHeaders(null),\n "cache-control": "no-store"\n });\n if (traceId !== null) headers.set("x-naro-trace-id", traceId);\n return Response.json(\n {\n data: null,\n error: {\n code,\n // A function, not a string: `$&` in a name stays as it is.\n message: WRAPPER_ERRORS[code].replace("{name}", () => name)\n }\n },\n { status: 500, headers }\n );\n}\nfunction withFunctionHeaders(response, traceId) {\n if (response.status === 101) return response;\n const out = new Response(response.body, response);\n const headers = out.headers;\n headers.delete("x-relay-error");\n if (!headers.has("access-control-allow-origin"))\n headers.set("access-control-allow-origin", "*");\n headers.set(\n "access-control-expose-headers",\n appendExposeHeaders(headers.get("access-control-expose-headers"))\n );\n headers.set("x-naro-trace-id", traceId);\n return out;\n}\n\n// src/runtime-state.ts\nvar RUNTIME_KEY = /* @__PURE__ */ Symbol.for("naro.functions.runtime");\nvar CONTEXT_KEY = /* @__PURE__ */ Symbol.for("naro.functions.context");\nvar SHIM_FIRST = "The Naro functions shim must be imported before the wrapper.";\nfunction globals() {\n return globalThis;\n}\nfunction runtimeState() {\n const state2 = globals()[RUNTIME_KEY];\n if (state2 === void 0) throw new Error(SHIM_FIRST);\n return state2;\n}\nfunction callContexts() {\n const contexts2 = globals()[CONTEXT_KEY];\n if (contexts2 === void 0) throw new Error(SHIM_FIRST);\n return contexts2;\n}\n\n// src/naro-entry.ts\nvar state = runtimeState();\nvar contexts = callContexts();\nasync function serveWithInfo(registered, request) {\n let complete;\n const info = {\n remoteAddr: {\n transport: "tcp",\n hostname: request.headers.get("cf-connecting-ip") ?? "0.0.0.0",\n port: 0\n },\n completed: new Promise((resolve) => {\n complete = resolve;\n })\n };\n try {\n return await registered(request, info);\n } finally {\n complete();\n }\n}\nfunction chooseHandler() {\n const registered = state.handler;\n if (registered !== null)\n return {\n handler: async (request) => serveWithInfo(registered, request),\n onError: state.onError\n };\n const exported = user.default;\n if (typeof exported === "object" && exported !== null) {\n const fetchHandler = exported.fetch;\n if (typeof fetchHandler === "function")\n return {\n handler: async (request, env, ctx) => fetchHandler.call(exported, request, env, ctx),\n onError: null\n };\n }\n if (typeof exported === "function") {\n const prototype = exported.prototype;\n if (typeof prototype === "object" && prototype !== null && typeof prototype.fetch === "function")\n return {\n handler: async (request, env, ctx) => new exported(ctx, env).fetch(request),\n onError: null\n };\n return {\n handler: async (request, env, ctx) => exported(request, env, ctx),\n onError: null\n };\n }\n return null;\n}\nfunction callerRequest(request, context) {\n const headers = new Headers();\n for (const [name, value] of request.headers)\n if (!name.startsWith("x-naro-")) headers.append(name, value);\n if (context.role !== null) headers.set("x-naro-role", context.role);\n if (context.user !== null) headers.set("x-naro-user-id", context.user.id);\n headers.set("x-naro-trace-id", context.traceId);\n return new Request(request, { headers });\n}\nfunction logWaitUntilRejections(ctx, name) {\n const waitUntil = ctx.waitUntil.bind(ctx);\n try {\n Object.defineProperty(ctx, "waitUntil", {\n configurable: true,\n writable: true,\n value: (promise) => waitUntil(loggedRejection(promise, name))\n });\n } catch {\n }\n}\nfunction textOf(value) {\n return typeof value === "string" ? value : "";\n}\nfunction responseOf(value, what) {\n if (value instanceof Response) return value;\n throw new TypeError(`${what} did not return a Response.`);\n}\nasync function answerOf(chosen, name, request, env, ctx) {\n try {\n return responseOf(await chosen.handler(request, env, ctx), "The handler");\n } catch (error) {\n if (chosen.onError === null) {\n console.error(`naro functions: ${name} threw`, error);\n return null;\n }\n try {\n return responseOf(await chosen.onError(error), "onError");\n } catch (failed) {\n console.error(`naro functions: ${name} threw`, error, failed);\n return null;\n }\n }\n}\nasync function answerCall(request, env, ctx, seen) {\n const name = textOf(env.NARO_FUNCTION_NAME);\n const context = decodeContext(request.headers.get(CONTEXT_HEADER), env);\n if (context === null) return wrapperError("FUNCTION_MISROUTED", name, null);\n let forwarded;\n try {\n forwarded = callerRequest(request, context);\n } catch (error) {\n console.error(`naro functions: ${name} misrouted`, error);\n return wrapperError("FUNCTION_MISROUTED", name, null);\n }\n seen.traceId = context.traceId;\n const call = {\n role: context.role,\n user: context.user,\n traceId: context.traceId\n };\n contexts.set(forwarded, call);\n const chosen = chooseHandler();\n if (chosen === null)\n return wrapperError("FUNCTION_NO_HANDLER", name, context.traceId);\n logWaitUntilRejections(ctx, name);\n const answer = await state.requests.run(\n { ctx, hop: context.hop, traceId: context.traceId, call },\n () => answerOf(chosen, name, forwarded, env, ctx)\n );\n if (answer === null)\n return wrapperError("FUNCTION_ERROR", name, context.traceId);\n try {\n return withFunctionHeaders(answer, context.traceId);\n } catch (error) {\n console.error(\n `naro functions: ${name} answered with a response that cannot be sent`,\n error\n );\n return wrapperError("FUNCTION_ERROR", name, context.traceId);\n }\n}\nvar naro_entry_default = {\n async fetch(request, env, ctx) {\n const seen = { traceId: null };\n let answer;\n try {\n answer = await answerCall(request, env, ctx, seen);\n } catch (error) {\n return state.lastResort(error, seen.traceId);\n }\n if (state.isResponse(answer)) return answer;\n return state.lastResort(\n new TypeError("The wrapper\'s answer is not a Response."),\n seen.traceId\n );\n }\n};\nexport {\n naro_entry_default as default\n};\n';
|
|
36062
|
-
var NARO_SHIM_SOURCE = '// src/naro-shim.ts\nimport { AsyncLocalStorage } from "node:async_hooks";\nimport { env } from "cloudflare:workers";\n\n// src/hosts.ts\nfunction bareHost(host) {\n return host.toLowerCase().replace(/\\.+$/, "");\n}\nfunction isNaroHost(hostname, rootDomain2, naroUrl2) {\n const host = bareHost(hostname);\n if (host === "") return false;\n const root = rootDomain2.toLowerCase().replace(/^\\.+|\\.+$/g, "");\n if (root !== "" && host.endsWith(`.${root}`)) return true;\n if (naroUrl2 === "") return false;\n try {\n return bareHost(new URL(naroUrl2).hostname) === host;\n } catch {\n return false;\n }\n}\n\n// src/redact.ts\nvar MIN_REDACTED_LENGTH = 8;\nvar REDACTED = "[redacted]";\nfunction isSecretBinding(name) {\n return name === "NARO_SERVICE_KEY" || !name.startsWith("NARO_") && name !== "DB";\n}\nfunction secretValues(bindings2) {\n const values = /* @__PURE__ */ new Set();\n for (const [name, value] of Object.entries(bindings2)) {\n if (typeof value !== "string" || value.length < MIN_REDACTED_LENGTH)\n continue;\n if (!isSecretBinding(name)) continue;\n values.add(value);\n values.add(JSON.stringify(value).slice(1, -1));\n }\n return [...values].toSorted(\n (left, right) => right.length - left.length || (left < right ? -1 : left > right ? 1 : 0)\n );\n}\nfunction redactText(text, secrets2) {\n const spans = [];\n for (const secret of secrets2) {\n if (secret === "") continue;\n let last;\n for (let at = text.indexOf(secret); at !== -1; at = text.indexOf(secret, at + 1)) {\n if (last !== void 0 && at <= last[1]) last[1] = at + secret.length;\n else spans.push(last = [at, at + secret.length]);\n }\n }\n if (spans.length === 0) return text;\n const ordered = spans.toSorted((left, right) => left[0] - right[0]);\n let out = "";\n let kept = 0;\n let [start, end] = ordered[0];\n for (const [nextStart, nextEnd] of ordered) {\n if (nextStart <= end) {\n end = Math.max(end, nextEnd);\n continue;\n }\n out += text.slice(kept, start) + REDACTED;\n kept = end;\n start = nextStart;\n end = nextEnd;\n }\n return out + text.slice(kept, start) + REDACTED + text.slice(end);\n}\nfunction stringOf(value) {\n try {\n return String(value);\n } catch {\n return void 0;\n }\n}\nfunction withHead(stack, head) {\n if (stack === head || stack.startsWith(`${head}\n`)) return stack;\n const frames = stack.indexOf("\\n at ");\n return frames === -1 ? `${head}\n${stack}` : `${head}${stack.slice(frames)}`;\n}\nfunction redactError(error, secrets2) {\n const stack = typeof error.stack === "string" ? error.stack : `${error.name}: ${error.message}`;\n const head = stringOf(error);\n let json;\n try {\n json = JSON.stringify(error);\n } catch {\n json = void 0;\n }\n const cleanStack = redactText(stack, secrets2);\n const cleanHead = head === void 0 ? void 0 : redactText(head, secrets2);\n const cleanJson = json === void 0 ? void 0 : redactText(json, secrets2);\n if (cleanStack === stack && head !== void 0 && cleanHead === head && json !== void 0 && cleanJson === json)\n return error;\n const text = cleanHead === void 0 ? cleanStack : withHead(cleanStack, cleanHead);\n return cleanJson === void 0 || cleanJson === "{}" ? text : `${text}\n${cleanJson}`;\n}\nfunction isPlain(value) {\n if (Array.isArray(value)) return true;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\nfunction redactArg(arg, secrets2) {\n if (typeof arg === "string") return redactText(arg, secrets2);\n if (arg === null || typeof arg !== "object" && typeof arg !== "function")\n return arg;\n try {\n if (arg instanceof Error) return redactError(arg, secrets2);\n let json;\n try {\n json = JSON.stringify(arg);\n } catch {\n return redactText(String(arg), secrets2);\n }\n if (json !== void 0) {\n const clean2 = redactText(json, secrets2);\n if (clean2 !== json) return clean2;\n }\n if (isPlain(arg)) return arg;\n const text = stringOf(arg);\n if (text === void 0) return arg;\n const clean = redactText(text, secrets2);\n return clean === text ? arg : clean;\n } catch {\n return REDACTED;\n }\n}\nfunction redactArgs(args, secrets2) {\n if (secrets2.length === 0) return [...args];\n return args.map((arg) => redactArg(arg, secrets2));\n}\n\n// src/rejections.ts\nfunction loggedRejection(promise, name) {\n return Promise.resolve(promise).then(\n () => void 0,\n (error) => {\n console.error(`naro functions: ${name} waitUntil rejected`, error);\n }\n );\n}\n\n// src/respond.ts\nvar FUNCTION_EXPOSE_HEADERS = [\n "x-relay-error",\n "x-naro-trace-id",\n "retry-after",\n "x-retry-after"\n];\nvar WRAPPER_ERRORS = {\n FUNCTION_ERROR: "The function threw an error. See `naro functions logs {name}`.",\n FUNCTION_NO_HANDLER: "The function has no handler. Call Deno.serve(handler) or export default { fetch }.",\n FUNCTION_MISROUTED: "This function was reached without the Naro gateway."\n};\nfunction appendExposeHeaders(value) {\n const listed = (value ?? "").split(",").map((part) => part.trim()).filter((part) => part !== "");\n const seen = new Set(listed.map((part) => part.toLowerCase()));\n for (const name of FUNCTION_EXPOSE_HEADERS)\n if (!seen.has(name)) listed.push(name);\n return listed.join(", ");\n}\n\n// src/runtime-state.ts\nvar RUNTIME_KEY = /* @__PURE__ */ Symbol.for("naro.functions.runtime");\nvar CONTEXT_KEY = /* @__PURE__ */ Symbol.for("naro.functions.context");\n\n// src/naro-shim.ts\nvar bindings = env;\nvar globals = globalThis;\nvar functionName = typeof bindings.NARO_FUNCTION_NAME === "string" ? bindings.NARO_FUNCTION_NAME : "";\nvar secrets = secretValues(bindings);\nfor (const level of ["log", "info", "warn", "error", "debug"]) {\n const original = console[level].bind(console);\n console[level] = (...args) => {\n original(...redactArgs(args, secrets));\n };\n}\nvar logError = console.error;\nvar NativeResponse = Response;\nvar bareRecord = Object.create;\nvar failedBody = JSON.stringify({\n data: null,\n error: {\n code: "FUNCTION_ERROR",\n message: WRAPPER_ERRORS.FUNCTION_ERROR.replace(\n "{name}",\n () => functionName\n )\n }\n});\nvar exposedHeaders = appendExposeHeaders(null);\nfunction lastResort(error, traceId) {\n try {\n logError(`naro functions: ${functionName} threw`, error);\n } catch {\n }\n const headers = bareRecord(null);\n headers["content-type"] = "application/json";\n headers["access-control-allow-origin"] = "*";\n headers["access-control-expose-headers"] = exposedHeaders;\n headers["cache-control"] = "no-store";\n if (traceId !== null) headers["x-naro-trace-id"] = traceId;\n const init = bareRecord(null);\n init.status = 500;\n init.headers = headers;\n return new NativeResponse(failedBody, init);\n}\nvar reflectApply = Reflect.apply;\nvar responseStatus = Object.getOwnPropertyDescriptor(\n NativeResponse.prototype,\n "status"\n)?.get;\nfunction isResponse(value) {\n if (typeof value !== "object" || value === null) return false;\n try {\n if (responseStatus === void 0) return value instanceof NativeResponse;\n reflectApply(responseStatus, value, []);\n return true;\n } catch {\n return false;\n }\n}\nvar state = {\n handler: null,\n onError: null,\n requests: new AsyncLocalStorage(),\n lastResort,\n isResponse\n};\nglobals[RUNTIME_KEY] = state;\nglobals[CONTEXT_KEY] = /* @__PURE__ */ new WeakMap();\nvar rootDomain = typeof bindings.NARO_ROOT_DOMAIN === "string" ? bindings.NARO_ROOT_DOMAIN : "";\nvar naroUrl = typeof bindings.NARO_URL === "string" ? bindings.NARO_URL : "";\nvar originalFetch = globalThis.fetch.bind(globalThis);\nasync function naroFetch(input, init) {\n const store = state.requests.getStore();\n if (store === void 0) return originalFetch(input, init);\n let host;\n try {\n host = new URL(input instanceof Request ? input.url : String(input)).hostname;\n } catch {\n return originalFetch(input, init);\n }\n if (!isNaroHost(host, rootDomain, naroUrl)) return originalFetch(input, init);\n const request = new Request(input, init);\n const headers = new Headers(request.headers);\n headers.set("x-naro-fn-hop", String(store.hop + 1));\n headers.set("x-naro-trace-id", store.traceId);\n return originalFetch(new Request(request, { headers }));\n}\nglobalThis.fetch = naroFetch;\nvar addListener = EventTarget.prototype.addEventListener;\nvar removeListener = EventTarget.prototype.removeEventListener;\nvar standIns = /* @__PURE__ */ new WeakMap();\nfunction standInFor(socket, listener, store) {\n let listeners = standIns.get(socket);\n if (listeners === void 0)\n standIns.set(socket, listeners = /* @__PURE__ */ new WeakMap());\n let standIn = listeners.get(listener);\n if (standIn === void 0) {\n standIn = function(event) {\n state.requests.run(\n store,\n () => typeof listener === "function" ? Reflect.apply(listener, this, [event]) : listener.handleEvent(event)\n );\n };\n listeners.set(listener, standIn);\n }\n return standIn;\n}\nObject.defineProperties(WebSocket.prototype, {\n addEventListener: {\n configurable: true,\n writable: true,\n value(type, listener, ...rest) {\n const store = state.requests.getStore();\n const runs = store !== void 0 && listener !== null && (typeof listener === "function" || typeof listener === "object");\n return Reflect.apply(addListener, this, [\n type,\n runs ? standInFor(this, listener, store) : listener,\n ...rest\n ]);\n }\n },\n removeEventListener: {\n configurable: true,\n writable: true,\n value(type, listener, ...rest) {\n const standIn = typeof listener === "function" || typeof listener === "object" && listener !== null ? standIns.get(this)?.get(listener) : void 0;\n return Reflect.apply(removeListener, this, [\n type,\n standIn ?? listener,\n ...rest\n ]);\n }\n }\n});\nvar ON_EVENTS = ["onopen", "onmessage", "onclose", "onerror"];\nvar onHandlers = /* @__PURE__ */ new WeakMap();\nfunction nativeAccessor(target, name) {\n for (let at = target; at !== null; at = Object.getPrototypeOf(at)) {\n const descriptor = Object.getOwnPropertyDescriptor(at, name);\n if (descriptor === void 0) continue;\n return descriptor.get !== void 0 && descriptor.set !== void 0 ? { get: descriptor.get, set: descriptor.set } : void 0;\n }\n return void 0;\n}\nfor (const name of ON_EVENTS) {\n const native = nativeAccessor(WebSocket.prototype, name);\n Object.defineProperty(WebSocket.prototype, name, {\n configurable: true,\n enumerable: true,\n get() {\n const set = onHandlers.get(this)?.get(name);\n if (set === void 0)\n return native === void 0 ? void 0 : Reflect.apply(native.get, this, []);\n return state.requests.getStore() === void 0 ? set.standIn : set.handler;\n },\n set(handler) {\n const store = state.requests.getStore();\n const standIn = store !== void 0 && typeof handler === "function" ? standInFor(this, handler, store) : handler;\n let handlers = onHandlers.get(this);\n if (handlers === void 0) onHandlers.set(this, handlers = /* @__PURE__ */ new Map());\n handlers.set(name, { handler, standIn });\n if (native !== void 0) Reflect.apply(native.set, this, [standIn]);\n }\n });\n}\nfunction serve(first, second) {\n const options = typeof first === "function" ? {} : first ?? {};\n const handler = typeof first === "function" ? first : second ?? options.handler;\n if (typeof handler !== "function")\n throw new TypeError("Deno.serve needs a handler.");\n state.handler = handler;\n state.onError = typeof options.onError === "function" ? options.onError : null;\n return {\n finished: new Promise(() => {\n }),\n shutdown: () => Promise.resolve()\n };\n}\nfunction textBindings() {\n const out = {};\n for (const [name, value] of Object.entries(bindings))\n if (typeof value === "string") out[name] = value;\n return out;\n}\nvar build = Object.freeze({\n os: "linux",\n arch: "x86_64",\n target: "x86_64-unknown-linux-gnu",\n vendor: "unknown",\n env: "gnu"\n});\nvar version = Object.freeze({\n deno: "2.1.4",\n v8: "13.0.245.12",\n typescript: "5.6.2"\n});\nvar DENO_ERROR_NAMES = [\n "NotFound",\n "PermissionDenied",\n "ConnectionRefused",\n "ConnectionReset",\n "ConnectionAborted",\n "NotConnected",\n "AddrInUse",\n "AddrNotAvailable",\n "BrokenPipe",\n "AlreadyExists",\n "InvalidData",\n "TimedOut",\n "Interrupted",\n "WriteZero",\n "WouldBlock",\n "UnexpectedEof",\n "BadResource",\n "Http",\n "Busy",\n "NotSupported",\n "FilesystemLoop",\n "IsADirectory",\n "NetworkUnreachable",\n "NotADirectory",\n "NotCapable"\n];\nfunction denoError(name) {\n const made = { [name]: class extends Error {\n } }[name];\n Object.defineProperty(made.prototype, "name", {\n configurable: true,\n writable: true,\n value: name\n });\n return made;\n}\nvar errors = Object.freeze(\n Object.fromEntries(DENO_ERROR_NAMES.map((name) => [name, denoError(name)]))\n);\nvar deno = {\n serve,\n env: {\n get: (name) => {\n const value = bindings[name];\n return typeof value === "string" ? value : void 0;\n },\n has: (name) => typeof bindings[name] === "string",\n toObject: () => textBindings(),\n set: () => {\n throw new Error("Deno.env.set is not available on Naro Functions.");\n },\n delete: () => {\n throw new Error("Deno.env.delete is not available on Naro Functions.");\n }\n },\n build,\n version,\n errors\n};\nvar PROBES = /* @__PURE__ */ new Set(["then", "toJSON"]);\nglobals.Deno = new Proxy(deno, {\n get(target, property) {\n if (typeof property === "symbol" || PROBES.has(property)) return void 0;\n if (Object.hasOwn(target, property)) return target[property];\n return () => {\n throw new Error(`Deno.${property} is not available on Naro Functions.`);\n };\n }\n});\nglobals.EdgeRuntime = {\n waitUntil(promise) {\n const logged = loggedRejection(promise, functionName);\n state.requests.getStore()?.ctx.waitUntil(logged);\n return promise;\n }\n};\naddEventListener("unhandledrejection", (event) => {\n event.preventDefault();\n console.error(\n `naro functions: ${functionName} unhandled rejection`,\n event.reason\n );\n});\n';
|
|
36063
|
-
var NARO_BOOT_SOURCE = '// src/naro-boot.ts\nimport "./naro-shim.js";\nimport * as user from "./user.js";\n\n// src/runtime-state.ts\nvar RUNTIME_KEY = /* @__PURE__ */ Symbol.for("naro.functions.runtime");\nvar SHIM_FIRST = "The Naro functions shim must be imported before the wrapper.";\nfunction globals() {\n return globalThis;\n}\nfunction runtimeState() {\n const state2 = globals()[RUNTIME_KEY];\n if (state2 === void 0) throw new Error(SHIM_FIRST);\n return state2;\n}\n\n// src/naro-boot.ts\nvar BOOT_PATH = "/__naro/boot";\nvar state = runtimeState();\nfunction hasHandler(exported) {\n if (state.handler !== null) return true;\n if (typeof exported === "function") return true;\n return typeof exported === "object" && exported !== null && typeof exported.fetch === "function";\n}\nvar naro_boot_default = {\n fetch(request) {\n if (new URL(request.url).pathname !== BOOT_PATH)\n return new Response("Not found", { status: 404 });\n return Response.json({ handler: hasHandler(user.default) });\n }\n};\nexport {\n naro_boot_default as default\n};\n';
|
|
36307
|
+
var NARO_SHIM_SOURCE = '// src/naro-shim.ts\nimport { AsyncLocalStorage } from "node:async_hooks";\nimport { env } from "cloudflare:workers";\n\n// src/hosts.ts\nfunction bareHost(host) {\n return host.toLowerCase().replace(/\\.+$/, "");\n}\nfunction isNaroHost(hostname, rootDomain2, naroUrl2) {\n const host = bareHost(hostname);\n if (host === "") return false;\n const root = rootDomain2.toLowerCase().replace(/^\\.+|\\.+$/g, "");\n if (root !== "" && host.endsWith(`.${root}`)) return true;\n if (naroUrl2 === "") return false;\n try {\n return bareHost(new URL(naroUrl2).hostname) === host;\n } catch {\n return false;\n }\n}\n\n// src/redact.ts\nvar MIN_REDACTED_LENGTH = 8;\nvar REDACTED = "[redacted]";\nfunction isSecretBinding(name) {\n return name === "NARO_SERVICE_KEY" || !name.startsWith("NARO_") && name !== "DB";\n}\nfunction secretValues(bindings2) {\n const values = /* @__PURE__ */ new Set();\n for (const [name, value] of Object.entries(bindings2)) {\n if (typeof value !== "string" || value.length < MIN_REDACTED_LENGTH)\n continue;\n if (!isSecretBinding(name)) continue;\n values.add(value);\n values.add(JSON.stringify(value).slice(1, -1));\n }\n return [...values].toSorted(\n (left, right) => right.length - left.length || (left < right ? -1 : left > right ? 1 : 0)\n );\n}\nfunction redactText(text, secrets2) {\n const spans = [];\n for (const secret of secrets2) {\n if (secret === "") continue;\n let last;\n for (let at = text.indexOf(secret); at !== -1; at = text.indexOf(secret, at + 1)) {\n if (last !== void 0 && at <= last[1]) last[1] = at + secret.length;\n else spans.push(last = [at, at + secret.length]);\n }\n }\n if (spans.length === 0) return text;\n const ordered = spans.toSorted((left, right) => left[0] - right[0]);\n let out = "";\n let kept = 0;\n let [start, end] = ordered[0];\n for (const [nextStart, nextEnd] of ordered) {\n if (nextStart <= end) {\n end = Math.max(end, nextEnd);\n continue;\n }\n out += text.slice(kept, start) + REDACTED;\n kept = end;\n start = nextStart;\n end = nextEnd;\n }\n return out + text.slice(kept, start) + REDACTED + text.slice(end);\n}\nfunction stringOf(value) {\n try {\n return String(value);\n } catch {\n return void 0;\n }\n}\nfunction withHead(stack, head) {\n if (stack === head || stack.startsWith(`${head}\n`)) return stack;\n const frames = stack.indexOf("\\n at ");\n return frames === -1 ? `${head}\n${stack}` : `${head}${stack.slice(frames)}`;\n}\nfunction redactError(error, secrets2) {\n const stack = typeof error.stack === "string" ? error.stack : `${error.name}: ${error.message}`;\n const head = stringOf(error);\n let json;\n try {\n json = JSON.stringify(error);\n } catch {\n json = void 0;\n }\n const cleanStack = redactText(stack, secrets2);\n const cleanHead = head === void 0 ? void 0 : redactText(head, secrets2);\n const cleanJson = json === void 0 ? void 0 : redactText(json, secrets2);\n if (cleanStack === stack && head !== void 0 && cleanHead === head && json !== void 0 && cleanJson === json)\n return error;\n const text = cleanHead === void 0 ? cleanStack : withHead(cleanStack, cleanHead);\n return cleanJson === void 0 || cleanJson === "{}" ? text : `${text}\n${cleanJson}`;\n}\nfunction isPlain(value) {\n if (Array.isArray(value)) return true;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\nfunction redactArg(arg, secrets2) {\n if (typeof arg === "string") return redactText(arg, secrets2);\n if (arg === null || typeof arg !== "object" && typeof arg !== "function")\n return arg;\n try {\n if (arg instanceof Error) return redactError(arg, secrets2);\n let json;\n try {\n json = JSON.stringify(arg);\n } catch {\n return redactText(String(arg), secrets2);\n }\n if (json !== void 0) {\n const clean2 = redactText(json, secrets2);\n if (clean2 !== json) return clean2;\n }\n if (isPlain(arg)) return arg;\n const text = stringOf(arg);\n if (text === void 0) return arg;\n const clean = redactText(text, secrets2);\n return clean === text ? arg : clean;\n } catch {\n return REDACTED;\n }\n}\nfunction redactArgs(args, secrets2) {\n if (secrets2.length === 0) return [...args];\n return args.map((arg) => redactArg(arg, secrets2));\n}\n\n// src/rejections.ts\nfunction loggedRejection(promise, name) {\n return Promise.resolve(promise).then(\n () => void 0,\n (error) => {\n console.error(`naro functions: ${name} waitUntil rejected`, error);\n }\n );\n}\n\n// src/respond.ts\nvar FUNCTION_EXPOSE_HEADERS = [\n "x-relay-error",\n "x-naro-trace-id",\n "retry-after",\n "x-retry-after"\n];\nvar WRAPPER_ERRORS = {\n FUNCTION_ERROR: "The function threw an error. See `naro functions logs {name}`.",\n FUNCTION_NO_HANDLER: "The function has no handler. Call Deno.serve(handler) or export default { fetch }.",\n FUNCTION_MISROUTED: "This function was reached without the Naro gateway.",\n FUNCTION_NOT_FOUND: "Function {name} was not found."\n};\nfunction appendExposeHeaders(value) {\n const listed = (value ?? "").split(",").map((part) => part.trim()).filter((part) => part !== "");\n const seen = new Set(listed.map((part) => part.toLowerCase()));\n for (const name of FUNCTION_EXPOSE_HEADERS)\n if (!seen.has(name)) listed.push(name);\n return listed.join(", ");\n}\n\n// src/runtime-state.ts\nvar RUNTIME_KEY = /* @__PURE__ */ Symbol.for("naro.functions.runtime");\nvar CONTEXT_KEY = /* @__PURE__ */ Symbol.for("naro.functions.context");\n\n// src/naro-shim.ts\nvar bindings = env;\nvar globals = globalThis;\nfunction callName() {\n return state.requests.getStore()?.name ?? "";\n}\nvar secrets = secretValues(bindings);\nfor (const level of ["log", "info", "warn", "error", "debug"]) {\n const original = console[level].bind(console);\n console[level] = (...args) => {\n original(...redactArgs(args, secrets));\n };\n}\nvar logError = console.error;\nvar NativeResponse = Response;\nvar bareRecord = Object.create;\nvar NAME_SLOT = "\\0";\nvar [failedHead = "", failedTail = ""] = JSON.stringify({\n data: null,\n error: {\n code: "FUNCTION_ERROR",\n message: WRAPPER_ERRORS.FUNCTION_ERROR.replace("{name}", () => NAME_SLOT)\n }\n}).split(JSON.stringify(NAME_SLOT).slice(1, -1));\nvar exposedHeaders = appendExposeHeaders(null);\nfunction lastResort(error, traceId, name) {\n try {\n logError(`naro functions: ${name} threw`, error);\n } catch {\n }\n const headers = bareRecord(null);\n headers["content-type"] = "application/json";\n headers["access-control-allow-origin"] = "*";\n headers["access-control-expose-headers"] = exposedHeaders;\n headers["cache-control"] = "no-store";\n if (traceId !== null) headers["x-naro-trace-id"] = traceId;\n const init = bareRecord(null);\n init.status = 500;\n init.headers = headers;\n return new NativeResponse(\n failedHead + name + failedTail,\n init\n );\n}\nvar reflectApply = Reflect.apply;\nvar responseStatus = Object.getOwnPropertyDescriptor(\n NativeResponse.prototype,\n "status"\n)?.get;\nfunction isResponse(value) {\n if (typeof value !== "object" || value === null) return false;\n try {\n if (responseStatus === void 0) return value instanceof NativeResponse;\n reflectApply(responseStatus, value, []);\n return true;\n } catch {\n return false;\n }\n}\nvar state = {\n router: null,\n registered: null,\n requests: new AsyncLocalStorage(),\n lastResort,\n isResponse\n};\nglobals[RUNTIME_KEY] = state;\nglobals[CONTEXT_KEY] = /* @__PURE__ */ new WeakMap();\nvar rootDomain = typeof bindings.NARO_ROOT_DOMAIN === "string" ? bindings.NARO_ROOT_DOMAIN : "";\nvar naroUrl = typeof bindings.NARO_URL === "string" ? bindings.NARO_URL : "";\nvar originalFetch = globalThis.fetch.bind(globalThis);\nasync function naroFetch(input, init) {\n const store = state.requests.getStore();\n if (store === void 0) return originalFetch(input, init);\n let host;\n try {\n host = new URL(input instanceof Request ? input.url : String(input)).hostname;\n } catch {\n return originalFetch(input, init);\n }\n if (!isNaroHost(host, rootDomain, naroUrl)) return originalFetch(input, init);\n const request = new Request(input, init);\n const headers = new Headers(request.headers);\n headers.set("x-naro-fn-hop", String(store.hop + 1));\n headers.set("x-naro-trace-id", store.traceId);\n return originalFetch(new Request(request, { headers }));\n}\nglobalThis.fetch = naroFetch;\nvar addListener = EventTarget.prototype.addEventListener;\nvar removeListener = EventTarget.prototype.removeEventListener;\nvar standIns = /* @__PURE__ */ new WeakMap();\nfunction standInFor(socket, listener, store) {\n let listeners = standIns.get(socket);\n if (listeners === void 0)\n standIns.set(socket, listeners = /* @__PURE__ */ new WeakMap());\n let standIn = listeners.get(listener);\n if (standIn === void 0) {\n standIn = function(event) {\n state.requests.run(\n store,\n () => typeof listener === "function" ? Reflect.apply(listener, this, [event]) : listener.handleEvent(event)\n );\n };\n listeners.set(listener, standIn);\n }\n return standIn;\n}\nObject.defineProperties(WebSocket.prototype, {\n addEventListener: {\n configurable: true,\n writable: true,\n value(type, listener, ...rest) {\n const store = state.requests.getStore();\n const runs = store !== void 0 && listener !== null && (typeof listener === "function" || typeof listener === "object");\n return Reflect.apply(addListener, this, [\n type,\n runs ? standInFor(this, listener, store) : listener,\n ...rest\n ]);\n }\n },\n removeEventListener: {\n configurable: true,\n writable: true,\n value(type, listener, ...rest) {\n const standIn = typeof listener === "function" || typeof listener === "object" && listener !== null ? standIns.get(this)?.get(listener) : void 0;\n return Reflect.apply(removeListener, this, [\n type,\n standIn ?? listener,\n ...rest\n ]);\n }\n }\n});\nvar ON_EVENTS = ["onopen", "onmessage", "onclose", "onerror"];\nvar onHandlers = /* @__PURE__ */ new WeakMap();\nfunction nativeAccessor(target, name) {\n for (let at = target; at !== null; at = Object.getPrototypeOf(at)) {\n const descriptor = Object.getOwnPropertyDescriptor(at, name);\n if (descriptor === void 0) continue;\n return descriptor.get !== void 0 && descriptor.set !== void 0 ? { get: descriptor.get, set: descriptor.set } : void 0;\n }\n return void 0;\n}\nfor (const name of ON_EVENTS) {\n const native = nativeAccessor(WebSocket.prototype, name);\n Object.defineProperty(WebSocket.prototype, name, {\n configurable: true,\n enumerable: true,\n get() {\n const set = onHandlers.get(this)?.get(name);\n if (set === void 0)\n return native === void 0 ? void 0 : Reflect.apply(native.get, this, []);\n return state.requests.getStore() === void 0 ? set.standIn : set.handler;\n },\n set(handler) {\n const store = state.requests.getStore();\n const standIn = store !== void 0 && typeof handler === "function" ? standInFor(this, handler, store) : handler;\n let handlers = onHandlers.get(this);\n if (handlers === void 0) onHandlers.set(this, handlers = /* @__PURE__ */ new Map());\n handlers.set(name, { handler, standIn });\n if (native !== void 0) Reflect.apply(native.set, this, [standIn]);\n }\n });\n}\nfunction serve(first, second) {\n const options = typeof first === "function" ? {} : first ?? {};\n const handler = typeof first === "function" ? first : second ?? options.handler;\n if (typeof handler !== "function")\n throw new TypeError("Deno.serve needs a handler.");\n const onError = typeof options.onError === "function" ? options.onError : null;\n if (state.requests.getStore() === void 0) {\n if (state.router !== null) state.router.register(handler, onError);\n else state.registered = { handler, onError };\n }\n return {\n finished: new Promise(() => {\n }),\n shutdown: () => Promise.resolve()\n };\n}\nfunction textBindings() {\n const out = {};\n for (const [name, value] of Object.entries(bindings))\n if (typeof value === "string") out[name] = value;\n return out;\n}\nvar build = Object.freeze({\n os: "linux",\n arch: "x86_64",\n target: "x86_64-unknown-linux-gnu",\n vendor: "unknown",\n env: "gnu"\n});\nvar version = Object.freeze({\n deno: "2.1.4",\n v8: "13.0.245.12",\n typescript: "5.6.2"\n});\nvar DENO_ERROR_NAMES = [\n "NotFound",\n "PermissionDenied",\n "ConnectionRefused",\n "ConnectionReset",\n "ConnectionAborted",\n "NotConnected",\n "AddrInUse",\n "AddrNotAvailable",\n "BrokenPipe",\n "AlreadyExists",\n "InvalidData",\n "TimedOut",\n "Interrupted",\n "WriteZero",\n "WouldBlock",\n "UnexpectedEof",\n "BadResource",\n "Http",\n "Busy",\n "NotSupported",\n "FilesystemLoop",\n "IsADirectory",\n "NetworkUnreachable",\n "NotADirectory",\n "NotCapable"\n];\nfunction denoError(name) {\n const made = { [name]: class extends Error {\n } }[name];\n Object.defineProperty(made.prototype, "name", {\n configurable: true,\n writable: true,\n value: name\n });\n return made;\n}\nvar errors = Object.freeze(\n Object.fromEntries(DENO_ERROR_NAMES.map((name) => [name, denoError(name)]))\n);\nvar deno = {\n serve,\n env: {\n get: (name) => {\n const value = bindings[name];\n return typeof value === "string" ? value : void 0;\n },\n has: (name) => typeof bindings[name] === "string",\n toObject: () => textBindings(),\n set: () => {\n throw new Error("Deno.env.set is not available on Naro Functions.");\n },\n delete: () => {\n throw new Error("Deno.env.delete is not available on Naro Functions.");\n }\n },\n build,\n version,\n errors\n};\nvar PROBES = /* @__PURE__ */ new Set(["then", "toJSON"]);\nglobals.Deno = new Proxy(deno, {\n get(target, property) {\n if (typeof property === "symbol" || PROBES.has(property)) return void 0;\n if (Object.hasOwn(target, property)) return target[property];\n return () => {\n throw new Error(`Deno.${property} is not available on Naro Functions.`);\n };\n }\n});\nglobals.EdgeRuntime = {\n waitUntil(promise) {\n const logged = loggedRejection(promise, callName());\n state.requests.getStore()?.ctx.waitUntil(logged);\n return promise;\n }\n};\naddEventListener("unhandledrejection", (event) => {\n event.preventDefault();\n console.error(\n `naro functions: ${callName()} unhandled rejection`,\n event.reason\n );\n});\n';
|
|
36308
|
+
var NARO_BOOT_SOURCE = '// src/naro-boot.ts\nimport "./naro-shim.js";\nimport * as user from "./user.js";\n\n// src/choose.ts\nasync function serveWithInfo(registered, request) {\n let complete;\n const info = {\n remoteAddr: {\n transport: "tcp",\n hostname: request.headers.get("cf-connecting-ip") ?? "0.0.0.0",\n port: 0\n },\n completed: new Promise((resolve) => {\n complete = resolve;\n })\n };\n try {\n return await registered(request, info);\n } finally {\n complete();\n }\n}\nfunction chosenOf(registered, module) {\n if (registered !== null) {\n const handler = registered.handler;\n return {\n handler: async (request) => serveWithInfo(handler, request),\n onError: registered.onError\n };\n }\n const exported = module.default;\n if (typeof exported === "object" && exported !== null) {\n const fetchHandler = exported.fetch;\n if (typeof fetchHandler === "function")\n return {\n handler: async (request, env, ctx) => fetchHandler.call(exported, request, env, ctx),\n onError: null\n };\n }\n if (typeof exported === "function") {\n const prototype = exported.prototype;\n if (typeof prototype === "object" && prototype !== null && typeof prototype.fetch === "function")\n return {\n handler: async (request, env, ctx) => new exported(ctx, env).fetch(request),\n onError: null\n };\n return {\n handler: async (request, env, ctx) => exported(request, env, ctx),\n onError: null\n };\n }\n return null;\n}\n\n// src/runtime-state.ts\nvar RUNTIME_KEY = /* @__PURE__ */ Symbol.for("naro.functions.runtime");\nvar SHIM_FIRST = "The Naro functions shim must be imported before the wrapper.";\nfunction globals() {\n return globalThis;\n}\nfunction runtimeState() {\n const state2 = globals()[RUNTIME_KEY];\n if (state2 === void 0) throw new Error(SHIM_FIRST);\n return state2;\n}\n\n// src/naro-boot.ts\nvar BOOT_PATH = "/__naro/boot";\nvar state = runtimeState();\nvar naro_boot_default = {\n fetch(request) {\n if (new URL(request.url).pathname !== BOOT_PATH)\n return new Response("Not found", { status: 404 });\n const module = user;\n return Response.json({\n handler: chosenOf(state.registered, module) !== null\n });\n }\n};\nexport {\n naro_boot_default as default\n};\n';
|
|
36064
36309
|
var FUNCTIONS_COMPATIBILITY_DATE = "2026-08-19";
|
|
36065
36310
|
var FUNCTIONS_COMPATIBILITY_FLAGS = Object.freeze(["nodejs_compat", "global_fetch_strictly_public"]);
|
|
36066
36311
|
|
|
36067
36312
|
// ../../packages/functions-bundler/src/bundle.ts
|
|
36068
36313
|
var NARO_JS_FUNCTIONS = /Could not resolve "naro-js\/functions"/;
|
|
36069
|
-
var runtimeGzip;
|
|
36070
|
-
function runtimeCompressedBytes() {
|
|
36071
|
-
runtimeGzip ??= gzipSync(NARO_ENTRY_SOURCE).byteLength + gzipSync(NARO_SHIM_SOURCE).byteLength;
|
|
36072
|
-
return runtimeGzip;
|
|
36073
|
-
}
|
|
36074
36314
|
function relativeLabel(root, file2) {
|
|
36075
36315
|
return relative10(root, file2).split(sep9).join("/");
|
|
36076
36316
|
}
|
|
@@ -36177,18 +36417,11 @@ async function bundleFunction(source, folder, options = {}) {
|
|
|
36177
36417
|
BUNDLE_MESSAGES.tooLarge(source.name, bytes2, biggest)
|
|
36178
36418
|
);
|
|
36179
36419
|
}
|
|
36180
|
-
const compressedBytes = gzipSync(output.contents).byteLength + runtimeCompressedBytes();
|
|
36181
|
-
if (compressedBytes > (options.maxCompressedBytes ?? FUNCTION_COMPRESSED_BUNDLE_MAX_BYTES))
|
|
36182
|
-
throw new FunctionBundleError(
|
|
36183
|
-
"FUNCTION_BUNDLE_FAILED",
|
|
36184
|
-
BUNDLE_MESSAGES.tooLargeCompressed(source.name, compressedBytes)
|
|
36185
|
-
);
|
|
36186
36420
|
return {
|
|
36187
36421
|
name: source.name,
|
|
36188
36422
|
code: output.text,
|
|
36189
36423
|
contents: output.contents,
|
|
36190
36424
|
bytes: bytes2,
|
|
36191
|
-
compressedBytes,
|
|
36192
36425
|
sha256: createHash4("sha256").update(output.contents).digest("hex"),
|
|
36193
36426
|
warnings: [...state.warnings]
|
|
36194
36427
|
};
|
|
@@ -36312,7 +36545,16 @@ function retryAfterSeconds(error51) {
|
|
|
36312
36545
|
const value = error51.details?.retryAfter;
|
|
36313
36546
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(Math.ceil(value), FUNCTION_RATE_LIMIT_MAX_SECONDS) : FUNCTION_RATE_LIMIT_DEFAULT_SECONDS;
|
|
36314
36547
|
}
|
|
36548
|
+
var functionWords = (name) => ({
|
|
36549
|
+
rateLimited: (seconds, attempt, of) => `\u23F3 Cloudflare is rate limiting function uploads; trying ${name} again in ${String(seconds)} seconds (${String(attempt)}/${String(of)}).`,
|
|
36550
|
+
busy: (attempt, of) => `\u23F3 ${name} is being changed right now; trying again in ${String(FUNCTION_BUSY_WAIT_MS / 1e3)} seconds (${String(attempt)}/${String(of)}).`
|
|
36551
|
+
});
|
|
36552
|
+
var batchRetryWords = (count2) => ({
|
|
36553
|
+
rateLimited: (seconds, attempt, of) => `\u23F3 Cloudflare is rate limiting function uploads; trying the ${String(count2)} functions again in ${String(seconds)} seconds (${String(attempt)}/${String(of)}).`,
|
|
36554
|
+
busy: (attempt, of) => `\u23F3 Functions in this project are being changed right now; trying again in ${String(FUNCTION_BUSY_WAIT_MS / 1e3)} seconds (${String(attempt)}/${String(of)}).`
|
|
36555
|
+
});
|
|
36315
36556
|
async function withFunctionRetries(op, name, io) {
|
|
36557
|
+
const words = typeof name === "string" ? functionWords(name) : name;
|
|
36316
36558
|
const rateRetries = io.rateLimitRetries ?? FUNCTION_RETRIES;
|
|
36317
36559
|
const busyRetries = io.busyRetries ?? FUNCTION_RETRIES;
|
|
36318
36560
|
let rate = 0;
|
|
@@ -36325,17 +36567,13 @@ async function withFunctionRetries(op, name, io) {
|
|
|
36325
36567
|
if (error51.code === "FUNCTIONS_RATE_LIMITED" && rate < rateRetries) {
|
|
36326
36568
|
rate += 1;
|
|
36327
36569
|
const seconds = retryAfterSeconds(error51);
|
|
36328
|
-
io.log(
|
|
36329
|
-
`\u23F3 Cloudflare is rate limiting function uploads; trying ${name} again in ${String(seconds)} seconds (${String(rate)}/${String(rateRetries)}).`
|
|
36330
|
-
);
|
|
36570
|
+
io.log(words.rateLimited(seconds, rate, rateRetries));
|
|
36331
36571
|
await io.sleep(seconds * 1e3);
|
|
36332
36572
|
continue;
|
|
36333
36573
|
}
|
|
36334
36574
|
if (error51.code === "FUNCTION_BUSY" && busy < busyRetries) {
|
|
36335
36575
|
busy += 1;
|
|
36336
|
-
io.log(
|
|
36337
|
-
`\u23F3 ${name} is being changed right now; trying again in ${String(FUNCTION_BUSY_WAIT_MS / 1e3)} seconds (${String(busy)}/${String(busyRetries)}).`
|
|
36338
|
-
);
|
|
36576
|
+
io.log(words.busy(busy, busyRetries));
|
|
36339
36577
|
await io.sleep(FUNCTION_BUSY_WAIT_MS);
|
|
36340
36578
|
continue;
|
|
36341
36579
|
}
|
|
@@ -36354,7 +36592,6 @@ var PER_FUNCTION_CODES = /* @__PURE__ */ new Set([
|
|
|
36354
36592
|
"BUNDLE_MISSING",
|
|
36355
36593
|
"BUNDLE_MISMATCH",
|
|
36356
36594
|
"FUNCTION_LIMIT",
|
|
36357
|
-
"FUNCTION_BUSY",
|
|
36358
36595
|
"FUNCTION_NOT_FOUND",
|
|
36359
36596
|
"VERSION_NOT_FOUND",
|
|
36360
36597
|
"VERSION_NOT_DEPLOYABLE",
|
|
@@ -36384,16 +36621,20 @@ function uploadTimeoutMs(upload, now = Date.now()) {
|
|
|
36384
36621
|
var SECRETS_LIST_TIMEOUT_MS = 45e3;
|
|
36385
36622
|
var FUNCTION_CALL_TIMEOUT_MS = 12e4;
|
|
36386
36623
|
var deadlineReached = (name, deadlineMs) => `Deploying ${name} reached the ${String(deadlineMs / 1e3)}-second limit before it went live; the live version did not change. Try again.`;
|
|
36624
|
+
var batchDeadlineReached = (count2, deadlineMs) => `Deploying ${String(count2)} functions reached the ${String(deadlineMs / 1e3)}-second limit before they went live; the live versions did not change. Try again.`;
|
|
36387
36625
|
function budgetOf(options) {
|
|
36388
36626
|
const { deadlineMs } = options;
|
|
36389
36627
|
if (deadlineMs === void 0) return null;
|
|
36390
36628
|
const now = options.now ?? Date.now;
|
|
36391
36629
|
const end = now() + deadlineMs;
|
|
36392
36630
|
return {
|
|
36393
|
-
left(name, cap) {
|
|
36631
|
+
left(name, cap, message) {
|
|
36394
36632
|
const left = end - now();
|
|
36395
36633
|
if (left <= 0)
|
|
36396
|
-
throw new NaroError(
|
|
36634
|
+
throw new NaroError(
|
|
36635
|
+
"TIMEOUT",
|
|
36636
|
+
message ?? deadlineReached(name, deadlineMs)
|
|
36637
|
+
);
|
|
36397
36638
|
return Math.min(cap, left);
|
|
36398
36639
|
}
|
|
36399
36640
|
};
|
|
@@ -36438,58 +36679,79 @@ async function projectSecretNames(client, projectId, timeoutMs) {
|
|
|
36438
36679
|
return [];
|
|
36439
36680
|
}
|
|
36440
36681
|
}
|
|
36441
|
-
|
|
36682
|
+
function callLimit(budget, name, message) {
|
|
36683
|
+
return budget ? { timeoutMs: budget.left(name, FUNCTION_CALL_TIMEOUT_MS, message) } : null;
|
|
36684
|
+
}
|
|
36685
|
+
async function prepareOne(options, folder, name, importMap, secretNames, io, budget) {
|
|
36442
36686
|
const steps = options.steps ?? { bundle: bundleFunction, boot: bootCheck };
|
|
36443
36687
|
const { client, projectId } = options;
|
|
36444
|
-
const call = () => budget ? { timeoutMs: budget.left(name, FUNCTION_CALL_TIMEOUT_MS) } : null;
|
|
36445
36688
|
io.log(`\u23F3 Deploying ${name}\u2026`);
|
|
36446
|
-
|
|
36447
|
-
|
|
36448
|
-
|
|
36449
|
-
|
|
36450
|
-
|
|
36451
|
-
await steps.boot(
|
|
36452
|
-
name,
|
|
36453
|
-
bundle.code,
|
|
36454
|
-
budget ? {
|
|
36455
|
-
secretNames,
|
|
36456
|
-
timeoutMs: bootSeconds(budget.left(name, BOOT_TIMEOUT_MS))
|
|
36457
|
-
} : { secretNames }
|
|
36458
|
-
);
|
|
36459
|
-
const verifyJwt = options.verifyJwt ?? source.verifyJwt ?? true;
|
|
36460
|
-
const request2 = {
|
|
36461
|
-
size: bundle.bytes,
|
|
36462
|
-
sha256: bundle.sha256,
|
|
36463
|
-
verifyJwt,
|
|
36464
|
-
entrypoint: source.entrypoint,
|
|
36465
|
-
...options.cliVersion ? { cliVersion: options.cliVersion } : {}
|
|
36466
|
-
};
|
|
36467
|
-
const created = await withFunctionRetries(
|
|
36468
|
-
() => {
|
|
36469
|
-
const limit = call();
|
|
36470
|
-
return limit ? client.functions.createVersion(projectId, name, request2, limit) : client.functions.createVersion(projectId, name, request2);
|
|
36471
|
-
},
|
|
36689
|
+
const source = readFunctionSource(folder, name);
|
|
36690
|
+
const bundle = await steps.bundle(source, folder, { importMap });
|
|
36691
|
+
for (const warning of bundle.warnings) io.log(warning);
|
|
36692
|
+
if (!options.skipCheck)
|
|
36693
|
+
await steps.boot(
|
|
36472
36694
|
name,
|
|
36473
|
-
|
|
36474
|
-
|
|
36475
|
-
|
|
36476
|
-
|
|
36477
|
-
|
|
36695
|
+
bundle.code,
|
|
36696
|
+
budget ? {
|
|
36697
|
+
secretNames,
|
|
36698
|
+
timeoutMs: bootSeconds(budget.left(name, BOOT_TIMEOUT_MS))
|
|
36699
|
+
} : { secretNames }
|
|
36700
|
+
);
|
|
36701
|
+
const verifyJwt = options.verifyJwt ?? source.verifyJwt ?? true;
|
|
36702
|
+
const request2 = {
|
|
36703
|
+
size: bundle.bytes,
|
|
36704
|
+
sha256: bundle.sha256,
|
|
36705
|
+
verifyJwt,
|
|
36706
|
+
entrypoint: source.entrypoint,
|
|
36707
|
+
...options.cliVersion ? { cliVersion: options.cliVersion } : {}
|
|
36708
|
+
};
|
|
36709
|
+
const created = await withFunctionRetries(
|
|
36710
|
+
() => {
|
|
36711
|
+
const limit = callLimit(budget, name);
|
|
36712
|
+
return limit ? client.functions.createVersion(projectId, name, request2, limit) : client.functions.createVersion(projectId, name, request2);
|
|
36713
|
+
},
|
|
36714
|
+
name,
|
|
36715
|
+
io
|
|
36716
|
+
);
|
|
36717
|
+
const urlLife = uploadTimeoutMs(created.upload);
|
|
36718
|
+
await putBundle(
|
|
36719
|
+
options.fetch ?? fetch,
|
|
36720
|
+
name,
|
|
36721
|
+
created.upload,
|
|
36722
|
+
bundle.contents,
|
|
36723
|
+
budget ? budget.left(name, urlLife) : urlLife
|
|
36724
|
+
);
|
|
36725
|
+
return {
|
|
36726
|
+
name,
|
|
36727
|
+
versionId: created.version.id,
|
|
36728
|
+
number: created.version.number,
|
|
36729
|
+
url: created.function.url,
|
|
36730
|
+
verifyJwt: created.version.verifyJwt
|
|
36731
|
+
};
|
|
36732
|
+
}
|
|
36733
|
+
async function deployOne(options, folder, name, importMap, secretNames, io, budget) {
|
|
36734
|
+
const { client, projectId } = options;
|
|
36735
|
+
try {
|
|
36736
|
+
const ready = await prepareOne(
|
|
36737
|
+
options,
|
|
36738
|
+
folder,
|
|
36478
36739
|
name,
|
|
36479
|
-
|
|
36480
|
-
|
|
36481
|
-
|
|
36740
|
+
importMap,
|
|
36741
|
+
secretNames,
|
|
36742
|
+
io,
|
|
36743
|
+
budget
|
|
36482
36744
|
);
|
|
36483
36745
|
const live = await withFunctionRetries(
|
|
36484
36746
|
() => {
|
|
36485
|
-
const limit =
|
|
36486
|
-
return limit ? client.functions.deploy(projectId, name,
|
|
36747
|
+
const limit = callLimit(budget, name);
|
|
36748
|
+
return limit ? client.functions.deploy(projectId, name, ready.versionId, limit) : client.functions.deploy(projectId, name, ready.versionId);
|
|
36487
36749
|
},
|
|
36488
36750
|
name,
|
|
36489
36751
|
io
|
|
36490
36752
|
);
|
|
36491
36753
|
io.log(
|
|
36492
|
-
`\u2705 Deployed ${name} (v${String(live.version ??
|
|
36754
|
+
`\u2705 Deployed ${name} (v${String(live.version ?? ready.number)}) \u2014 ${live.url}`
|
|
36493
36755
|
);
|
|
36494
36756
|
return {
|
|
36495
36757
|
name,
|
|
@@ -36501,6 +36763,30 @@ async function deployOne(options, folder, name, importMap, secretNames, io, budg
|
|
|
36501
36763
|
throw asNaroError(error51);
|
|
36502
36764
|
}
|
|
36503
36765
|
}
|
|
36766
|
+
async function deployReady(options, ready, io, budget) {
|
|
36767
|
+
const { client, projectId, deadlineMs } = options;
|
|
36768
|
+
const versions = ready.map(({ name, versionId }) => ({ name, versionId }));
|
|
36769
|
+
const [only] = ready;
|
|
36770
|
+
const one = ready.length === 1 && only !== void 0 ? only.name : null;
|
|
36771
|
+
const late = deadlineMs === void 0 ? void 0 : one === null ? batchDeadlineReached(ready.length, deadlineMs) : deadlineReached(one, deadlineMs);
|
|
36772
|
+
io.log(
|
|
36773
|
+
one === null ? `\u23F3 Putting ${String(ready.length)} functions live in one upload\u2026` : `\u23F3 Putting ${one} live\u2026`
|
|
36774
|
+
);
|
|
36775
|
+
const { functions } = await withFunctionRetries(
|
|
36776
|
+
() => {
|
|
36777
|
+
const limit = callLimit(budget, one ?? "", late);
|
|
36778
|
+
return limit ? client.functions.deployMany(projectId, versions, limit) : client.functions.deployMany(projectId, versions);
|
|
36779
|
+
},
|
|
36780
|
+
one ?? batchRetryWords(ready.length),
|
|
36781
|
+
io
|
|
36782
|
+
);
|
|
36783
|
+
const live = new Map(functions.map((fn) => [fn.name, fn.version]));
|
|
36784
|
+
return ready.map((fn) => {
|
|
36785
|
+
const version3 = live.get(fn.name) ?? fn.number;
|
|
36786
|
+
io.log(`\u2705 Deployed ${fn.name} (v${String(version3)}) \u2014 ${fn.url}`);
|
|
36787
|
+
return { name: fn.name, version: version3, url: fn.url, verifyJwt: fn.verifyJwt };
|
|
36788
|
+
});
|
|
36789
|
+
}
|
|
36504
36790
|
function readFlagImportMap(options, folder) {
|
|
36505
36791
|
if (options.importMap === void 0) return void 0;
|
|
36506
36792
|
const file2 = resolve14(options.cwd, options.importMap);
|
|
@@ -36564,12 +36850,12 @@ async function deployFunctions(options) {
|
|
|
36564
36850
|
)
|
|
36565
36851
|
]
|
|
36566
36852
|
};
|
|
36567
|
-
const
|
|
36853
|
+
const ready = [];
|
|
36568
36854
|
const failures = [];
|
|
36569
36855
|
for (const name of names) {
|
|
36570
36856
|
try {
|
|
36571
|
-
|
|
36572
|
-
await
|
|
36857
|
+
ready.push(
|
|
36858
|
+
await prepareOne(
|
|
36573
36859
|
options,
|
|
36574
36860
|
folder,
|
|
36575
36861
|
name,
|
|
@@ -36579,14 +36865,23 @@ async function deployFunctions(options) {
|
|
|
36579
36865
|
budget
|
|
36580
36866
|
)
|
|
36581
36867
|
);
|
|
36582
|
-
} catch (
|
|
36868
|
+
} catch (caught) {
|
|
36869
|
+
const error51 = asNaroError(caught);
|
|
36583
36870
|
if (!isNaroError(error51)) throw error51;
|
|
36584
36871
|
if (!PER_FUNCTION_CODES.has(error51.code))
|
|
36585
|
-
throw withProgress(error51,
|
|
36872
|
+
throw withProgress(error51, [], failures);
|
|
36586
36873
|
failures.push({ name, code: error51.code, message: error51.message });
|
|
36587
36874
|
io.log(`\u2717 ${name}: ${printable(error51.message)}`);
|
|
36588
36875
|
}
|
|
36589
36876
|
}
|
|
36877
|
+
let deployed = [];
|
|
36878
|
+
if (ready.length > 0)
|
|
36879
|
+
try {
|
|
36880
|
+
deployed = await deployReady(options, ready, io, budget);
|
|
36881
|
+
} catch (error51) {
|
|
36882
|
+
if (!isNaroError(error51)) throw error51;
|
|
36883
|
+
throw withProgress(error51, [], failures);
|
|
36884
|
+
}
|
|
36590
36885
|
if (failures.length > 0)
|
|
36591
36886
|
throw new NaroError(
|
|
36592
36887
|
"FUNCTIONS_DEPLOY_PARTIAL",
|
|
@@ -37108,6 +37403,8 @@ export {
|
|
|
37108
37403
|
formatRedirectEntry,
|
|
37109
37404
|
policiesForTypes,
|
|
37110
37405
|
generateTypes,
|
|
37406
|
+
isSymbolicLink,
|
|
37407
|
+
assertProjectLinkWritable,
|
|
37111
37408
|
readProjectLink,
|
|
37112
37409
|
writeProjectLink,
|
|
37113
37410
|
removeProjectLink,
|