@madewithremy/admin 0.1.14 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +149 -43
- package/dist/index.js +68 -12
- package/dist/index.js.map +1 -1
- package/dist/prod.js +136 -95
- package/package.json +1 -1
package/dist/prod.js
CHANGED
|
@@ -342,6 +342,62 @@ function statsForMethod(ctx, methodId, params = {}) {
|
|
|
342
342
|
);
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
+
// src/sleep.ts
|
|
346
|
+
function sleep(ms) {
|
|
347
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/poll.ts
|
|
351
|
+
async function pollUntil(read, isSettled, options) {
|
|
352
|
+
const start2 = Date.now();
|
|
353
|
+
let movedAt = start2;
|
|
354
|
+
let lastKey;
|
|
355
|
+
let transientSince = null;
|
|
356
|
+
while (true) {
|
|
357
|
+
let value;
|
|
358
|
+
try {
|
|
359
|
+
value = await read();
|
|
360
|
+
transientSince = null;
|
|
361
|
+
} catch (err) {
|
|
362
|
+
if (!isTransientError(err)) {
|
|
363
|
+
throw err;
|
|
364
|
+
}
|
|
365
|
+
transientSince ??= Date.now();
|
|
366
|
+
if (Date.now() - transientSince > TRANSIENT_GRACE_MS) {
|
|
367
|
+
throw err;
|
|
368
|
+
}
|
|
369
|
+
options.onProgress?.(
|
|
370
|
+
`retrying after a gateway error\u2026 (${elapsedSeconds(start2)}s)`
|
|
371
|
+
);
|
|
372
|
+
await sleep(options.pollMs);
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (isSettled(value)) {
|
|
376
|
+
return { settled: true, value };
|
|
377
|
+
}
|
|
378
|
+
if (options.progressKey) {
|
|
379
|
+
const key = options.progressKey(value);
|
|
380
|
+
if (key !== lastKey) {
|
|
381
|
+
lastKey = key;
|
|
382
|
+
movedAt = Date.now();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (Date.now() - movedAt > options.timeoutMs) {
|
|
386
|
+
return { settled: false, value };
|
|
387
|
+
}
|
|
388
|
+
if (options.describe) {
|
|
389
|
+
options.onProgress?.(options.describe(value, start2));
|
|
390
|
+
}
|
|
391
|
+
await sleep(options.pollMs);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function elapsedSeconds(start2) {
|
|
395
|
+
return Math.round((Date.now() - start2) / 1e3);
|
|
396
|
+
}
|
|
397
|
+
function timedOut(timeoutMs, still) {
|
|
398
|
+
return `Timed out after ${Math.round(timeoutMs / 1e3)}s ${still}`;
|
|
399
|
+
}
|
|
400
|
+
|
|
345
401
|
// src/output.ts
|
|
346
402
|
var PRETTY = process.stdout.isTTY === true;
|
|
347
403
|
function out(data) {
|
|
@@ -352,6 +408,31 @@ function progress(message) {
|
|
|
352
408
|
process.stderr.write(`${message}
|
|
353
409
|
`);
|
|
354
410
|
}
|
|
411
|
+
function describeEta(eta, now = Date.now()) {
|
|
412
|
+
if (!eta) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
const minutes = Math.max(1, Math.round((Date.parse(eta) - now) / 6e4));
|
|
416
|
+
if (minutes < 90) {
|
|
417
|
+
return `about ${minutes} min left`;
|
|
418
|
+
}
|
|
419
|
+
const hours = Math.round(minutes / 60 * 10) / 10;
|
|
420
|
+
return `about ${hours} h left`;
|
|
421
|
+
}
|
|
422
|
+
function renderProgress(p, start2, extra) {
|
|
423
|
+
const count = p.total !== null ? `${p.done.toLocaleString()}/${p.total.toLocaleString()} ${p.unit}` : p.done > 0 ? `${p.done.toLocaleString()} ${p.unit}` : null;
|
|
424
|
+
const rate = p.ratePerMinute !== null && p.ratePerMinute > 0 ? `${Math.round(p.ratePerMinute).toLocaleString()}/min` : null;
|
|
425
|
+
const parts = [
|
|
426
|
+
`${p.phase}\u2026`,
|
|
427
|
+
count,
|
|
428
|
+
rate,
|
|
429
|
+
describeEta(p.etaAt),
|
|
430
|
+
extra ?? null,
|
|
431
|
+
p.stalled ? "no progress for 30 min" : null
|
|
432
|
+
].filter((part) => Boolean(part));
|
|
433
|
+
const elapsed = start2 !== void 0 ? ` (${elapsedSeconds(start2)}s)` : "";
|
|
434
|
+
return `${parts.join(" \xB7 ")}${elapsed}`;
|
|
435
|
+
}
|
|
355
436
|
|
|
356
437
|
// src/commands/requests.ts
|
|
357
438
|
var requestsSpecs = {
|
|
@@ -866,11 +947,6 @@ import { execFileSync } from "child_process";
|
|
|
866
947
|
// src/config.ts
|
|
867
948
|
var WORKSPACE_DIR = process.env["WORKSPACE_DIR"] || "/home/vercel-sandbox/workspace";
|
|
868
949
|
|
|
869
|
-
// src/sleep.ts
|
|
870
|
-
function sleep(ms) {
|
|
871
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
872
|
-
}
|
|
873
|
-
|
|
874
950
|
// src/ops/releases.ts
|
|
875
951
|
function list3(ctx, params = {}) {
|
|
876
952
|
return call(
|
|
@@ -2874,50 +2950,6 @@ import path5 from "path";
|
|
|
2874
2950
|
|
|
2875
2951
|
// src/ops/dataSources.ts
|
|
2876
2952
|
import { createHash as createHash2 } from "crypto";
|
|
2877
|
-
|
|
2878
|
-
// src/poll.ts
|
|
2879
|
-
async function pollUntil(read, isSettled, options) {
|
|
2880
|
-
const start2 = Date.now();
|
|
2881
|
-
let transientSince = null;
|
|
2882
|
-
while (true) {
|
|
2883
|
-
let value;
|
|
2884
|
-
try {
|
|
2885
|
-
value = await read();
|
|
2886
|
-
transientSince = null;
|
|
2887
|
-
} catch (err) {
|
|
2888
|
-
if (!isTransientError(err)) {
|
|
2889
|
-
throw err;
|
|
2890
|
-
}
|
|
2891
|
-
transientSince ??= Date.now();
|
|
2892
|
-
if (Date.now() - transientSince > TRANSIENT_GRACE_MS) {
|
|
2893
|
-
throw err;
|
|
2894
|
-
}
|
|
2895
|
-
options.onProgress?.(
|
|
2896
|
-
`retrying after a gateway error\u2026 (${elapsedSeconds(start2)}s)`
|
|
2897
|
-
);
|
|
2898
|
-
await sleep(options.pollMs);
|
|
2899
|
-
continue;
|
|
2900
|
-
}
|
|
2901
|
-
if (isSettled(value)) {
|
|
2902
|
-
return { settled: true, value };
|
|
2903
|
-
}
|
|
2904
|
-
if (Date.now() - start2 > options.timeoutMs) {
|
|
2905
|
-
return { settled: false, value };
|
|
2906
|
-
}
|
|
2907
|
-
if (options.describe) {
|
|
2908
|
-
options.onProgress?.(options.describe(value, start2));
|
|
2909
|
-
}
|
|
2910
|
-
await sleep(options.pollMs);
|
|
2911
|
-
}
|
|
2912
|
-
}
|
|
2913
|
-
function elapsedSeconds(start2) {
|
|
2914
|
-
return Math.round((Date.now() - start2) / 1e3);
|
|
2915
|
-
}
|
|
2916
|
-
function timedOut(timeoutMs, still) {
|
|
2917
|
-
return `Timed out after ${Math.round(timeoutMs / 1e3)}s ${still}`;
|
|
2918
|
-
}
|
|
2919
|
-
|
|
2920
|
-
// src/ops/dataSources.ts
|
|
2921
2953
|
var DEFAULT_WAIT_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
2922
2954
|
var POLL_MS2 = 3e3;
|
|
2923
2955
|
function base(appId) {
|
|
@@ -2998,6 +3030,7 @@ async function waitForIngest(ctx, params) {
|
|
|
2998
3030
|
timeoutMs,
|
|
2999
3031
|
pollMs: POLL_MS2,
|
|
3000
3032
|
describe: (tracked2, start2) => `ingesting\u2026 ${tracked2.length - pendingOf(tracked2).length}/${tracked2.length} done (${elapsedSeconds(start2)}s)`,
|
|
3033
|
+
progressKey: (tracked2) => pendingOf(tracked2).length,
|
|
3001
3034
|
onProgress
|
|
3002
3035
|
}
|
|
3003
3036
|
);
|
|
@@ -3034,6 +3067,7 @@ async function waitForCandidate(ctx, params) {
|
|
|
3034
3067
|
timeoutMs,
|
|
3035
3068
|
pollMs: POLL_MS2,
|
|
3036
3069
|
describe: (progress2, start2) => progress2 ? `rebuilding\u2026 ${progress2.done}/${progress2.total} done (${elapsedSeconds(start2)}s)` : `rebuilding\u2026 (${elapsedSeconds(start2)}s)`,
|
|
3070
|
+
progressKey: (progress2) => progress2?.processing ?? null,
|
|
3037
3071
|
onProgress: params.onProgress
|
|
3038
3072
|
}
|
|
3039
3073
|
);
|
|
@@ -3186,7 +3220,10 @@ async function waitForMove(ctx, params) {
|
|
|
3186
3220
|
{
|
|
3187
3221
|
timeoutMs,
|
|
3188
3222
|
pollMs: POLL_MS2,
|
|
3189
|
-
describe: (source2, start2) =>
|
|
3223
|
+
describe: (source2, start2) => renderProgress(source2.migration.progress, start2),
|
|
3224
|
+
// A corpus of any size moves as long as it keeps moving; the timeout
|
|
3225
|
+
// bounds silence.
|
|
3226
|
+
progressKey: (source2) => source2.migration?.copied ?? null,
|
|
3190
3227
|
onProgress: params.onProgress
|
|
3191
3228
|
}
|
|
3192
3229
|
);
|
|
@@ -3380,6 +3417,9 @@ async function settle(ctx, params, isSettled) {
|
|
|
3380
3417
|
timeoutMs,
|
|
3381
3418
|
pollMs: POLL_MS3,
|
|
3382
3419
|
describe: describeProgress,
|
|
3420
|
+
// A load of any length is waited out while it moves; the timeout bounds
|
|
3421
|
+
// silence. Planning moves by objects listed, a run by documents.
|
|
3422
|
+
progressKey: (job2) => `${job2.state}:${job2.progress.done}`,
|
|
3383
3423
|
onProgress: params.onProgress
|
|
3384
3424
|
}
|
|
3385
3425
|
);
|
|
@@ -3415,14 +3455,15 @@ async function settle(ctx, params, isSettled) {
|
|
|
3415
3455
|
return { status: "done", job };
|
|
3416
3456
|
}
|
|
3417
3457
|
function describeProgress(job, start2) {
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3458
|
+
if (job.state === "planning" && job.enumerationDone) {
|
|
3459
|
+
return renderProgress(
|
|
3460
|
+
{ ...job.progress, phase: "planning" },
|
|
3461
|
+
start2,
|
|
3462
|
+
`reading a sample of ${job.counts.objectsSeen.toLocaleString()} objects`
|
|
3463
|
+
);
|
|
3421
3464
|
}
|
|
3422
|
-
const
|
|
3423
|
-
|
|
3424
|
-
const spend = `$${job.estimatedDollars.toFixed(2)}${job.budgetDollars !== null ? ` of $${job.budgetDollars.toFixed(2)}` : ""}`;
|
|
3425
|
-
return `${job.state}\u2026 ${processed.toLocaleString()}/${total.toLocaleString()} documents \xB7 ${spend} ${elapsed}`;
|
|
3465
|
+
const spend = job.state === "planning" ? null : `$${job.estimatedDollars.toFixed(2)}${job.budgetDollars !== null ? ` of $${job.budgetDollars.toFixed(2)}` : ""}`;
|
|
3466
|
+
return renderProgress(job.progress, start2, spend);
|
|
3426
3467
|
}
|
|
3427
3468
|
|
|
3428
3469
|
// src/commands/_shared/dataSources.ts
|
|
@@ -3690,7 +3731,7 @@ function hydrate(ctx, params) {
|
|
|
3690
3731
|
ctx,
|
|
3691
3732
|
"POST",
|
|
3692
3733
|
`${base4(ctx.appId)}/hydrate`,
|
|
3693
|
-
{ slug: params.slug
|
|
3734
|
+
{ slug: params.slug }
|
|
3694
3735
|
);
|
|
3695
3736
|
}
|
|
3696
3737
|
function reindex(ctx, params) {
|
|
@@ -3716,7 +3757,10 @@ async function waitForHydration(ctx, params) {
|
|
|
3716
3757
|
{
|
|
3717
3758
|
timeoutMs,
|
|
3718
3759
|
pollMs: POLL_MS4,
|
|
3719
|
-
describe: (source2, start2) =>
|
|
3760
|
+
describe: (source2, start2) => renderProgress(source2.hydration.progress, start2),
|
|
3761
|
+
// A reload of any size is waited out while it moves; the timeout
|
|
3762
|
+
// bounds silence.
|
|
3763
|
+
progressKey: (source2) => source2.hydration?.copied ?? null,
|
|
3720
3764
|
onProgress: params.onProgress
|
|
3721
3765
|
}
|
|
3722
3766
|
);
|
|
@@ -3904,10 +3948,9 @@ async function waitForSet(ctx, params) {
|
|
|
3904
3948
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3905
3949
|
var dataSourceEvalsSpecs = {
|
|
3906
3950
|
"datasources hydrate": {
|
|
3907
|
-
usage: "Usage: remy-admin datasources hydrate [--source <slug>] [--
|
|
3951
|
+
usage: "Usage: remy-admin datasources hydrate [--source <slug>] [--wait] [--timeout <sec>]",
|
|
3908
3952
|
flags: {
|
|
3909
3953
|
source: { type: "string" },
|
|
3910
|
-
since: { type: "string" },
|
|
3911
3954
|
wait: { type: "boolean" },
|
|
3912
3955
|
timeout: { type: "string" }
|
|
3913
3956
|
}
|
|
@@ -4078,14 +4121,7 @@ function selectorFromFlag(raw) {
|
|
|
4078
4121
|
}
|
|
4079
4122
|
async function hydrate2(ctx, a) {
|
|
4080
4123
|
const slug = a.str("source") || DEFAULT_SOURCE;
|
|
4081
|
-
const
|
|
4082
|
-
if (since && Number.isNaN(Date.parse(since))) {
|
|
4083
|
-
fatal("--since must be an ISO 8601 timestamp, e.g. 2026-09-08T15:17:00Z.");
|
|
4084
|
-
}
|
|
4085
|
-
const started = await hydrate(ctx, {
|
|
4086
|
-
slug,
|
|
4087
|
-
...since ? { since: new Date(since).toISOString() } : {}
|
|
4088
|
-
});
|
|
4124
|
+
const started = await hydrate(ctx, { slug });
|
|
4089
4125
|
if (started.resident || !a.bool("wait")) {
|
|
4090
4126
|
out({
|
|
4091
4127
|
dataSource: slug,
|
|
@@ -4380,7 +4416,7 @@ var dataSourceEvalsHandlers = {
|
|
|
4380
4416
|
};
|
|
4381
4417
|
var dataSourceEvalsHelp = `
|
|
4382
4418
|
Warming and sampling:
|
|
4383
|
-
remy-admin datasources hydrate [--source <slug>] [--
|
|
4419
|
+
remy-admin datasources hydrate [--source <slug>] [--wait]
|
|
4384
4420
|
remy-admin datasources sample --source <slug> --size <n> [--as <slug>] [--filter <k=v,...|json>] [--stratify <key>] [--placement <id|shared>] [--wait]
|
|
4385
4421
|
|
|
4386
4422
|
A large source whose index was evicted (the shared pool holds a working set,
|
|
@@ -4388,15 +4424,9 @@ Warming and sampling:
|
|
|
4388
4424
|
background; small sources reload inside the search and never show it.
|
|
4389
4425
|
\`hydrate\` starts that reload ahead of time \u2014 before a demo, say \u2014 and
|
|
4390
4426
|
\`--wait\` blocks until the index is back. \`datasources list\` shows the
|
|
4391
|
-
reload's progress under \`hydration\`.
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
document built at or after that time into the index whether or not the index
|
|
4395
|
-
is resident. Dedicated capacity that restored from a snapshot is missing
|
|
4396
|
-
everything written after the snapshot was taken; the platform starts this
|
|
4397
|
-
refill itself when it sees the restore, and \`infra logs\` shows it. Use the
|
|
4398
|
-
flag for a restore that predates that, with the snapshot's time from the
|
|
4399
|
-
resource log.
|
|
4427
|
+
reload's progress under \`hydration\`. The same reload is what dedicated
|
|
4428
|
+
capacity runs on its own after a lost node or a resume; \`infra logs\` shows
|
|
4429
|
+
it start, and the source's \`hydration\` shows it land.
|
|
4400
4430
|
|
|
4401
4431
|
\`sample\` draws documents from a source into a new one that shares its
|
|
4402
4432
|
exact pinned config, so what you measure on the sample says something about
|
|
@@ -7643,7 +7673,14 @@ async function waitForPhase(ctx, params) {
|
|
|
7643
7673
|
{
|
|
7644
7674
|
timeoutMs,
|
|
7645
7675
|
pollMs: POLL_MS5,
|
|
7646
|
-
describe: (resource2, start2) =>
|
|
7676
|
+
describe: (resource2, start2) => resource2.progress ? renderProgress(
|
|
7677
|
+
resource2.progress,
|
|
7678
|
+
start2,
|
|
7679
|
+
resource2.resizingTo ? `resizing to ${resource2.resizingTo.label}` : null
|
|
7680
|
+
) : `${resource2.phase}${resource2.detail ? ` \xB7 ${resource2.detail}` : ""}${resource2.resizingTo ? ` \xB7 resizing to ${resource2.resizingTo.label}` : ""}\u2026 (${elapsedSeconds(start2)}s)`,
|
|
7681
|
+
// A rebuild after a resume runs as long as it must; the timeout bounds
|
|
7682
|
+
// silence, and a phase or step change counts as movement.
|
|
7683
|
+
progressKey: (resource2) => `${resource2.phase}:${resource2.progress?.done ?? ""}:${resource2.detail ?? ""}`,
|
|
7647
7684
|
onProgress: params.onProgress
|
|
7648
7685
|
}
|
|
7649
7686
|
);
|
|
@@ -7929,26 +7966,29 @@ Placing a data source on a resource:
|
|
|
7929
7966
|
|
|
7930
7967
|
Searches on a source whose resource is not active fail with
|
|
7931
7968
|
capacity_<phase>: capacity_hibernated once parked, capacity_hibernating /
|
|
7932
|
-
capacity_resuming while it moves.
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
user; the stable code
|
|
7969
|
+
capacity_resuming while it moves. Once active, a source whose index is being
|
|
7970
|
+
rebuilt from durable storage (after a resume, a resize or a lost node)
|
|
7971
|
+
answers index_warming with the rebuild's progress and ETA until it lands;
|
|
7972
|
+
\`datasources list\` shows it under hydration and the resource under
|
|
7973
|
+
progress. The messages are written for the app's end user; the stable code
|
|
7974
|
+
is what to act on.
|
|
7937
7975
|
|
|
7938
7976
|
Resizing:
|
|
7939
|
-
A resize keeps the
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7977
|
+
A resize keeps the documents: the resource is parked, its spec is swapped,
|
|
7978
|
+
and it comes back up with its sources' indexes rebuilt from durable storage
|
|
7979
|
+
in the background (nothing is re-embedded). Searches on those sources answer
|
|
7980
|
+
index_warming until the rebuild lands. A hibernated resource swaps in place
|
|
7981
|
+
and stays parked. Growing needs a month's credits at the new rate; shrinking
|
|
7982
|
+
is refused below what the resource holds (resize_too_small). \`infra get\`
|
|
7983
|
+
shows resizingTo until the swap has happened.
|
|
7945
7984
|
|
|
7946
7985
|
Phases and timing:
|
|
7947
7986
|
requested \u2192 provisioning \u2192 active \u21C4 hibernated \u2192 destroyed, with hibernating /
|
|
7948
7987
|
resuming / decommissioning in between and failed when a change cannot complete
|
|
7949
|
-
(retried automatically; resume or hibernate to retry by hand).
|
|
7950
|
-
takes
|
|
7951
|
-
with
|
|
7988
|
+
(retried automatically; resume or hibernate to retry by hand). A transition
|
|
7989
|
+
takes a minute or two; the rebuild that follows a resume runs under its own
|
|
7990
|
+
progress with a measured ETA. --wait gives up only after --timeout (default
|
|
7991
|
+
600s) WITHOUT movement, so a long rebuild is waited out while it moves.
|
|
7952
7992
|
|
|
7953
7993
|
Billing and the log:
|
|
7954
7994
|
\`infra get\` reports resource.billing: what the resource has actually been
|
|
@@ -7957,7 +7997,8 @@ Billing and the log:
|
|
|
7957
7997
|
|
|
7958
7998
|
The log is the platform's narration of the resource, not the instance's
|
|
7959
7999
|
stdout: each step a transition went through ("Waiting for capacity",
|
|
7960
|
-
"
|
|
8000
|
+
"Rebuilding archive v1 from its artifacts: 14,333,673 document(s)"), each
|
|
8001
|
+
failed attempt, and each failure with the
|
|
7961
8002
|
pod's recent Kubernetes events and the warnings-and-errors tail of the
|
|
7962
8003
|
instance log at that moment (line.data). \`infra get\` includes the newest
|
|
7963
8004
|
200 lines; \`infra logs\` pages the rest and, with --follow, prints new lines
|