naro-cli 0.14.0 → 0.16.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 +48 -3
- package/dist/{chunk-U7AZSAFS.js → chunk-XNT7SINN.js} +150 -41
- package/dist/deploy.d.ts +4 -1
- package/dist/deploy.js +1 -1
- package/dist/index.js +458 -91
- package/dist/output.d.ts +5 -0
- package/dist/upload.d.ts +2 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -173,14 +173,14 @@ naro policy set posts --from-file policy.json
|
|
|
173
173
|
Shorthand: writes the policies naro_select … naro_delete
|
|
174
174
|
naro policy remove todos Drop every policy on the table: back to deny (asks first)
|
|
175
175
|
|
|
176
|
-
naro storage Naro Storage: on or off, limits, usage, buckets
|
|
176
|
+
naro storage Naro Storage: on or off, limits, usage, today's operations, buckets
|
|
177
177
|
naro storage enable Turn it on: the project's R2 bucket, bound to the backend
|
|
178
178
|
naro storage bucket create avatars --public --file-size-limit 5MiB --allowed-mime-types image/png,image/jpeg
|
|
179
179
|
naro storage bucket update avatars --private | --file-size-limit none | --allowed-mime-types any
|
|
180
180
|
naro storage bucket update docs --public (asks first: anyone with an object's URL can download it)
|
|
181
181
|
naro storage bucket list | empty <name> | remove <name> [--empty] (empty and remove ask first)
|
|
182
182
|
naro storage ls naro://avatars/u1 [--recursive]
|
|
183
|
-
naro storage cp ./me.png naro://avatars/u1/me.png [--upsert] Up to 50 MiB, with the service key
|
|
183
|
+
naro storage cp ./me.png naro://avatars/u1/me.png [--upsert] Up to the object limit, under 4 GiB (over 50 MiB in parts), with the service key
|
|
184
184
|
naro storage cp naro://docs/team-1/plan.pdf ./ Replaces a local file of the same name
|
|
185
185
|
naro storage rm naro://avatars/u1/old.png [--recursive] (asks first)
|
|
186
186
|
naro storage rm naro://avatars/u1/ --recursive Everything under u1/; written u1, an object u1 too
|
|
@@ -793,6 +793,50 @@ An `--update-check` you give replaces the default: without
|
|
|
793
793
|
|
|
794
794
|
## Release notes
|
|
795
795
|
|
|
796
|
+
### 0.16.0
|
|
797
|
+
|
|
798
|
+
- `naro storage cp` uploads a file up to the object limit, under 4 GiB. Over
|
|
799
|
+
50 MiB it goes in parts, four at a time, and a part that fails is sent again
|
|
800
|
+
(up to three more times) before the upload stops. The 50 MiB refusal before
|
|
801
|
+
any request is gone: the worker judges the size when the upload starts, and a
|
|
802
|
+
file over the bucket's or the plan's limit answers `413 OBJECT_TOO_LARGE`
|
|
803
|
+
before a byte is sent. An upload in parts must finish within 24 hours of its
|
|
804
|
+
start (R2 ends unfinished multipart uploads after a day). A big upload prints its progress on stderr: one line
|
|
805
|
+
rewritten in place at a terminal, a line every 10% otherwise
|
|
806
|
+
(`↑ 312 MiB / 1.2 GiB (26%)`), and nothing with `--json`.
|
|
807
|
+
- Ctrl-C during an upload (or a TERM, or the terminal closing) stops it:
|
|
808
|
+
`Stopping the upload…` on stderr, the upload is called off — one in parts on
|
|
809
|
+
the backend too, so its name and the space it reserved are free at once
|
|
810
|
+
rather than locked for about an hour — and the run ends with the signal. A
|
|
811
|
+
second Ctrl-C ends it at once, without waiting for the call-off. A process
|
|
812
|
+
killed outright, or a network lost, still leaves the name locked until the
|
|
813
|
+
lock runs out.
|
|
814
|
+
- A file of 4 GiB or more is refused before any request
|
|
815
|
+
(`VALIDATION_FAILED`): Node reads its size modulo 4 GiB (4 GiB + 1 KiB reads
|
|
816
|
+
as 1 KiB), and the upload would send only that much and report success.
|
|
817
|
+
Uploading one needs a change in naro-js that is not there yet.
|
|
818
|
+
- A download is written to disk as it arrives, to a hidden `.naro-partial`
|
|
819
|
+
file that replaces the destination once it is whole, instead of being held in
|
|
820
|
+
memory first.
|
|
821
|
+
- `naro storage` says `50 MiB per request (larger files go in parts)`, and
|
|
822
|
+
that uploads stopped over the storage limit reopen usually within an hour and
|
|
823
|
+
a half: R2 reports the bucket's size 30 minutes to about an hour and a half
|
|
824
|
+
late.
|
|
825
|
+
|
|
826
|
+
### 0.15.0
|
|
827
|
+
|
|
828
|
+
- `naro storage` shows the Storage meter's day between `Used:` and `Buckets:`:
|
|
829
|
+
`Today (UTC):` has the write operations (R2 Class A) against the daily limit
|
|
830
|
+
and the reads, `Measured:` the bucket size R2 reported and when R2 measured
|
|
831
|
+
it (R2 reports the size 30 minutes to about an hour and a half late, and the
|
|
832
|
+
meter reads it every 15 minutes). A `✗` line
|
|
833
|
+
says uploads are stopped, over the storage limit until objects are deleted
|
|
834
|
+
or over the day's write operations until 00:00 UTC (downloads still work);
|
|
835
|
+
a `⚠` line warns from 80% of the storage limit or of the day's write
|
|
836
|
+
operations. Before the meter has run for the UTC day nothing is added, and
|
|
837
|
+
`--json` prints `storage.today` and `storage.limits.dailyOps` as the
|
|
838
|
+
control plane answered.
|
|
839
|
+
|
|
796
840
|
### 0.14.0
|
|
797
841
|
|
|
798
842
|
- `naro analytics` shows the project's Web Analytics from the terminal: the
|
|
@@ -858,7 +902,8 @@ An `--update-check` you give replaces the default: without
|
|
|
858
902
|
(public or private, the largest file, the types taken), and `ls`, `cp`
|
|
859
903
|
and `rm` work on `naro://<bucket>/<path>` with the project's service key
|
|
860
904
|
(`NARO_SERVICE_KEY`, or fetched with a warning) — the bytes go to the
|
|
861
|
-
project's backend, not through naro. One
|
|
905
|
+
project's backend, not through naro. One request carries 50 MiB at most; a
|
|
906
|
+
larger file goes in parts since 0.16.0 (under 4 GiB).
|
|
862
907
|
`bucket update --public`, `empty`, `remove` and `rm` ask first; without a
|
|
863
908
|
terminal they need `--yes` (`CONFIRMATION_REQUIRED` otherwise, nothing
|
|
864
909
|
sent).
|
|
@@ -26,6 +26,7 @@ function artifactUrlKey(kind, name) {
|
|
|
26
26
|
return `${kind}/${name}`;
|
|
27
27
|
}
|
|
28
28
|
var ARTIFACT_URL_BATCH_SIZE = 100;
|
|
29
|
+
var ARTIFACT_UPLOAD_CONCURRENCY = 8;
|
|
29
30
|
function parseArtifactPutUrls(payload) {
|
|
30
31
|
if (typeof payload !== "object" || payload === null) return null;
|
|
31
32
|
const { urls, disabled } = payload;
|
|
@@ -37,28 +38,55 @@ function parseArtifactPutUrls(payload) {
|
|
|
37
38
|
}
|
|
38
39
|
return resolved;
|
|
39
40
|
}
|
|
41
|
+
async function eachWithLimit(items, limit, work) {
|
|
42
|
+
let next = 0;
|
|
43
|
+
const lanes = Array.from(
|
|
44
|
+
{ length: Math.min(limit, items.length) },
|
|
45
|
+
async () => {
|
|
46
|
+
while (next < items.length) {
|
|
47
|
+
const item = items[next];
|
|
48
|
+
next += 1;
|
|
49
|
+
await work(item);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
await Promise.all(lanes);
|
|
54
|
+
}
|
|
40
55
|
async function uploadArtifactFiles(artifact, ports) {
|
|
41
56
|
const entries = artifactUploadEntries(artifact);
|
|
42
57
|
let proxied = 0;
|
|
43
58
|
for (let index = 0; index < entries.length; index += ARTIFACT_URL_BATCH_SIZE) {
|
|
44
59
|
const batch = entries.slice(index, index + ARTIFACT_URL_BATCH_SIZE);
|
|
45
60
|
const urls = await ports.resolvePutUrls(batch);
|
|
46
|
-
|
|
61
|
+
const viaProxy = [];
|
|
62
|
+
await eachWithLimit(batch, ARTIFACT_UPLOAD_CONCURRENCY, async (entry) => {
|
|
47
63
|
const url2 = urls?.get(artifactUrlKey(entry.kind, entry.file.name));
|
|
48
64
|
if (!url2) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
continue;
|
|
65
|
+
viaProxy.push(entry);
|
|
66
|
+
return;
|
|
52
67
|
}
|
|
53
68
|
try {
|
|
54
69
|
await ports.putSigned(url2, entry);
|
|
55
70
|
} catch {
|
|
56
|
-
|
|
57
|
-
await ports.putProxied(entry);
|
|
71
|
+
viaProxy.push(entry);
|
|
58
72
|
}
|
|
73
|
+
});
|
|
74
|
+
for (const entry of viaProxy) {
|
|
75
|
+
proxied += 1;
|
|
76
|
+
await ports.putProxied(entry);
|
|
59
77
|
}
|
|
60
78
|
}
|
|
61
|
-
|
|
79
|
+
const bytes = entries.reduce(
|
|
80
|
+
(total, entry) => total + entry.file.data.byteLength,
|
|
81
|
+
0
|
|
82
|
+
);
|
|
83
|
+
return { proxied, total: entries.length, bytes };
|
|
84
|
+
}
|
|
85
|
+
function artifactUploadSummary(report, elapsedMs) {
|
|
86
|
+
const kilobytes = Math.ceil(report.bytes / 1e3);
|
|
87
|
+
const size = kilobytes >= 1e3 ? `${(report.bytes / 1e6).toFixed(1)} MB` : `${String(kilobytes)} kB`;
|
|
88
|
+
const files = report.total === 1 ? "file" : "files";
|
|
89
|
+
return `Uploaded ${String(report.total)} artifact ${files} (${size}) in ${(elapsedMs / 1e3).toFixed(1)} s.`;
|
|
62
90
|
}
|
|
63
91
|
|
|
64
92
|
// ../../packages/build/src/cache.ts
|
|
@@ -100,6 +128,7 @@ async function reportUnavailable(scope, ports, observer) {
|
|
|
100
128
|
"Build cache is unavailable - this build runs uncached and saves nothing for the next one."
|
|
101
129
|
);
|
|
102
130
|
}
|
|
131
|
+
var UNKNOWN_PAX_KEYWORD = /^tar: Ignoring unknown extended header keyword /;
|
|
103
132
|
async function restoreCache(args) {
|
|
104
133
|
const { scope, key, targetDir, ports, observer } = args;
|
|
105
134
|
const urls = await ports.transport.resolveUrls(scope, key);
|
|
@@ -132,7 +161,9 @@ async function restoreCache(args) {
|
|
|
132
161
|
stage: STAGE[scope],
|
|
133
162
|
errorCode: ERROR_CODE[scope]
|
|
134
163
|
},
|
|
135
|
-
|
|
164
|
+
{
|
|
165
|
+
onLog: (entry) => UNKNOWN_PAX_KEYWORD.test(entry.message) ? void 0 : observer.onLog(entry)
|
|
166
|
+
}
|
|
136
167
|
);
|
|
137
168
|
await log(observer, scope, "INFO", `Restored ${scope} cache (${key}).`);
|
|
138
169
|
return true;
|
|
@@ -160,7 +191,14 @@ async function saveCache(args) {
|
|
|
160
191
|
await ports.runCommand(
|
|
161
192
|
{
|
|
162
193
|
command: "tar",
|
|
163
|
-
|
|
194
|
+
// --no-xattrs: macOS bsdtar would store extended attributes that the
|
|
195
|
+
// build container's GNU tar warns about per file (UNKNOWN_PAX_KEYWORD).
|
|
196
|
+
// GNU tar 1.27+ and bsdtar 3.x both take it.
|
|
197
|
+
args: ["--no-xattrs", "--zstd", "-cf", archive, "-C", sourceDir, "."],
|
|
198
|
+
// macOS can also write the same metadata as AppleDouble `._name` files,
|
|
199
|
+
// which GNU tar restores as ordinary files (PR #114 review; not seen on
|
|
200
|
+
// bsdtar 3.5.3 here). GNU tar ignores the variable.
|
|
201
|
+
env: { COPYFILE_DISABLE: "1" },
|
|
164
202
|
cwd: sourceDir,
|
|
165
203
|
timeoutMs: 12e4,
|
|
166
204
|
stage: STAGE[scope],
|
|
@@ -257,6 +295,9 @@ var PHASES = [
|
|
|
257
295
|
];
|
|
258
296
|
var ACTIVE_STATUSES = new Set(PHASES);
|
|
259
297
|
|
|
298
|
+
// ../../packages/core/src/deployment-stall.ts
|
|
299
|
+
var STALL_WINDOW_MS = 10 * 6e4;
|
|
300
|
+
|
|
260
301
|
// ../../packages/core/src/deployment-strategy.ts
|
|
261
302
|
var MIN_SUPPORTED_NEXT_MAJOR = 15;
|
|
262
303
|
|
|
@@ -294,9 +335,6 @@ function pathnameOf(url2) {
|
|
|
294
335
|
}
|
|
295
336
|
}
|
|
296
337
|
|
|
297
|
-
// ../../packages/core/src/internal-auth.ts
|
|
298
|
-
import { Buffer as Buffer2 } from "buffer";
|
|
299
|
-
|
|
300
338
|
// ../../packages/core/src/migration-names.ts
|
|
301
339
|
function normalizeMigrationName(name) {
|
|
302
340
|
return name.trim().toLowerCase().replace(/\.sql$/, "").replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -15253,6 +15291,21 @@ function classifyNextVersion(raw, now) {
|
|
|
15253
15291
|
// ../../packages/build/src/package-manager.ts
|
|
15254
15292
|
import { access, readFile as readFile2 } from "fs/promises";
|
|
15255
15293
|
import { join as join2 } from "path";
|
|
15294
|
+
function cacheEnv(dir) {
|
|
15295
|
+
return {
|
|
15296
|
+
npm_config_store_dir: dir,
|
|
15297
|
+
npm_config_cache: dir,
|
|
15298
|
+
YARN_CACHE_FOLDER: dir,
|
|
15299
|
+
YARN_ENABLE_GLOBAL_CACHE: "0"
|
|
15300
|
+
};
|
|
15301
|
+
}
|
|
15302
|
+
function withSharedCache(spec, dir) {
|
|
15303
|
+
return {
|
|
15304
|
+
...spec,
|
|
15305
|
+
addArgs: (specs) => [...spec.addArgs(specs), ...spec.cacheArgs(dir)],
|
|
15306
|
+
addEnv: { ...spec.addEnv, ...cacheEnv(dir) }
|
|
15307
|
+
};
|
|
15308
|
+
}
|
|
15256
15309
|
async function exists(path) {
|
|
15257
15310
|
try {
|
|
15258
15311
|
await access(path);
|
|
@@ -15302,7 +15355,8 @@ async function detectPackageManager(root) {
|
|
|
15302
15355
|
"install",
|
|
15303
15356
|
modern ? "--immutable" : "--frozen-lockfile"
|
|
15304
15357
|
],
|
|
15305
|
-
// Berry takes no install-time cache flag;
|
|
15358
|
+
// Berry takes no install-time cache flag; cacheEnv covers it
|
|
15359
|
+
// (YARN_CACHE_FOLDER with the global cache switched off).
|
|
15306
15360
|
cacheArgs: (dir) => modern ? [] : ["--cache-folder", dir],
|
|
15307
15361
|
addArgs: (specs) => ["yarn", "add", ...specs]
|
|
15308
15362
|
};
|
|
@@ -15706,7 +15760,7 @@ export default defineCloudflareConfig({});
|
|
|
15706
15760
|
|
|
15707
15761
|
// ../../packages/build/src/vinext.ts
|
|
15708
15762
|
var VINEXT_TOOLCHAIN = [
|
|
15709
|
-
"vinext@1.
|
|
15763
|
+
"vinext@1.1.0",
|
|
15710
15764
|
"@cloudflare/vite-plugin@1.54.7",
|
|
15711
15765
|
"@vitejs/plugin-react@6.1.1",
|
|
15712
15766
|
"@vitejs/plugin-rsc@0.5.34",
|
|
@@ -15722,7 +15776,7 @@ var VITE_CONFIG_NAMES = [
|
|
|
15722
15776
|
"vite.config.mts",
|
|
15723
15777
|
"vite.config.cts"
|
|
15724
15778
|
];
|
|
15725
|
-
var GENERATED_VITE_CONFIG = "vite.config.
|
|
15779
|
+
var GENERATED_VITE_CONFIG = "vite.config.js";
|
|
15726
15780
|
var OUTPUT_DIR = "dist";
|
|
15727
15781
|
var SERVER_DIR = "server";
|
|
15728
15782
|
var CLIENT_DIR = "client";
|
|
@@ -15787,6 +15841,8 @@ async function createVinextBuildConfig(appRoot, deploymentId, writer = async (pa
|
|
|
15787
15841
|
}
|
|
15788
15842
|
var AUTO_FIXED_ISSUES = [/Missing "type": "module"/i];
|
|
15789
15843
|
var ISSUE_LINE = /^\s*✗\s+(.+)$/;
|
|
15844
|
+
var PARTIAL_LINE = /^\s*~\s+(.+)$/;
|
|
15845
|
+
var BLOCKING_PARTIALS = [/^webpack\b/i];
|
|
15790
15846
|
var ANSI_ESCAPE = /\u001B\[[0-9;]*m/g;
|
|
15791
15847
|
var SUMMARY_LINE = /Overall:\s*(\d+)%\s*compatible\s*\(\s*(\d+)\s+supported,\s*(\d+)\s+partial,\s*(\d+)\s+issues?\s*\)/i;
|
|
15792
15848
|
function parseVinextCheckReport(output) {
|
|
@@ -15798,12 +15854,18 @@ function parseVinextCheckReport(output) {
|
|
|
15798
15854
|
plain.split("\n").map((line) => ISSUE_LINE.exec(line)?.[1]?.trim()).filter((line) => Boolean(line))
|
|
15799
15855
|
)
|
|
15800
15856
|
];
|
|
15857
|
+
const partialLines = [
|
|
15858
|
+
...new Set(
|
|
15859
|
+
plain.split("\n").map((line) => PARTIAL_LINE.exec(line)?.[1]?.trim()).filter((line) => Boolean(line))
|
|
15860
|
+
)
|
|
15861
|
+
];
|
|
15801
15862
|
return {
|
|
15802
15863
|
percent: Number(match[1]),
|
|
15803
15864
|
supported: Number(match[2]),
|
|
15804
15865
|
partial: Number(match[3]),
|
|
15805
15866
|
issues: Number(match[4]),
|
|
15806
|
-
issueLines
|
|
15867
|
+
issueLines,
|
|
15868
|
+
partialLines
|
|
15807
15869
|
};
|
|
15808
15870
|
}
|
|
15809
15871
|
function vinextRouteFromCheck(report) {
|
|
@@ -15815,7 +15877,10 @@ function vinextRouteFromCheck(report) {
|
|
|
15815
15877
|
const autoFixed = report.issueLines.filter(
|
|
15816
15878
|
(line) => AUTO_FIXED_ISSUES.some((pattern) => pattern.test(line))
|
|
15817
15879
|
);
|
|
15818
|
-
const
|
|
15880
|
+
const blockingPartials = report.partialLines.filter(
|
|
15881
|
+
(line) => BLOCKING_PARTIALS.some((pattern) => pattern.test(line))
|
|
15882
|
+
);
|
|
15883
|
+
const blocking = Math.max(0, report.issues - autoFixed.length) + blockingPartials.length;
|
|
15819
15884
|
if (blocking > 0)
|
|
15820
15885
|
return {
|
|
15821
15886
|
eligible: false,
|
|
@@ -17836,6 +17901,7 @@ function createVinextCloudflareStrategy(options) {
|
|
|
17836
17901
|
// Not production: npm and classic Yarn read that as omit=dev and
|
|
17837
17902
|
// would prune the project's devDependencies while adding ours.
|
|
17838
17903
|
env: {
|
|
17904
|
+
...context.packageManager.addEnv,
|
|
17839
17905
|
NODE_ENV: "development",
|
|
17840
17906
|
NPM_CONFIG_USERCONFIG: context.packageManagerConfigPath
|
|
17841
17907
|
},
|
|
@@ -18217,11 +18283,7 @@ var LocalBuildExecutor = class {
|
|
|
18217
18283
|
env: {
|
|
18218
18284
|
NODE_ENV: "development",
|
|
18219
18285
|
NPM_CONFIG_USERCONFIG: packageManagerConfigPath,
|
|
18220
|
-
...cachePorts ? {
|
|
18221
|
-
npm_config_store_dir: pmCacheDir,
|
|
18222
|
-
npm_config_cache: pmCacheDir,
|
|
18223
|
-
YARN_CACHE_FOLDER: pmCacheDir
|
|
18224
|
-
} : {}
|
|
18286
|
+
...cachePorts ? cacheEnv(pmCacheDir) : {}
|
|
18225
18287
|
},
|
|
18226
18288
|
timeoutMs: 10 * 6e4,
|
|
18227
18289
|
stage: "INSTALLING",
|
|
@@ -18305,7 +18367,9 @@ var LocalBuildExecutor = class {
|
|
|
18305
18367
|
identity,
|
|
18306
18368
|
branch: input.branch,
|
|
18307
18369
|
env: input.env ?? {},
|
|
18308
|
-
|
|
18370
|
+
// The toolchain add has to use the store the install linked
|
|
18371
|
+
// node_modules from (withSharedCache).
|
|
18372
|
+
packageManager: cachePorts ? withSharedCache(packageManager, pmCacheDir) : packageManager,
|
|
18309
18373
|
packageManagerConfigPath,
|
|
18310
18374
|
buildScript: resolved.buildScript,
|
|
18311
18375
|
nextVersion: nextVersion ? `${String(nextVersion.major)}.${String(nextVersion.minor)}.${String(nextVersion.patch)}` : "unknown",
|
|
@@ -18425,22 +18489,31 @@ function createLogBatcher(options) {
|
|
|
18425
18489
|
let buffer = [];
|
|
18426
18490
|
let timer = null;
|
|
18427
18491
|
let pending = null;
|
|
18492
|
+
let sending = Promise.resolve();
|
|
18428
18493
|
function disarm() {
|
|
18429
18494
|
if (timer === null) return;
|
|
18430
18495
|
clearTimeout(timer);
|
|
18431
18496
|
timer = null;
|
|
18432
18497
|
}
|
|
18498
|
+
function surfacePending() {
|
|
18499
|
+
if (pending === null) return;
|
|
18500
|
+
const { error: error51 } = pending;
|
|
18501
|
+
pending = null;
|
|
18502
|
+
throw error51;
|
|
18503
|
+
}
|
|
18433
18504
|
async function flush() {
|
|
18434
18505
|
disarm();
|
|
18435
|
-
|
|
18436
|
-
|
|
18437
|
-
|
|
18438
|
-
|
|
18506
|
+
surfacePending();
|
|
18507
|
+
if (buffer.length === 0) {
|
|
18508
|
+
await sending;
|
|
18509
|
+
surfacePending();
|
|
18510
|
+
return;
|
|
18439
18511
|
}
|
|
18440
|
-
if (buffer.length === 0) return;
|
|
18441
18512
|
const batch = buffer;
|
|
18442
18513
|
buffer = [];
|
|
18443
|
-
|
|
18514
|
+
const send = sending.then(() => options.send(batch));
|
|
18515
|
+
sending = send.catch(() => void 0);
|
|
18516
|
+
await send;
|
|
18444
18517
|
}
|
|
18445
18518
|
function arm() {
|
|
18446
18519
|
if (timer !== null || !options.intervalMs) return;
|
|
@@ -21059,6 +21132,10 @@ var CATALOG = {
|
|
|
21059
21132
|
status: 404,
|
|
21060
21133
|
message: ({ bucket, path }) => `Object ${storageName(path)} was not found in bucket ${storageName(bucket)}.`
|
|
21061
21134
|
},
|
|
21135
|
+
UPLOAD_NOT_FOUND: {
|
|
21136
|
+
status: 404,
|
|
21137
|
+
message: ({ upload }) => `Upload ${storageName(upload)} was not found or has expired.`
|
|
21138
|
+
},
|
|
21062
21139
|
INVALID_CONTENT_TYPE: {
|
|
21063
21140
|
status: 400,
|
|
21064
21141
|
message: ({ type }) => `Content-Type ${storageName(type)} is not a media type like image/png.`
|
|
@@ -21073,7 +21150,7 @@ var CATALOG = {
|
|
|
21073
21150
|
},
|
|
21074
21151
|
OBJECT_TOO_LARGE: {
|
|
21075
21152
|
status: 413,
|
|
21076
|
-
message: (vars) => "single" in vars ? "Uploads over 50 MiB
|
|
21153
|
+
message: (vars) => "single" in vars ? "Uploads over 50 MiB go in parts: use the multipart upload." : `The object is ${String(vars.size)} bytes; bucket ${storageName(vars.bucket)} takes objects up to ${String(vars.max)} bytes.`
|
|
21077
21154
|
},
|
|
21078
21155
|
MIME_TYPE_NOT_ALLOWED: {
|
|
21079
21156
|
status: 415,
|
|
@@ -21109,6 +21186,12 @@ var CATALOG = {
|
|
|
21109
21186
|
status: 507,
|
|
21110
21187
|
message: () => "This project has used all of its storage. Delete objects to free space; uploads fail until then."
|
|
21111
21188
|
},
|
|
21189
|
+
// Storage 3-1d (design §6.4): like WRITE_QUOTA_EXCEEDED, a 429 that clears
|
|
21190
|
+
// by itself at midnight; downloads, deletes and listings are not refused.
|
|
21191
|
+
STORAGE_OPS_QUOTA_EXCEEDED: {
|
|
21192
|
+
status: 429,
|
|
21193
|
+
message: () => "This project has reached its daily storage operation limit. Downloads still work; uploads resume at 00:00 UTC."
|
|
21194
|
+
},
|
|
21112
21195
|
// R2 or its binding did not answer; the original error is logged, never sent.
|
|
21113
21196
|
STORAGE_UNAVAILABLE: {
|
|
21114
21197
|
status: 503,
|
|
@@ -25813,7 +25896,7 @@ function claimUpload(i) {
|
|
|
25813
25896
|
const nonce = nonceOf(i.nonce);
|
|
25814
25897
|
return compose(
|
|
25815
25898
|
[
|
|
25816
|
-
`INSERT INTO ${STORAGE_OBJECTS_TABLE} (id, bucket_id, name, owner, owner_id, metadata, version, parent, size, status, lock_id, lock_expires_at) SELECT ?, ?, ?, ?, ?, ?, NULL, ?, ?, 'uploading', ?, ? WHERE CASE WHEN NOT EXISTS (SELECT 1 FROM ${STORAGE_BUCKETS_TABLE} WHERE id = ? AND ${LIVE_BUCKET}) THEN ${marker("bucket", nonce)} WHEN (`,
|
|
25899
|
+
`INSERT INTO ${STORAGE_OBJECTS_TABLE} (id, bucket_id, name, owner, owner_id, metadata, version, parent, size, status, lock_id, lock_expires_at, pending) SELECT ?, ?, ?, ?, ?, ?, NULL, ?, ?, 'uploading', ?, ?, ? WHERE CASE WHEN NOT EXISTS (SELECT 1 FROM ${STORAGE_BUCKETS_TABLE} WHERE id = ? AND ${LIVE_BUCKET}) THEN ${marker("bucket", nonce)} WHEN (`,
|
|
25817
25900
|
i.check,
|
|
25818
25901
|
`) <> 1 THEN 0 WHEN EXISTS (SELECT 1 FROM ${STORAGE_OBJECTS_TABLE} WHERE bucket_id = ? AND name = ? AND status = 'ready') THEN ${marker("exists", nonce)} WHEN EXISTS (SELECT 1 FROM ${STORAGE_OBJECTS_TABLE} WHERE bucket_id = ? AND name = ?) THEN ${marker("busy", nonce)} WHEN ${PROJECT_BYTES} + ? > ? THEN ${marker("quota", nonce)} ELSE 1 END RETURNING id`
|
|
25819
25902
|
],
|
|
@@ -25829,6 +25912,7 @@ function claimUpload(i) {
|
|
|
25829
25912
|
i.size,
|
|
25830
25913
|
i.lockId,
|
|
25831
25914
|
i.expiresAt,
|
|
25915
|
+
i.pending ?? null,
|
|
25832
25916
|
i.bucket
|
|
25833
25917
|
],
|
|
25834
25918
|
[i.bucket, i.name, i.bucket, i.name, i.size, i.limitBytes]
|
|
@@ -25852,6 +25936,13 @@ function overwriteCheckRow(i) {
|
|
|
25852
25936
|
}
|
|
25853
25937
|
function claimOverwrite(i) {
|
|
25854
25938
|
const nonce = nonceOf(i.nonce);
|
|
25939
|
+
const room = i.inParts ? {
|
|
25940
|
+
sql: `${PROJECT_BYTES} + ? > ?`,
|
|
25941
|
+
values: [i.size, i.limitBytes]
|
|
25942
|
+
} : {
|
|
25943
|
+
sql: `? > o.size AND ${PROJECT_BYTES} + ? - o.size > ?`,
|
|
25944
|
+
values: [i.size, i.size, i.limitBytes]
|
|
25945
|
+
};
|
|
25855
25946
|
return compose(
|
|
25856
25947
|
[
|
|
25857
25948
|
`UPDATE ${STORAGE_OBJECTS_TABLE} AS o SET lock_id = ?, lock_expires_at = ?, pending = ? WHERE o.bucket_id = ? AND o.name = ? AND CASE WHEN NOT (`,
|
|
@@ -25859,17 +25950,14 @@ function claimOverwrite(i) {
|
|
|
25859
25950
|
// The row's own bucket (`o.bucket_id`), so no value is bound for it.
|
|
25860
25951
|
`) THEN ${marker("hidden", nonce)} WHEN NOT EXISTS (SELECT 1 FROM ${STORAGE_BUCKETS_TABLE} AS b WHERE b.id = o.bucket_id AND b.${LIVE_BUCKET}) THEN ${marker("bucket", nonce)} WHEN o.status <> 'ready' OR o.lock_id IS NOT NULL THEN ${marker("busy", nonce)} WHEN NOT (`,
|
|
25861
25952
|
i.updateUsing,
|
|
25862
|
-
`) THEN ${marker("denied", nonce)} WHEN
|
|
25953
|
+
`) THEN ${marker("denied", nonce)} WHEN ${room.sql} THEN ${marker("quota", nonce)} ELSE (`,
|
|
25863
25954
|
i.check,
|
|
25864
25955
|
// RETURNING names the columns bare: it does not see the UPDATE's
|
|
25865
25956
|
// alias (SQLite 3.53: `no such column: o.id`), and the table is the
|
|
25866
25957
|
// only one there.
|
|
25867
25958
|
") END RETURNING id, version, size, owner, owner_id, created_at"
|
|
25868
25959
|
],
|
|
25869
|
-
[
|
|
25870
|
-
[i.lockId, i.expiresAt, i.pending, i.bucket, i.name],
|
|
25871
|
-
[i.size, i.size, i.limitBytes]
|
|
25872
|
-
]
|
|
25960
|
+
[[i.lockId, i.expiresAt, i.pending, i.bucket, i.name], room.values]
|
|
25873
25961
|
);
|
|
25874
25962
|
}
|
|
25875
25963
|
function claimDelete(i) {
|
|
@@ -27048,6 +27136,17 @@ function formatRedirectEntry(entry) {
|
|
|
27048
27136
|
return entry.path === "/" ? entry.origin : `${entry.origin}${entry.path}`;
|
|
27049
27137
|
}
|
|
27050
27138
|
|
|
27139
|
+
// ../../packages/data-core/src/storage-multipart.ts
|
|
27140
|
+
var MiB = 1024 * 1024;
|
|
27141
|
+
var MULTIPART_PART_SIZES = [16 * MiB, 32 * MiB, 64 * MiB];
|
|
27142
|
+
var MAX_MULTIPART_PARTS = 1e4;
|
|
27143
|
+
var MAX_MULTIPART_BYTES = 64 * MiB * MAX_MULTIPART_PARTS;
|
|
27144
|
+
var MULTIPART_LOCK_MS = 60 * 60 * 1e3;
|
|
27145
|
+
var MULTIPART_PART_EXTEND_BELOW_MS = 30 * 60 * 1e3;
|
|
27146
|
+
var MULTIPART_COMPLETE_LOCK_MS = 15 * 60 * 1e3;
|
|
27147
|
+
var UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
27148
|
+
var UPLOAD_ID = new RegExp(`^(${UUID})\\.(${UUID})$`);
|
|
27149
|
+
|
|
27051
27150
|
// ../../packages/data-core/src/system-sql.ts
|
|
27052
27151
|
async function convertLegacyPolicies(db, found) {
|
|
27053
27152
|
const rows = rowsOf(await db.prepare(POLICY_SQL.list).all());
|
|
@@ -28176,7 +28275,9 @@ function manifest(artifact) {
|
|
|
28176
28275
|
};
|
|
28177
28276
|
}
|
|
28178
28277
|
async function uploadArtifact(artifact, opts) {
|
|
28179
|
-
|
|
28278
|
+
const started = Date.now();
|
|
28279
|
+
const report = await uploadArtifactFiles(artifact, uploadPorts(opts));
|
|
28280
|
+
const uploadSummary = artifactUploadSummary(report, Date.now() - started);
|
|
28180
28281
|
const response = await authed(opts, "/artifacts/complete", {
|
|
28181
28282
|
method: "POST",
|
|
28182
28283
|
headers: { "Content-Type": "application/json" },
|
|
@@ -28187,7 +28288,8 @@ async function uploadArtifact(artifact, opts) {
|
|
|
28187
28288
|
throw new Error("Control plane did not return an artifact key.");
|
|
28188
28289
|
return {
|
|
28189
28290
|
artifactKey: payload.artifactKey,
|
|
28190
|
-
previewUrl: typeof payload.previewUrl === "string" ? payload.previewUrl : null
|
|
28291
|
+
previewUrl: typeof payload.previewUrl === "string" ? payload.previewUrl : null,
|
|
28292
|
+
uploadSummary
|
|
28191
28293
|
};
|
|
28192
28294
|
}
|
|
28193
28295
|
|
|
@@ -28263,7 +28365,8 @@ function eventsObserver(cp3, deploymentId, token, fetchImpl = fetch, logIngestUr
|
|
|
28263
28365
|
await flushLogs();
|
|
28264
28366
|
await post({ type: "metadata", metadata: rest }).catch(() => {
|
|
28265
28367
|
});
|
|
28266
|
-
}
|
|
28368
|
+
},
|
|
28369
|
+
flush: flushLogs
|
|
28267
28370
|
};
|
|
28268
28371
|
}
|
|
28269
28372
|
function slugify2(value) {
|
|
@@ -28458,12 +28561,19 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
28458
28561
|
{ details: { deploymentId: ctx.deploymentId }, cause: error51 }
|
|
28459
28562
|
);
|
|
28460
28563
|
}
|
|
28461
|
-
await uploadArtifact(artifact, {
|
|
28564
|
+
const { uploadSummary } = await uploadArtifact(artifact, {
|
|
28462
28565
|
controlPlaneUrl: cpUrl,
|
|
28463
28566
|
deploymentId: ctx.deploymentId,
|
|
28464
28567
|
callbackToken: ctx.callbackToken,
|
|
28465
28568
|
...options.fetch ? { fetch: options.fetch } : {}
|
|
28466
28569
|
});
|
|
28570
|
+
log2(`\u25B8 ${uploadSummary}`);
|
|
28571
|
+
await observer.onLog({
|
|
28572
|
+
level: "INFO",
|
|
28573
|
+
stage: "DEPLOYING",
|
|
28574
|
+
message: uploadSummary
|
|
28575
|
+
});
|
|
28576
|
+
await observer.flush();
|
|
28467
28577
|
log2("\u25B8 publishing worker");
|
|
28468
28578
|
const detail = await waitForFinish(client, ctx, log2);
|
|
28469
28579
|
const url2 = deploymentUrl(detail, ctx, production);
|
|
@@ -28541,7 +28651,6 @@ export {
|
|
|
28541
28651
|
parseCacheControl,
|
|
28542
28652
|
STORAGE_OBJECTS_TABLE,
|
|
28543
28653
|
systemTablePredicate,
|
|
28544
|
-
dataError,
|
|
28545
28654
|
SCHEMA_SQL,
|
|
28546
28655
|
tableNames,
|
|
28547
28656
|
snapshotFromRows,
|
package/dist/deploy.d.ts
CHANGED
|
@@ -21,7 +21,10 @@ export interface DeployBuildExecutor {
|
|
|
21
21
|
export declare function parseDotenv(text: string): Record<string, string>;
|
|
22
22
|
/** Reads the first of .env.local / .env.production / .env in a directory. */
|
|
23
23
|
export declare function readProjectEnv(dir: string): Record<string, string>;
|
|
24
|
-
export declare function eventsObserver(cp: string, deploymentId: string, token: string, fetchImpl?: typeof fetch, logIngestUrl?: string): BuildObserver
|
|
24
|
+
export declare function eventsObserver(cp: string, deploymentId: string, token: string, fetchImpl?: typeof fetch, logIngestUrl?: string): BuildObserver & {
|
|
25
|
+
/** Sends what is buffered now; never throws. */
|
|
26
|
+
flush(): Promise<void>;
|
|
27
|
+
};
|
|
25
28
|
export declare function slugify(value: string): string;
|
|
26
29
|
export interface DeployOptions {
|
|
27
30
|
name?: string | undefined;
|