run402 4.0.0 → 4.0.2
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 +4 -1
- package/lib/deploy-v2.mjs +1 -0
- package/lib/up.mjs +146 -0
- package/package.json +1 -1
- package/sdk/dist/actions.d.ts +7 -1
- package/sdk/dist/actions.d.ts.map +1 -1
- package/sdk/dist/app-up.d.ts +14 -3
- package/sdk/dist/app-up.d.ts.map +1 -1
- package/sdk/dist/app-up.js +1 -0
- package/sdk/dist/app-up.js.map +1 -1
- package/sdk/dist/config.d.ts +22 -5
- package/sdk/dist/config.d.ts.map +1 -1
- package/sdk/dist/config.js +6 -1
- package/sdk/dist/config.js.map +1 -1
- package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
- package/sdk/dist/namespaces/deploy.js +59 -8
- package/sdk/dist/namespaces/deploy.js.map +1 -1
- package/sdk/dist/namespaces/deploy.types.d.ts +43 -5
- package/sdk/dist/namespaces/deploy.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/deploy.types.js +24 -2
- package/sdk/dist/namespaces/deploy.types.js.map +1 -1
- package/sdk/dist/node/actions-node.d.ts.map +1 -1
- package/sdk/dist/node/actions-node.js +460 -26
- package/sdk/dist/node/actions-node.js.map +1 -1
- package/sdk/dist/node/deploy-manifest.d.ts +14 -2
- package/sdk/dist/node/deploy-manifest.d.ts.map +1 -1
- package/sdk/dist/node/deploy-manifest.js +45 -8
- package/sdk/dist/node/deploy-manifest.js.map +1 -1
|
@@ -1612,6 +1612,7 @@ async function pollUntilReady(client, commit, diff, warnings, emit, projectId, s
|
|
|
1612
1612
|
urls: commit.urls,
|
|
1613
1613
|
diff,
|
|
1614
1614
|
warnings,
|
|
1615
|
+
...(commit.subdomain_bindings ? { subdomain_bindings: commit.subdomain_bindings } : {}),
|
|
1615
1616
|
};
|
|
1616
1617
|
}
|
|
1617
1618
|
const opHeaders = projectId ? await apikeyHeaders(client, projectId) : {};
|
|
@@ -1710,6 +1711,7 @@ async function pollSnapshotUntilReady(client, initial, diff, warnings, emit, pro
|
|
|
1710
1711
|
urls: snapshot.urls,
|
|
1711
1712
|
diff,
|
|
1712
1713
|
warnings,
|
|
1714
|
+
...(snapshot.subdomain_bindings ? { subdomain_bindings: snapshot.subdomain_bindings } : {}),
|
|
1713
1715
|
};
|
|
1714
1716
|
}
|
|
1715
1717
|
if (TERMINAL_STATUSES.includes(snapshot.status)) {
|
|
@@ -1960,7 +1962,7 @@ const DEPLOYABLE_SPEC_FIELDS = [
|
|
|
1960
1962
|
];
|
|
1961
1963
|
const BASE_SPEC_FIELDS = new Set(["release", "release_id"]);
|
|
1962
1964
|
const DATABASE_SPEC_FIELDS = new Set(["migrations", "expose", "zero_downtime"]);
|
|
1963
|
-
const MIGRATION_SPEC_FIELDS = new Set(["id", "checksum", "sql", "sql_ref", "transaction"]);
|
|
1965
|
+
const MIGRATION_SPEC_FIELDS = new Set(["id", "name", "checksum", "sql", "sql_ref", "transaction"]);
|
|
1964
1966
|
const FUNCTIONS_SPEC_FIELDS = new Set(["replace", "patch"]);
|
|
1965
1967
|
const FUNCTIONS_PATCH_FIELDS = new Set(["set", "delete"]);
|
|
1966
1968
|
const FUNCTION_SPEC_FIELDS = new Set([
|
|
@@ -1995,6 +1997,7 @@ const I18N_LOCALE_TAG_REGEX = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
|
1995
1997
|
const I18N_COOKIE_NAME_REGEX = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
1996
1998
|
const I18N_MAX_LOCALES = 50;
|
|
1997
1999
|
const I18N_MAX_DETECT_SOURCES = 10;
|
|
2000
|
+
const MIGRATION_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
|
|
1998
2001
|
function validateSpec(spec) {
|
|
1999
2002
|
if (!spec || typeof spec !== "object") {
|
|
2000
2003
|
throw new Run402DeployError("ReleaseSpec must be an object", {
|
|
@@ -2072,15 +2075,38 @@ function validateDatabaseSpec(database) {
|
|
|
2072
2075
|
if (!Array.isArray(obj.migrations)) {
|
|
2073
2076
|
throw invalidSpec("ReleaseSpec.database.migrations must be an array", "database.migrations");
|
|
2074
2077
|
}
|
|
2078
|
+
const seenNames = new Map();
|
|
2075
2079
|
for (const [index, migration] of obj.migrations.entries()) {
|
|
2076
2080
|
const m = requireObject(migration, `database.migrations.${index}`);
|
|
2077
2081
|
validateKnownFields(m, `database.migrations.${index}`, MIGRATION_SPEC_FIELDS);
|
|
2082
|
+
validateMigrationIdentity(m, index, seenNames);
|
|
2078
2083
|
}
|
|
2079
2084
|
}
|
|
2080
2085
|
if (obj.expose !== undefined) {
|
|
2081
2086
|
requireObject(obj.expose, "database.expose");
|
|
2082
2087
|
}
|
|
2083
2088
|
}
|
|
2089
|
+
function validateMigrationIdentity(migration, index, seenNames) {
|
|
2090
|
+
const resource = `database.migrations.${index}`;
|
|
2091
|
+
const hasId = hasOwn(migration, "id") && migration.id !== undefined;
|
|
2092
|
+
const hasName = hasOwn(migration, "name") && migration.name !== undefined;
|
|
2093
|
+
if (hasId && hasName) {
|
|
2094
|
+
throw invalidSpec(`ReleaseSpec.${resource} declares both id and name; use exactly one`, resource);
|
|
2095
|
+
}
|
|
2096
|
+
if (!hasId && !hasName) {
|
|
2097
|
+
throw invalidSpec(`ReleaseSpec.${resource} must declare exactly one of id or name`, resource);
|
|
2098
|
+
}
|
|
2099
|
+
if (hasName) {
|
|
2100
|
+
if (typeof migration.name !== "string" || !MIGRATION_NAME_RE.test(migration.name)) {
|
|
2101
|
+
throw invalidSpec(`ReleaseSpec.${resource}.name must match /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/`, `${resource}.name`);
|
|
2102
|
+
}
|
|
2103
|
+
const firstIndex = seenNames.get(migration.name);
|
|
2104
|
+
if (firstIndex !== undefined) {
|
|
2105
|
+
throw invalidSpec(`ReleaseSpec.${resource}.name duplicates database.migrations.${firstIndex}.name ${JSON.stringify(migration.name)}`, `${resource}.name`);
|
|
2106
|
+
}
|
|
2107
|
+
seenNames.set(migration.name, index);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2084
2110
|
function validateFunctionsSpec(functions) {
|
|
2085
2111
|
if (functions === undefined)
|
|
2086
2112
|
return;
|
|
@@ -3021,6 +3047,7 @@ async function normalizeReleaseSpec(client, spec, opts = {}) {
|
|
|
3021
3047
|
}
|
|
3022
3048
|
if (spec.database.migrations && spec.database.migrations.length > 0) {
|
|
3023
3049
|
db.migrations = await Promise.all(spec.database.migrations.map(async (m) => normalizeMigration(client, spec.project, m, rememberRelease, opts)));
|
|
3050
|
+
assertUniqueMigrationIds(db.migrations);
|
|
3024
3051
|
}
|
|
3025
3052
|
normalized.database = db;
|
|
3026
3053
|
}
|
|
@@ -3438,8 +3465,10 @@ async function normalizeAssetSlice(slice, remember) {
|
|
|
3438
3465
|
return out;
|
|
3439
3466
|
}
|
|
3440
3467
|
async function normalizeMigration(client, projectId, m, remember, opts = {}) {
|
|
3441
|
-
|
|
3442
|
-
|
|
3468
|
+
const originalId = "id" in m ? m.id : undefined;
|
|
3469
|
+
const name = "name" in m ? m.name : undefined;
|
|
3470
|
+
if (!originalId && !name) {
|
|
3471
|
+
throw new Run402DeployError("MigrationSpec.id or MigrationSpec.name is required", {
|
|
3443
3472
|
code: "INVALID_SPEC",
|
|
3444
3473
|
phase: "validate",
|
|
3445
3474
|
resource: "database.migrations",
|
|
@@ -3451,37 +3480,42 @@ async function normalizeMigration(client, projectId, m, remember, opts = {}) {
|
|
|
3451
3480
|
let sql_ref;
|
|
3452
3481
|
let sql;
|
|
3453
3482
|
let checksum;
|
|
3483
|
+
let contentHash;
|
|
3484
|
+
const identityLabel = originalId ?? name ?? "<unknown>";
|
|
3454
3485
|
if (m.sql_ref) {
|
|
3455
3486
|
sql_ref = m.sql_ref;
|
|
3487
|
+
contentHash = m.sql_ref.sha256;
|
|
3456
3488
|
checksum = m.checksum ?? m.sql_ref.sha256;
|
|
3457
3489
|
}
|
|
3458
3490
|
else if (m.sql !== undefined) {
|
|
3459
3491
|
const bytes = new TextEncoder().encode(m.sql);
|
|
3460
3492
|
const sha256 = await sha256Hex(bytes);
|
|
3493
|
+
contentHash = sha256;
|
|
3461
3494
|
if (opts.inlineMigrationSql) {
|
|
3462
3495
|
sql = m.sql;
|
|
3463
3496
|
}
|
|
3464
3497
|
else {
|
|
3465
3498
|
const ref = { sha256, size: bytes.byteLength, contentType: "application/sql" };
|
|
3466
|
-
remember({ ref, reader: makeBytesReader(bytes, `migration:${
|
|
3499
|
+
remember({ ref, reader: makeBytesReader(bytes, `migration:${identityLabel}`) });
|
|
3467
3500
|
sql_ref = ref;
|
|
3468
3501
|
}
|
|
3469
3502
|
checksum = m.checksum ?? sha256;
|
|
3470
3503
|
}
|
|
3471
3504
|
else {
|
|
3472
|
-
throw new Run402DeployError(`MigrationSpec ${
|
|
3505
|
+
throw new Run402DeployError(`MigrationSpec ${identityLabel} must include sql or sql_ref`, {
|
|
3473
3506
|
code: "INVALID_SPEC",
|
|
3474
3507
|
phase: "validate",
|
|
3475
|
-
resource: `database.migrations.${
|
|
3508
|
+
resource: `database.migrations.${identityLabel}`,
|
|
3476
3509
|
retryable: false,
|
|
3477
3510
|
fix: {
|
|
3478
3511
|
action: "set_field",
|
|
3479
|
-
path: `database.migrations.${
|
|
3512
|
+
path: `database.migrations.${identityLabel}.sql`,
|
|
3480
3513
|
},
|
|
3481
3514
|
context: "validating spec",
|
|
3482
3515
|
});
|
|
3483
3516
|
}
|
|
3484
|
-
const
|
|
3517
|
+
const id = originalId ?? `${name}_${contentHash.slice(0, 16)}`;
|
|
3518
|
+
const out = { id, checksum };
|
|
3485
3519
|
if (sql_ref)
|
|
3486
3520
|
out.sql_ref = sql_ref;
|
|
3487
3521
|
if (sql !== undefined)
|
|
@@ -3493,6 +3527,23 @@ async function normalizeMigration(client, projectId, m, remember, opts = {}) {
|
|
|
3493
3527
|
void client;
|
|
3494
3528
|
void projectId;
|
|
3495
3529
|
}
|
|
3530
|
+
function assertUniqueMigrationIds(migrations) {
|
|
3531
|
+
const seen = new Map();
|
|
3532
|
+
for (const [index, migration] of migrations.entries()) {
|
|
3533
|
+
const firstIndex = seen.get(migration.id);
|
|
3534
|
+
if (firstIndex !== undefined) {
|
|
3535
|
+
throw new Run402DeployError(`ReleaseSpec.database.migrations.${index}.id duplicates database.migrations.${firstIndex}.id ${JSON.stringify(migration.id)}`, {
|
|
3536
|
+
code: "INVALID_SPEC",
|
|
3537
|
+
phase: "validate",
|
|
3538
|
+
resource: `database.migrations.${index}.id`,
|
|
3539
|
+
retryable: false,
|
|
3540
|
+
fix: { action: "set_field", path: `database.migrations.${index}.id` },
|
|
3541
|
+
context: "validating spec",
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3544
|
+
seen.set(migration.id, index);
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3496
3547
|
// ─── Content source resolution ───────────────────────────────────────────────
|
|
3497
3548
|
async function resolveContent(source, label) {
|
|
3498
3549
|
// Pre-resolved ContentRef — pass through, no reader needed (caller is
|