@hot-updater/vercel 1.1.3 → 1.2.0-rc.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/dist/function/index.mjs +846 -396
- package/dist/index.cjs +5 -4
- package/dist/index.mjs +5 -4
- package/package.json +7 -7
package/dist/function/index.mjs
CHANGED
|
@@ -241,7 +241,7 @@ const bundleMetadataToRow = (metadata) => {
|
|
|
241
241
|
return metadata;
|
|
242
242
|
};
|
|
243
243
|
//#endregion
|
|
244
|
-
//#region ../../packages/protocol/dist/
|
|
244
|
+
//#region ../../packages/protocol/dist/uuid-kZvLA0Ly.mjs
|
|
245
245
|
const LETTER_DASH_NUMBER$2 = "[a-zA-Z0-9-]";
|
|
246
246
|
const NUMERIC_IDENTIFIER$2 = String.raw`0|[1-9]\d*`;
|
|
247
247
|
const NUMERIC_IDENTIFIER_LOOSE$2 = String.raw`\d+`;
|
|
@@ -348,128 +348,6 @@ function canonicalizeAppVersion(appVersion) {
|
|
|
348
348
|
const version = coerce$1(appVersion);
|
|
349
349
|
return version ? normalizeFull(version) : null;
|
|
350
350
|
}
|
|
351
|
-
const getManifestStorageUri = (bundle) => bundle.manifestStorageUri;
|
|
352
|
-
const getManifestFileHash = (bundle) => bundle.manifestFileHash;
|
|
353
|
-
const getAssetBaseStorageUri = (bundle) => bundle.assetBaseStorageUri;
|
|
354
|
-
const isBundlePatchArtifact = (value) => {
|
|
355
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
356
|
-
const candidate = value;
|
|
357
|
-
return typeof candidate.baseBundleId === "string" && typeof candidate.baseFileHash === "string" && typeof candidate.patchFileHash === "string" && typeof candidate.patchStorageUri === "string";
|
|
358
|
-
};
|
|
359
|
-
const readBundlePatchArray = (patches) => {
|
|
360
|
-
if (!Array.isArray(patches)) return [];
|
|
361
|
-
return patches.filter(isBundlePatchArtifact);
|
|
362
|
-
};
|
|
363
|
-
const getBundlePatches = (bundle) => {
|
|
364
|
-
const patches = readBundlePatchArray(bundle.patches);
|
|
365
|
-
const seenBaseBundleIds = /* @__PURE__ */ new Set();
|
|
366
|
-
return patches.filter((patch) => {
|
|
367
|
-
if (seenBaseBundleIds.has(patch.baseBundleId)) return false;
|
|
368
|
-
seenBaseBundleIds.add(patch.baseBundleId);
|
|
369
|
-
return true;
|
|
370
|
-
});
|
|
371
|
-
};
|
|
372
|
-
const getBundlePatch = (bundle, baseBundleId) => {
|
|
373
|
-
return getBundlePatches(bundle).find((patch) => patch.baseBundleId === baseBundleId) ?? null;
|
|
374
|
-
};
|
|
375
|
-
const NUMERIC_COHORT_SIZE = 1e3;
|
|
376
|
-
const DEFAULT_ROLLOUT_COHORT_COUNT = NUMERIC_COHORT_SIZE;
|
|
377
|
-
const CUSTOM_COHORT_PATTERN = /^[a-z0-9-]+$/;
|
|
378
|
-
function parseNumericCohortValue(cohort) {
|
|
379
|
-
if (!/^\d+$/.test(cohort)) return null;
|
|
380
|
-
const parsed = Number.parseInt(cohort, 10);
|
|
381
|
-
if (Number.isNaN(parsed) || parsed < 1 || parsed > 1e3) return null;
|
|
382
|
-
return parsed;
|
|
383
|
-
}
|
|
384
|
-
function positiveMod(value, modulus) {
|
|
385
|
-
return (value % modulus + modulus) % modulus;
|
|
386
|
-
}
|
|
387
|
-
function hashString(value) {
|
|
388
|
-
let hash = 0;
|
|
389
|
-
for (let i = 0; i < value.length; i++) {
|
|
390
|
-
const char = value.charCodeAt(i);
|
|
391
|
-
hash = (hash << 5) - hash + char;
|
|
392
|
-
hash |= 0;
|
|
393
|
-
}
|
|
394
|
-
return hash;
|
|
395
|
-
}
|
|
396
|
-
function gcd(a, b) {
|
|
397
|
-
let x = Math.abs(a);
|
|
398
|
-
let y = Math.abs(b);
|
|
399
|
-
while (y !== 0) {
|
|
400
|
-
const next = x % y;
|
|
401
|
-
x = y;
|
|
402
|
-
y = next;
|
|
403
|
-
}
|
|
404
|
-
return x;
|
|
405
|
-
}
|
|
406
|
-
function modularInverse(value, modulus) {
|
|
407
|
-
let t = 0;
|
|
408
|
-
let newT = 1;
|
|
409
|
-
let r = modulus;
|
|
410
|
-
let newR = positiveMod(value, modulus);
|
|
411
|
-
while (newR !== 0) {
|
|
412
|
-
const quotient = Math.floor(r / newR);
|
|
413
|
-
[t, newT] = [newT, t - quotient * newT];
|
|
414
|
-
[r, newR] = [newR, r - quotient * newR];
|
|
415
|
-
}
|
|
416
|
-
if (r > 1) throw new Error(`No modular inverse for ${value} mod ${modulus}`);
|
|
417
|
-
return positiveMod(t, modulus);
|
|
418
|
-
}
|
|
419
|
-
function getRolloutShuffleParameters(bundleId) {
|
|
420
|
-
let multiplier = positiveMod(hashString(`${bundleId}:multiplier`), 997);
|
|
421
|
-
if (multiplier === 0) multiplier = 1;
|
|
422
|
-
while (gcd(multiplier, NUMERIC_COHORT_SIZE) !== 1) {
|
|
423
|
-
multiplier = positiveMod(multiplier + 1, NUMERIC_COHORT_SIZE);
|
|
424
|
-
if (multiplier === 0) multiplier = 1;
|
|
425
|
-
}
|
|
426
|
-
const offset = positiveMod(hashString(`${bundleId}:offset`), NUMERIC_COHORT_SIZE);
|
|
427
|
-
return {
|
|
428
|
-
multiplier,
|
|
429
|
-
offset,
|
|
430
|
-
inverseMultiplier: modularInverse(multiplier, NUMERIC_COHORT_SIZE)
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
function normalizeRolloutCohortCount(rolloutCohortCount) {
|
|
434
|
-
if (rolloutCohortCount === null || rolloutCohortCount === void 0) return DEFAULT_ROLLOUT_COHORT_COUNT;
|
|
435
|
-
if (rolloutCohortCount <= 0) return 0;
|
|
436
|
-
if (rolloutCohortCount >= 1e3) return NUMERIC_COHORT_SIZE;
|
|
437
|
-
return Math.floor(rolloutCohortCount);
|
|
438
|
-
}
|
|
439
|
-
function normalizeCohortValue(cohort) {
|
|
440
|
-
const normalized = cohort.trim().toLowerCase();
|
|
441
|
-
const numericCohort = parseNumericCohortValue(normalized);
|
|
442
|
-
if (numericCohort !== null) return String(numericCohort);
|
|
443
|
-
return normalized;
|
|
444
|
-
}
|
|
445
|
-
function getNumericCohortValue(cohort) {
|
|
446
|
-
return parseNumericCohortValue(normalizeCohortValue(cohort));
|
|
447
|
-
}
|
|
448
|
-
function isNumericCohort(cohort) {
|
|
449
|
-
return getNumericCohortValue(cohort) !== null;
|
|
450
|
-
}
|
|
451
|
-
function isCustomCohort(cohort) {
|
|
452
|
-
const normalized = normalizeCohortValue(cohort);
|
|
453
|
-
return normalized.length > 0 && normalized.length <= 64 && !/^\d+$/.test(normalized) && CUSTOM_COHORT_PATTERN.test(normalized);
|
|
454
|
-
}
|
|
455
|
-
function isValidCohort(cohort) {
|
|
456
|
-
const normalized = normalizeCohortValue(cohort);
|
|
457
|
-
return isNumericCohort(normalized) || isCustomCohort(normalized);
|
|
458
|
-
}
|
|
459
|
-
function getNumericCohortRolloutPosition(bundleId, cohortValue) {
|
|
460
|
-
if (cohortValue < 1 || cohortValue > 1e3) throw new Error(`Invalid numeric cohort: ${cohortValue}`);
|
|
461
|
-
const { offset, inverseMultiplier } = getRolloutShuffleParameters(bundleId);
|
|
462
|
-
return positiveMod(inverseMultiplier * (cohortValue - 1 - offset), NUMERIC_COHORT_SIZE);
|
|
463
|
-
}
|
|
464
|
-
function getRolledOutNumericCohorts(bundleId, rolloutCohortCount) {
|
|
465
|
-
const normalizedRolloutCount = normalizeRolloutCohortCount(rolloutCohortCount);
|
|
466
|
-
if (normalizedRolloutCount <= 0) return [];
|
|
467
|
-
if (normalizedRolloutCount >= 1e3) return Array.from({ length: NUMERIC_COHORT_SIZE }, (_, index) => index + 1);
|
|
468
|
-
const { multiplier, offset } = getRolloutShuffleParameters(bundleId);
|
|
469
|
-
return Array.from({ length: normalizedRolloutCount }, (_, position) => positiveMod(multiplier * position + offset, NUMERIC_COHORT_SIZE)).map((zeroBasedCohort) => zeroBasedCohort + 1).sort((left, right) => left - right);
|
|
470
|
-
}
|
|
471
|
-
const RELEASE_CATALOG_FALLBACK_POLICY = "BUILTIN_IF_ACTIVE_INELIGIBLE";
|
|
472
|
-
const MAX_COMPILED_CATALOG_BYTES = 262144;
|
|
473
351
|
const BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
474
352
|
const MAX_CATALOG_SEGMENT_LENGTH = 255;
|
|
475
353
|
function assertCatalogSegment(value, name) {
|
|
@@ -594,6 +472,130 @@ const NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
|
594
472
|
const UUID_V7_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
595
473
|
const isUUIDv7 = (value) => typeof value === "string" && UUID_V7_PATTERN.test(value);
|
|
596
474
|
//#endregion
|
|
475
|
+
//#region ../../packages/protocol/dist/index.mjs
|
|
476
|
+
const getManifestStorageUri = (bundle) => bundle.manifestStorageUri;
|
|
477
|
+
const getManifestFileHash = (bundle) => bundle.manifestFileHash;
|
|
478
|
+
const getAssetBaseStorageUri = (bundle) => bundle.assetBaseStorageUri;
|
|
479
|
+
const isBundlePatchArtifact = (value) => {
|
|
480
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
481
|
+
const candidate = value;
|
|
482
|
+
return typeof candidate.baseBundleId === "string" && typeof candidate.baseFileHash === "string" && typeof candidate.patchFileHash === "string" && typeof candidate.patchStorageUri === "string";
|
|
483
|
+
};
|
|
484
|
+
const readBundlePatchArray = (patches) => {
|
|
485
|
+
if (!Array.isArray(patches)) return [];
|
|
486
|
+
return patches.filter(isBundlePatchArtifact);
|
|
487
|
+
};
|
|
488
|
+
const getBundlePatches = (bundle) => {
|
|
489
|
+
const patches = readBundlePatchArray(bundle.patches);
|
|
490
|
+
const seenBaseBundleIds = /* @__PURE__ */ new Set();
|
|
491
|
+
return patches.filter((patch) => {
|
|
492
|
+
if (seenBaseBundleIds.has(patch.baseBundleId)) return false;
|
|
493
|
+
seenBaseBundleIds.add(patch.baseBundleId);
|
|
494
|
+
return true;
|
|
495
|
+
});
|
|
496
|
+
};
|
|
497
|
+
const getBundlePatch = (bundle, baseBundleId) => {
|
|
498
|
+
return getBundlePatches(bundle).find((patch) => patch.baseBundleId === baseBundleId) ?? null;
|
|
499
|
+
};
|
|
500
|
+
const NUMERIC_COHORT_SIZE = 1e3;
|
|
501
|
+
const DEFAULT_ROLLOUT_COHORT_COUNT = NUMERIC_COHORT_SIZE;
|
|
502
|
+
const CUSTOM_COHORT_PATTERN = /^[a-z0-9-]+$/;
|
|
503
|
+
function parseNumericCohortValue(cohort) {
|
|
504
|
+
if (!/^\d+$/.test(cohort)) return null;
|
|
505
|
+
const parsed = Number.parseInt(cohort, 10);
|
|
506
|
+
if (Number.isNaN(parsed) || parsed < 1 || parsed > 1e3) return null;
|
|
507
|
+
return parsed;
|
|
508
|
+
}
|
|
509
|
+
function positiveMod(value, modulus) {
|
|
510
|
+
return (value % modulus + modulus) % modulus;
|
|
511
|
+
}
|
|
512
|
+
function hashString(value) {
|
|
513
|
+
let hash = 0;
|
|
514
|
+
for (let i = 0; i < value.length; i++) {
|
|
515
|
+
const char = value.charCodeAt(i);
|
|
516
|
+
hash = (hash << 5) - hash + char;
|
|
517
|
+
hash |= 0;
|
|
518
|
+
}
|
|
519
|
+
return hash;
|
|
520
|
+
}
|
|
521
|
+
function gcd(a, b) {
|
|
522
|
+
let x = Math.abs(a);
|
|
523
|
+
let y = Math.abs(b);
|
|
524
|
+
while (y !== 0) {
|
|
525
|
+
const next = x % y;
|
|
526
|
+
x = y;
|
|
527
|
+
y = next;
|
|
528
|
+
}
|
|
529
|
+
return x;
|
|
530
|
+
}
|
|
531
|
+
function modularInverse(value, modulus) {
|
|
532
|
+
let t = 0;
|
|
533
|
+
let newT = 1;
|
|
534
|
+
let r = modulus;
|
|
535
|
+
let newR = positiveMod(value, modulus);
|
|
536
|
+
while (newR !== 0) {
|
|
537
|
+
const quotient = Math.floor(r / newR);
|
|
538
|
+
[t, newT] = [newT, t - quotient * newT];
|
|
539
|
+
[r, newR] = [newR, r - quotient * newR];
|
|
540
|
+
}
|
|
541
|
+
if (r > 1) throw new Error(`No modular inverse for ${value} mod ${modulus}`);
|
|
542
|
+
return positiveMod(t, modulus);
|
|
543
|
+
}
|
|
544
|
+
function getRolloutShuffleParameters(bundleId) {
|
|
545
|
+
let multiplier = positiveMod(hashString(`${bundleId}:multiplier`), 997);
|
|
546
|
+
if (multiplier === 0) multiplier = 1;
|
|
547
|
+
while (gcd(multiplier, NUMERIC_COHORT_SIZE) !== 1) {
|
|
548
|
+
multiplier = positiveMod(multiplier + 1, NUMERIC_COHORT_SIZE);
|
|
549
|
+
if (multiplier === 0) multiplier = 1;
|
|
550
|
+
}
|
|
551
|
+
const offset = positiveMod(hashString(`${bundleId}:offset`), NUMERIC_COHORT_SIZE);
|
|
552
|
+
return {
|
|
553
|
+
multiplier,
|
|
554
|
+
offset,
|
|
555
|
+
inverseMultiplier: modularInverse(multiplier, NUMERIC_COHORT_SIZE)
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function normalizeRolloutCohortCount(rolloutCohortCount) {
|
|
559
|
+
if (rolloutCohortCount === null || rolloutCohortCount === void 0) return DEFAULT_ROLLOUT_COHORT_COUNT;
|
|
560
|
+
if (rolloutCohortCount <= 0) return 0;
|
|
561
|
+
if (rolloutCohortCount >= 1e3) return NUMERIC_COHORT_SIZE;
|
|
562
|
+
return Math.floor(rolloutCohortCount);
|
|
563
|
+
}
|
|
564
|
+
function normalizeCohortValue(cohort) {
|
|
565
|
+
const normalized = cohort.trim().toLowerCase();
|
|
566
|
+
const numericCohort = parseNumericCohortValue(normalized);
|
|
567
|
+
if (numericCohort !== null) return String(numericCohort);
|
|
568
|
+
return normalized;
|
|
569
|
+
}
|
|
570
|
+
function getNumericCohortValue(cohort) {
|
|
571
|
+
return parseNumericCohortValue(normalizeCohortValue(cohort));
|
|
572
|
+
}
|
|
573
|
+
function isNumericCohort(cohort) {
|
|
574
|
+
return getNumericCohortValue(cohort) !== null;
|
|
575
|
+
}
|
|
576
|
+
function isCustomCohort(cohort) {
|
|
577
|
+
const normalized = normalizeCohortValue(cohort);
|
|
578
|
+
return normalized.length > 0 && normalized.length <= 64 && !/^\d+$/.test(normalized) && CUSTOM_COHORT_PATTERN.test(normalized);
|
|
579
|
+
}
|
|
580
|
+
function isValidCohort(cohort) {
|
|
581
|
+
const normalized = normalizeCohortValue(cohort);
|
|
582
|
+
return isNumericCohort(normalized) || isCustomCohort(normalized);
|
|
583
|
+
}
|
|
584
|
+
function getNumericCohortRolloutPosition(bundleId, cohortValue) {
|
|
585
|
+
if (cohortValue < 1 || cohortValue > 1e3) throw new Error(`Invalid numeric cohort: ${cohortValue}`);
|
|
586
|
+
const { offset, inverseMultiplier } = getRolloutShuffleParameters(bundleId);
|
|
587
|
+
return positiveMod(inverseMultiplier * (cohortValue - 1 - offset), NUMERIC_COHORT_SIZE);
|
|
588
|
+
}
|
|
589
|
+
function getRolledOutNumericCohorts(bundleId, rolloutCohortCount) {
|
|
590
|
+
const normalizedRolloutCount = normalizeRolloutCohortCount(rolloutCohortCount);
|
|
591
|
+
if (normalizedRolloutCount <= 0) return [];
|
|
592
|
+
if (normalizedRolloutCount >= 1e3) return Array.from({ length: NUMERIC_COHORT_SIZE }, (_, index) => index + 1);
|
|
593
|
+
const { multiplier, offset } = getRolloutShuffleParameters(bundleId);
|
|
594
|
+
return Array.from({ length: normalizedRolloutCount }, (_, position) => positiveMod(multiplier * position + offset, NUMERIC_COHORT_SIZE)).map((zeroBasedCohort) => zeroBasedCohort + 1).sort((left, right) => left - right);
|
|
595
|
+
}
|
|
596
|
+
const RELEASE_CATALOG_FALLBACK_POLICY = "BUILTIN_IF_ACTIVE_INELIGIBLE";
|
|
597
|
+
const MAX_COMPILED_CATALOG_BYTES = 262144;
|
|
598
|
+
//#endregion
|
|
597
599
|
//#region ../plugin-core/dist/databaseRows.mjs
|
|
598
600
|
const releaseRowToRelease = (row) => ({
|
|
599
601
|
id: row.id,
|
|
@@ -1862,10 +1864,11 @@ const SETTINGS_TABLE = {
|
|
|
1862
1864
|
const ENGINE_SCHEMA_KEY = "schema.engine";
|
|
1863
1865
|
const CORE_SCHEMA_KEY = "schema.core";
|
|
1864
1866
|
const stored = ({ key, expected, found }) => `"${key}" is ${found === null ? "missing" : `"${found}"`}; expected "${expected}"`;
|
|
1865
|
-
const
|
|
1867
|
+
const MIGRATE_SCHEMA_FIX = "Run `npx hot-updater doctor` in the project whose hot-updater.config.ts names this database: it names the command that migrates it. In a server project, `hot-updater db migrate` migrates the database its server file names.";
|
|
1868
|
+
const settingMessage = (adapterName, { key, expected, found }) => `Hot Updater schema setting "${key}" for ${adapterName} is ${found === null ? "missing" : `"${found}"`}; expected "${expected}". ${MIGRATE_SCHEMA_FIX}`;
|
|
1866
1869
|
const pluginOf = ({ key }) => key.startsWith("schema.") && key !== "schema.engine" && key !== CORE_SCHEMA_KEY ? key.slice(7) : void 0;
|
|
1867
1870
|
const listed = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1)}`;
|
|
1868
|
-
const
|
|
1871
|
+
const settingsMessage = (adapterName, settings) => `Hot Updater schema settings for ${adapterName} are not migrated: ${settings.map(stored).join(", and ")}. ${MIGRATE_SCHEMA_FIX}`;
|
|
1869
1872
|
const pluginsMessage = (adapterName, plugins, settings, fix) => `The tables of ${plugins.length === 1 ? "plugin" : "plugins"} ${listed(plugins.map((id) => `"${id}"`))} are not migrated on ${adapterName}: schema ${settings.length === 1 ? "setting" : "settings"} ${settings.map(stored).join(", and ")}. ${fix}`;
|
|
1870
1873
|
var HotUpdaterSchemaMigrationRequiredError = class extends Error {
|
|
1871
1874
|
adapterName;
|
|
@@ -1876,7 +1879,7 @@ var HotUpdaterSchemaMigrationRequiredError = class extends Error {
|
|
|
1876
1879
|
constructor(adapterName, currentVersion, setting, options) {
|
|
1877
1880
|
const settings = options?.settings ?? (setting === void 0 ? [] : [setting]);
|
|
1878
1881
|
const plugins = settings.flatMap((mismatch) => pluginOf(mismatch) ?? []);
|
|
1879
|
-
super(setting === void 0 ? currentVersion === void 0 ? `Hot Updater database schema is not initialized for ${adapterName}.
|
|
1882
|
+
super(setting === void 0 ? currentVersion === void 0 ? `Hot Updater database schema is not initialized for ${adapterName}. ${MIGRATE_SCHEMA_FIX}` : `Hot Updater v1 cannot migrate schema ${currentVersion} in place. Create a new empty database and run \`hot-updater db migrate\`.` : plugins.length > 0 && plugins.length === settings.length ? pluginsMessage(adapterName, plugins, settings, options?.fix ?? MIGRATE_SCHEMA_FIX) : settings.length > 1 && !(setting.key === "schema.engine" && setting.found === null) ? settingsMessage(adapterName, settings) : settingMessage(adapterName, setting), options?.cause === void 0 ? void 0 : { cause: options.cause });
|
|
1880
1883
|
this.adapterName = adapterName;
|
|
1881
1884
|
this.currentVersion = currentVersion;
|
|
1882
1885
|
this.setting = setting;
|
|
@@ -1929,11 +1932,23 @@ const checkSchemaFence = async (adapter, adapterName, expected, fix) => {
|
|
|
1929
1932
|
});
|
|
1930
1933
|
};
|
|
1931
1934
|
const FENCED = Symbol.for("@hot-updater/server/schema-fence");
|
|
1935
|
+
const FENCED_SETTINGS = Symbol.for("@hot-updater/server/schema-fence-settings");
|
|
1932
1936
|
const fencedName = (adapter) => adapter[FENCED];
|
|
1933
1937
|
const withSchemaFence = (adapter, adapterName, expected, fix) => {
|
|
1938
|
+
const inner = adapter[FENCED_SETTINGS];
|
|
1939
|
+
const settings = inner === void 0 ? {
|
|
1940
|
+
adapter,
|
|
1941
|
+
expected
|
|
1942
|
+
} : {
|
|
1943
|
+
adapter: inner.adapter,
|
|
1944
|
+
expected: {
|
|
1945
|
+
...inner.expected,
|
|
1946
|
+
...expected
|
|
1947
|
+
}
|
|
1948
|
+
};
|
|
1934
1949
|
let ready;
|
|
1935
1950
|
const fence = () => {
|
|
1936
|
-
ready ??= checkSchemaFence(adapter, adapterName, expected, fix).catch((error) => {
|
|
1951
|
+
ready ??= checkSchemaFence(settings.adapter, adapterName, settings.expected, fix).catch((error) => {
|
|
1937
1952
|
ready = void 0;
|
|
1938
1953
|
throw error;
|
|
1939
1954
|
});
|
|
@@ -1942,6 +1957,7 @@ const withSchemaFence = (adapter, adapterName, expected, fix) => {
|
|
|
1942
1957
|
return {
|
|
1943
1958
|
...adapter,
|
|
1944
1959
|
[FENCED]: adapterName,
|
|
1960
|
+
[FENCED_SETTINGS]: settings,
|
|
1945
1961
|
get: (table, keys) => fence().then(() => adapter.get(table, keys)),
|
|
1946
1962
|
query: (table, request) => fence().then(() => adapter.query(table, request)),
|
|
1947
1963
|
write: (ops) => fence().then(() => adapter.write(ops)),
|
|
@@ -2131,257 +2147,360 @@ const CHANNEL_ID = {
|
|
|
2131
2147
|
maxLength: 1408,
|
|
2132
2148
|
ascii: true
|
|
2133
2149
|
};
|
|
2134
|
-
const HOT_UPDATER_SCHEMA_VERSION = "1.
|
|
2150
|
+
const HOT_UPDATER_SCHEMA_VERSION = "1.2.0";
|
|
2151
|
+
const bundles = defineTable({
|
|
2152
|
+
id: {
|
|
2153
|
+
type: "string",
|
|
2154
|
+
maxLength: 36
|
|
2155
|
+
},
|
|
2156
|
+
platform: {
|
|
2157
|
+
type: "string",
|
|
2158
|
+
maxLength: 16
|
|
2159
|
+
},
|
|
2160
|
+
git_commit_hash: {
|
|
2161
|
+
type: "string",
|
|
2162
|
+
maxLength: 255,
|
|
2163
|
+
required: false
|
|
2164
|
+
},
|
|
2165
|
+
metadata: { type: "json" },
|
|
2166
|
+
manifest_storage_uri: { type: "string" },
|
|
2167
|
+
manifest_file_hash: { type: "string" },
|
|
2168
|
+
asset_base_storage_uri: { type: "string" }
|
|
2169
|
+
}, {
|
|
2170
|
+
key: ["id"],
|
|
2171
|
+
indexes: {
|
|
2172
|
+
byPlatform: {
|
|
2173
|
+
eq: ["platform"],
|
|
2174
|
+
sort: ["id"]
|
|
2175
|
+
},
|
|
2176
|
+
all: {
|
|
2177
|
+
eq: [],
|
|
2178
|
+
sort: ["id"]
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
});
|
|
2182
|
+
const bundlePatches = defineTable({
|
|
2183
|
+
id: {
|
|
2184
|
+
type: "string",
|
|
2185
|
+
maxLength: 255
|
|
2186
|
+
},
|
|
2187
|
+
bundle_id: {
|
|
2188
|
+
type: "string",
|
|
2189
|
+
maxLength: 36,
|
|
2190
|
+
references: {
|
|
2191
|
+
model: "bundles",
|
|
2192
|
+
onDelete: "cascade"
|
|
2193
|
+
}
|
|
2194
|
+
},
|
|
2195
|
+
base_bundle_id: {
|
|
2196
|
+
type: "string",
|
|
2197
|
+
maxLength: 36,
|
|
2198
|
+
references: {
|
|
2199
|
+
model: "bundles",
|
|
2200
|
+
onDelete: "cascade"
|
|
2201
|
+
}
|
|
2202
|
+
},
|
|
2203
|
+
base_file_hash: { type: "string" },
|
|
2204
|
+
patch_file_hash: { type: "string" },
|
|
2205
|
+
patch_storage_uri: { type: "string" },
|
|
2206
|
+
byte_size: { type: "integer" },
|
|
2207
|
+
order_index: { type: "integer" }
|
|
2208
|
+
}, {
|
|
2209
|
+
key: ["id"],
|
|
2210
|
+
indexes: {
|
|
2211
|
+
pair: {
|
|
2212
|
+
eq: ["bundle_id", "base_bundle_id"],
|
|
2213
|
+
sort: [],
|
|
2214
|
+
unique: true
|
|
2215
|
+
},
|
|
2216
|
+
byBundle: {
|
|
2217
|
+
eq: ["bundle_id"],
|
|
2218
|
+
sort: ["order_index"],
|
|
2219
|
+
root: { model: "bundles" }
|
|
2220
|
+
},
|
|
2221
|
+
byBase: {
|
|
2222
|
+
eq: ["base_bundle_id"],
|
|
2223
|
+
sort: ["bundle_id"]
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
});
|
|
2227
|
+
const releases = defineTable({
|
|
2228
|
+
id: {
|
|
2229
|
+
type: "string",
|
|
2230
|
+
maxLength: 36
|
|
2231
|
+
},
|
|
2232
|
+
revision: { type: "integer" },
|
|
2233
|
+
scope_key: {
|
|
2234
|
+
type: "string",
|
|
2235
|
+
maxLength: 2048,
|
|
2236
|
+
ascii: true
|
|
2237
|
+
},
|
|
2238
|
+
channel_id: {
|
|
2239
|
+
...CHANNEL_ID,
|
|
2240
|
+
references: {
|
|
2241
|
+
model: "channels",
|
|
2242
|
+
onDelete: "restrict"
|
|
2243
|
+
}
|
|
2244
|
+
},
|
|
2245
|
+
platform: {
|
|
2246
|
+
type: "string",
|
|
2247
|
+
maxLength: 16
|
|
2248
|
+
},
|
|
2249
|
+
kind: {
|
|
2250
|
+
type: "string",
|
|
2251
|
+
maxLength: 16
|
|
2252
|
+
},
|
|
2253
|
+
bundle_id: {
|
|
2254
|
+
type: "string",
|
|
2255
|
+
maxLength: 36,
|
|
2256
|
+
required: false,
|
|
2257
|
+
references: {
|
|
2258
|
+
model: "bundles",
|
|
2259
|
+
onDelete: "restrict"
|
|
2260
|
+
}
|
|
2261
|
+
},
|
|
2262
|
+
strategy: {
|
|
2263
|
+
type: "string",
|
|
2264
|
+
maxLength: 16
|
|
2265
|
+
},
|
|
2266
|
+
target_app_version: {
|
|
2267
|
+
type: "string",
|
|
2268
|
+
required: false
|
|
2269
|
+
},
|
|
2270
|
+
fingerprint_hash: {
|
|
2271
|
+
type: "string",
|
|
2272
|
+
maxLength: 255,
|
|
2273
|
+
required: false
|
|
2274
|
+
},
|
|
2275
|
+
enabled: { type: "boolean" },
|
|
2276
|
+
should_force_update: { type: "boolean" },
|
|
2277
|
+
message: {
|
|
2278
|
+
type: "string",
|
|
2279
|
+
required: false
|
|
2280
|
+
},
|
|
2281
|
+
rollout_cohort_count: { type: "integer" },
|
|
2282
|
+
target_cohorts: { type: "json" },
|
|
2283
|
+
operation: {
|
|
2284
|
+
type: "string",
|
|
2285
|
+
maxLength: 16
|
|
2286
|
+
},
|
|
2287
|
+
source_release_id: {
|
|
2288
|
+
type: "string",
|
|
2289
|
+
maxLength: 36,
|
|
2290
|
+
required: false,
|
|
2291
|
+
references: {
|
|
2292
|
+
model: "releases",
|
|
2293
|
+
onDelete: "none"
|
|
2294
|
+
}
|
|
2295
|
+
},
|
|
2296
|
+
created_at_ms: { type: "integer" },
|
|
2297
|
+
updated_at_ms: { type: "integer" }
|
|
2298
|
+
}, {
|
|
2299
|
+
key: ["id"],
|
|
2300
|
+
indexes: {
|
|
2301
|
+
byScope: {
|
|
2302
|
+
eq: ["scope_key"],
|
|
2303
|
+
sort: ["id"],
|
|
2304
|
+
root: { model: "release_catalogs" }
|
|
2305
|
+
},
|
|
2306
|
+
byScopeEnabled: {
|
|
2307
|
+
eq: ["scope_key", "enabled"],
|
|
2308
|
+
sort: ["id"],
|
|
2309
|
+
root: { model: "release_catalogs" }
|
|
2310
|
+
},
|
|
2311
|
+
byChannelPlatform: {
|
|
2312
|
+
eq: ["channel_id", "platform"],
|
|
2313
|
+
sort: ["id"]
|
|
2314
|
+
},
|
|
2315
|
+
byChannelPlatformEnabled: {
|
|
2316
|
+
eq: [
|
|
2317
|
+
"channel_id",
|
|
2318
|
+
"platform",
|
|
2319
|
+
"enabled"
|
|
2320
|
+
],
|
|
2321
|
+
sort: ["id"]
|
|
2322
|
+
},
|
|
2323
|
+
byBundle: {
|
|
2324
|
+
eq: ["bundle_id"],
|
|
2325
|
+
sort: ["id"]
|
|
2326
|
+
},
|
|
2327
|
+
all: {
|
|
2328
|
+
eq: [],
|
|
2329
|
+
sort: ["id"]
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
});
|
|
2333
|
+
const releaseCatalogs = defineTable({
|
|
2334
|
+
scope_key: {
|
|
2335
|
+
type: "string",
|
|
2336
|
+
maxLength: 2048,
|
|
2337
|
+
ascii: true
|
|
2338
|
+
},
|
|
2339
|
+
catalog_id: {
|
|
2340
|
+
type: "string",
|
|
2341
|
+
maxLength: 255
|
|
2342
|
+
},
|
|
2343
|
+
strategy: {
|
|
2344
|
+
type: "string",
|
|
2345
|
+
maxLength: 16
|
|
2346
|
+
},
|
|
2347
|
+
channel_id: {
|
|
2348
|
+
...CHANNEL_ID,
|
|
2349
|
+
references: {
|
|
2350
|
+
model: "channels",
|
|
2351
|
+
onDelete: "none"
|
|
2352
|
+
}
|
|
2353
|
+
},
|
|
2354
|
+
channel_key: {
|
|
2355
|
+
type: "string",
|
|
2356
|
+
maxLength: 1400,
|
|
2357
|
+
ascii: true
|
|
2358
|
+
},
|
|
2359
|
+
platform: {
|
|
2360
|
+
type: "string",
|
|
2361
|
+
maxLength: 16
|
|
2362
|
+
},
|
|
2363
|
+
fingerprint_hash: {
|
|
2364
|
+
type: "string",
|
|
2365
|
+
maxLength: 255,
|
|
2366
|
+
required: false
|
|
2367
|
+
},
|
|
2368
|
+
generation: { type: "integer" },
|
|
2369
|
+
payload: { type: "string" },
|
|
2370
|
+
catalog_hash: {
|
|
2371
|
+
type: "string",
|
|
2372
|
+
maxLength: 71
|
|
2373
|
+
},
|
|
2374
|
+
byte_size: { type: "integer" },
|
|
2375
|
+
is_tombstone: { type: "boolean" },
|
|
2376
|
+
updated_at_ms: { type: "integer" }
|
|
2377
|
+
}, {
|
|
2378
|
+
key: ["scope_key"],
|
|
2379
|
+
indexes: { all: {
|
|
2380
|
+
eq: [],
|
|
2381
|
+
sort: ["scope_key"]
|
|
2382
|
+
} }
|
|
2383
|
+
});
|
|
2384
|
+
const channels = defineTable({
|
|
2385
|
+
id: CHANNEL_ID,
|
|
2386
|
+
name: {
|
|
2387
|
+
type: "string",
|
|
2388
|
+
maxLength: 255,
|
|
2389
|
+
unique: true
|
|
2390
|
+
}
|
|
2391
|
+
}, {
|
|
2392
|
+
key: ["id"],
|
|
2393
|
+
indexes: { all: {
|
|
2394
|
+
eq: [],
|
|
2395
|
+
sort: ["name"]
|
|
2396
|
+
} }
|
|
2397
|
+
});
|
|
2398
|
+
const nativeBuilds = defineTable({
|
|
2399
|
+
id: {
|
|
2400
|
+
type: "string",
|
|
2401
|
+
maxLength: 36
|
|
2402
|
+
},
|
|
2403
|
+
scope_key: {
|
|
2404
|
+
type: "string",
|
|
2405
|
+
maxLength: 2048,
|
|
2406
|
+
ascii: true
|
|
2407
|
+
},
|
|
2408
|
+
platform: {
|
|
2409
|
+
type: "string",
|
|
2410
|
+
maxLength: 16
|
|
2411
|
+
},
|
|
2412
|
+
channel_id: {
|
|
2413
|
+
...CHANNEL_ID,
|
|
2414
|
+
references: {
|
|
2415
|
+
model: "channels",
|
|
2416
|
+
onDelete: "none"
|
|
2417
|
+
}
|
|
2418
|
+
},
|
|
2419
|
+
fingerprint_hash: {
|
|
2420
|
+
type: "string",
|
|
2421
|
+
maxLength: 255,
|
|
2422
|
+
required: false
|
|
2423
|
+
},
|
|
2424
|
+
app_version: {
|
|
2425
|
+
type: "string",
|
|
2426
|
+
required: false
|
|
2427
|
+
},
|
|
2428
|
+
min_bundle_id: {
|
|
2429
|
+
type: "string",
|
|
2430
|
+
maxLength: 36,
|
|
2431
|
+
unique: true,
|
|
2432
|
+
references: {
|
|
2433
|
+
model: "bundles",
|
|
2434
|
+
onDelete: "none"
|
|
2435
|
+
}
|
|
2436
|
+
},
|
|
2437
|
+
artifact_kind: {
|
|
2438
|
+
type: "string",
|
|
2439
|
+
maxLength: 16
|
|
2440
|
+
},
|
|
2441
|
+
artifact_storage_uri: { type: "string" },
|
|
2442
|
+
byte_size: { type: "integer" },
|
|
2443
|
+
git_commit_hash: {
|
|
2444
|
+
type: "string",
|
|
2445
|
+
maxLength: 255,
|
|
2446
|
+
required: false
|
|
2447
|
+
},
|
|
2448
|
+
created_at_ms: { type: "integer" },
|
|
2449
|
+
build_key: {
|
|
2450
|
+
type: "string",
|
|
2451
|
+
maxLength: 69,
|
|
2452
|
+
ascii: true,
|
|
2453
|
+
required: false
|
|
2454
|
+
},
|
|
2455
|
+
build_number: {
|
|
2456
|
+
type: "string",
|
|
2457
|
+
maxLength: 64,
|
|
2458
|
+
required: false
|
|
2459
|
+
},
|
|
2460
|
+
submitted_to: {
|
|
2461
|
+
type: "string",
|
|
2462
|
+
maxLength: 16,
|
|
2463
|
+
required: false
|
|
2464
|
+
},
|
|
2465
|
+
submitted_at_ms: {
|
|
2466
|
+
type: "integer",
|
|
2467
|
+
required: false
|
|
2468
|
+
},
|
|
2469
|
+
submitted_version: {
|
|
2470
|
+
type: "string",
|
|
2471
|
+
maxLength: 255,
|
|
2472
|
+
required: false
|
|
2473
|
+
}
|
|
2474
|
+
}, {
|
|
2475
|
+
key: ["id"],
|
|
2476
|
+
indexes: {
|
|
2477
|
+
byScope: {
|
|
2478
|
+
eq: ["scope_key"],
|
|
2479
|
+
sort: ["id"]
|
|
2480
|
+
},
|
|
2481
|
+
byBuildKey: {
|
|
2482
|
+
eq: ["scope_key", "build_key"],
|
|
2483
|
+
sort: ["id"]
|
|
2484
|
+
},
|
|
2485
|
+
byChannel: {
|
|
2486
|
+
eq: ["channel_id"],
|
|
2487
|
+
sort: ["id"],
|
|
2488
|
+
root: { model: "channels" }
|
|
2489
|
+
},
|
|
2490
|
+
all: {
|
|
2491
|
+
eq: [],
|
|
2492
|
+
sort: ["id"]
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
});
|
|
2135
2496
|
const coreModule = {
|
|
2136
2497
|
id: "core",
|
|
2137
2498
|
schema: {
|
|
2138
|
-
bundles
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
platform: {
|
|
2144
|
-
type: "string",
|
|
2145
|
-
maxLength: 16
|
|
2146
|
-
},
|
|
2147
|
-
git_commit_hash: {
|
|
2148
|
-
type: "string",
|
|
2149
|
-
maxLength: 255,
|
|
2150
|
-
required: false
|
|
2151
|
-
},
|
|
2152
|
-
metadata: { type: "json" },
|
|
2153
|
-
manifest_storage_uri: { type: "string" },
|
|
2154
|
-
manifest_file_hash: { type: "string" },
|
|
2155
|
-
asset_base_storage_uri: { type: "string" }
|
|
2156
|
-
}, {
|
|
2157
|
-
key: ["id"],
|
|
2158
|
-
indexes: {
|
|
2159
|
-
byPlatform: {
|
|
2160
|
-
eq: ["platform"],
|
|
2161
|
-
sort: ["id"]
|
|
2162
|
-
},
|
|
2163
|
-
all: {
|
|
2164
|
-
eq: [],
|
|
2165
|
-
sort: ["id"]
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
}),
|
|
2169
|
-
bundle_patches: defineTable({
|
|
2170
|
-
id: {
|
|
2171
|
-
type: "string",
|
|
2172
|
-
maxLength: 255
|
|
2173
|
-
},
|
|
2174
|
-
bundle_id: {
|
|
2175
|
-
type: "string",
|
|
2176
|
-
maxLength: 36,
|
|
2177
|
-
references: {
|
|
2178
|
-
model: "bundles",
|
|
2179
|
-
onDelete: "cascade"
|
|
2180
|
-
}
|
|
2181
|
-
},
|
|
2182
|
-
base_bundle_id: {
|
|
2183
|
-
type: "string",
|
|
2184
|
-
maxLength: 36,
|
|
2185
|
-
references: {
|
|
2186
|
-
model: "bundles",
|
|
2187
|
-
onDelete: "cascade"
|
|
2188
|
-
}
|
|
2189
|
-
},
|
|
2190
|
-
base_file_hash: { type: "string" },
|
|
2191
|
-
patch_file_hash: { type: "string" },
|
|
2192
|
-
patch_storage_uri: { type: "string" },
|
|
2193
|
-
byte_size: { type: "integer" },
|
|
2194
|
-
order_index: { type: "integer" }
|
|
2195
|
-
}, {
|
|
2196
|
-
key: ["id"],
|
|
2197
|
-
indexes: {
|
|
2198
|
-
pair: {
|
|
2199
|
-
eq: ["bundle_id", "base_bundle_id"],
|
|
2200
|
-
sort: [],
|
|
2201
|
-
unique: true
|
|
2202
|
-
},
|
|
2203
|
-
byBundle: {
|
|
2204
|
-
eq: ["bundle_id"],
|
|
2205
|
-
sort: ["order_index"],
|
|
2206
|
-
root: { model: "bundles" }
|
|
2207
|
-
},
|
|
2208
|
-
byBase: {
|
|
2209
|
-
eq: ["base_bundle_id"],
|
|
2210
|
-
sort: ["bundle_id"]
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
}),
|
|
2214
|
-
releases: defineTable({
|
|
2215
|
-
id: {
|
|
2216
|
-
type: "string",
|
|
2217
|
-
maxLength: 36
|
|
2218
|
-
},
|
|
2219
|
-
revision: { type: "integer" },
|
|
2220
|
-
scope_key: {
|
|
2221
|
-
type: "string",
|
|
2222
|
-
maxLength: 2048,
|
|
2223
|
-
ascii: true
|
|
2224
|
-
},
|
|
2225
|
-
channel_id: {
|
|
2226
|
-
...CHANNEL_ID,
|
|
2227
|
-
references: {
|
|
2228
|
-
model: "channels",
|
|
2229
|
-
onDelete: "restrict"
|
|
2230
|
-
}
|
|
2231
|
-
},
|
|
2232
|
-
platform: {
|
|
2233
|
-
type: "string",
|
|
2234
|
-
maxLength: 16
|
|
2235
|
-
},
|
|
2236
|
-
kind: {
|
|
2237
|
-
type: "string",
|
|
2238
|
-
maxLength: 16
|
|
2239
|
-
},
|
|
2240
|
-
bundle_id: {
|
|
2241
|
-
type: "string",
|
|
2242
|
-
maxLength: 36,
|
|
2243
|
-
required: false,
|
|
2244
|
-
references: {
|
|
2245
|
-
model: "bundles",
|
|
2246
|
-
onDelete: "restrict"
|
|
2247
|
-
}
|
|
2248
|
-
},
|
|
2249
|
-
strategy: {
|
|
2250
|
-
type: "string",
|
|
2251
|
-
maxLength: 16
|
|
2252
|
-
},
|
|
2253
|
-
target_app_version: {
|
|
2254
|
-
type: "string",
|
|
2255
|
-
required: false
|
|
2256
|
-
},
|
|
2257
|
-
fingerprint_hash: {
|
|
2258
|
-
type: "string",
|
|
2259
|
-
maxLength: 255,
|
|
2260
|
-
required: false
|
|
2261
|
-
},
|
|
2262
|
-
enabled: { type: "boolean" },
|
|
2263
|
-
should_force_update: { type: "boolean" },
|
|
2264
|
-
message: {
|
|
2265
|
-
type: "string",
|
|
2266
|
-
required: false
|
|
2267
|
-
},
|
|
2268
|
-
rollout_cohort_count: { type: "integer" },
|
|
2269
|
-
target_cohorts: { type: "json" },
|
|
2270
|
-
operation: {
|
|
2271
|
-
type: "string",
|
|
2272
|
-
maxLength: 16
|
|
2273
|
-
},
|
|
2274
|
-
source_release_id: {
|
|
2275
|
-
type: "string",
|
|
2276
|
-
maxLength: 36,
|
|
2277
|
-
required: false,
|
|
2278
|
-
references: {
|
|
2279
|
-
model: "releases",
|
|
2280
|
-
onDelete: "none"
|
|
2281
|
-
}
|
|
2282
|
-
},
|
|
2283
|
-
created_at_ms: { type: "integer" },
|
|
2284
|
-
updated_at_ms: { type: "integer" }
|
|
2285
|
-
}, {
|
|
2286
|
-
key: ["id"],
|
|
2287
|
-
indexes: {
|
|
2288
|
-
byScope: {
|
|
2289
|
-
eq: ["scope_key"],
|
|
2290
|
-
sort: ["id"],
|
|
2291
|
-
root: { model: "release_catalogs" }
|
|
2292
|
-
},
|
|
2293
|
-
byScopeEnabled: {
|
|
2294
|
-
eq: ["scope_key", "enabled"],
|
|
2295
|
-
sort: ["id"],
|
|
2296
|
-
root: { model: "release_catalogs" }
|
|
2297
|
-
},
|
|
2298
|
-
byChannelPlatform: {
|
|
2299
|
-
eq: ["channel_id", "platform"],
|
|
2300
|
-
sort: ["id"]
|
|
2301
|
-
},
|
|
2302
|
-
byChannelPlatformEnabled: {
|
|
2303
|
-
eq: [
|
|
2304
|
-
"channel_id",
|
|
2305
|
-
"platform",
|
|
2306
|
-
"enabled"
|
|
2307
|
-
],
|
|
2308
|
-
sort: ["id"]
|
|
2309
|
-
},
|
|
2310
|
-
byBundle: {
|
|
2311
|
-
eq: ["bundle_id"],
|
|
2312
|
-
sort: ["id"]
|
|
2313
|
-
},
|
|
2314
|
-
all: {
|
|
2315
|
-
eq: [],
|
|
2316
|
-
sort: ["id"]
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
}),
|
|
2320
|
-
release_catalogs: defineTable({
|
|
2321
|
-
scope_key: {
|
|
2322
|
-
type: "string",
|
|
2323
|
-
maxLength: 2048,
|
|
2324
|
-
ascii: true
|
|
2325
|
-
},
|
|
2326
|
-
catalog_id: {
|
|
2327
|
-
type: "string",
|
|
2328
|
-
maxLength: 255
|
|
2329
|
-
},
|
|
2330
|
-
strategy: {
|
|
2331
|
-
type: "string",
|
|
2332
|
-
maxLength: 16
|
|
2333
|
-
},
|
|
2334
|
-
channel_id: {
|
|
2335
|
-
...CHANNEL_ID,
|
|
2336
|
-
references: {
|
|
2337
|
-
model: "channels",
|
|
2338
|
-
onDelete: "none"
|
|
2339
|
-
}
|
|
2340
|
-
},
|
|
2341
|
-
channel_key: {
|
|
2342
|
-
type: "string",
|
|
2343
|
-
maxLength: 1400,
|
|
2344
|
-
ascii: true
|
|
2345
|
-
},
|
|
2346
|
-
platform: {
|
|
2347
|
-
type: "string",
|
|
2348
|
-
maxLength: 16
|
|
2349
|
-
},
|
|
2350
|
-
fingerprint_hash: {
|
|
2351
|
-
type: "string",
|
|
2352
|
-
maxLength: 255,
|
|
2353
|
-
required: false
|
|
2354
|
-
},
|
|
2355
|
-
generation: { type: "integer" },
|
|
2356
|
-
payload: { type: "string" },
|
|
2357
|
-
catalog_hash: {
|
|
2358
|
-
type: "string",
|
|
2359
|
-
maxLength: 71
|
|
2360
|
-
},
|
|
2361
|
-
byte_size: { type: "integer" },
|
|
2362
|
-
is_tombstone: { type: "boolean" },
|
|
2363
|
-
updated_at_ms: { type: "integer" }
|
|
2364
|
-
}, {
|
|
2365
|
-
key: ["scope_key"],
|
|
2366
|
-
indexes: { all: {
|
|
2367
|
-
eq: [],
|
|
2368
|
-
sort: ["scope_key"]
|
|
2369
|
-
} }
|
|
2370
|
-
}),
|
|
2371
|
-
channels: defineTable({
|
|
2372
|
-
id: CHANNEL_ID,
|
|
2373
|
-
name: {
|
|
2374
|
-
type: "string",
|
|
2375
|
-
maxLength: 255,
|
|
2376
|
-
unique: true
|
|
2377
|
-
}
|
|
2378
|
-
}, {
|
|
2379
|
-
key: ["id"],
|
|
2380
|
-
indexes: { all: {
|
|
2381
|
-
eq: [],
|
|
2382
|
-
sort: ["name"]
|
|
2383
|
-
} }
|
|
2384
|
-
}),
|
|
2499
|
+
bundles,
|
|
2500
|
+
bundle_patches: bundlePatches,
|
|
2501
|
+
releases,
|
|
2502
|
+
release_catalogs: releaseCatalogs,
|
|
2503
|
+
channels,
|
|
2385
2504
|
bundle_totals: defineAggregate({ platform_key: {
|
|
2386
2505
|
type: "string",
|
|
2387
2506
|
maxLength: 16
|
|
@@ -2392,7 +2511,8 @@ const coreModule = {
|
|
|
2392
2511
|
eq: ["platform_key"],
|
|
2393
2512
|
sort: []
|
|
2394
2513
|
} }
|
|
2395
|
-
})
|
|
2514
|
+
}),
|
|
2515
|
+
native_builds: nativeBuilds
|
|
2396
2516
|
}
|
|
2397
2517
|
};
|
|
2398
2518
|
//#endregion
|
|
@@ -3361,14 +3481,14 @@ const createEngineMigrator = (options) => {
|
|
|
3361
3481
|
return {
|
|
3362
3482
|
getVersion,
|
|
3363
3483
|
getNameVariants: async () => void 0,
|
|
3364
|
-
next: async () => await getVersion() === "1.
|
|
3484
|
+
next: async () => await getVersion() === "1.2.0" ? void 0 : { version: HOT_UPDATER_SCHEMA_VERSION },
|
|
3365
3485
|
previous: async () => void 0,
|
|
3366
3486
|
up: makeResult,
|
|
3367
3487
|
down: async () => {
|
|
3368
3488
|
throw new Error("No previous schema to migrate to.");
|
|
3369
3489
|
},
|
|
3370
3490
|
migrateTo: async (version, migrate) => {
|
|
3371
|
-
if (version !== "1.
|
|
3491
|
+
if (version !== "1.2.0") throw new Error(`Invalid version ${version}`);
|
|
3372
3492
|
return makeResult(migrate);
|
|
3373
3493
|
},
|
|
3374
3494
|
migrateToLatest: makeResult
|
|
@@ -4056,9 +4176,9 @@ const createDatabaseEngine = ({ batching, now = Date.now, meter, ...options }) =
|
|
|
4056
4176
|
//#region ../plugin-core/dist/engine/createEngine.mjs
|
|
4057
4177
|
const pluginTablesFix = (database) => {
|
|
4058
4178
|
const { createMigrator, generateSchema } = database;
|
|
4059
|
-
const
|
|
4179
|
+
const doctor = "Run `npx hot-updater doctor` in the project whose hot-updater.config.ts names this database: it names the command that migrates it.";
|
|
4060
4180
|
const serverProject = "Run `hot-updater db` commands in the server's project: they load the server file named on the command line, or src/hotUpdater.* or src/db.*, and don't read hot-updater.config.ts.";
|
|
4061
|
-
return createMigrator !== void 0 ?
|
|
4181
|
+
return createMigrator !== void 0 ? `${doctor} In a server project, run \`hot-updater db migrate\`. ${serverProject}` : generateSchema !== void 0 ? `${doctor} On a database \`hot-updater init\` created, that is init again with its saved inputs; for your own server, run \`hot-updater db generate\` and apply the file it writes. ${serverProject}` : `${doctor} On a database \`hot-updater init\` created, that is init again with its saved inputs.`;
|
|
4062
4182
|
};
|
|
4063
4183
|
const createEngine = (database, { plugins = [], now = Date.now } = {}) => {
|
|
4064
4184
|
const modules = /* @__PURE__ */ new Map();
|
|
@@ -17545,20 +17665,32 @@ var require_lib$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
17545
17665
|
//#region ../postgres/dist/index.mjs
|
|
17546
17666
|
var import_lib$1 = /* @__PURE__ */ __toESM(require_lib$2(), 1);
|
|
17547
17667
|
const { Pool } = import_lib$1.default;
|
|
17668
|
+
const DATABASE_TARGET = Symbol.for("@hot-updater/database-target");
|
|
17669
|
+
const targetOf = ({ connectionString, host, port, database }) => {
|
|
17670
|
+
if (connectionString !== void 0) try {
|
|
17671
|
+
const url = new URL(connectionString);
|
|
17672
|
+
return `postgres/${url.hostname}:${url.port || "5432"}/${decodeURIComponent(url.pathname.slice(1))}`;
|
|
17673
|
+
} catch {
|
|
17674
|
+
return;
|
|
17675
|
+
}
|
|
17676
|
+
return host === void 0 || database === void 0 ? void 0 : `postgres/${host}:${port ?? 5432}/${database}`;
|
|
17677
|
+
};
|
|
17548
17678
|
const postgres = (config) => {
|
|
17549
17679
|
const { dialect, ...poolConfig } = config;
|
|
17680
|
+
const target = targetOf(poolConfig);
|
|
17550
17681
|
const db = new Kysely({ dialect: dialect ?? new PostgresDialect({ pool: new Pool(poolConfig) }) });
|
|
17551
17682
|
return {
|
|
17552
17683
|
...createEngineDatabase({
|
|
17553
17684
|
name: "postgres",
|
|
17554
17685
|
adapter: createSqlAdapter({ executor: kyselyExecutor(db, "postgresql") })
|
|
17555
17686
|
}),
|
|
17687
|
+
...target === void 0 ? {} : { [DATABASE_TARGET]: target },
|
|
17556
17688
|
dispose: () => db.destroy()
|
|
17557
17689
|
};
|
|
17558
17690
|
};
|
|
17559
17691
|
//#endregion
|
|
17560
17692
|
//#region ../../packages/server/dist/version.mjs
|
|
17561
|
-
const HOT_UPDATER_SERVER_VERSION = "1.
|
|
17693
|
+
const HOT_UPDATER_SERVER_VERSION = "1.2.0-rc.0";
|
|
17562
17694
|
//#endregion
|
|
17563
17695
|
//#region ../../packages/server/dist/handlerVersionRoutes.mjs
|
|
17564
17696
|
const createVersionRouteHandlers = (plugins = []) => {
|
|
@@ -18291,8 +18423,8 @@ const baseBundleIdsOf = (row, range, bundleId, limit) => {
|
|
|
18291
18423
|
//#endregion
|
|
18292
18424
|
//#region ../../packages/server/dist/core/reads.mjs
|
|
18293
18425
|
const PAGE$3 = 500;
|
|
18294
|
-
const pick = (fields) => (row) => Object.fromEntries(fields.map((field) => [field, row[field]]));
|
|
18295
|
-
const toBundleRow = pick([
|
|
18426
|
+
const pick$1 = (fields) => (row) => Object.fromEntries(fields.map((field) => [field, row[field]]));
|
|
18427
|
+
const toBundleRow = pick$1([
|
|
18296
18428
|
"id",
|
|
18297
18429
|
"platform",
|
|
18298
18430
|
"git_commit_hash",
|
|
@@ -18301,7 +18433,7 @@ const toBundleRow = pick([
|
|
|
18301
18433
|
"manifest_file_hash",
|
|
18302
18434
|
"asset_base_storage_uri"
|
|
18303
18435
|
]);
|
|
18304
|
-
const toPatchRow = pick([
|
|
18436
|
+
const toPatchRow = pick$1([
|
|
18305
18437
|
"id",
|
|
18306
18438
|
"bundle_id",
|
|
18307
18439
|
"base_bundle_id",
|
|
@@ -18311,7 +18443,7 @@ const toPatchRow = pick([
|
|
|
18311
18443
|
"byte_size",
|
|
18312
18444
|
"order_index"
|
|
18313
18445
|
]);
|
|
18314
|
-
const toReleaseRow = pick([
|
|
18446
|
+
const toReleaseRow = pick$1([
|
|
18315
18447
|
"id",
|
|
18316
18448
|
"revision",
|
|
18317
18449
|
"scope_key",
|
|
@@ -18332,7 +18464,7 @@ const toReleaseRow = pick([
|
|
|
18332
18464
|
"created_at_ms",
|
|
18333
18465
|
"updated_at_ms"
|
|
18334
18466
|
]);
|
|
18335
|
-
const toCatalogRow = pick([
|
|
18467
|
+
const toCatalogRow = pick$1([
|
|
18336
18468
|
"scope_key",
|
|
18337
18469
|
"catalog_id",
|
|
18338
18470
|
"strategy",
|
|
@@ -18347,7 +18479,7 @@ const toCatalogRow = pick([
|
|
|
18347
18479
|
"is_tombstone",
|
|
18348
18480
|
"updated_at_ms"
|
|
18349
18481
|
]);
|
|
18350
|
-
const toChannelRow = pick(["id", "name"]);
|
|
18482
|
+
const toChannelRow = pick$1(["id", "name"]);
|
|
18351
18483
|
const drain$1 = async (read) => {
|
|
18352
18484
|
const rows = [];
|
|
18353
18485
|
let cursor;
|
|
@@ -18358,6 +18490,7 @@ const drain$1 = async (read) => {
|
|
|
18358
18490
|
} while (cursor !== void 0);
|
|
18359
18491
|
return rows;
|
|
18360
18492
|
};
|
|
18493
|
+
const after = (input) => input.after === void 0 ? {} : { range: (input.order ?? "asc") === "asc" ? { gt: input.after } : { lt: input.after } };
|
|
18361
18494
|
const createCoreReads = (db, storage) => {
|
|
18362
18495
|
const detail = async (row) => {
|
|
18363
18496
|
const count = row._refs_bundle_patches_bundle_id ?? 0;
|
|
@@ -18380,7 +18513,6 @@ const createCoreReads = (db, storage) => {
|
|
|
18380
18513
|
childCount: row._refs_bundle_patches_base_bundle_id ?? 0
|
|
18381
18514
|
};
|
|
18382
18515
|
};
|
|
18383
|
-
const after = (input) => input.after === void 0 ? {} : { range: (input.order ?? "asc") === "asc" ? { gt: input.after } : { lt: input.after } };
|
|
18384
18516
|
return {
|
|
18385
18517
|
async getReleaseCatalog(input) {
|
|
18386
18518
|
const scopeKey = releaseCatalogScopeKeyOf(input);
|
|
@@ -18558,9 +18690,9 @@ const insertBundle = (tx, row) => {
|
|
|
18558
18690
|
tx.create("bundles", row);
|
|
18559
18691
|
countBundle(tx, row.platform, 1);
|
|
18560
18692
|
};
|
|
18561
|
-
const updateBundle = (tx, current, set) => {
|
|
18693
|
+
const updateBundle = (tx, current, set, { counted = true } = {}) => {
|
|
18562
18694
|
tx.update("bundles", current, set);
|
|
18563
|
-
if (set.platform !== void 0 && set.platform !== current.platform) {
|
|
18695
|
+
if (counted && set.platform !== void 0 && set.platform !== current.platform) {
|
|
18564
18696
|
countBundle(tx, current.platform, -1);
|
|
18565
18697
|
countBundle(tx, set.platform, 1);
|
|
18566
18698
|
}
|
|
@@ -18625,10 +18757,223 @@ const deleteChannel = (db, id) => db.transaction(async (tx) => {
|
|
|
18625
18757
|
deleted: false,
|
|
18626
18758
|
reason: "not_empty"
|
|
18627
18759
|
};
|
|
18760
|
+
const { rows: builds } = await tx.findMany("native_builds", {
|
|
18761
|
+
index: "byChannel",
|
|
18762
|
+
where: { channel_id: id },
|
|
18763
|
+
limit: 1
|
|
18764
|
+
});
|
|
18765
|
+
if (builds.length > 0) return {
|
|
18766
|
+
deleted: false,
|
|
18767
|
+
reason: "not_empty"
|
|
18768
|
+
};
|
|
18628
18769
|
await tx.delete("channels", row);
|
|
18629
18770
|
return { deleted: true };
|
|
18630
18771
|
});
|
|
18631
18772
|
//#endregion
|
|
18773
|
+
//#region ../../packages/server/dist/core/nativeBuilds.mjs
|
|
18774
|
+
const ARTIFACT_KINDS = {
|
|
18775
|
+
android: ["apk", "aab"],
|
|
18776
|
+
ios: ["ipa", "app"]
|
|
18777
|
+
};
|
|
18778
|
+
const SCOPE_KEY_MAX_LENGTH = 2048;
|
|
18779
|
+
const GIT_COMMIT_HASH_MAX_LENGTH = 255;
|
|
18780
|
+
const BUILD_KEY = /^nbk1:[0-9a-f]{64}$/;
|
|
18781
|
+
var NativeBuildInputError = class extends TypeError {
|
|
18782
|
+
name = "NativeBuildInputError";
|
|
18783
|
+
};
|
|
18784
|
+
const pick = (row) => ({
|
|
18785
|
+
id: row.id,
|
|
18786
|
+
scope_key: row.scope_key,
|
|
18787
|
+
platform: row.platform,
|
|
18788
|
+
channel_id: row.channel_id,
|
|
18789
|
+
fingerprint_hash: row.fingerprint_hash ?? null,
|
|
18790
|
+
app_version: row.app_version ?? null,
|
|
18791
|
+
min_bundle_id: row.min_bundle_id,
|
|
18792
|
+
artifact_kind: row.artifact_kind,
|
|
18793
|
+
artifact_storage_uri: row.artifact_storage_uri,
|
|
18794
|
+
byte_size: row.byte_size,
|
|
18795
|
+
git_commit_hash: row.git_commit_hash ?? null,
|
|
18796
|
+
created_at_ms: row.created_at_ms,
|
|
18797
|
+
build_key: row.build_key ?? null,
|
|
18798
|
+
build_number: row.build_number ?? null,
|
|
18799
|
+
submitted_to: row.submitted_to ?? null,
|
|
18800
|
+
submitted_at_ms: row.submitted_at_ms ?? null,
|
|
18801
|
+
submitted_version: row.submitted_version ?? null
|
|
18802
|
+
});
|
|
18803
|
+
const SUBMISSION_DESTINATIONS = {
|
|
18804
|
+
android: "play-internal",
|
|
18805
|
+
ios: "testflight"
|
|
18806
|
+
};
|
|
18807
|
+
const BUILD_NUMBER_MAX_LENGTH = 64;
|
|
18808
|
+
const SUBMITTED_VERSION_MAX_LENGTH = 255;
|
|
18809
|
+
const nativeBuildScopeKey = (scope) => {
|
|
18810
|
+
let scopeKey;
|
|
18811
|
+
try {
|
|
18812
|
+
const channelKey = encodeChannelKey(scope.channel);
|
|
18813
|
+
scopeKey = scope.fingerprintHash === null ? `${createReleaseCatalogScopeKey({
|
|
18814
|
+
channelKey,
|
|
18815
|
+
platform: scope.platform,
|
|
18816
|
+
strategy: "APP_VERSION"
|
|
18817
|
+
})}:${encodeURIComponent(scope.appVersion)}` : createReleaseCatalogScopeKey({
|
|
18818
|
+
channelKey,
|
|
18819
|
+
fingerprintHash: scope.fingerprintHash,
|
|
18820
|
+
platform: scope.platform,
|
|
18821
|
+
strategy: "FINGERPRINT"
|
|
18822
|
+
});
|
|
18823
|
+
} catch (error) {
|
|
18824
|
+
throw new NativeBuildInputError(`Invalid native build scope: ${error instanceof Error ? error.message : String(error)}.`);
|
|
18825
|
+
}
|
|
18826
|
+
if (scopeKey.length > SCOPE_KEY_MAX_LENGTH) throw new NativeBuildInputError(`Invalid native build scope: its key is longer than ${SCOPE_KEY_MAX_LENGTH} characters; shorten the channel or app version.`);
|
|
18827
|
+
return scopeKey;
|
|
18828
|
+
};
|
|
18829
|
+
const isBuiltInBundle = async (db, bundleId) => await db.findOne("native_builds", { min_bundle_id: bundleId }) !== null;
|
|
18830
|
+
const problemOf = ({ id, minBundleId, platform, channel, fingerprintHash, appVersion, gitCommitHash, buildKey, buildNumber, artifact }) => {
|
|
18831
|
+
if (!isUUIDv7(id)) return "id must be a UUIDv7";
|
|
18832
|
+
if (!isUUIDv7(minBundleId)) return "minBundleId must be the UUIDv7 a release build compiles in";
|
|
18833
|
+
if (platform !== "ios" && platform !== "android") return "platform must be ios or android";
|
|
18834
|
+
if (channel.trim().length === 0) return "channel is required";
|
|
18835
|
+
if (gitCommitHash !== null && gitCommitHash.length > GIT_COMMIT_HASH_MAX_LENGTH) return `gitCommitHash must be at most ${GIT_COMMIT_HASH_MAX_LENGTH} characters`;
|
|
18836
|
+
if (fingerprintHash === null === (appVersion === null)) return "exactly one of fingerprintHash and appVersion is required";
|
|
18837
|
+
if (buildKey !== null && !BUILD_KEY.test(buildKey)) return "buildKey must be nbk1: and a SHA-256, or null";
|
|
18838
|
+
if (buildNumber !== void 0 && buildNumber !== null && (buildNumber.trim().length === 0 || buildNumber.length > BUILD_NUMBER_MAX_LENGTH)) return `buildNumber must be the artifact's build number, up to ${BUILD_NUMBER_MAX_LENGTH} characters, or null`;
|
|
18839
|
+
const kinds = ARTIFACT_KINDS[platform];
|
|
18840
|
+
if (!kinds.includes(artifact.kind)) return `artifact.kind of an ${platform} build must be ${kinds.join(" or ")}`;
|
|
18841
|
+
if (!Number.isSafeInteger(artifact.byteSize) || artifact.byteSize < 0) return "artifact.byteSize must be a byte count";
|
|
18842
|
+
return null;
|
|
18843
|
+
};
|
|
18844
|
+
const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
18845
|
+
async registerNativeBuild(registration) {
|
|
18846
|
+
const problem = problemOf(registration);
|
|
18847
|
+
if (problem !== null) throw new NativeBuildInputError(`Invalid native build: ${problem}.`);
|
|
18848
|
+
const scopeKey = nativeBuildScopeKey(registration);
|
|
18849
|
+
const channelId = `channel:${encodeChannelKey(registration.channel)}`;
|
|
18850
|
+
const { embeddedBundle } = registration;
|
|
18851
|
+
const row = {
|
|
18852
|
+
id: registration.id,
|
|
18853
|
+
scope_key: scopeKey,
|
|
18854
|
+
platform: registration.platform,
|
|
18855
|
+
channel_id: channelId,
|
|
18856
|
+
fingerprint_hash: registration.fingerprintHash,
|
|
18857
|
+
app_version: registration.appVersion,
|
|
18858
|
+
min_bundle_id: registration.minBundleId,
|
|
18859
|
+
artifact_kind: registration.artifact.kind,
|
|
18860
|
+
artifact_storage_uri: registration.artifact.storageUri,
|
|
18861
|
+
byte_size: registration.artifact.byteSize,
|
|
18862
|
+
git_commit_hash: registration.gitCommitHash,
|
|
18863
|
+
created_at_ms: now(),
|
|
18864
|
+
build_key: registration.buildKey,
|
|
18865
|
+
build_number: registration.buildNumber ?? null,
|
|
18866
|
+
submitted_to: null,
|
|
18867
|
+
submitted_at_ms: null,
|
|
18868
|
+
submitted_version: null
|
|
18869
|
+
};
|
|
18870
|
+
for (let attempt = 1;; attempt += 1) {
|
|
18871
|
+
await insertChannel(db, {
|
|
18872
|
+
id: channelId,
|
|
18873
|
+
name: registration.channel
|
|
18874
|
+
});
|
|
18875
|
+
try {
|
|
18876
|
+
await db.transaction(async (tx) => {
|
|
18877
|
+
tx.create("bundles", {
|
|
18878
|
+
id: registration.minBundleId,
|
|
18879
|
+
platform: registration.platform,
|
|
18880
|
+
git_commit_hash: registration.gitCommitHash,
|
|
18881
|
+
metadata: embeddedBundle.metadata ?? {},
|
|
18882
|
+
manifest_storage_uri: embeddedBundle.manifestStorageUri,
|
|
18883
|
+
manifest_file_hash: embeddedBundle.manifestFileHash,
|
|
18884
|
+
asset_base_storage_uri: embeddedBundle.assetBaseStorageUri
|
|
18885
|
+
});
|
|
18886
|
+
tx.create("native_builds", row);
|
|
18887
|
+
});
|
|
18888
|
+
break;
|
|
18889
|
+
} catch (error) {
|
|
18890
|
+
if (!(error instanceof DatabaseConstraintError)) throw error;
|
|
18891
|
+
if (error.reason === "exists" || error.reason === "unique") throw new DatabaseConstraintError(error.reason, "native_builds");
|
|
18892
|
+
if (attempt < 3 && error.reason === "not_found" && error.model === "channels") continue;
|
|
18893
|
+
throw error;
|
|
18894
|
+
}
|
|
18895
|
+
}
|
|
18896
|
+
return row;
|
|
18897
|
+
},
|
|
18898
|
+
async getNativeBuild(id) {
|
|
18899
|
+
const row = await db.findOne("native_builds", { id });
|
|
18900
|
+
return row === null ? null : pick(row);
|
|
18901
|
+
},
|
|
18902
|
+
async findNativeBuild(scope, options) {
|
|
18903
|
+
const scopeKey = nativeBuildScopeKey(scope);
|
|
18904
|
+
const buildKey = options?.buildKey;
|
|
18905
|
+
if (buildKey !== void 0 && !BUILD_KEY.test(buildKey)) throw new NativeBuildInputError("Invalid native build key: it must be nbk1: and a SHA-256.");
|
|
18906
|
+
const { rows } = buildKey === void 0 ? await db.findMany("native_builds", {
|
|
18907
|
+
index: "byScope",
|
|
18908
|
+
where: { scope_key: scopeKey },
|
|
18909
|
+
order: "desc",
|
|
18910
|
+
limit: 1
|
|
18911
|
+
}) : await db.findMany("native_builds", {
|
|
18912
|
+
index: "byBuildKey",
|
|
18913
|
+
where: {
|
|
18914
|
+
scope_key: scopeKey,
|
|
18915
|
+
build_key: buildKey
|
|
18916
|
+
},
|
|
18917
|
+
order: "desc",
|
|
18918
|
+
limit: 1
|
|
18919
|
+
});
|
|
18920
|
+
return rows[0] === void 0 ? null : pick(rows[0]);
|
|
18921
|
+
},
|
|
18922
|
+
async recordNativeBuildSubmission(id, { destination, version }) {
|
|
18923
|
+
if (version.trim().length === 0 || version.length > SUBMITTED_VERSION_MAX_LENGTH) throw new NativeBuildInputError(`Invalid native build submission: version must be the store's label of the build, up to ${SUBMITTED_VERSION_MAX_LENGTH} characters.`);
|
|
18924
|
+
return db.transaction(async (tx) => {
|
|
18925
|
+
const row = await tx.findOne("native_builds", { id });
|
|
18926
|
+
if (row === null) return null;
|
|
18927
|
+
const build = pick(row);
|
|
18928
|
+
if (SUBMISSION_DESTINATIONS[build.platform] !== destination) throw new NativeBuildInputError(`Invalid native build submission: an ${build.platform} build is submitted to ${SUBMISSION_DESTINATIONS[build.platform]}, not ${destination}.`);
|
|
18929
|
+
const set = {
|
|
18930
|
+
submitted_to: destination,
|
|
18931
|
+
submitted_at_ms: now(),
|
|
18932
|
+
submitted_version: version
|
|
18933
|
+
};
|
|
18934
|
+
tx.update("native_builds", row, set);
|
|
18935
|
+
return {
|
|
18936
|
+
...build,
|
|
18937
|
+
...set
|
|
18938
|
+
};
|
|
18939
|
+
});
|
|
18940
|
+
},
|
|
18941
|
+
async deleteNativeBuild(id) {
|
|
18942
|
+
return db.transaction(async (tx) => {
|
|
18943
|
+
const row = await tx.findOne("native_builds", { id });
|
|
18944
|
+
if (row === null) return {
|
|
18945
|
+
deleted: false,
|
|
18946
|
+
reason: "not_found"
|
|
18947
|
+
};
|
|
18948
|
+
await tx.delete("native_builds", row);
|
|
18949
|
+
const bundle = await tx.findOne("bundles", { id: row.min_bundle_id });
|
|
18950
|
+
if (bundle !== null) await tx.delete("bundles", bundle);
|
|
18951
|
+
return {
|
|
18952
|
+
deleted: true,
|
|
18953
|
+
nativeBuild: pick(row)
|
|
18954
|
+
};
|
|
18955
|
+
});
|
|
18956
|
+
},
|
|
18957
|
+
async listNativeBuilds(input) {
|
|
18958
|
+
const page = {
|
|
18959
|
+
order: input.order ?? "asc",
|
|
18960
|
+
limit: input.limit
|
|
18961
|
+
};
|
|
18962
|
+
const { rows } = input.channelId === void 0 ? await db.findMany("native_builds", {
|
|
18963
|
+
index: "all",
|
|
18964
|
+
where: {},
|
|
18965
|
+
...page,
|
|
18966
|
+
...after(input)
|
|
18967
|
+
}) : await db.findMany("native_builds", {
|
|
18968
|
+
index: "byChannel",
|
|
18969
|
+
where: { channel_id: input.channelId },
|
|
18970
|
+
...page,
|
|
18971
|
+
...after(input)
|
|
18972
|
+
});
|
|
18973
|
+
return rows.map(pick);
|
|
18974
|
+
}
|
|
18975
|
+
});
|
|
18976
|
+
//#endregion
|
|
18632
18977
|
//#region ../../packages/server/dist/core/releases.mjs
|
|
18633
18978
|
const PAGE$1 = 500;
|
|
18634
18979
|
const fail$1 = (code, message) => {
|
|
@@ -18915,6 +19260,7 @@ const createCoreOperations = (db, { now = Date.now } = {}) => {
|
|
|
18915
19260
|
if (!("bundleId" in deployment) || platforms.has(deployment.bundleId)) continue;
|
|
18916
19261
|
const row = await db.findOne("bundles", { id: deployment.bundleId });
|
|
18917
19262
|
if (row === null) throw new DatabaseBundleNotFoundError(deployment.bundleId);
|
|
19263
|
+
if (await isBuiltInBundle(db, deployment.bundleId)) throw new ReleaseManagementError("TARGET_RELEASE_INVALID", `Bundle "${deployment.bundleId}" is a native build's built-in bundle, which no release publishes.`);
|
|
18918
19264
|
platforms.set(deployment.bundleId, toBundleRow(row).platform);
|
|
18919
19265
|
}
|
|
18920
19266
|
const updatedAtMs = now();
|
|
@@ -19110,7 +19456,8 @@ const createCoreOperations = (db, { now = Date.now } = {}) => {
|
|
|
19110
19456
|
id
|
|
19111
19457
|
};
|
|
19112
19458
|
const { id: _id, ...set } = bundleToRow(next);
|
|
19113
|
-
|
|
19459
|
+
const counted = set.platform === current.platform || !await isBuiltInBundle(tx, id);
|
|
19460
|
+
updateBundle(tx, current, set, { counted });
|
|
19114
19461
|
if (patches !== void 0) await replaceBundlePatches(tx, current, bundleToPatchRows({
|
|
19115
19462
|
...next,
|
|
19116
19463
|
patches
|
|
@@ -19121,7 +19468,9 @@ const createCoreOperations = (db, { now = Date.now } = {}) => {
|
|
|
19121
19468
|
await db.transaction(async (tx) => {
|
|
19122
19469
|
for (const id of new Set(ids)) {
|
|
19123
19470
|
const current = await tx.findOne("bundles", { id });
|
|
19124
|
-
if (current
|
|
19471
|
+
if (current === null) continue;
|
|
19472
|
+
if (await isBuiltInBundle(tx, id)) throw new DatabaseRowReferencedError();
|
|
19473
|
+
await deleteBundle(tx, current);
|
|
19125
19474
|
}
|
|
19126
19475
|
});
|
|
19127
19476
|
} catch (error) {
|
|
@@ -19167,6 +19516,7 @@ const createCoreApi = (db, storage, options = {}) => {
|
|
|
19167
19516
|
return {
|
|
19168
19517
|
...reads,
|
|
19169
19518
|
...createCoreOperations(purgingCachedRoutes(db, options.onCachedRoutesChange), options),
|
|
19519
|
+
...createNativeBuildApi(db, options),
|
|
19170
19520
|
ready: async () => {
|
|
19171
19521
|
await reads.listReleaseCatalogs({ limit: 1 });
|
|
19172
19522
|
}
|
|
@@ -19358,6 +19708,10 @@ const answer = async (operation) => {
|
|
|
19358
19708
|
code: "REFERENCED",
|
|
19359
19709
|
error: error.message
|
|
19360
19710
|
}, 409);
|
|
19711
|
+
if (error instanceof DatabaseConstraintError && error.model === "native_builds") return json$2({
|
|
19712
|
+
code: "NATIVE_BUILD_EXISTS",
|
|
19713
|
+
error: "A native build with this ID or built-in bundle ID is already registered."
|
|
19714
|
+
}, 409);
|
|
19361
19715
|
throw error;
|
|
19362
19716
|
}
|
|
19363
19717
|
};
|
|
@@ -19453,6 +19807,40 @@ const bundleUpdateOf = (value, bundleId) => {
|
|
|
19453
19807
|
if (id !== void 0 && id !== bundleId) throw new HandlerBadRequestError("Bundle id mismatch");
|
|
19454
19808
|
return update;
|
|
19455
19809
|
};
|
|
19810
|
+
const NATIVE_BUILD_ARTIFACT_KINDS = /* @__PURE__ */ new Set([
|
|
19811
|
+
"apk",
|
|
19812
|
+
"aab",
|
|
19813
|
+
"ipa",
|
|
19814
|
+
"app"
|
|
19815
|
+
]);
|
|
19816
|
+
const nativeBuildScopeOf = (url) => {
|
|
19817
|
+
const params = url.searchParams;
|
|
19818
|
+
const platform = platformOf(url);
|
|
19819
|
+
const channel = params.get("channel");
|
|
19820
|
+
const fingerprintHash = params.get("fingerprintHash");
|
|
19821
|
+
const appVersion = params.get("appVersion");
|
|
19822
|
+
if (platform === void 0 || !isText$2(channel) || fingerprintHash === null === (appVersion === null) || fingerprintHash === "" || appVersion === "") throw new HandlerBadRequestError("A native build lookup needs platform, channel, and fingerprintHash or appVersion.");
|
|
19823
|
+
return fingerprintHash !== null ? {
|
|
19824
|
+
platform,
|
|
19825
|
+
channel,
|
|
19826
|
+
fingerprintHash,
|
|
19827
|
+
appVersion: null
|
|
19828
|
+
} : {
|
|
19829
|
+
platform,
|
|
19830
|
+
channel,
|
|
19831
|
+
fingerprintHash: null,
|
|
19832
|
+
appVersion
|
|
19833
|
+
};
|
|
19834
|
+
};
|
|
19835
|
+
const refusingInvalid = async (operation) => {
|
|
19836
|
+
try {
|
|
19837
|
+
return await operation();
|
|
19838
|
+
} catch (error) {
|
|
19839
|
+
if (error instanceof NativeBuildInputError) throw new HandlerBadRequestError(error.message);
|
|
19840
|
+
throw error;
|
|
19841
|
+
}
|
|
19842
|
+
};
|
|
19843
|
+
const isNativeBuildRegistration = (value) => isRecord$5(value) && isText$2(value.id) && isText$2(value.minBundleId) && typeof value.platform === "string" && isPlatform(value.platform) && isText$2(value.channel) && isTextOrNull$1(value.fingerprintHash) && isTextOrNull$1(value.appVersion) && value.fingerprintHash === null !== (value.appVersion === null) && isTextOrNull$1(value.gitCommitHash) && isTextOrNull$1(value.buildKey) && (value.buildNumber === void 0 || isTextOrNull$1(value.buildNumber)) && isRecord$5(value.artifact) && typeof value.artifact.kind === "string" && NATIVE_BUILD_ARTIFACT_KINDS.has(value.artifact.kind) && isText$2(value.artifact.storageUri) && Number.isSafeInteger(value.artifact.byteSize) && value.artifact.byteSize >= 0 && isRecord$5(value.embeddedBundle) && isText$2(value.embeddedBundle.manifestStorageUri) && isText$2(value.embeddedBundle.manifestFileHash) && isText$2(value.embeddedBundle.assetBaseStorageUri) && (value.embeddedBundle.metadata === void 0 || isRecord$5(value.embeddedBundle.metadata));
|
|
19456
19844
|
const scopeKeyOf = (params) => decodeMaybe(requireRouteParam(params, "scopeKey")) ?? "";
|
|
19457
19845
|
const createAdminRouteHandlers = () => ({
|
|
19458
19846
|
listBundles: onCore(async (core, request) => {
|
|
@@ -19567,6 +19955,37 @@ const createAdminRouteHandlers = () => ({
|
|
|
19567
19955
|
}) });
|
|
19568
19956
|
}),
|
|
19569
19957
|
preflightReleaseCatalogRebuild: onCore(async (core, _request, params) => json$2({ data: await core.preflightReleaseCatalogRebuild(scopeKeyOf(params)) })),
|
|
19958
|
+
registerNativeBuild: onCore(async (core, request) => {
|
|
19959
|
+
const input = await body(request);
|
|
19960
|
+
if (!isNativeBuildRegistration(input)) throw new HandlerBadRequestError("Invalid native build");
|
|
19961
|
+
return json$2({ data: await refusingInvalid(() => core.registerNativeBuild(input)) }, 201);
|
|
19962
|
+
}),
|
|
19963
|
+
findNativeBuild: onCore(async (core, request) => {
|
|
19964
|
+
const url = new URL(request.url);
|
|
19965
|
+
const scope = nativeBuildScopeOf(url);
|
|
19966
|
+
const buildKey = url.searchParams.get("buildKey");
|
|
19967
|
+
return json$2({ data: await refusingInvalid(() => buildKey === null ? core.findNativeBuild(scope) : core.findNativeBuild(scope, { buildKey })) });
|
|
19968
|
+
}),
|
|
19969
|
+
getNativeBuild: onCore(async (core, _request, params) => json$2({ data: await core.getNativeBuild(requireRouteParam(params, "id")) })),
|
|
19970
|
+
listNativeBuilds: onCore(async (core, request) => {
|
|
19971
|
+
const url = new URL(request.url);
|
|
19972
|
+
const input = keyset(url);
|
|
19973
|
+
const channelId = url.searchParams.get("channelId");
|
|
19974
|
+
return page(await core.listNativeBuilds({
|
|
19975
|
+
...input,
|
|
19976
|
+
...channelId === null ? {} : { channelId }
|
|
19977
|
+
}), input, ({ id }) => id);
|
|
19978
|
+
}),
|
|
19979
|
+
recordNativeBuildSubmission: onCore(async (core, request, params) => {
|
|
19980
|
+
const input = await body(request);
|
|
19981
|
+
if (!isRecord$5(input) || input.destination !== "testflight" && input.destination !== "play-internal" || !isText$2(input.version)) throw new HandlerBadRequestError("A native build submission needs destination (testflight or play-internal) and version.");
|
|
19982
|
+
const submission = {
|
|
19983
|
+
destination: input.destination,
|
|
19984
|
+
version: input.version
|
|
19985
|
+
};
|
|
19986
|
+
return json$2({ data: await refusingInvalid(() => core.recordNativeBuildSubmission(requireRouteParam(params, "id"), submission)) });
|
|
19987
|
+
}),
|
|
19988
|
+
deleteNativeBuild: onCore(async (core, _request, params) => json$2({ data: await core.deleteNativeBuild(requireRouteParam(params, "id")) })),
|
|
19570
19989
|
deleteBundles: onCore(async (core, request) => {
|
|
19571
19990
|
const { ids } = await body(request);
|
|
19572
19991
|
if (!Array.isArray(ids) || !ids.every((id) => typeof id === "string" && id.length > 0)) throw new HandlerBadRequestError("Bundle deletion needs ids");
|
|
@@ -19684,6 +20103,36 @@ const ADMIN_ROUTES = [
|
|
|
19684
20103
|
method: "GET",
|
|
19685
20104
|
path: "/base-candidates/:candidateKey",
|
|
19686
20105
|
handler: "findBaseCandidates"
|
|
20106
|
+
},
|
|
20107
|
+
{
|
|
20108
|
+
method: "POST",
|
|
20109
|
+
path: "/native-builds",
|
|
20110
|
+
handler: "registerNativeBuild"
|
|
20111
|
+
},
|
|
20112
|
+
{
|
|
20113
|
+
method: "GET",
|
|
20114
|
+
path: "/native-builds/latest",
|
|
20115
|
+
handler: "findNativeBuild"
|
|
20116
|
+
},
|
|
20117
|
+
{
|
|
20118
|
+
method: "GET",
|
|
20119
|
+
path: "/native-builds/:id",
|
|
20120
|
+
handler: "getNativeBuild"
|
|
20121
|
+
},
|
|
20122
|
+
{
|
|
20123
|
+
method: "GET",
|
|
20124
|
+
path: "/native-builds",
|
|
20125
|
+
handler: "listNativeBuilds"
|
|
20126
|
+
},
|
|
20127
|
+
{
|
|
20128
|
+
method: "POST",
|
|
20129
|
+
path: "/native-builds/:id/submission",
|
|
20130
|
+
handler: "recordNativeBuildSubmission"
|
|
20131
|
+
},
|
|
20132
|
+
{
|
|
20133
|
+
method: "DELETE",
|
|
20134
|
+
path: "/native-builds/:id",
|
|
20135
|
+
handler: "deleteNativeBuild"
|
|
19687
20136
|
}
|
|
19688
20137
|
];
|
|
19689
20138
|
//#endregion
|
|
@@ -53537,7 +53986,8 @@ const storeIdOf = (token) => {
|
|
|
53537
53986
|
const access = "private";
|
|
53538
53987
|
const vercelBlobStorage = (config) => {
|
|
53539
53988
|
const { token } = config;
|
|
53540
|
-
|
|
53989
|
+
let store;
|
|
53990
|
+
const storeId = () => store ??= storeIdOf(token);
|
|
53541
53991
|
const normalizedBasePath = config.basePath?.replace(/^\/+|\/+$/g, "") ?? "";
|
|
53542
53992
|
const getStorageKey = createStorageKeyBuilder(normalizedBasePath);
|
|
53543
53993
|
const expiresIn = config.signedUrlExpiresIn ?? 3600;
|
|
@@ -53557,7 +54007,7 @@ const vercelBlobStorage = (config) => {
|
|
|
53557
54007
|
};
|
|
53558
54008
|
const parseAndValidate = (storageUri) => {
|
|
53559
54009
|
const parsed = parseStorageUri(storageUri, PROTOCOL);
|
|
53560
|
-
if (parsed.bucket !== storeId) throw new Error(`Blob store mismatch: expected "${storeId}", but found "${parsed.bucket}".`);
|
|
54010
|
+
if (parsed.bucket !== storeId()) throw new Error(`Blob store mismatch: expected "${storeId()}", but found "${parsed.bucket}".`);
|
|
53561
54011
|
return parsed;
|
|
53562
54012
|
};
|
|
53563
54013
|
const getListPrefix = (prefix = "") => {
|
|
@@ -53576,7 +54026,7 @@ const vercelBlobStorage = (config) => {
|
|
|
53576
54026
|
const storageKey = getStorageKey(key);
|
|
53577
54027
|
const storageUri = createStorageUri({
|
|
53578
54028
|
protocol: PROTOCOL,
|
|
53579
|
-
bucket: storeId,
|
|
54029
|
+
bucket: storeId(),
|
|
53580
54030
|
key: storageKey
|
|
53581
54031
|
});
|
|
53582
54032
|
await put(toPathname(storageKey), body, {
|
|
@@ -53648,7 +54098,7 @@ const vercelBlobStorage = (config) => {
|
|
|
53648
54098
|
try {
|
|
53649
54099
|
storageUri = createStorageUri({
|
|
53650
54100
|
protocol: PROTOCOL,
|
|
53651
|
-
bucket: storeId,
|
|
54101
|
+
bucket: storeId(),
|
|
53652
54102
|
key
|
|
53653
54103
|
});
|
|
53654
54104
|
} catch {
|