@samebase/convex-hosting 0.3.2 → 0.4.1
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/CHANGELOG.md +17 -132
- package/README.md +188 -406
- package/dist/cli/convex.d.ts +63 -0
- package/dist/cli/convex.js +108 -0
- package/dist/cli/convex.js.map +1 -0
- package/dist/cli/files.d.ts +18 -0
- package/dist/cli/files.js +106 -0
- package/dist/cli/files.js.map +1 -0
- package/dist/cli/index.d.ts +1 -11
- package/dist/cli/index.js +3 -58
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/main.d.ts +5 -0
- package/dist/cli/main.js +377 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/client/index.d.ts +8 -115
- package/dist/client/index.js +65 -242
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts +5 -0
- package/dist/client/version-module.d.ts +6 -0
- package/dist/component/_generated/api.d.ts +0 -5
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +20 -18
- package/dist/component/_generated/dataModel.d.ts +0 -1
- package/dist/component/_generated/server.d.ts +12 -1
- package/dist/component/_generated/server.js +2 -0
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/convex.config.d.ts +0 -1
- package/dist/component/lib.d.ts +175 -156
- package/dist/component/lib.js +396 -710
- package/dist/component/lib.js.map +1 -1
- package/dist/component/schema.d.ts +76 -72
- package/dist/component/schema.js +35 -59
- package/dist/component/schema.js.map +1 -1
- package/dist/workers-shared/asset-worker/src/analytics.d.ts +39 -0
- package/dist/workers-shared/asset-worker/src/analytics.js +86 -0
- package/dist/workers-shared/asset-worker/src/analytics.js.map +1 -0
- package/dist/workers-shared/asset-worker/src/compatibility-flags.d.ts +19 -0
- package/dist/workers-shared/asset-worker/src/compatibility-flags.js +29 -0
- package/dist/workers-shared/asset-worker/src/compatibility-flags.js.map +1 -0
- package/dist/workers-shared/asset-worker/src/configuration.d.ts +3 -0
- package/dist/workers-shared/asset-worker/src/configuration.js +30 -0
- package/dist/workers-shared/asset-worker/src/configuration.js.map +1 -0
- package/dist/workers-shared/asset-worker/src/constants.d.ts +1 -0
- package/dist/workers-shared/asset-worker/src/constants.js +3 -0
- package/dist/workers-shared/asset-worker/src/constants.js.map +1 -0
- package/dist/workers-shared/asset-worker/src/handler.d.ts +28 -0
- package/dist/workers-shared/asset-worker/src/handler.js +717 -0
- package/dist/workers-shared/asset-worker/src/handler.js.map +1 -0
- package/dist/workers-shared/asset-worker/src/types.d.ts +13 -0
- package/dist/workers-shared/asset-worker/src/types.js +2 -0
- package/dist/workers-shared/asset-worker/src/types.js.map +1 -0
- package/dist/workers-shared/asset-worker/src/utils/base-path.d.ts +29 -0
- package/dist/workers-shared/asset-worker/src/utils/base-path.js +56 -0
- package/dist/workers-shared/asset-worker/src/utils/base-path.js.map +1 -0
- package/dist/workers-shared/asset-worker/src/utils/headers.d.ts +11 -0
- package/dist/workers-shared/asset-worker/src/utils/headers.js +77 -0
- package/dist/workers-shared/asset-worker/src/utils/headers.js.map +1 -0
- package/dist/workers-shared/asset-worker/src/utils/rules-engine.d.ts +23 -0
- package/dist/workers-shared/asset-worker/src/utils/rules-engine.js +133 -0
- package/dist/workers-shared/asset-worker/src/utils/rules-engine.js.map +1 -0
- package/dist/workers-shared/asset-worker/src/worker.d.ts +14 -0
- package/dist/workers-shared/asset-worker/src/worker.js +12 -0
- package/dist/workers-shared/asset-worker/src/worker.js.map +1 -0
- package/dist/workers-shared/utils/base-path.d.ts +32 -0
- package/dist/workers-shared/utils/base-path.js +94 -0
- package/dist/workers-shared/utils/base-path.js.map +1 -0
- package/dist/workers-shared/utils/configuration/constants.d.ts +15 -0
- package/dist/workers-shared/utils/configuration/constants.js +16 -0
- package/dist/workers-shared/utils/configuration/constants.js.map +1 -0
- package/dist/workers-shared/utils/configuration/constructConfiguration.d.ts +12 -0
- package/dist/workers-shared/utils/configuration/constructConfiguration.js +103 -0
- package/dist/workers-shared/utils/configuration/constructConfiguration.js.map +1 -0
- package/dist/workers-shared/utils/configuration/parseHeaders.d.ts +5 -0
- package/dist/workers-shared/utils/configuration/parseHeaders.js +181 -0
- package/dist/workers-shared/utils/configuration/parseHeaders.js.map +1 -0
- package/dist/workers-shared/utils/configuration/parseRedirects.d.ts +8 -0
- package/dist/workers-shared/utils/configuration/parseRedirects.js +129 -0
- package/dist/workers-shared/utils/configuration/parseRedirects.js.map +1 -0
- package/dist/workers-shared/utils/configuration/parseStaticRouting.d.ts +2 -0
- package/dist/workers-shared/utils/configuration/parseStaticRouting.js +94 -0
- package/dist/workers-shared/utils/configuration/parseStaticRouting.js.map +1 -0
- package/dist/workers-shared/utils/configuration/types.d.ts +38 -0
- package/dist/workers-shared/utils/configuration/types.js +2 -0
- package/dist/workers-shared/utils/configuration/types.js.map +1 -0
- package/dist/workers-shared/utils/configuration/validateURL.d.ts +3 -0
- package/dist/workers-shared/utils/configuration/validateURL.js +56 -0
- package/dist/workers-shared/utils/configuration/validateURL.js.map +1 -0
- package/dist/workers-shared/utils/constants.d.ts +28 -0
- package/dist/workers-shared/utils/constants.js +34 -0
- package/dist/workers-shared/utils/constants.js.map +1 -0
- package/dist/workers-shared/utils/helpers.d.ts +22 -0
- package/dist/workers-shared/utils/helpers.js +73 -0
- package/dist/workers-shared/utils/helpers.js.map +1 -0
- package/dist/workers-shared/utils/responses.d.ts +43 -0
- package/dist/workers-shared/utils/responses.js +125 -0
- package/dist/workers-shared/utils/responses.js.map +1 -0
- package/dist/workers-shared/utils/tracing.d.ts +3 -0
- package/dist/workers-shared/utils/tracing.js +29 -0
- package/dist/workers-shared/utils/tracing.js.map +1 -0
- package/dist/workers-shared/utils/types.d.ts +131 -0
- package/dist/workers-shared/utils/types.js +72 -0
- package/dist/workers-shared/utils/types.js.map +1 -0
- package/package.json +21 -37
- package/INTEGRATION.md +0 -399
- package/dist/cli/args.d.ts +0 -28
- package/dist/cli/args.d.ts.map +0 -1
- package/dist/cli/args.js +0 -148
- package/dist/cli/args.js.map +0 -1
- package/dist/cli/argumentChunks.d.ts +0 -3
- package/dist/cli/argumentChunks.d.ts.map +0 -1
- package/dist/cli/argumentChunks.js +0 -29
- package/dist/cli/argumentChunks.js.map +0 -1
- package/dist/cli/collectFiles.d.ts +0 -10
- package/dist/cli/collectFiles.d.ts.map +0 -1
- package/dist/cli/collectFiles.js +0 -53
- package/dist/cli/collectFiles.js.map +0 -1
- package/dist/cli/commands.d.ts +0 -8
- package/dist/cli/commands.d.ts.map +0 -1
- package/dist/cli/commands.js +0 -56
- package/dist/cli/commands.js.map +0 -1
- package/dist/cli/componentName.d.ts +0 -32
- package/dist/cli/componentName.d.ts.map +0 -1
- package/dist/cli/componentName.js +0 -43
- package/dist/cli/componentName.js.map +0 -1
- package/dist/cli/deploy.d.ts +0 -16
- package/dist/cli/deploy.d.ts.map +0 -1
- package/dist/cli/deploy.js +0 -283
- package/dist/cli/deploy.js.map +0 -1
- package/dist/cli/deployEnvironment.d.ts +0 -10
- package/dist/cli/deployEnvironment.d.ts.map +0 -1
- package/dist/cli/deployEnvironment.js +0 -12
- package/dist/cli/deployEnvironment.js.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/upload.d.ts +0 -22
- package/dist/cli/upload.d.ts.map +0 -1
- package/dist/cli/upload.js +0 -630
- package/dist/cli/upload.js.map +0 -1
- package/dist/client/_generated/_ignore.d.ts +0 -1
- package/dist/client/_generated/_ignore.d.ts.map +0 -1
- package/dist/client/_generated/_ignore.js +0 -3
- package/dist/client/_generated/_ignore.js.map +0 -1
- package/dist/client/index.d.ts.map +0 -1
- package/dist/component/_generated/api.d.ts.map +0 -1
- package/dist/component/_generated/component.d.ts.map +0 -1
- package/dist/component/_generated/dataModel.d.ts.map +0 -1
- package/dist/component/_generated/server.d.ts.map +0 -1
- package/dist/component/convex.config.d.ts.map +0 -1
- package/dist/component/http.d.ts +0 -4
- package/dist/component/http.d.ts.map +0 -1
- package/dist/component/http.js +0 -113
- package/dist/component/http.js.map +0 -1
- package/dist/component/lib.d.ts.map +0 -1
- package/dist/component/schema.d.ts.map +0 -1
- package/dist/component/serving.d.ts +0 -23
- package/dist/component/serving.d.ts.map +0 -1
- package/dist/component/serving.js +0 -161
- package/dist/component/serving.js.map +0 -1
- package/dist/react/index.d.ts +0 -59
- package/dist/react/index.d.ts.map +0 -1
- package/dist/react/index.js +0 -137
- package/dist/react/index.js.map +0 -1
- package/src/cli/args.test.ts +0 -79
- package/src/cli/args.ts +0 -153
- package/src/cli/argumentChunks.test.ts +0 -27
- package/src/cli/argumentChunks.ts +0 -40
- package/src/cli/collectFiles.test.ts +0 -26
- package/src/cli/collectFiles.ts +0 -59
- package/src/cli/commands.ts +0 -68
- package/src/cli/componentName.test.ts +0 -41
- package/src/cli/componentName.ts +0 -46
- package/src/cli/deploy.ts +0 -336
- package/src/cli/deployEnvironment.test.ts +0 -27
- package/src/cli/deployEnvironment.ts +0 -17
- package/src/cli/index.ts +0 -65
- package/src/cli/upload.ts +0 -837
- package/src/client/_generated/_ignore.ts +0 -1
- package/src/client/index.test.ts +0 -549
- package/src/client/index.ts +0 -344
- package/src/client/setup.test.ts +0 -22
- package/src/component/_generated/api.ts +0 -54
- package/src/component/_generated/component.ts +0 -55
- package/src/component/_generated/dataModel.ts +0 -60
- package/src/component/_generated/server.ts +0 -156
- package/src/component/convex.config.ts +0 -3
- package/src/component/http.test.ts +0 -254
- package/src/component/http.ts +0 -133
- package/src/component/lib.test.ts +0 -1007
- package/src/component/lib.ts +0 -862
- package/src/component/schema.ts +0 -75
- package/src/component/serving.ts +0 -170
- package/src/component/setup.test.ts +0 -11
- package/src/react/index.tsx +0 -197
- package/src/test.ts +0 -23
package/dist/cli/upload.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/cli/upload.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;GAkBG"}
|
package/dist/cli/upload.js
DELETED
|
@@ -1,630 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* CLI tool to upload static files to a Convex convex-hosting component.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* npx @samebase/convex-hosting upload [options]
|
|
7
|
-
*
|
|
8
|
-
* Options:
|
|
9
|
-
* --dist <path> Path to dist directory (default: ./dist)
|
|
10
|
-
* --component <name> Component instance name (default: hosting)
|
|
11
|
-
* --prod Deploy to production deployment
|
|
12
|
-
* --preview-name <name> Upload to the named preview deployment
|
|
13
|
-
* --build Run the frontend build before uploading
|
|
14
|
-
* --build-command <cmd> Override the frontend build command
|
|
15
|
-
* --spa / --no-spa Enable or disable SPA fallback
|
|
16
|
-
* --cdn Use the legacy convex-fs integration
|
|
17
|
-
* --cdn-delete-function App function that deletes old CDN blobs
|
|
18
|
-
* --concurrency <n> Parallel upload workers (default: 5)
|
|
19
|
-
* --help Show help
|
|
20
|
-
*/
|
|
21
|
-
import { readFileSync, existsSync } from "fs";
|
|
22
|
-
import { resolve } from "path";
|
|
23
|
-
import { randomUUID } from "crypto";
|
|
24
|
-
import { runConvexAsync, spawnShell } from "./commands.js";
|
|
25
|
-
import { chunkBySerializedArgument, MAX_CONVEX_ARGUMENT_BYTES } from "./argumentChunks.js";
|
|
26
|
-
import { parseUploadArgs } from "./args.js";
|
|
27
|
-
import { componentNameCandidates, isLegacyAutoDetected, legacyComponentNameWarning, } from "./componentName.js";
|
|
28
|
-
import { collectFiles } from "./collectFiles.js";
|
|
29
|
-
function showHelp() {
|
|
30
|
-
console.log(`
|
|
31
|
-
Usage: npx @samebase/convex-hosting upload [options]
|
|
32
|
-
|
|
33
|
-
Upload static files from a dist directory to Convex storage.
|
|
34
|
-
|
|
35
|
-
Options:
|
|
36
|
-
-d, --dist <path> Path to dist directory (default: ./dist)
|
|
37
|
-
-c, --component <name> Static-hosting component instance name (default: hosting)
|
|
38
|
-
--prod Deploy to production deployment
|
|
39
|
-
--preview-name <name> Upload to the named preview deployment
|
|
40
|
-
-b, --build Run the build command with VITE_CONVEX_URL +
|
|
41
|
-
CONVEX_HOSTING_BASE_PATH set before uploading
|
|
42
|
-
--build-command <cmd> Build command to run (default: 'npm run build').
|
|
43
|
-
Implies --build.
|
|
44
|
-
--no-spa Disable SPA fallback for this deployment (return a
|
|
45
|
-
404 instead of falling back to /index.html)
|
|
46
|
-
--spa Enable SPA fallback for this deployment (default)
|
|
47
|
-
--cdn Use the legacy convex-fs integration
|
|
48
|
-
--cdn-delete-function <name> Legacy app function that deletes CDN blobs
|
|
49
|
-
-j, --concurrency <n> Number of parallel uploads (default: 5)
|
|
50
|
-
-h, --help Show this help message
|
|
51
|
-
|
|
52
|
-
Examples:
|
|
53
|
-
# Upload to Convex storage
|
|
54
|
-
npx @samebase/convex-hosting upload
|
|
55
|
-
npx @samebase/convex-hosting upload --dist ./build --prod
|
|
56
|
-
npx @samebase/convex-hosting upload --build --prod
|
|
57
|
-
|
|
58
|
-
# Upload through an existing legacy convex-fs integration
|
|
59
|
-
npx @samebase/convex-hosting upload --cdn --prod
|
|
60
|
-
`);
|
|
61
|
-
}
|
|
62
|
-
// Global flag for production mode
|
|
63
|
-
let useProd = true;
|
|
64
|
-
let previewName = null;
|
|
65
|
-
const MAX_ASSETS_PER_DEPLOYMENT = 1800;
|
|
66
|
-
const MAX_MANIFEST_SERIALIZED_BYTES = 2 * 1024 * 1024;
|
|
67
|
-
// A Convex CLI function result can be truncated around 64 KiB. Signed upload
|
|
68
|
-
// URLs are long enough that 250 sometimes crosses that boundary.
|
|
69
|
-
const UPLOAD_URL_BATCH_SIZE = 100;
|
|
70
|
-
const MAINTENANCE_PAGE_SIZE = 256;
|
|
71
|
-
const MAX_MAINTENANCE_PAGES = 20;
|
|
72
|
-
/**
|
|
73
|
-
* Resolve the component's deployment URLs and the instance name that answered.
|
|
74
|
-
*
|
|
75
|
-
* When the caller relied on the default name we also probe the
|
|
76
|
-
* @convex-dev/static-hosting name, so an app that kept that instance works without
|
|
77
|
-
* `--component staticHosting`
|
|
78
|
-
* on every command; landing on the legacy name prints a one-time warning. Bails
|
|
79
|
-
* the CLI if no candidate is deployed. Uploading wouldn't work either, so a
|
|
80
|
-
* fallback would only hide the real problem.
|
|
81
|
-
*/
|
|
82
|
-
async function fetchUrls(requested) {
|
|
83
|
-
const candidates = componentNameCandidates(requested);
|
|
84
|
-
for (const name of candidates) {
|
|
85
|
-
try {
|
|
86
|
-
const out = await convexRunAsync(name, "lib:getUrls", {}, { quiet: true });
|
|
87
|
-
const urls = JSON.parse(out);
|
|
88
|
-
if (isLegacyAutoDetected(requested, name)) {
|
|
89
|
-
console.warn(legacyComponentNameWarning(name));
|
|
90
|
-
}
|
|
91
|
-
return { urls, componentName: name };
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
// Try the next candidate name.
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
console.error(`Could not reach component ${candidates
|
|
98
|
-
.map((name) => `"${name}"`)
|
|
99
|
-
.join(" or ")}. Deploy the Convex backend first and ensure --component ` +
|
|
100
|
-
`matches the name in convex.config.ts.`);
|
|
101
|
-
process.exit(1);
|
|
102
|
-
}
|
|
103
|
-
function convexRunAsync(componentName, functionPath, args = {}, options = {}) {
|
|
104
|
-
const serializedArgs = JSON.stringify(args);
|
|
105
|
-
const argumentBytes = Buffer.byteLength(serializedArgs);
|
|
106
|
-
if (argumentBytes > MAX_CONVEX_ARGUMENT_BYTES) {
|
|
107
|
-
throw new Error("The Convex function argument is " +
|
|
108
|
-
argumentBytes +
|
|
109
|
-
" bytes, which exceeds the portable command-line limit. " +
|
|
110
|
-
"This is an internal chunking error; please report it.");
|
|
111
|
-
}
|
|
112
|
-
return runConvexAsync([
|
|
113
|
-
"run",
|
|
114
|
-
...(componentName ? ["--component", componentName] : []),
|
|
115
|
-
functionPath,
|
|
116
|
-
serializedArgs,
|
|
117
|
-
"--typecheck=disable",
|
|
118
|
-
"--codegen=disable",
|
|
119
|
-
...(useProd ? ["--prod"] : []),
|
|
120
|
-
...(previewName === null ? [] : ["--preview-name", previewName]),
|
|
121
|
-
], options);
|
|
122
|
-
}
|
|
123
|
-
function errorMessage(error) {
|
|
124
|
-
return error instanceof Error ? error.message : String(error);
|
|
125
|
-
}
|
|
126
|
-
async function cleanUpPendingCdnBlobs(componentName, cdnDeleteFunction) {
|
|
127
|
-
if (!cdnDeleteFunction)
|
|
128
|
-
return 0;
|
|
129
|
-
let cleaned = 0;
|
|
130
|
-
for (let page = 0; page < MAX_MAINTENANCE_PAGES; page++) {
|
|
131
|
-
const output = await convexRunAsync(componentName, "lib:listPendingBlobCleanup", {
|
|
132
|
-
limit: 250,
|
|
133
|
-
});
|
|
134
|
-
const blobIds = JSON.parse(output);
|
|
135
|
-
if (!Array.isArray(blobIds) || !blobIds.every((blobId) => typeof blobId === "string")) {
|
|
136
|
-
throw new Error("Component returned invalid pending CDN cleanup data");
|
|
137
|
-
}
|
|
138
|
-
if (blobIds.length === 0)
|
|
139
|
-
return cleaned;
|
|
140
|
-
const deleteChunks = chunkBySerializedArgument(blobIds, (chunk) => ({
|
|
141
|
-
blobIds: chunk,
|
|
142
|
-
}));
|
|
143
|
-
for (const chunk of deleteChunks) {
|
|
144
|
-
await convexRunAsync(undefined, cdnDeleteFunction, { blobIds: chunk });
|
|
145
|
-
await convexRunAsync(componentName, "lib:acknowledgeBlobCleanup", {
|
|
146
|
-
blobIds: chunk,
|
|
147
|
-
});
|
|
148
|
-
cleaned += chunk.length;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
console.warn("Warning: CDN cleanup reached its per-run maintenance limit; a later upload will continue it.");
|
|
152
|
-
return cleaned;
|
|
153
|
-
}
|
|
154
|
-
async function cleanUpAbandonedUploads(componentName, cdnDeleteFunction) {
|
|
155
|
-
let discarded = 0;
|
|
156
|
-
let deletedFiles = 0;
|
|
157
|
-
let queuedBlobIds = 0;
|
|
158
|
-
try {
|
|
159
|
-
let stagingDrained = false;
|
|
160
|
-
for (let page = 0; page < MAX_MAINTENANCE_PAGES; page++) {
|
|
161
|
-
const output = await convexRunAsync(componentName, "lib:cleanupAbandonedStaging", {
|
|
162
|
-
limit: MAINTENANCE_PAGE_SIZE,
|
|
163
|
-
});
|
|
164
|
-
const result = JSON.parse(output);
|
|
165
|
-
if (typeof result.discarded !== "number" ||
|
|
166
|
-
typeof result.deletedFiles !== "number" ||
|
|
167
|
-
typeof result.queuedBlobIds !== "number") {
|
|
168
|
-
throw new Error("Component returned invalid staging cleanup data");
|
|
169
|
-
}
|
|
170
|
-
discarded += result.discarded;
|
|
171
|
-
deletedFiles += result.deletedFiles;
|
|
172
|
-
queuedBlobIds += result.queuedBlobIds;
|
|
173
|
-
if (result.discarded < MAINTENANCE_PAGE_SIZE) {
|
|
174
|
-
stagingDrained = true;
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (!stagingDrained) {
|
|
179
|
-
console.warn("Warning: Staging cleanup reached its per-run maintenance limit; a later upload will continue it.");
|
|
180
|
-
}
|
|
181
|
-
deletedFiles += await cleanUpPendingStorage(componentName);
|
|
182
|
-
deletedFiles += await cleanUpUnreferencedStorage(componentName, undefined, "desc");
|
|
183
|
-
deletedFiles += await cleanUpUnreferencedStorage(componentName, undefined, "asc");
|
|
184
|
-
if (discarded > 0 || deletedFiles > 0 || queuedBlobIds > 0) {
|
|
185
|
-
console.log(`Recovered ${discarded} abandoned manifest record(s), removed ${deletedFiles} old unreferenced file(s), and queued ${queuedBlobIds} CDN blob(s) for cleanup.`);
|
|
186
|
-
}
|
|
187
|
-
if (cdnDeleteFunction) {
|
|
188
|
-
const cleaned = await cleanUpPendingCdnBlobs(componentName, cdnDeleteFunction);
|
|
189
|
-
if (cleaned > 0) {
|
|
190
|
-
console.log(`Cleaned up ${cleaned} pending CDN blob(s).`);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
catch (cleanupError) {
|
|
195
|
-
console.warn("Warning: Could not finish abandoned-upload maintenance: " + errorMessage(cleanupError));
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
async function cleanUpUnreferencedStorage(componentName, before, order = "asc") {
|
|
199
|
-
let deleted = 0;
|
|
200
|
-
for (let page = 0; page < MAX_MAINTENANCE_PAGES; page++) {
|
|
201
|
-
const output = await convexRunAsync(componentName, "lib:cleanupUnreferencedStorage", {
|
|
202
|
-
limit: MAINTENANCE_PAGE_SIZE,
|
|
203
|
-
order,
|
|
204
|
-
...(before === undefined ? {} : { before }),
|
|
205
|
-
});
|
|
206
|
-
const result = JSON.parse(output);
|
|
207
|
-
if (typeof result.deleted !== "number" || typeof result.needsAnotherPass !== "boolean") {
|
|
208
|
-
throw new Error("Component returned invalid storage cleanup data");
|
|
209
|
-
}
|
|
210
|
-
deleted += result.deleted;
|
|
211
|
-
if (!result.needsAnotherPass)
|
|
212
|
-
return deleted;
|
|
213
|
-
}
|
|
214
|
-
console.warn("Warning: Orphan storage cleanup reached its per-run maintenance limit; a later upload will continue it.");
|
|
215
|
-
return deleted;
|
|
216
|
-
}
|
|
217
|
-
async function cleanUpPendingStorage(componentName) {
|
|
218
|
-
let deleted = 0;
|
|
219
|
-
for (let page = 0; page < MAX_MAINTENANCE_PAGES; page++) {
|
|
220
|
-
const output = await convexRunAsync(componentName, "lib:cleanupPendingStorage", {
|
|
221
|
-
limit: MAINTENANCE_PAGE_SIZE,
|
|
222
|
-
});
|
|
223
|
-
const result = JSON.parse(output);
|
|
224
|
-
if (typeof result.deleted !== "number" || typeof result.needsAnotherPass !== "boolean") {
|
|
225
|
-
throw new Error("Component returned invalid pending-storage cleanup data");
|
|
226
|
-
}
|
|
227
|
-
deleted += result.deleted;
|
|
228
|
-
if (!result.needsAnotherPass)
|
|
229
|
-
return deleted;
|
|
230
|
-
}
|
|
231
|
-
console.warn("Warning: Published storage cleanup reached its per-run maintenance limit; a later upload will continue it.");
|
|
232
|
-
return deleted;
|
|
233
|
-
}
|
|
234
|
-
async function forEachWithConcurrency(items, concurrency, worker) {
|
|
235
|
-
let nextIndex = 0;
|
|
236
|
-
let firstError;
|
|
237
|
-
async function runWorker() {
|
|
238
|
-
while (firstError === undefined) {
|
|
239
|
-
const index = nextIndex++;
|
|
240
|
-
if (index >= items.length)
|
|
241
|
-
return;
|
|
242
|
-
try {
|
|
243
|
-
await worker(items[index], index);
|
|
244
|
-
}
|
|
245
|
-
catch (error) {
|
|
246
|
-
firstError ??= error;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
const workerCount = Math.min(concurrency, items.length);
|
|
251
|
-
await Promise.all(Array.from({ length: workerCount }, () => runWorker()));
|
|
252
|
-
if (firstError !== undefined)
|
|
253
|
-
throw firstError;
|
|
254
|
-
}
|
|
255
|
-
async function cleanUpFailedUpload(componentName, deploymentId, uploaded, cdnDeleteFunction, publishAttempted) {
|
|
256
|
-
let discardSucceeded = false;
|
|
257
|
-
let discarded = 0;
|
|
258
|
-
try {
|
|
259
|
-
const output = await convexRunAsync(componentName, "lib:discardStagedDeployment", {
|
|
260
|
-
deploymentId,
|
|
261
|
-
});
|
|
262
|
-
const result = JSON.parse(output);
|
|
263
|
-
if (typeof result.discarded !== "number") {
|
|
264
|
-
throw new Error("Component returned an invalid discard result");
|
|
265
|
-
}
|
|
266
|
-
discardSucceeded = true;
|
|
267
|
-
discarded = result.discarded;
|
|
268
|
-
}
|
|
269
|
-
catch (cleanupError) {
|
|
270
|
-
console.warn("Warning: Could not discard the staged manifest: " + errorMessage(cleanupError));
|
|
271
|
-
}
|
|
272
|
-
// Convex mutations have a single transaction order. If discard succeeds,
|
|
273
|
-
// either it removed the staged rows before a late publish could use them, or
|
|
274
|
-
// publish already committed and removed them first. In the latter case the
|
|
275
|
-
// component cleanup below sees the live references and keeps those files.
|
|
276
|
-
const canCleanComponentFiles = !publishAttempted || discardSucceeded;
|
|
277
|
-
if (uploaded.storageIds.length > 0 && canCleanComponentFiles) {
|
|
278
|
-
try {
|
|
279
|
-
let deleted = 0;
|
|
280
|
-
let stillReferenced = 0;
|
|
281
|
-
const chunks = chunkBySerializedArgument(uploaded.storageIds, (storageIds) => ({
|
|
282
|
-
storageIds,
|
|
283
|
-
}));
|
|
284
|
-
for (const storageIds of chunks) {
|
|
285
|
-
const output = await convexRunAsync(componentName, "lib:deleteUploadedFiles", {
|
|
286
|
-
storageIds,
|
|
287
|
-
});
|
|
288
|
-
const result = JSON.parse(output);
|
|
289
|
-
if (typeof result.deleted === "number")
|
|
290
|
-
deleted += result.deleted;
|
|
291
|
-
if (typeof result.stillReferenced === "number") {
|
|
292
|
-
stillReferenced += result.stillReferenced;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
console.log(`Removed ${deleted} file(s) from the failed upload.`);
|
|
296
|
-
if (stillReferenced > 0) {
|
|
297
|
-
console.warn(`Kept ${stillReferenced} file(s) because the live deployment references them. The publish may have completed even though its response was lost.`);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
catch (cleanupError) {
|
|
301
|
-
console.warn("Warning: Could not remove component files from the failed upload: " +
|
|
302
|
-
errorMessage(cleanupError));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
else if (uploaded.storageIds.length > 0) {
|
|
306
|
-
console.warn(`${uploaded.storageIds.length} component file(s) were left in place because the CLI could not cancel or confirm the publish.`);
|
|
307
|
-
}
|
|
308
|
-
// The app-level CDN delete function cannot inspect component-private live
|
|
309
|
-
// references. Only delete CDN blobs when no publish was attempted, or when
|
|
310
|
-
// discard removed staged rows and therefore won the transaction race.
|
|
311
|
-
const canDeleteCdnBlobs = !publishAttempted || (discardSucceeded && discarded > 0);
|
|
312
|
-
if (uploaded.blobIds.length > 0 && canDeleteCdnBlobs) {
|
|
313
|
-
try {
|
|
314
|
-
// Record these IDs before calling app-owned deletion. If that call fails
|
|
315
|
-
// or only partially completes, the next upload can retry safely.
|
|
316
|
-
const chunks = chunkBySerializedArgument(uploaded.blobIds, (blobIds) => ({
|
|
317
|
-
blobIds,
|
|
318
|
-
}));
|
|
319
|
-
for (const blobIds of chunks) {
|
|
320
|
-
await convexRunAsync(componentName, "lib:queueBlobCleanup", {
|
|
321
|
-
blobIds,
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
if (cdnDeleteFunction) {
|
|
325
|
-
const cleaned = await cleanUpPendingCdnBlobs(componentName, cdnDeleteFunction);
|
|
326
|
-
console.log(`Removed ${cleaned} pending CDN blob(s).`);
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
console.warn(`${uploaded.blobIds.length} CDN blob(s) from the failed upload were queued for cleanup. Pass --cdn-delete-function on a later upload to delete them.`);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
catch (cleanupError) {
|
|
333
|
-
console.warn("Warning: Could not finish queuing or deleting CDN blobs from the failed upload: " +
|
|
334
|
-
errorMessage(cleanupError));
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
else if (uploaded.blobIds.length > 0 && !canDeleteCdnBlobs) {
|
|
338
|
-
console.warn(`${uploaded.blobIds.length} CDN blob(s) were left in place because the CLI could not determine whether the publish completed.`);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
async function uploadWithConcurrency(files, componentName, deploymentId, useCdn, cdnUploadBase, concurrency, spaFallback, cdnDeleteFunction) {
|
|
342
|
-
const total = files.length;
|
|
343
|
-
const uploaded = { storageIds: [], blobIds: [] };
|
|
344
|
-
// Separate CDN and storage files
|
|
345
|
-
const cdnFiles = [];
|
|
346
|
-
const storageFiles = [];
|
|
347
|
-
for (const file of files) {
|
|
348
|
-
const isHtml = file.contentType.startsWith("text/html");
|
|
349
|
-
if (useCdn && !isHtml && cdnUploadBase) {
|
|
350
|
-
cdnFiles.push(file);
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
storageFiles.push(file);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
// Upload storage files using batch operations
|
|
357
|
-
let completed = 0;
|
|
358
|
-
const allAssets = [];
|
|
359
|
-
let publishAttempted = false;
|
|
360
|
-
try {
|
|
361
|
-
if (storageFiles.length > 0) {
|
|
362
|
-
// Keep both the Convex array value and the child-process stdout buffer
|
|
363
|
-
// bounded, even for a deployment with thousands of files.
|
|
364
|
-
console.log(` Generating ${storageFiles.length} upload URLs...`);
|
|
365
|
-
const uploadUrls = [];
|
|
366
|
-
for (let offset = 0; offset < storageFiles.length; offset += UPLOAD_URL_BATCH_SIZE) {
|
|
367
|
-
const count = Math.min(UPLOAD_URL_BATCH_SIZE, storageFiles.length - offset);
|
|
368
|
-
const urlsOutput = await convexRunAsync(componentName, "lib:generateUploadUrls", { count });
|
|
369
|
-
const batch = JSON.parse(urlsOutput);
|
|
370
|
-
if (!Array.isArray(batch) ||
|
|
371
|
-
batch.length !== count ||
|
|
372
|
-
!batch.every((url) => typeof url === "string")) {
|
|
373
|
-
throw new Error("Component returned invalid upload URLs");
|
|
374
|
-
}
|
|
375
|
-
uploadUrls.push(...batch);
|
|
376
|
-
}
|
|
377
|
-
// Step 2: Upload all files in parallel via fetch
|
|
378
|
-
const storageIds = new Array(storageFiles.length);
|
|
379
|
-
await forEachWithConcurrency(storageFiles, concurrency, async (file, idx) => {
|
|
380
|
-
const content = readFileSync(file.localPath);
|
|
381
|
-
const response = await fetch(uploadUrls[idx], {
|
|
382
|
-
method: "POST",
|
|
383
|
-
headers: { "Content-Type": file.contentType },
|
|
384
|
-
body: content,
|
|
385
|
-
});
|
|
386
|
-
if (!response.ok) {
|
|
387
|
-
throw new Error("Storage upload failed for " +
|
|
388
|
-
file.path +
|
|
389
|
-
": " +
|
|
390
|
-
response.status +
|
|
391
|
-
" " +
|
|
392
|
-
response.statusText);
|
|
393
|
-
}
|
|
394
|
-
const { storageId } = (await response.json());
|
|
395
|
-
if (typeof storageId !== "string") {
|
|
396
|
-
throw new Error("Storage upload returned no storageId for " + file.path);
|
|
397
|
-
}
|
|
398
|
-
storageIds[idx] = storageId;
|
|
399
|
-
uploaded.storageIds.push(storageId);
|
|
400
|
-
completed++;
|
|
401
|
-
const isHtml = file.contentType.startsWith("text/html");
|
|
402
|
-
console.log(` [${completed}/${total}] ${file.path} (${isHtml ? "storage/html" : "storage"})`);
|
|
403
|
-
});
|
|
404
|
-
for (let i = 0; i < storageFiles.length; i++) {
|
|
405
|
-
allAssets.push({
|
|
406
|
-
path: storageFiles[i].path,
|
|
407
|
-
storageId: storageIds[i],
|
|
408
|
-
contentType: storageFiles[i].contentType,
|
|
409
|
-
deploymentId,
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
// Upload CDN files (still uses per-file calls since CDN has its own upload endpoint)
|
|
414
|
-
if (cdnFiles.length > 0 && cdnUploadBase) {
|
|
415
|
-
await forEachWithConcurrency(cdnFiles, concurrency, async (file) => {
|
|
416
|
-
const content = readFileSync(file.localPath);
|
|
417
|
-
const uploadResponse = await fetch(`${cdnUploadBase}/fs/upload`, {
|
|
418
|
-
method: "POST",
|
|
419
|
-
headers: { "Content-Type": file.contentType },
|
|
420
|
-
body: content,
|
|
421
|
-
});
|
|
422
|
-
if (!uploadResponse.ok) {
|
|
423
|
-
throw new Error(`CDN upload failed for ${file.path}: ${uploadResponse.status}`);
|
|
424
|
-
}
|
|
425
|
-
const { blobId } = (await uploadResponse.json());
|
|
426
|
-
if (typeof blobId !== "string") {
|
|
427
|
-
throw new Error("CDN upload returned no blobId for " + file.path);
|
|
428
|
-
}
|
|
429
|
-
uploaded.blobIds.push(blobId);
|
|
430
|
-
allAssets.push({
|
|
431
|
-
path: file.path,
|
|
432
|
-
blobId,
|
|
433
|
-
contentType: file.contentType,
|
|
434
|
-
deploymentId,
|
|
435
|
-
});
|
|
436
|
-
completed++;
|
|
437
|
-
console.log(` [${completed}/${total}] ${file.path} (cdn)`);
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
// Stage the manifest in portable command-line chunks. These rows are not
|
|
441
|
-
// served, so the previous deployment stays intact until publish succeeds.
|
|
442
|
-
const manifestChunks = chunkBySerializedArgument(allAssets, (assets) => ({
|
|
443
|
-
assets,
|
|
444
|
-
}));
|
|
445
|
-
for (let index = 0; index < manifestChunks.length; index++) {
|
|
446
|
-
console.log(` Staging manifest chunk ${index + 1}/${manifestChunks.length}...`);
|
|
447
|
-
await convexRunAsync(componentName, "lib:stageAssets", {
|
|
448
|
-
assets: manifestChunks[index],
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
// Publish the staged manifest, SPA setting, and old-asset GC in one
|
|
452
|
-
// mutation. The previous deployment stays intact if this call fails.
|
|
453
|
-
console.log(" Publishing deployment...");
|
|
454
|
-
publishAttempted = true;
|
|
455
|
-
const output = await convexRunAsync(componentName, "lib:publishDeployment", {
|
|
456
|
-
currentDeploymentId: deploymentId,
|
|
457
|
-
expectedAssetCount: allAssets.length,
|
|
458
|
-
spaFallback,
|
|
459
|
-
});
|
|
460
|
-
const result = JSON.parse(output);
|
|
461
|
-
if (typeof result.deleted !== "number" || typeof result.pendingBlobCleanup !== "number") {
|
|
462
|
-
throw new Error("Component returned an invalid publish result");
|
|
463
|
-
}
|
|
464
|
-
return {
|
|
465
|
-
deleted: result.deleted,
|
|
466
|
-
pendingBlobCleanup: result.pendingBlobCleanup,
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
catch (error) {
|
|
470
|
-
if (publishAttempted) {
|
|
471
|
-
try {
|
|
472
|
-
const output = await convexRunAsync(componentName, "lib:getCurrentDeployment");
|
|
473
|
-
const current = JSON.parse(output);
|
|
474
|
-
if (current?.currentDeploymentId === deploymentId) {
|
|
475
|
-
console.warn("The publish completed, but the CLI could not read a valid response. The new deployment is live and its files were kept.");
|
|
476
|
-
return {
|
|
477
|
-
deleted: 0,
|
|
478
|
-
pendingBlobCleanup: typeof current.pendingBlobCleanupCount === "number"
|
|
479
|
-
? current.pendingBlobCleanupCount
|
|
480
|
-
: 0,
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
catch (reconcileError) {
|
|
485
|
-
console.warn("Warning: Could not determine whether the publish completed: " +
|
|
486
|
-
errorMessage(reconcileError));
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
await cleanUpFailedUpload(componentName, deploymentId, uploaded, cdnDeleteFunction, publishAttempted);
|
|
490
|
-
throw error;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
function estimateManifestBytes(files, deploymentId) {
|
|
494
|
-
// IDs returned by Convex storage and convex-fs are much shorter than this.
|
|
495
|
-
// The generous placeholder lets us reject oversized path metadata before
|
|
496
|
-
// uploading while the component still enforces the exact serialized size.
|
|
497
|
-
const locationPlaceholder = "x".repeat(128);
|
|
498
|
-
return Buffer.byteLength(JSON.stringify(files.map((file) => ({
|
|
499
|
-
path: file.path,
|
|
500
|
-
storageId: locationPlaceholder,
|
|
501
|
-
contentType: file.contentType,
|
|
502
|
-
deploymentId,
|
|
503
|
-
}))));
|
|
504
|
-
}
|
|
505
|
-
async function main() {
|
|
506
|
-
const args = parseUploadArgs(process.argv.slice(2));
|
|
507
|
-
if (args.help) {
|
|
508
|
-
showHelp();
|
|
509
|
-
process.exit(0);
|
|
510
|
-
}
|
|
511
|
-
// Set global prod flag
|
|
512
|
-
useProd = args.prod;
|
|
513
|
-
previewName = args.previewName;
|
|
514
|
-
// The component knows both its CONVEX_SITE_URL (where the app is served,
|
|
515
|
-
// including the mount prefix) and CONVEX_CLOUD_URL (what the frontend
|
|
516
|
-
// connects to). We fetch both in one call and trust neither hostname. This
|
|
517
|
-
// also resolves the instance name actually mounted (default or legacy).
|
|
518
|
-
const { urls: { siteUrl: componentSiteUrl, cloudUrl: convexUrl }, componentName: resolvedComponentName, } = await fetchUrls(args.component);
|
|
519
|
-
// Run build if requested
|
|
520
|
-
if (args.build) {
|
|
521
|
-
const basePath = new URL(componentSiteUrl).pathname || "/";
|
|
522
|
-
const envLabel = previewName === null ? (useProd ? "production" : "development") : `preview (${previewName})`;
|
|
523
|
-
console.log(`🔨 Building for ${envLabel}...`);
|
|
524
|
-
console.log(` Build command: ${args.buildCommand}`);
|
|
525
|
-
console.log(` VITE_CONVEX_URL=${convexUrl}`);
|
|
526
|
-
console.log(` CONVEX_HOSTING_BASE_PATH=${basePath}`);
|
|
527
|
-
console.log("");
|
|
528
|
-
const buildResult = spawnShell(args.buildCommand, {
|
|
529
|
-
...process.env,
|
|
530
|
-
VITE_CONVEX_URL: convexUrl,
|
|
531
|
-
CONVEX_HOSTING_BASE_PATH: basePath,
|
|
532
|
-
});
|
|
533
|
-
if (buildResult !== 0) {
|
|
534
|
-
console.error("Build failed.");
|
|
535
|
-
process.exit(1);
|
|
536
|
-
}
|
|
537
|
-
console.log("");
|
|
538
|
-
}
|
|
539
|
-
const distDir = resolve(args.dist);
|
|
540
|
-
const componentName = resolvedComponentName;
|
|
541
|
-
const useCdn = args.cdn;
|
|
542
|
-
// Convex storage deployment
|
|
543
|
-
if (!existsSync(distDir)) {
|
|
544
|
-
console.error(`Error: dist directory not found: ${distDir}`);
|
|
545
|
-
console.error("Run your build command first (e.g., 'npm run build' or add --build flag)");
|
|
546
|
-
process.exit(1);
|
|
547
|
-
}
|
|
548
|
-
// /fs/upload lives at the deployment root, not under the component's
|
|
549
|
-
// mount prefix.
|
|
550
|
-
const cdnUploadBase = useCdn ? new URL(componentSiteUrl).origin : null;
|
|
551
|
-
const deploymentId = randomUUID();
|
|
552
|
-
const { files, skipped } = collectFiles(distDir);
|
|
553
|
-
for (const path of skipped) {
|
|
554
|
-
console.log(`Skipping ${path}: convex-hosting does not apply hosting rule files.`);
|
|
555
|
-
}
|
|
556
|
-
if (files.length === 0) {
|
|
557
|
-
console.error(`Error: dist directory is empty: ${distDir}`);
|
|
558
|
-
console.error("Refusing to replace the live deployment with no files.");
|
|
559
|
-
process.exit(1);
|
|
560
|
-
}
|
|
561
|
-
if (!files.some((file) => file.path === "/index.html")) {
|
|
562
|
-
console.error(`Error: dist directory has no index.html: ${distDir}`);
|
|
563
|
-
console.error("Refusing to publish a static site without its entry point.");
|
|
564
|
-
process.exit(1);
|
|
565
|
-
}
|
|
566
|
-
if (files.length > MAX_ASSETS_PER_DEPLOYMENT) {
|
|
567
|
-
console.error(`Error: dist directory contains ${files.length} files; the supported maximum is ${MAX_ASSETS_PER_DEPLOYMENT}.`);
|
|
568
|
-
console.error("Reduce the number of emitted files so publication stays within Convex transaction limits.");
|
|
569
|
-
process.exit(1);
|
|
570
|
-
}
|
|
571
|
-
const estimatedManifestBytes = estimateManifestBytes(files, deploymentId);
|
|
572
|
-
if (estimatedManifestBytes > MAX_MANIFEST_SERIALIZED_BYTES) {
|
|
573
|
-
console.error(`Error: asset manifest metadata is approximately ${estimatedManifestBytes} bytes; the supported maximum is ${MAX_MANIFEST_SERIALIZED_BYTES}.`);
|
|
574
|
-
console.error("Shorten emitted asset paths or reduce the number of files so publication stays within Convex transaction byte limits.");
|
|
575
|
-
process.exit(1);
|
|
576
|
-
}
|
|
577
|
-
await cleanUpAbandonedUploads(componentName, args.cdnDeleteFunction);
|
|
578
|
-
const envLabel = previewName === null ? (useProd ? "production" : "development") : `preview (${previewName})`;
|
|
579
|
-
console.log(`🚀 Deploying to ${envLabel} environment`);
|
|
580
|
-
if (useCdn) {
|
|
581
|
-
console.log("☁️ CDN mode: non-HTML assets will be uploaded to convex-fs");
|
|
582
|
-
}
|
|
583
|
-
console.log("🔒 Using secure internal functions (requires Convex CLI auth)");
|
|
584
|
-
console.log(`Uploading ${files.length} files with deployment ID: ${deploymentId}`);
|
|
585
|
-
console.log(`Component: ${componentName}`);
|
|
586
|
-
console.log("");
|
|
587
|
-
const publishResult = await uploadWithConcurrency(files, componentName, deploymentId, useCdn, cdnUploadBase, args.concurrency, args.spaFallback, args.cdnDeleteFunction);
|
|
588
|
-
console.log("");
|
|
589
|
-
let deletedCount = publishResult.deleted;
|
|
590
|
-
try {
|
|
591
|
-
// Publish queues only IDs from the old live manifest. Deleting that queue
|
|
592
|
-
// cannot race with files from another uploader that are not staged yet.
|
|
593
|
-
deletedCount += await cleanUpPendingStorage(componentName);
|
|
594
|
-
}
|
|
595
|
-
catch (cleanupError) {
|
|
596
|
-
console.warn("Warning: The new deployment is live, but old component storage could not be fully removed: " +
|
|
597
|
-
errorMessage(cleanupError));
|
|
598
|
-
}
|
|
599
|
-
if (deletedCount > 0) {
|
|
600
|
-
console.log(`Cleaned up ${deletedCount} old storage file(s) from previous deployments`);
|
|
601
|
-
}
|
|
602
|
-
// Old CDN IDs are persisted by the component until the app-level delete
|
|
603
|
-
// function succeeds, including when the publish response was lost.
|
|
604
|
-
if (args.cdnDeleteFunction) {
|
|
605
|
-
try {
|
|
606
|
-
const cleaned = await cleanUpPendingCdnBlobs(componentName, args.cdnDeleteFunction);
|
|
607
|
-
if (cleaned > 0) {
|
|
608
|
-
console.log(`Cleaned up ${cleaned} old CDN blob(s) from previous deployments`);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
catch (error) {
|
|
612
|
-
console.warn("Warning: Could not delete old CDN blobs via " +
|
|
613
|
-
args.cdnDeleteFunction +
|
|
614
|
-
": " +
|
|
615
|
-
errorMessage(error));
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
else if (publishResult.pendingBlobCleanup > 0) {
|
|
619
|
-
console.log(`${publishResult.pendingBlobCleanup} CDN blob(s) are pending cleanup. Pass --cdn-delete-function to delete them.`);
|
|
620
|
-
}
|
|
621
|
-
console.log("");
|
|
622
|
-
console.log("✨ Upload complete!");
|
|
623
|
-
console.log("");
|
|
624
|
-
console.log(`Your app is now available at: ${componentSiteUrl}`);
|
|
625
|
-
}
|
|
626
|
-
main().catch((error) => {
|
|
627
|
-
console.error("Upload failed:", error);
|
|
628
|
-
process.exit(1);
|
|
629
|
-
});
|
|
630
|
-
//# sourceMappingURL=upload.js.map
|