@hot-updater/cloudflare 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/index.cjs +49 -29
- package/dist/index.d.cts +11 -6
- package/dist/index.d.mts +11 -6
- package/dist/index.mjs +49 -29
- package/dist/init/index.cjs +49 -29
- package/dist/init/index.mjs +49 -29
- package/package.json +6 -6
- package/sql/bundles.sql +11 -1
- package/src/d1Database.ts +29 -69
- package/src/d1Rest.ts +54 -0
- package/src/d1Schema.ts +33 -3
- package/worker/dist/README.md +1 -1
- package/worker/dist/index.js +865 -425
- package/worker/dist/index.js.map +4 -4
- package/worker/migrations/0002_hot-updater_1.2.0.sql +21 -0
package/worker/dist/index.js
CHANGED
|
@@ -214,7 +214,7 @@ var bundleMetadataToRow = /* @__PURE__ */ __name((metadata) => {
|
|
|
214
214
|
return metadata;
|
|
215
215
|
}, "bundleMetadataToRow");
|
|
216
216
|
|
|
217
|
-
// ../../packages/protocol/dist/
|
|
217
|
+
// ../../packages/protocol/dist/uuid-kZvLA0Ly.mjs
|
|
218
218
|
var LETTER_DASH_NUMBER = "[a-zA-Z0-9-]";
|
|
219
219
|
var NUMERIC_IDENTIFIER = String.raw`0|[1-9]\d*`;
|
|
220
220
|
var NUMERIC_IDENTIFIER_LOOSE = String.raw`\d+`;
|
|
@@ -330,6 +330,142 @@ function canonicalizeAppVersion(appVersion) {
|
|
|
330
330
|
return version2 ? normalizeFull(version2) : null;
|
|
331
331
|
}
|
|
332
332
|
__name(canonicalizeAppVersion, "canonicalizeAppVersion");
|
|
333
|
+
var BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
334
|
+
var MAX_CATALOG_SEGMENT_LENGTH = 255;
|
|
335
|
+
function assertCatalogSegment(value, name) {
|
|
336
|
+
if (value.length === 0 || value.length > MAX_CATALOG_SEGMENT_LENGTH || !/^[A-Za-z0-9._~-]+$/.test(value)) throw new Error(`${name} must be 1-${MAX_CATALOG_SEGMENT_LENGTH} URL-safe ASCII characters`);
|
|
337
|
+
}
|
|
338
|
+
__name(assertCatalogSegment, "assertCatalogSegment");
|
|
339
|
+
function encodeUtf8(value) {
|
|
340
|
+
const bytes = [];
|
|
341
|
+
for (const codePointText of value) {
|
|
342
|
+
const codePoint = codePointText.codePointAt(0);
|
|
343
|
+
if (codePoint <= 127) bytes.push(codePoint);
|
|
344
|
+
else if (codePoint <= 2047) bytes.push(192 | codePoint >> 6, 128 | codePoint & 63);
|
|
345
|
+
else if (codePoint <= 65535) bytes.push(224 | codePoint >> 12, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
|
|
346
|
+
else bytes.push(240 | codePoint >> 18, 128 | codePoint >> 12 & 63, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
|
|
347
|
+
}
|
|
348
|
+
return bytes;
|
|
349
|
+
}
|
|
350
|
+
__name(encodeUtf8, "encodeUtf8");
|
|
351
|
+
function decodeUtf8(bytes) {
|
|
352
|
+
let result = "";
|
|
353
|
+
for (let index = 0; index < bytes.length; ) {
|
|
354
|
+
const first = bytes[index++];
|
|
355
|
+
let codePoint;
|
|
356
|
+
let continuationCount;
|
|
357
|
+
if (first <= 127) {
|
|
358
|
+
codePoint = first;
|
|
359
|
+
continuationCount = 0;
|
|
360
|
+
} else if ((first & 224) === 192) {
|
|
361
|
+
codePoint = first & 31;
|
|
362
|
+
continuationCount = 1;
|
|
363
|
+
} else if ((first & 240) === 224) {
|
|
364
|
+
codePoint = first & 15;
|
|
365
|
+
continuationCount = 2;
|
|
366
|
+
} else if ((first & 248) === 240) {
|
|
367
|
+
codePoint = first & 7;
|
|
368
|
+
continuationCount = 3;
|
|
369
|
+
} else throw new Error("Invalid UTF-8 channel key");
|
|
370
|
+
for (let offset = 0; offset < continuationCount; offset += 1) {
|
|
371
|
+
const continuation = bytes[index++];
|
|
372
|
+
if (continuation === void 0 || (continuation & 192) !== 128) throw new Error("Invalid UTF-8 channel key");
|
|
373
|
+
codePoint = codePoint << 6 | continuation & 63;
|
|
374
|
+
}
|
|
375
|
+
if (codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343 || continuationCount === 1 && codePoint < 128 || continuationCount === 2 && codePoint < 2048 || continuationCount === 3 && codePoint < 65536) throw new Error("Invalid UTF-8 channel key");
|
|
376
|
+
result += String.fromCodePoint(codePoint);
|
|
377
|
+
}
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
__name(decodeUtf8, "decodeUtf8");
|
|
381
|
+
function base64UrlEncode(bytes) {
|
|
382
|
+
let result = "";
|
|
383
|
+
for (let index = 0; index < bytes.length; index += 3) {
|
|
384
|
+
const first = bytes[index];
|
|
385
|
+
const second = bytes[index + 1];
|
|
386
|
+
const third = bytes[index + 2];
|
|
387
|
+
const value = first << 16 | (second ?? 0) << 8 | (third ?? 0);
|
|
388
|
+
result += BASE64URL_ALPHABET[value >> 18 & 63];
|
|
389
|
+
result += BASE64URL_ALPHABET[value >> 12 & 63];
|
|
390
|
+
if (second !== void 0) result += BASE64URL_ALPHABET[value >> 6 & 63];
|
|
391
|
+
if (third !== void 0) result += BASE64URL_ALPHABET[value & 63];
|
|
392
|
+
}
|
|
393
|
+
return result;
|
|
394
|
+
}
|
|
395
|
+
__name(base64UrlEncode, "base64UrlEncode");
|
|
396
|
+
function base64UrlDecode(value) {
|
|
397
|
+
if (!/^[A-Za-z0-9_-]*$/.test(value) || value.length % 4 === 1) throw new Error("Invalid channel key");
|
|
398
|
+
const bytes = [];
|
|
399
|
+
for (let index = 0; index < value.length; index += 4) {
|
|
400
|
+
const chunk = value.slice(index, index + 4);
|
|
401
|
+
const encoded = [...chunk].map((character) => BASE64URL_ALPHABET.indexOf(character));
|
|
402
|
+
if (encoded.some((part) => part < 0)) throw new Error("Invalid channel key");
|
|
403
|
+
const bits = encoded[0] << 18 | encoded[1] << 12 | (encoded[2] ?? 0) << 6 | (encoded[3] ?? 0);
|
|
404
|
+
bytes.push(bits >> 16 & 255);
|
|
405
|
+
if (chunk.length >= 3) bytes.push(bits >> 8 & 255);
|
|
406
|
+
if (chunk.length === 4) bytes.push(bits & 255);
|
|
407
|
+
}
|
|
408
|
+
return bytes;
|
|
409
|
+
}
|
|
410
|
+
__name(base64UrlDecode, "base64UrlDecode");
|
|
411
|
+
function normalizeChannelName(channel) {
|
|
412
|
+
return channel.trim().normalize("NFC");
|
|
413
|
+
}
|
|
414
|
+
__name(normalizeChannelName, "normalizeChannelName");
|
|
415
|
+
function assertCanonicalChannelName(channel) {
|
|
416
|
+
const normalized = normalizeChannelName(channel);
|
|
417
|
+
if (normalized.length === 0 || normalized !== channel) throw new Error("Channel name must be non-empty, trimmed, and NFC-normalized");
|
|
418
|
+
let codePointCount = 0;
|
|
419
|
+
for (const _codePoint of normalized) {
|
|
420
|
+
codePointCount += 1;
|
|
421
|
+
if (codePointCount > 255) throw new Error("Channel name must not exceed 255 characters");
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
__name(assertCanonicalChannelName, "assertCanonicalChannelName");
|
|
425
|
+
function encodeChannelKey(channel) {
|
|
426
|
+
assertCanonicalChannelName(channel);
|
|
427
|
+
return base64UrlEncode(encodeUtf8(channel));
|
|
428
|
+
}
|
|
429
|
+
__name(encodeChannelKey, "encodeChannelKey");
|
|
430
|
+
function decodeChannelKey(channelKey) {
|
|
431
|
+
const channel = decodeUtf8(base64UrlDecode(channelKey));
|
|
432
|
+
assertCanonicalChannelName(channel);
|
|
433
|
+
if (encodeChannelKey(channel) !== channelKey) throw new Error("Channel key is not canonically encoded");
|
|
434
|
+
return channel;
|
|
435
|
+
}
|
|
436
|
+
__name(decodeChannelKey, "decodeChannelKey");
|
|
437
|
+
function createReleaseCatalogScopeKey(input) {
|
|
438
|
+
decodeChannelKey(input.channelKey);
|
|
439
|
+
if (input.strategy === "APP_VERSION") return `v1:app-version:${input.platform}:${input.channelKey}`;
|
|
440
|
+
assertCatalogSegment(input.fingerprintHash, "Fingerprint hash");
|
|
441
|
+
return `v1:fingerprint:${input.platform}:${input.channelKey}:${input.fingerprintHash}`;
|
|
442
|
+
}
|
|
443
|
+
__name(createReleaseCatalogScopeKey, "createReleaseCatalogScopeKey");
|
|
444
|
+
function parseReleaseCatalogScopeKey(scopeKey) {
|
|
445
|
+
const segments = scopeKey.split(":");
|
|
446
|
+
const [version2, strategy, platform, channelKey] = segments;
|
|
447
|
+
if (version2 !== "v1" || platform !== "ios" && platform !== "android" || channelKey === void 0) throw new Error("Invalid release catalog scope key");
|
|
448
|
+
const input = strategy === "app-version" && segments.length === 4 ? {
|
|
449
|
+
channelKey,
|
|
450
|
+
platform,
|
|
451
|
+
strategy: "APP_VERSION"
|
|
452
|
+
} : strategy === "fingerprint" && segments.length === 5 && segments[4] !== void 0 ? {
|
|
453
|
+
channelKey,
|
|
454
|
+
fingerprintHash: segments[4],
|
|
455
|
+
platform,
|
|
456
|
+
strategy: "FINGERPRINT"
|
|
457
|
+
} : (() => {
|
|
458
|
+
throw new Error("Invalid release catalog scope key");
|
|
459
|
+
})();
|
|
460
|
+
if (createReleaseCatalogScopeKey(input) !== scopeKey) throw new Error("Release catalog scope key is not canonical");
|
|
461
|
+
return input;
|
|
462
|
+
}
|
|
463
|
+
__name(parseReleaseCatalogScopeKey, "parseReleaseCatalogScopeKey");
|
|
464
|
+
var NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
465
|
+
var 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}$/;
|
|
466
|
+
var isUUIDv7 = /* @__PURE__ */ __name((value) => typeof value === "string" && UUID_V7_PATTERN.test(value), "isUUIDv7");
|
|
467
|
+
|
|
468
|
+
// ../../packages/protocol/dist/index.mjs
|
|
333
469
|
var getManifestStorageUri = /* @__PURE__ */ __name((bundle) => bundle.manifestStorageUri, "getManifestStorageUri");
|
|
334
470
|
var getManifestFileHash = /* @__PURE__ */ __name((bundle) => bundle.manifestFileHash, "getManifestFileHash");
|
|
335
471
|
var getAssetBaseStorageUri = /* @__PURE__ */ __name((bundle) => bundle.assetBaseStorageUri, "getAssetBaseStorageUri");
|
|
@@ -469,140 +605,6 @@ var RELEASE_CATALOG_FALLBACK_POLICY = "BUILTIN_IF_ACTIVE_INELIGIBLE";
|
|
|
469
605
|
var MAX_TARGET_COHORTS_PER_RELEASE = 100;
|
|
470
606
|
var MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE = 512;
|
|
471
607
|
var MAX_COMPILED_CATALOG_BYTES = 262144;
|
|
472
|
-
var BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
473
|
-
var MAX_CATALOG_SEGMENT_LENGTH = 255;
|
|
474
|
-
function assertCatalogSegment(value, name) {
|
|
475
|
-
if (value.length === 0 || value.length > MAX_CATALOG_SEGMENT_LENGTH || !/^[A-Za-z0-9._~-]+$/.test(value)) throw new Error(`${name} must be 1-${MAX_CATALOG_SEGMENT_LENGTH} URL-safe ASCII characters`);
|
|
476
|
-
}
|
|
477
|
-
__name(assertCatalogSegment, "assertCatalogSegment");
|
|
478
|
-
function encodeUtf8(value) {
|
|
479
|
-
const bytes = [];
|
|
480
|
-
for (const codePointText of value) {
|
|
481
|
-
const codePoint = codePointText.codePointAt(0);
|
|
482
|
-
if (codePoint <= 127) bytes.push(codePoint);
|
|
483
|
-
else if (codePoint <= 2047) bytes.push(192 | codePoint >> 6, 128 | codePoint & 63);
|
|
484
|
-
else if (codePoint <= 65535) bytes.push(224 | codePoint >> 12, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
|
|
485
|
-
else bytes.push(240 | codePoint >> 18, 128 | codePoint >> 12 & 63, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
|
|
486
|
-
}
|
|
487
|
-
return bytes;
|
|
488
|
-
}
|
|
489
|
-
__name(encodeUtf8, "encodeUtf8");
|
|
490
|
-
function decodeUtf8(bytes) {
|
|
491
|
-
let result = "";
|
|
492
|
-
for (let index = 0; index < bytes.length; ) {
|
|
493
|
-
const first = bytes[index++];
|
|
494
|
-
let codePoint;
|
|
495
|
-
let continuationCount;
|
|
496
|
-
if (first <= 127) {
|
|
497
|
-
codePoint = first;
|
|
498
|
-
continuationCount = 0;
|
|
499
|
-
} else if ((first & 224) === 192) {
|
|
500
|
-
codePoint = first & 31;
|
|
501
|
-
continuationCount = 1;
|
|
502
|
-
} else if ((first & 240) === 224) {
|
|
503
|
-
codePoint = first & 15;
|
|
504
|
-
continuationCount = 2;
|
|
505
|
-
} else if ((first & 248) === 240) {
|
|
506
|
-
codePoint = first & 7;
|
|
507
|
-
continuationCount = 3;
|
|
508
|
-
} else throw new Error("Invalid UTF-8 channel key");
|
|
509
|
-
for (let offset = 0; offset < continuationCount; offset += 1) {
|
|
510
|
-
const continuation = bytes[index++];
|
|
511
|
-
if (continuation === void 0 || (continuation & 192) !== 128) throw new Error("Invalid UTF-8 channel key");
|
|
512
|
-
codePoint = codePoint << 6 | continuation & 63;
|
|
513
|
-
}
|
|
514
|
-
if (codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343 || continuationCount === 1 && codePoint < 128 || continuationCount === 2 && codePoint < 2048 || continuationCount === 3 && codePoint < 65536) throw new Error("Invalid UTF-8 channel key");
|
|
515
|
-
result += String.fromCodePoint(codePoint);
|
|
516
|
-
}
|
|
517
|
-
return result;
|
|
518
|
-
}
|
|
519
|
-
__name(decodeUtf8, "decodeUtf8");
|
|
520
|
-
function base64UrlEncode(bytes) {
|
|
521
|
-
let result = "";
|
|
522
|
-
for (let index = 0; index < bytes.length; index += 3) {
|
|
523
|
-
const first = bytes[index];
|
|
524
|
-
const second = bytes[index + 1];
|
|
525
|
-
const third = bytes[index + 2];
|
|
526
|
-
const value = first << 16 | (second ?? 0) << 8 | (third ?? 0);
|
|
527
|
-
result += BASE64URL_ALPHABET[value >> 18 & 63];
|
|
528
|
-
result += BASE64URL_ALPHABET[value >> 12 & 63];
|
|
529
|
-
if (second !== void 0) result += BASE64URL_ALPHABET[value >> 6 & 63];
|
|
530
|
-
if (third !== void 0) result += BASE64URL_ALPHABET[value & 63];
|
|
531
|
-
}
|
|
532
|
-
return result;
|
|
533
|
-
}
|
|
534
|
-
__name(base64UrlEncode, "base64UrlEncode");
|
|
535
|
-
function base64UrlDecode(value) {
|
|
536
|
-
if (!/^[A-Za-z0-9_-]*$/.test(value) || value.length % 4 === 1) throw new Error("Invalid channel key");
|
|
537
|
-
const bytes = [];
|
|
538
|
-
for (let index = 0; index < value.length; index += 4) {
|
|
539
|
-
const chunk = value.slice(index, index + 4);
|
|
540
|
-
const encoded = [...chunk].map((character) => BASE64URL_ALPHABET.indexOf(character));
|
|
541
|
-
if (encoded.some((part) => part < 0)) throw new Error("Invalid channel key");
|
|
542
|
-
const bits = encoded[0] << 18 | encoded[1] << 12 | (encoded[2] ?? 0) << 6 | (encoded[3] ?? 0);
|
|
543
|
-
bytes.push(bits >> 16 & 255);
|
|
544
|
-
if (chunk.length >= 3) bytes.push(bits >> 8 & 255);
|
|
545
|
-
if (chunk.length === 4) bytes.push(bits & 255);
|
|
546
|
-
}
|
|
547
|
-
return bytes;
|
|
548
|
-
}
|
|
549
|
-
__name(base64UrlDecode, "base64UrlDecode");
|
|
550
|
-
function normalizeChannelName(channel) {
|
|
551
|
-
return channel.trim().normalize("NFC");
|
|
552
|
-
}
|
|
553
|
-
__name(normalizeChannelName, "normalizeChannelName");
|
|
554
|
-
function assertCanonicalChannelName(channel) {
|
|
555
|
-
const normalized = normalizeChannelName(channel);
|
|
556
|
-
if (normalized.length === 0 || normalized !== channel) throw new Error("Channel name must be non-empty, trimmed, and NFC-normalized");
|
|
557
|
-
let codePointCount = 0;
|
|
558
|
-
for (const _codePoint of normalized) {
|
|
559
|
-
codePointCount += 1;
|
|
560
|
-
if (codePointCount > 255) throw new Error("Channel name must not exceed 255 characters");
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
__name(assertCanonicalChannelName, "assertCanonicalChannelName");
|
|
564
|
-
function encodeChannelKey(channel) {
|
|
565
|
-
assertCanonicalChannelName(channel);
|
|
566
|
-
return base64UrlEncode(encodeUtf8(channel));
|
|
567
|
-
}
|
|
568
|
-
__name(encodeChannelKey, "encodeChannelKey");
|
|
569
|
-
function decodeChannelKey(channelKey) {
|
|
570
|
-
const channel = decodeUtf8(base64UrlDecode(channelKey));
|
|
571
|
-
assertCanonicalChannelName(channel);
|
|
572
|
-
if (encodeChannelKey(channel) !== channelKey) throw new Error("Channel key is not canonically encoded");
|
|
573
|
-
return channel;
|
|
574
|
-
}
|
|
575
|
-
__name(decodeChannelKey, "decodeChannelKey");
|
|
576
|
-
function createReleaseCatalogScopeKey(input) {
|
|
577
|
-
decodeChannelKey(input.channelKey);
|
|
578
|
-
if (input.strategy === "APP_VERSION") return `v1:app-version:${input.platform}:${input.channelKey}`;
|
|
579
|
-
assertCatalogSegment(input.fingerprintHash, "Fingerprint hash");
|
|
580
|
-
return `v1:fingerprint:${input.platform}:${input.channelKey}:${input.fingerprintHash}`;
|
|
581
|
-
}
|
|
582
|
-
__name(createReleaseCatalogScopeKey, "createReleaseCatalogScopeKey");
|
|
583
|
-
function parseReleaseCatalogScopeKey(scopeKey) {
|
|
584
|
-
const segments = scopeKey.split(":");
|
|
585
|
-
const [version2, strategy, platform, channelKey] = segments;
|
|
586
|
-
if (version2 !== "v1" || platform !== "ios" && platform !== "android" || channelKey === void 0) throw new Error("Invalid release catalog scope key");
|
|
587
|
-
const input = strategy === "app-version" && segments.length === 4 ? {
|
|
588
|
-
channelKey,
|
|
589
|
-
platform,
|
|
590
|
-
strategy: "APP_VERSION"
|
|
591
|
-
} : strategy === "fingerprint" && segments.length === 5 && segments[4] !== void 0 ? {
|
|
592
|
-
channelKey,
|
|
593
|
-
fingerprintHash: segments[4],
|
|
594
|
-
platform,
|
|
595
|
-
strategy: "FINGERPRINT"
|
|
596
|
-
} : (() => {
|
|
597
|
-
throw new Error("Invalid release catalog scope key");
|
|
598
|
-
})();
|
|
599
|
-
if (createReleaseCatalogScopeKey(input) !== scopeKey) throw new Error("Release catalog scope key is not canonical");
|
|
600
|
-
return input;
|
|
601
|
-
}
|
|
602
|
-
__name(parseReleaseCatalogScopeKey, "parseReleaseCatalogScopeKey");
|
|
603
|
-
var NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
604
|
-
var 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}$/;
|
|
605
|
-
var isUUIDv7 = /* @__PURE__ */ __name((value) => typeof value === "string" && UUID_V7_PATTERN.test(value), "isUUIDv7");
|
|
606
608
|
var ARTIFACT_PROTOCOL_VERSION = 1;
|
|
607
609
|
var ARTIFACT_FILES_PROTOCOL_VERSION = 2;
|
|
608
610
|
var ARTIFACT_FILES_REQUEST_LIMIT = 100;
|
|
@@ -1202,8 +1204,8 @@ function releaseDescriptor(release) {
|
|
|
1202
1204
|
};
|
|
1203
1205
|
}
|
|
1204
1206
|
__name(releaseDescriptor, "releaseDescriptor");
|
|
1205
|
-
function validateReleases(
|
|
1206
|
-
const enabled =
|
|
1207
|
+
function validateReleases(releases2, strategy) {
|
|
1208
|
+
const enabled = releases2.filter((release) => release.enabled).sort((left, right) => right.id.localeCompare(left.id));
|
|
1207
1209
|
const seenIds = /* @__PURE__ */ new Set();
|
|
1208
1210
|
for (const release of enabled) {
|
|
1209
1211
|
if (!isUUIDv7(release.id)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} must use a canonical lowercase UUIDv7 ID`);
|
|
@@ -1218,11 +1220,11 @@ function validateReleases(releases, strategy) {
|
|
|
1218
1220
|
return enabled;
|
|
1219
1221
|
}
|
|
1220
1222
|
__name(validateReleases, "validateReleases");
|
|
1221
|
-
function collectFrontierReleaseIds(
|
|
1222
|
-
if (
|
|
1223
|
+
function collectFrontierReleaseIds(releases2) {
|
|
1224
|
+
if (releases2.every((release) => normalizeRolloutCohortCount(release.rolloutCohortCount) >= NUMERIC_COHORT_SIZE)) {
|
|
1223
1225
|
const retained3 = /* @__PURE__ */ new Set();
|
|
1224
1226
|
const bundleIds = /* @__PURE__ */ new Set();
|
|
1225
|
-
for (const release of
|
|
1227
|
+
for (const release of releases2) {
|
|
1226
1228
|
if (release.bundleId !== null && !bundleIds.has(release.bundleId)) {
|
|
1227
1229
|
bundleIds.add(release.bundleId);
|
|
1228
1230
|
retained3.add(release.id);
|
|
@@ -1231,7 +1233,7 @@ function collectFrontierReleaseIds(releases) {
|
|
|
1231
1233
|
}
|
|
1232
1234
|
return retained3;
|
|
1233
1235
|
}
|
|
1234
|
-
const explicitTargetCohorts = new Set(
|
|
1236
|
+
const explicitTargetCohorts = new Set(releases2.flatMap((release) => release.targetCohorts));
|
|
1235
1237
|
const cohortStates = [
|
|
1236
1238
|
void 0,
|
|
1237
1239
|
...Array.from({ length: 1e3 }, (_, index) => String(index + 1)),
|
|
@@ -1259,7 +1261,7 @@ function collectFrontierReleaseIds(releases) {
|
|
|
1259
1261
|
}
|
|
1260
1262
|
const retained2 = /* @__PURE__ */ new Set();
|
|
1261
1263
|
let incompleteStateCount = cohortStates.length;
|
|
1262
|
-
for (const release of
|
|
1264
|
+
for (const release of releases2) {
|
|
1263
1265
|
const rolloutCohortCount = normalizeRolloutCohortCount(release.rolloutCohortCount);
|
|
1264
1266
|
const eligibleStateIndexes = /* @__PURE__ */ new Set();
|
|
1265
1267
|
if (rolloutCohortCount >= NUMERIC_COHORT_SIZE) for (const index of fullRolloutStateIndexes) eligibleStateIndexes.add(index);
|
|
@@ -1360,11 +1362,11 @@ function mergeSegments(segments) {
|
|
|
1360
1362
|
return merged;
|
|
1361
1363
|
}
|
|
1362
1364
|
__name(mergeSegments, "mergeSegments");
|
|
1363
|
-
function compileAppVersion(
|
|
1365
|
+
function compileAppVersion(releases2) {
|
|
1364
1366
|
const ranges = /* @__PURE__ */ new Map();
|
|
1365
|
-
for (const release of
|
|
1367
|
+
for (const release of releases2) ranges.set(release.id, rangeToIntervals(release.targetAppVersion));
|
|
1366
1368
|
const segmentReleases = atomicVersionSegments([...ranges.values()].flat()).map((segment) => {
|
|
1367
|
-
const applicable =
|
|
1369
|
+
const applicable = releases2.filter((release) => ranges.get(release.id).some((range) => intervalContainsSegment(range, segment)));
|
|
1368
1370
|
return {
|
|
1369
1371
|
rollbackIds: new Set(applicable.map(({ id }) => id)),
|
|
1370
1372
|
segment,
|
|
@@ -1372,12 +1374,12 @@ function compileAppVersion(releases) {
|
|
|
1372
1374
|
};
|
|
1373
1375
|
});
|
|
1374
1376
|
const retainedIds = new Set(segmentReleases.flatMap(({ retainedIds: retainedIds2, rollbackIds }) => [...retainedIds2, ...rollbackIds]));
|
|
1375
|
-
const retainedReleases =
|
|
1377
|
+
const retainedReleases = releases2.filter((release) => retainedIds.has(release.id));
|
|
1376
1378
|
const descriptorIndex = new Map(retainedReleases.map((release, index) => [release.id, index]));
|
|
1377
1379
|
const segments = mergeSegments(segmentReleases.map(({ segment, retainedIds: retainedIds2, rollbackIds }) => ({
|
|
1378
1380
|
...segment,
|
|
1379
|
-
releaseIndexes:
|
|
1380
|
-
rollbackReleaseIndexes:
|
|
1381
|
+
releaseIndexes: releases2.filter((release) => retainedIds2.has(release.id)).map((release) => descriptorIndex.get(release.id)).filter((index) => index !== void 0),
|
|
1382
|
+
rollbackReleaseIndexes: releases2.filter((release) => rollbackIds.has(release.id)).map((release) => descriptorIndex.get(release.id)).filter((index) => index !== void 0)
|
|
1381
1383
|
}))).filter((segment) => segment.releaseIndexes.length > 0 || segment.rollbackReleaseIndexes.length > 0);
|
|
1382
1384
|
return {
|
|
1383
1385
|
envelope: {
|
|
@@ -1390,9 +1392,9 @@ function compileAppVersion(releases) {
|
|
|
1390
1392
|
};
|
|
1391
1393
|
}
|
|
1392
1394
|
__name(compileAppVersion, "compileAppVersion");
|
|
1393
|
-
function compileFingerprint(
|
|
1394
|
-
const retainedIds = collectFrontierReleaseIds(
|
|
1395
|
-
const retainedReleases =
|
|
1395
|
+
function compileFingerprint(releases2) {
|
|
1396
|
+
const retainedIds = collectFrontierReleaseIds(releases2);
|
|
1397
|
+
const retainedReleases = releases2;
|
|
1396
1398
|
return {
|
|
1397
1399
|
envelope: {
|
|
1398
1400
|
fallbackPolicy: RELEASE_CATALOG_FALLBACK_POLICY,
|
|
@@ -1418,10 +1420,10 @@ function canonicalStringify(value) {
|
|
|
1418
1420
|
}
|
|
1419
1421
|
__name(canonicalStringify, "canonicalStringify");
|
|
1420
1422
|
async function compileReleaseCatalog(input) {
|
|
1421
|
-
const
|
|
1422
|
-
const targetCohorts = new Set(
|
|
1423
|
+
const releases2 = validateReleases(input.releases, input.strategy);
|
|
1424
|
+
const targetCohorts = new Set(releases2.flatMap((release) => release.targetCohorts));
|
|
1423
1425
|
if (targetCohorts.size > MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE) throw new ReleaseCatalogCompilationError("DISTINCT_TARGET_COHORT_LIMIT", `Scope exceeds ${MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE} distinct target cohorts`);
|
|
1424
|
-
const { envelope, retainedReleases } = input.strategy === "APP_VERSION" ? compileAppVersion(
|
|
1426
|
+
const { envelope, retainedReleases } = input.strategy === "APP_VERSION" ? compileAppVersion(releases2) : compileFingerprint(releases2);
|
|
1425
1427
|
const payload = {
|
|
1426
1428
|
...envelope,
|
|
1427
1429
|
releaseDescriptors: retainedReleases.map(releaseDescriptor)
|
|
@@ -1437,7 +1439,7 @@ async function compileReleaseCatalog(input) {
|
|
|
1437
1439
|
byteSize,
|
|
1438
1440
|
descriptorCount: payload.releaseDescriptors.length,
|
|
1439
1441
|
distinctTargetCohortCount: targetCohorts.size,
|
|
1440
|
-
releaseCount:
|
|
1442
|
+
releaseCount: releases2.length,
|
|
1441
1443
|
segmentCount: payload.strategy === "APP_VERSION" ? payload.segments.length : 1
|
|
1442
1444
|
},
|
|
1443
1445
|
payload
|
|
@@ -1984,10 +1986,11 @@ var SETTINGS_TABLE = {
|
|
|
1984
1986
|
var ENGINE_SCHEMA_KEY = "schema.engine";
|
|
1985
1987
|
var CORE_SCHEMA_KEY = "schema.core";
|
|
1986
1988
|
var stored = /* @__PURE__ */ __name(({ key, expected, found }) => `"${key}" is ${found === null ? "missing" : `"${found}"`}; expected "${expected}"`, "stored");
|
|
1987
|
-
var
|
|
1989
|
+
var 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.";
|
|
1990
|
+
var settingMessage = /* @__PURE__ */ __name((adapterName, { key, expected, found }) => `Hot Updater schema setting "${key}" for ${adapterName} is ${found === null ? "missing" : `"${found}"`}; expected "${expected}". ${MIGRATE_SCHEMA_FIX}`, "settingMessage");
|
|
1988
1991
|
var pluginOf = /* @__PURE__ */ __name(({ key }) => key.startsWith("schema.") && key !== "schema.engine" && key !== CORE_SCHEMA_KEY ? key.slice(7) : void 0, "pluginOf");
|
|
1989
1992
|
var listed = /* @__PURE__ */ __name((items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1)}`, "listed");
|
|
1990
|
-
var
|
|
1993
|
+
var settingsMessage = /* @__PURE__ */ __name((adapterName, settings) => `Hot Updater schema settings for ${adapterName} are not migrated: ${settings.map(stored).join(", and ")}. ${MIGRATE_SCHEMA_FIX}`, "settingsMessage");
|
|
1991
1994
|
var pluginsMessage = /* @__PURE__ */ __name((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}`, "pluginsMessage");
|
|
1992
1995
|
var HotUpdaterSchemaMigrationRequiredError = class extends Error {
|
|
1993
1996
|
static {
|
|
@@ -2006,7 +2009,7 @@ var HotUpdaterSchemaMigrationRequiredError = class extends Error {
|
|
|
2006
2009
|
constructor(adapterName, currentVersion, setting, options) {
|
|
2007
2010
|
const settings = options?.settings ?? (setting === void 0 ? [] : [setting]);
|
|
2008
2011
|
const plugins = settings.flatMap((mismatch) => pluginOf(mismatch) ?? []);
|
|
2009
|
-
super(setting === void 0 ? currentVersion === void 0 ? `Hot Updater database schema is not initialized for ${adapterName}.
|
|
2012
|
+
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 });
|
|
2010
2013
|
this.adapterName = adapterName;
|
|
2011
2014
|
this.currentVersion = currentVersion;
|
|
2012
2015
|
this.setting = setting;
|
|
@@ -2059,11 +2062,23 @@ var checkSchemaFence = /* @__PURE__ */ __name(async (adapter, adapterName, expec
|
|
|
2059
2062
|
});
|
|
2060
2063
|
}, "checkSchemaFence");
|
|
2061
2064
|
var FENCED = /* @__PURE__ */ Symbol.for("@hot-updater/server/schema-fence");
|
|
2065
|
+
var FENCED_SETTINGS = /* @__PURE__ */ Symbol.for("@hot-updater/server/schema-fence-settings");
|
|
2062
2066
|
var fencedName = /* @__PURE__ */ __name((adapter) => adapter[FENCED], "fencedName");
|
|
2063
2067
|
var withSchemaFence = /* @__PURE__ */ __name((adapter, adapterName, expected, fix) => {
|
|
2068
|
+
const inner = adapter[FENCED_SETTINGS];
|
|
2069
|
+
const settings = inner === void 0 ? {
|
|
2070
|
+
adapter,
|
|
2071
|
+
expected
|
|
2072
|
+
} : {
|
|
2073
|
+
adapter: inner.adapter,
|
|
2074
|
+
expected: {
|
|
2075
|
+
...inner.expected,
|
|
2076
|
+
...expected
|
|
2077
|
+
}
|
|
2078
|
+
};
|
|
2064
2079
|
let ready;
|
|
2065
2080
|
const fence = /* @__PURE__ */ __name(() => {
|
|
2066
|
-
ready ??= checkSchemaFence(adapter, adapterName, expected, fix).catch((error) => {
|
|
2081
|
+
ready ??= checkSchemaFence(settings.adapter, adapterName, settings.expected, fix).catch((error) => {
|
|
2067
2082
|
ready = void 0;
|
|
2068
2083
|
throw error;
|
|
2069
2084
|
});
|
|
@@ -2072,6 +2087,7 @@ var withSchemaFence = /* @__PURE__ */ __name((adapter, adapterName, expected, fi
|
|
|
2072
2087
|
return {
|
|
2073
2088
|
...adapter,
|
|
2074
2089
|
[FENCED]: adapterName,
|
|
2090
|
+
[FENCED_SETTINGS]: settings,
|
|
2075
2091
|
get: /* @__PURE__ */ __name((table, keys) => fence().then(() => adapter.get(table, keys)), "get"),
|
|
2076
2092
|
query: /* @__PURE__ */ __name((table, request) => fence().then(() => adapter.query(table, request)), "query"),
|
|
2077
2093
|
write: /* @__PURE__ */ __name((ops) => fence().then(() => adapter.write(ops)), "write"),
|
|
@@ -2215,11 +2231,11 @@ var createTableStatements = /* @__PURE__ */ __name((dialect, tables, tablePrefix
|
|
|
2215
2231
|
const render = /* @__PURE__ */ __name((shape) => {
|
|
2216
2232
|
const definitions = shape.columns.map((column) => `${quote(column.name)} ${column.type}${column.notNull ? " NOT NULL" : ""}${column.default === void 0 ? "" : ` DEFAULT ${column.default}`}`);
|
|
2217
2233
|
definitions.push(`PRIMARY KEY (${list(shape.key)})`);
|
|
2218
|
-
const
|
|
2219
|
-
for (const index of shape.indexes) if (index.kind === "table")
|
|
2234
|
+
const after2 = [];
|
|
2235
|
+
for (const index of shape.indexes) if (index.kind === "table") after2.push(...render(index.table));
|
|
2220
2236
|
else if (dialect === "mysql") definitions.push(`${index.unique ? "UNIQUE " : ""}INDEX ${quote(index.name)} (${list(index.columns)})`);
|
|
2221
|
-
else
|
|
2222
|
-
return [`CREATE TABLE IF NOT EXISTS ${quote(shape.name)} (${definitions.join(", ")})`, ...
|
|
2237
|
+
else after2.push(`CREATE ${index.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${quote(index.name)} ON ${quote(shape.name)} (${list(index.columns)})`);
|
|
2238
|
+
return [`CREATE TABLE IF NOT EXISTS ${quote(shape.name)} (${definitions.join(", ")})`, ...after2];
|
|
2223
2239
|
}, "render");
|
|
2224
2240
|
return sqlTableShapes(dialect, tables, tablePrefix).flatMap(render);
|
|
2225
2241
|
}, "createTableStatements");
|
|
@@ -2261,255 +2277,358 @@ var CHANNEL_ID = {
|
|
|
2261
2277
|
maxLength: 1408,
|
|
2262
2278
|
ascii: true
|
|
2263
2279
|
};
|
|
2264
|
-
var HOT_UPDATER_SCHEMA_VERSION = "1.
|
|
2265
|
-
var
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2280
|
+
var HOT_UPDATER_SCHEMA_VERSION = "1.2.0";
|
|
2281
|
+
var bundles = defineTable({
|
|
2282
|
+
id: {
|
|
2283
|
+
type: "string",
|
|
2284
|
+
maxLength: 36
|
|
2285
|
+
},
|
|
2286
|
+
platform: {
|
|
2287
|
+
type: "string",
|
|
2288
|
+
maxLength: 16
|
|
2289
|
+
},
|
|
2290
|
+
git_commit_hash: {
|
|
2291
|
+
type: "string",
|
|
2292
|
+
maxLength: 255,
|
|
2293
|
+
required: false
|
|
2294
|
+
},
|
|
2295
|
+
metadata: { type: "json" },
|
|
2296
|
+
manifest_storage_uri: { type: "string" },
|
|
2297
|
+
manifest_file_hash: { type: "string" },
|
|
2298
|
+
asset_base_storage_uri: { type: "string" }
|
|
2299
|
+
}, {
|
|
2300
|
+
key: ["id"],
|
|
2301
|
+
indexes: {
|
|
2302
|
+
byPlatform: {
|
|
2303
|
+
eq: ["platform"],
|
|
2304
|
+
sort: ["id"]
|
|
2279
2305
|
},
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
asset_base_storage_uri: { type: "string" }
|
|
2284
|
-
}, {
|
|
2285
|
-
key: ["id"],
|
|
2286
|
-
indexes: {
|
|
2287
|
-
byPlatform: {
|
|
2288
|
-
eq: ["platform"],
|
|
2289
|
-
sort: ["id"]
|
|
2290
|
-
},
|
|
2291
|
-
all: {
|
|
2292
|
-
eq: [],
|
|
2293
|
-
sort: ["id"]
|
|
2294
|
-
}
|
|
2306
|
+
all: {
|
|
2307
|
+
eq: [],
|
|
2308
|
+
sort: ["id"]
|
|
2295
2309
|
}
|
|
2296
|
-
}
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
},
|
|
2310
|
-
base_bundle_id: {
|
|
2311
|
-
type: "string",
|
|
2312
|
-
maxLength: 36,
|
|
2313
|
-
references: {
|
|
2314
|
-
model: "bundles",
|
|
2315
|
-
onDelete: "cascade"
|
|
2316
|
-
}
|
|
2317
|
-
},
|
|
2318
|
-
base_file_hash: { type: "string" },
|
|
2319
|
-
patch_file_hash: { type: "string" },
|
|
2320
|
-
patch_storage_uri: { type: "string" },
|
|
2321
|
-
byte_size: { type: "integer" },
|
|
2322
|
-
order_index: { type: "integer" }
|
|
2323
|
-
}, {
|
|
2324
|
-
key: ["id"],
|
|
2325
|
-
indexes: {
|
|
2326
|
-
pair: {
|
|
2327
|
-
eq: ["bundle_id", "base_bundle_id"],
|
|
2328
|
-
sort: [],
|
|
2329
|
-
unique: true
|
|
2330
|
-
},
|
|
2331
|
-
byBundle: {
|
|
2332
|
-
eq: ["bundle_id"],
|
|
2333
|
-
sort: ["order_index"],
|
|
2334
|
-
root: { model: "bundles" }
|
|
2335
|
-
},
|
|
2336
|
-
byBase: {
|
|
2337
|
-
eq: ["base_bundle_id"],
|
|
2338
|
-
sort: ["bundle_id"]
|
|
2339
|
-
}
|
|
2310
|
+
}
|
|
2311
|
+
});
|
|
2312
|
+
var bundlePatches = defineTable({
|
|
2313
|
+
id: {
|
|
2314
|
+
type: "string",
|
|
2315
|
+
maxLength: 255
|
|
2316
|
+
},
|
|
2317
|
+
bundle_id: {
|
|
2318
|
+
type: "string",
|
|
2319
|
+
maxLength: 36,
|
|
2320
|
+
references: {
|
|
2321
|
+
model: "bundles",
|
|
2322
|
+
onDelete: "cascade"
|
|
2340
2323
|
}
|
|
2341
|
-
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
maxLength: 16
|
|
2363
|
-
},
|
|
2364
|
-
kind: {
|
|
2365
|
-
type: "string",
|
|
2366
|
-
maxLength: 16
|
|
2367
|
-
},
|
|
2368
|
-
bundle_id: {
|
|
2369
|
-
type: "string",
|
|
2370
|
-
maxLength: 36,
|
|
2371
|
-
required: false,
|
|
2372
|
-
references: {
|
|
2373
|
-
model: "bundles",
|
|
2374
|
-
onDelete: "restrict"
|
|
2375
|
-
}
|
|
2376
|
-
},
|
|
2377
|
-
strategy: {
|
|
2378
|
-
type: "string",
|
|
2379
|
-
maxLength: 16
|
|
2380
|
-
},
|
|
2381
|
-
target_app_version: {
|
|
2382
|
-
type: "string",
|
|
2383
|
-
required: false
|
|
2384
|
-
},
|
|
2385
|
-
fingerprint_hash: {
|
|
2386
|
-
type: "string",
|
|
2387
|
-
maxLength: 255,
|
|
2388
|
-
required: false
|
|
2389
|
-
},
|
|
2390
|
-
enabled: { type: "boolean" },
|
|
2391
|
-
should_force_update: { type: "boolean" },
|
|
2392
|
-
message: {
|
|
2393
|
-
type: "string",
|
|
2394
|
-
required: false
|
|
2395
|
-
},
|
|
2396
|
-
rollout_cohort_count: { type: "integer" },
|
|
2397
|
-
target_cohorts: { type: "json" },
|
|
2398
|
-
operation: {
|
|
2399
|
-
type: "string",
|
|
2400
|
-
maxLength: 16
|
|
2324
|
+
},
|
|
2325
|
+
base_bundle_id: {
|
|
2326
|
+
type: "string",
|
|
2327
|
+
maxLength: 36,
|
|
2328
|
+
references: {
|
|
2329
|
+
model: "bundles",
|
|
2330
|
+
onDelete: "cascade"
|
|
2331
|
+
}
|
|
2332
|
+
},
|
|
2333
|
+
base_file_hash: { type: "string" },
|
|
2334
|
+
patch_file_hash: { type: "string" },
|
|
2335
|
+
patch_storage_uri: { type: "string" },
|
|
2336
|
+
byte_size: { type: "integer" },
|
|
2337
|
+
order_index: { type: "integer" }
|
|
2338
|
+
}, {
|
|
2339
|
+
key: ["id"],
|
|
2340
|
+
indexes: {
|
|
2341
|
+
pair: {
|
|
2342
|
+
eq: ["bundle_id", "base_bundle_id"],
|
|
2343
|
+
sort: [],
|
|
2344
|
+
unique: true
|
|
2401
2345
|
},
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
references: {
|
|
2407
|
-
model: "releases",
|
|
2408
|
-
onDelete: "none"
|
|
2409
|
-
}
|
|
2346
|
+
byBundle: {
|
|
2347
|
+
eq: ["bundle_id"],
|
|
2348
|
+
sort: ["order_index"],
|
|
2349
|
+
root: { model: "bundles" }
|
|
2410
2350
|
},
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2351
|
+
byBase: {
|
|
2352
|
+
eq: ["base_bundle_id"],
|
|
2353
|
+
sort: ["bundle_id"]
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
});
|
|
2357
|
+
var releases = defineTable({
|
|
2358
|
+
id: {
|
|
2359
|
+
type: "string",
|
|
2360
|
+
maxLength: 36
|
|
2361
|
+
},
|
|
2362
|
+
revision: { type: "integer" },
|
|
2363
|
+
scope_key: {
|
|
2364
|
+
type: "string",
|
|
2365
|
+
maxLength: 2048,
|
|
2366
|
+
ascii: true
|
|
2367
|
+
},
|
|
2368
|
+
channel_id: {
|
|
2369
|
+
...CHANNEL_ID,
|
|
2370
|
+
references: {
|
|
2371
|
+
model: "channels",
|
|
2372
|
+
onDelete: "restrict"
|
|
2373
|
+
}
|
|
2374
|
+
},
|
|
2375
|
+
platform: {
|
|
2376
|
+
type: "string",
|
|
2377
|
+
maxLength: 16
|
|
2378
|
+
},
|
|
2379
|
+
kind: {
|
|
2380
|
+
type: "string",
|
|
2381
|
+
maxLength: 16
|
|
2382
|
+
},
|
|
2383
|
+
bundle_id: {
|
|
2384
|
+
type: "string",
|
|
2385
|
+
maxLength: 36,
|
|
2386
|
+
required: false,
|
|
2387
|
+
references: {
|
|
2388
|
+
model: "bundles",
|
|
2389
|
+
onDelete: "restrict"
|
|
2446
2390
|
}
|
|
2447
|
-
}
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2391
|
+
},
|
|
2392
|
+
strategy: {
|
|
2393
|
+
type: "string",
|
|
2394
|
+
maxLength: 16
|
|
2395
|
+
},
|
|
2396
|
+
target_app_version: {
|
|
2397
|
+
type: "string",
|
|
2398
|
+
required: false
|
|
2399
|
+
},
|
|
2400
|
+
fingerprint_hash: {
|
|
2401
|
+
type: "string",
|
|
2402
|
+
maxLength: 255,
|
|
2403
|
+
required: false
|
|
2404
|
+
},
|
|
2405
|
+
enabled: { type: "boolean" },
|
|
2406
|
+
should_force_update: { type: "boolean" },
|
|
2407
|
+
message: {
|
|
2408
|
+
type: "string",
|
|
2409
|
+
required: false
|
|
2410
|
+
},
|
|
2411
|
+
rollout_cohort_count: { type: "integer" },
|
|
2412
|
+
target_cohorts: { type: "json" },
|
|
2413
|
+
operation: {
|
|
2414
|
+
type: "string",
|
|
2415
|
+
maxLength: 16
|
|
2416
|
+
},
|
|
2417
|
+
source_release_id: {
|
|
2418
|
+
type: "string",
|
|
2419
|
+
maxLength: 36,
|
|
2420
|
+
required: false,
|
|
2421
|
+
references: {
|
|
2422
|
+
model: "releases",
|
|
2423
|
+
onDelete: "none"
|
|
2424
|
+
}
|
|
2425
|
+
},
|
|
2426
|
+
created_at_ms: { type: "integer" },
|
|
2427
|
+
updated_at_ms: { type: "integer" }
|
|
2428
|
+
}, {
|
|
2429
|
+
key: ["id"],
|
|
2430
|
+
indexes: {
|
|
2431
|
+
byScope: {
|
|
2432
|
+
eq: ["scope_key"],
|
|
2433
|
+
sort: ["id"],
|
|
2434
|
+
root: { model: "release_catalogs" }
|
|
2453
2435
|
},
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2436
|
+
byScopeEnabled: {
|
|
2437
|
+
eq: ["scope_key", "enabled"],
|
|
2438
|
+
sort: ["id"],
|
|
2439
|
+
root: { model: "release_catalogs" }
|
|
2457
2440
|
},
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2441
|
+
byChannelPlatform: {
|
|
2442
|
+
eq: ["channel_id", "platform"],
|
|
2443
|
+
sort: ["id"]
|
|
2461
2444
|
},
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2445
|
+
byChannelPlatformEnabled: {
|
|
2446
|
+
eq: [
|
|
2447
|
+
"channel_id",
|
|
2448
|
+
"platform",
|
|
2449
|
+
"enabled"
|
|
2450
|
+
],
|
|
2451
|
+
sort: ["id"]
|
|
2468
2452
|
},
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
ascii: true
|
|
2453
|
+
byBundle: {
|
|
2454
|
+
eq: ["bundle_id"],
|
|
2455
|
+
sort: ["id"]
|
|
2473
2456
|
},
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2457
|
+
all: {
|
|
2458
|
+
eq: [],
|
|
2459
|
+
sort: ["id"]
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
});
|
|
2463
|
+
var releaseCatalogs = defineTable({
|
|
2464
|
+
scope_key: {
|
|
2465
|
+
type: "string",
|
|
2466
|
+
maxLength: 2048,
|
|
2467
|
+
ascii: true
|
|
2468
|
+
},
|
|
2469
|
+
catalog_id: {
|
|
2470
|
+
type: "string",
|
|
2471
|
+
maxLength: 255
|
|
2472
|
+
},
|
|
2473
|
+
strategy: {
|
|
2474
|
+
type: "string",
|
|
2475
|
+
maxLength: 16
|
|
2476
|
+
},
|
|
2477
|
+
channel_id: {
|
|
2478
|
+
...CHANNEL_ID,
|
|
2479
|
+
references: {
|
|
2480
|
+
model: "channels",
|
|
2481
|
+
onDelete: "none"
|
|
2482
|
+
}
|
|
2483
|
+
},
|
|
2484
|
+
channel_key: {
|
|
2485
|
+
type: "string",
|
|
2486
|
+
maxLength: 1400,
|
|
2487
|
+
ascii: true
|
|
2488
|
+
},
|
|
2489
|
+
platform: {
|
|
2490
|
+
type: "string",
|
|
2491
|
+
maxLength: 16
|
|
2492
|
+
},
|
|
2493
|
+
fingerprint_hash: {
|
|
2494
|
+
type: "string",
|
|
2495
|
+
maxLength: 255,
|
|
2496
|
+
required: false
|
|
2497
|
+
},
|
|
2498
|
+
generation: { type: "integer" },
|
|
2499
|
+
payload: { type: "string" },
|
|
2500
|
+
catalog_hash: {
|
|
2501
|
+
type: "string",
|
|
2502
|
+
maxLength: 71
|
|
2503
|
+
},
|
|
2504
|
+
byte_size: { type: "integer" },
|
|
2505
|
+
is_tombstone: { type: "boolean" },
|
|
2506
|
+
updated_at_ms: { type: "integer" }
|
|
2507
|
+
}, {
|
|
2508
|
+
key: ["scope_key"],
|
|
2509
|
+
indexes: { all: {
|
|
2510
|
+
eq: [],
|
|
2511
|
+
sort: ["scope_key"]
|
|
2512
|
+
} }
|
|
2513
|
+
});
|
|
2514
|
+
var channels = defineTable({
|
|
2515
|
+
id: CHANNEL_ID,
|
|
2516
|
+
name: {
|
|
2517
|
+
type: "string",
|
|
2518
|
+
maxLength: 255,
|
|
2519
|
+
unique: true
|
|
2520
|
+
}
|
|
2521
|
+
}, {
|
|
2522
|
+
key: ["id"],
|
|
2523
|
+
indexes: { all: {
|
|
2524
|
+
eq: [],
|
|
2525
|
+
sort: ["name"]
|
|
2526
|
+
} }
|
|
2527
|
+
});
|
|
2528
|
+
var nativeBuilds = defineTable({
|
|
2529
|
+
id: {
|
|
2530
|
+
type: "string",
|
|
2531
|
+
maxLength: 36
|
|
2532
|
+
},
|
|
2533
|
+
scope_key: {
|
|
2534
|
+
type: "string",
|
|
2535
|
+
maxLength: 2048,
|
|
2536
|
+
ascii: true
|
|
2537
|
+
},
|
|
2538
|
+
platform: {
|
|
2539
|
+
type: "string",
|
|
2540
|
+
maxLength: 16
|
|
2541
|
+
},
|
|
2542
|
+
channel_id: {
|
|
2543
|
+
...CHANNEL_ID,
|
|
2544
|
+
references: {
|
|
2545
|
+
model: "channels",
|
|
2546
|
+
onDelete: "none"
|
|
2547
|
+
}
|
|
2548
|
+
},
|
|
2549
|
+
fingerprint_hash: {
|
|
2550
|
+
type: "string",
|
|
2551
|
+
maxLength: 255,
|
|
2552
|
+
required: false
|
|
2553
|
+
},
|
|
2554
|
+
app_version: {
|
|
2555
|
+
type: "string",
|
|
2556
|
+
required: false
|
|
2557
|
+
},
|
|
2558
|
+
min_bundle_id: {
|
|
2559
|
+
type: "string",
|
|
2560
|
+
maxLength: 36,
|
|
2561
|
+
unique: true,
|
|
2562
|
+
references: {
|
|
2563
|
+
model: "bundles",
|
|
2564
|
+
onDelete: "none"
|
|
2565
|
+
}
|
|
2566
|
+
},
|
|
2567
|
+
artifact_kind: {
|
|
2568
|
+
type: "string",
|
|
2569
|
+
maxLength: 16
|
|
2570
|
+
},
|
|
2571
|
+
artifact_storage_uri: { type: "string" },
|
|
2572
|
+
byte_size: { type: "integer" },
|
|
2573
|
+
git_commit_hash: {
|
|
2574
|
+
type: "string",
|
|
2575
|
+
maxLength: 255,
|
|
2576
|
+
required: false
|
|
2577
|
+
},
|
|
2578
|
+
created_at_ms: { type: "integer" },
|
|
2579
|
+
build_key: {
|
|
2580
|
+
type: "string",
|
|
2581
|
+
maxLength: 69,
|
|
2582
|
+
ascii: true,
|
|
2583
|
+
required: false
|
|
2584
|
+
},
|
|
2585
|
+
build_number: {
|
|
2586
|
+
type: "string",
|
|
2587
|
+
maxLength: 64,
|
|
2588
|
+
required: false
|
|
2589
|
+
},
|
|
2590
|
+
submitted_to: {
|
|
2591
|
+
type: "string",
|
|
2592
|
+
maxLength: 16,
|
|
2593
|
+
required: false
|
|
2594
|
+
},
|
|
2595
|
+
submitted_at_ms: {
|
|
2596
|
+
type: "integer",
|
|
2597
|
+
required: false
|
|
2598
|
+
},
|
|
2599
|
+
submitted_version: {
|
|
2600
|
+
type: "string",
|
|
2601
|
+
maxLength: 255,
|
|
2602
|
+
required: false
|
|
2603
|
+
}
|
|
2604
|
+
}, {
|
|
2605
|
+
key: ["id"],
|
|
2606
|
+
indexes: {
|
|
2607
|
+
byScope: {
|
|
2608
|
+
eq: ["scope_key"],
|
|
2609
|
+
sort: ["id"]
|
|
2477
2610
|
},
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
required: false
|
|
2611
|
+
byBuildKey: {
|
|
2612
|
+
eq: ["scope_key", "build_key"],
|
|
2613
|
+
sort: ["id"]
|
|
2482
2614
|
},
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
maxLength: 71
|
|
2615
|
+
byChannel: {
|
|
2616
|
+
eq: ["channel_id"],
|
|
2617
|
+
sort: ["id"],
|
|
2618
|
+
root: { model: "channels" }
|
|
2488
2619
|
},
|
|
2489
|
-
|
|
2490
|
-
is_tombstone: { type: "boolean" },
|
|
2491
|
-
updated_at_ms: { type: "integer" }
|
|
2492
|
-
}, {
|
|
2493
|
-
key: ["scope_key"],
|
|
2494
|
-
indexes: { all: {
|
|
2620
|
+
all: {
|
|
2495
2621
|
eq: [],
|
|
2496
|
-
sort: ["
|
|
2497
|
-
} }
|
|
2498
|
-
}),
|
|
2499
|
-
channels: defineTable({
|
|
2500
|
-
id: CHANNEL_ID,
|
|
2501
|
-
name: {
|
|
2502
|
-
type: "string",
|
|
2503
|
-
maxLength: 255,
|
|
2504
|
-
unique: true
|
|
2622
|
+
sort: ["id"]
|
|
2505
2623
|
}
|
|
2506
|
-
}
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2624
|
+
}
|
|
2625
|
+
});
|
|
2626
|
+
var coreSchema = {
|
|
2627
|
+
bundles,
|
|
2628
|
+
bundle_patches: bundlePatches,
|
|
2629
|
+
releases,
|
|
2630
|
+
release_catalogs: releaseCatalogs,
|
|
2631
|
+
channels,
|
|
2513
2632
|
bundle_totals: defineAggregate({ platform_key: {
|
|
2514
2633
|
type: "string",
|
|
2515
2634
|
maxLength: 16
|
|
@@ -2520,7 +2639,8 @@ var coreSchema = {
|
|
|
2520
2639
|
eq: ["platform_key"],
|
|
2521
2640
|
sort: []
|
|
2522
2641
|
} }
|
|
2523
|
-
})
|
|
2642
|
+
}),
|
|
2643
|
+
native_builds: nativeBuilds
|
|
2524
2644
|
};
|
|
2525
2645
|
var coreModule = {
|
|
2526
2646
|
id: "core",
|
|
@@ -2564,7 +2684,7 @@ var fnv1a = /* @__PURE__ */ __name((text) => {
|
|
|
2564
2684
|
return hash2;
|
|
2565
2685
|
}, "fnv1a");
|
|
2566
2686
|
var cursorScope = /* @__PURE__ */ __name((parts) => fnv1a(JSON.stringify(parts)).toString(36), "cursorScope");
|
|
2567
|
-
var encodeCursor = /* @__PURE__ */ __name((scope,
|
|
2687
|
+
var encodeCursor = /* @__PURE__ */ __name((scope, after2) => toBase64Url(JSON.stringify([scope, after2])), "encodeCursor");
|
|
2568
2688
|
var decodeCursor = /* @__PURE__ */ __name((scope, cursor) => {
|
|
2569
2689
|
let decoded;
|
|
2570
2690
|
try {
|
|
@@ -2572,10 +2692,10 @@ var decodeCursor = /* @__PURE__ */ __name((scope, cursor) => {
|
|
|
2572
2692
|
} catch {
|
|
2573
2693
|
decoded = void 0;
|
|
2574
2694
|
}
|
|
2575
|
-
const [from,
|
|
2576
|
-
if (typeof from !== "string" || !Array.isArray(
|
|
2695
|
+
const [from, after2] = Array.isArray(decoded) ? decoded : [];
|
|
2696
|
+
if (typeof from !== "string" || !Array.isArray(after2) || !after2.every(isKeyValue)) throw new DatabaseCursorError("The cursor is malformed.");
|
|
2577
2697
|
if (from !== scope) throw new DatabaseCursorError("The cursor belongs to a read with another model, index, filter, order, or range.");
|
|
2578
|
-
return
|
|
2698
|
+
return after2;
|
|
2579
2699
|
}, "decodeCursor");
|
|
2580
2700
|
|
|
2581
2701
|
// ../plugin-core/dist/engine/database/engineAggregates.mjs
|
|
@@ -3499,14 +3619,14 @@ var createEngineMigrator = /* @__PURE__ */ __name((options) => {
|
|
|
3499
3619
|
return {
|
|
3500
3620
|
getVersion,
|
|
3501
3621
|
getNameVariants: /* @__PURE__ */ __name(async () => void 0, "getNameVariants"),
|
|
3502
|
-
next: /* @__PURE__ */ __name(async () => await getVersion() === "1.
|
|
3622
|
+
next: /* @__PURE__ */ __name(async () => await getVersion() === "1.2.0" ? void 0 : { version: HOT_UPDATER_SCHEMA_VERSION }, "next"),
|
|
3503
3623
|
previous: /* @__PURE__ */ __name(async () => void 0, "previous"),
|
|
3504
3624
|
up: makeResult,
|
|
3505
3625
|
down: /* @__PURE__ */ __name(async () => {
|
|
3506
3626
|
throw new Error("No previous schema to migrate to.");
|
|
3507
3627
|
}, "down"),
|
|
3508
3628
|
migrateTo: /* @__PURE__ */ __name(async (version2, migrate) => {
|
|
3509
|
-
if (version2 !== "1.
|
|
3629
|
+
if (version2 !== "1.2.0") throw new Error(`Invalid version ${version2}`);
|
|
3510
3630
|
return makeResult(migrate);
|
|
3511
3631
|
}, "migrateTo"),
|
|
3512
3632
|
migrateToLatest: makeResult
|
|
@@ -3637,9 +3757,9 @@ var createTransactions = /* @__PURE__ */ __name(({ adapter, schema, reads, retry
|
|
|
3637
3757
|
else if (pending.type !== "delete") for (const [column, delta] of Object.entries(by)) pending.row[column] = Number(pending.row[column] ?? pending.read?.[column] ?? 0) + delta;
|
|
3638
3758
|
else if (Object.values(by).some((delta) => delta > 0)) throw new DatabaseConstraintError("not_found", model.table.name);
|
|
3639
3759
|
}, "bump");
|
|
3640
|
-
const touchParents = /* @__PURE__ */ __name((model, before,
|
|
3760
|
+
const touchParents = /* @__PURE__ */ __name((model, before, after2) => {
|
|
3641
3761
|
for (const { field, counter, target } of model.references) {
|
|
3642
|
-
const [from, to] = [before?.[field],
|
|
3762
|
+
const [from, to] = [before?.[field], after2?.[field]];
|
|
3643
3763
|
if (counter === void 0 || from === to) continue;
|
|
3644
3764
|
if (isKeyed(from)) bump(modelOf(target), [from], { [counter]: -1 });
|
|
3645
3765
|
if (isKeyed(to)) bump(modelOf(target), [to], { [counter]: 1 });
|
|
@@ -3647,7 +3767,7 @@ var createTransactions = /* @__PURE__ */ __name(({ adapter, schema, reads, retry
|
|
|
3647
3767
|
for (const [index, root] of model.roots) {
|
|
3648
3768
|
const parent = modelOf(root);
|
|
3649
3769
|
const { eq } = model.table.indexes.find(({ name }) => name === index);
|
|
3650
|
-
for (const row of [before,
|
|
3770
|
+
for (const row of [before, after2]) {
|
|
3651
3771
|
const key = eq.slice(0, parent.table.key.length).map((field) => row?.[field]);
|
|
3652
3772
|
if (row && key.every(isKeyed)) bump(parent, key, {});
|
|
3653
3773
|
}
|
|
@@ -4207,9 +4327,9 @@ var createDatabaseEngine = /* @__PURE__ */ __name(({ batching, now = Date.now, m
|
|
|
4207
4327
|
// ../plugin-core/dist/engine/createEngine.mjs
|
|
4208
4328
|
var pluginTablesFix = /* @__PURE__ */ __name((database) => {
|
|
4209
4329
|
const { createMigrator, generateSchema } = database;
|
|
4210
|
-
const
|
|
4330
|
+
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.";
|
|
4211
4331
|
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.";
|
|
4212
|
-
return createMigrator !== void 0 ?
|
|
4332
|
+
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.`;
|
|
4213
4333
|
}, "pluginTablesFix");
|
|
4214
4334
|
var createEngine = /* @__PURE__ */ __name((database, { plugins = [], now = Date.now } = {}) => {
|
|
4215
4335
|
const modules = /* @__PURE__ */ new Map();
|
|
@@ -4512,7 +4632,7 @@ var createSqlAdapter = /* @__PURE__ */ __name(({ executor, tablePrefix = "", max
|
|
|
4512
4632
|
}, "createSqlAdapter");
|
|
4513
4633
|
|
|
4514
4634
|
// ../../packages/server/dist/package.mjs
|
|
4515
|
-
var version = "1.
|
|
4635
|
+
var version = "1.2.0-rc.0";
|
|
4516
4636
|
|
|
4517
4637
|
// ../../packages/server/dist/version.mjs
|
|
4518
4638
|
var HOT_UPDATER_SERVER_VERSION = version;
|
|
@@ -5364,6 +5484,7 @@ var drain = /* @__PURE__ */ __name(async (read) => {
|
|
|
5364
5484
|
} while (cursor !== void 0);
|
|
5365
5485
|
return rows;
|
|
5366
5486
|
}, "drain");
|
|
5487
|
+
var after = /* @__PURE__ */ __name((input) => input.after === void 0 ? {} : { range: (input.order ?? "asc") === "asc" ? { gt: input.after } : { lt: input.after } }, "after");
|
|
5367
5488
|
var createCoreReads = /* @__PURE__ */ __name((db, storage) => {
|
|
5368
5489
|
const detail = /* @__PURE__ */ __name(async (row) => {
|
|
5369
5490
|
const count = row._refs_bundle_patches_bundle_id ?? 0;
|
|
@@ -5386,7 +5507,6 @@ var createCoreReads = /* @__PURE__ */ __name((db, storage) => {
|
|
|
5386
5507
|
childCount: row._refs_bundle_patches_base_bundle_id ?? 0
|
|
5387
5508
|
};
|
|
5388
5509
|
}, "detail");
|
|
5389
|
-
const after = /* @__PURE__ */ __name((input) => input.after === void 0 ? {} : { range: (input.order ?? "asc") === "asc" ? { gt: input.after } : { lt: input.after } }, "after");
|
|
5390
5510
|
return {
|
|
5391
5511
|
/** The update check: one point read of its scope's catalog. */
|
|
5392
5512
|
async getReleaseCatalog(input) {
|
|
@@ -5576,9 +5696,9 @@ var insertBundle = /* @__PURE__ */ __name((tx, row) => {
|
|
|
5576
5696
|
tx.create("bundles", row);
|
|
5577
5697
|
countBundle(tx, row.platform, 1);
|
|
5578
5698
|
}, "insertBundle");
|
|
5579
|
-
var updateBundle = /* @__PURE__ */ __name((tx, current, set) => {
|
|
5699
|
+
var updateBundle = /* @__PURE__ */ __name((tx, current, set, { counted = true } = {}) => {
|
|
5580
5700
|
tx.update("bundles", current, set);
|
|
5581
|
-
if (set.platform !== void 0 && set.platform !== current.platform) {
|
|
5701
|
+
if (counted && set.platform !== void 0 && set.platform !== current.platform) {
|
|
5582
5702
|
countBundle(tx, current.platform, -1);
|
|
5583
5703
|
countBundle(tx, set.platform, 1);
|
|
5584
5704
|
}
|
|
@@ -5643,10 +5763,226 @@ var deleteChannel = /* @__PURE__ */ __name((db, id) => db.transaction(async (tx)
|
|
|
5643
5763
|
deleted: false,
|
|
5644
5764
|
reason: "not_empty"
|
|
5645
5765
|
};
|
|
5766
|
+
const { rows: builds } = await tx.findMany("native_builds", {
|
|
5767
|
+
index: "byChannel",
|
|
5768
|
+
where: { channel_id: id },
|
|
5769
|
+
limit: 1
|
|
5770
|
+
});
|
|
5771
|
+
if (builds.length > 0) return {
|
|
5772
|
+
deleted: false,
|
|
5773
|
+
reason: "not_empty"
|
|
5774
|
+
};
|
|
5646
5775
|
await tx.delete("channels", row);
|
|
5647
5776
|
return { deleted: true };
|
|
5648
5777
|
}), "deleteChannel");
|
|
5649
5778
|
|
|
5779
|
+
// ../../packages/server/dist/core/nativeBuilds.mjs
|
|
5780
|
+
var ARTIFACT_KINDS = {
|
|
5781
|
+
android: ["apk", "aab"],
|
|
5782
|
+
ios: ["ipa", "app"]
|
|
5783
|
+
};
|
|
5784
|
+
var SCOPE_KEY_MAX_LENGTH = 2048;
|
|
5785
|
+
var GIT_COMMIT_HASH_MAX_LENGTH = 255;
|
|
5786
|
+
var BUILD_KEY = /^nbk1:[0-9a-f]{64}$/;
|
|
5787
|
+
var NativeBuildInputError = class extends TypeError {
|
|
5788
|
+
static {
|
|
5789
|
+
__name(this, "NativeBuildInputError");
|
|
5790
|
+
}
|
|
5791
|
+
name = "NativeBuildInputError";
|
|
5792
|
+
};
|
|
5793
|
+
var pick2 = /* @__PURE__ */ __name((row) => ({
|
|
5794
|
+
id: row.id,
|
|
5795
|
+
scope_key: row.scope_key,
|
|
5796
|
+
platform: row.platform,
|
|
5797
|
+
channel_id: row.channel_id,
|
|
5798
|
+
fingerprint_hash: row.fingerprint_hash ?? null,
|
|
5799
|
+
app_version: row.app_version ?? null,
|
|
5800
|
+
min_bundle_id: row.min_bundle_id,
|
|
5801
|
+
artifact_kind: row.artifact_kind,
|
|
5802
|
+
artifact_storage_uri: row.artifact_storage_uri,
|
|
5803
|
+
byte_size: row.byte_size,
|
|
5804
|
+
git_commit_hash: row.git_commit_hash ?? null,
|
|
5805
|
+
created_at_ms: row.created_at_ms,
|
|
5806
|
+
build_key: row.build_key ?? null,
|
|
5807
|
+
build_number: row.build_number ?? null,
|
|
5808
|
+
submitted_to: row.submitted_to ?? null,
|
|
5809
|
+
submitted_at_ms: row.submitted_at_ms ?? null,
|
|
5810
|
+
submitted_version: row.submitted_version ?? null
|
|
5811
|
+
}), "pick");
|
|
5812
|
+
var SUBMISSION_DESTINATIONS = {
|
|
5813
|
+
android: "play-internal",
|
|
5814
|
+
ios: "testflight"
|
|
5815
|
+
};
|
|
5816
|
+
var BUILD_NUMBER_MAX_LENGTH = 64;
|
|
5817
|
+
var SUBMITTED_VERSION_MAX_LENGTH = 255;
|
|
5818
|
+
var nativeBuildScopeKey = /* @__PURE__ */ __name((scope) => {
|
|
5819
|
+
let scopeKey;
|
|
5820
|
+
try {
|
|
5821
|
+
const channelKey = encodeChannelKey(scope.channel);
|
|
5822
|
+
scopeKey = scope.fingerprintHash === null ? `${createReleaseCatalogScopeKey({
|
|
5823
|
+
channelKey,
|
|
5824
|
+
platform: scope.platform,
|
|
5825
|
+
strategy: "APP_VERSION"
|
|
5826
|
+
})}:${encodeURIComponent(scope.appVersion)}` : createReleaseCatalogScopeKey({
|
|
5827
|
+
channelKey,
|
|
5828
|
+
fingerprintHash: scope.fingerprintHash,
|
|
5829
|
+
platform: scope.platform,
|
|
5830
|
+
strategy: "FINGERPRINT"
|
|
5831
|
+
});
|
|
5832
|
+
} catch (error) {
|
|
5833
|
+
throw new NativeBuildInputError(`Invalid native build scope: ${error instanceof Error ? error.message : String(error)}.`);
|
|
5834
|
+
}
|
|
5835
|
+
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.`);
|
|
5836
|
+
return scopeKey;
|
|
5837
|
+
}, "nativeBuildScopeKey");
|
|
5838
|
+
var isBuiltInBundle = /* @__PURE__ */ __name(async (db, bundleId) => await db.findOne("native_builds", { min_bundle_id: bundleId }) !== null, "isBuiltInBundle");
|
|
5839
|
+
var problemOf = /* @__PURE__ */ __name(({ id, minBundleId, platform, channel, fingerprintHash, appVersion, gitCommitHash, buildKey, buildNumber, artifact }) => {
|
|
5840
|
+
if (!isUUIDv7(id)) return "id must be a UUIDv7";
|
|
5841
|
+
if (!isUUIDv7(minBundleId)) return "minBundleId must be the UUIDv7 a release build compiles in";
|
|
5842
|
+
if (platform !== "ios" && platform !== "android") return "platform must be ios or android";
|
|
5843
|
+
if (channel.trim().length === 0) return "channel is required";
|
|
5844
|
+
if (gitCommitHash !== null && gitCommitHash.length > GIT_COMMIT_HASH_MAX_LENGTH) return `gitCommitHash must be at most ${GIT_COMMIT_HASH_MAX_LENGTH} characters`;
|
|
5845
|
+
if (fingerprintHash === null === (appVersion === null)) return "exactly one of fingerprintHash and appVersion is required";
|
|
5846
|
+
if (buildKey !== null && !BUILD_KEY.test(buildKey)) return "buildKey must be nbk1: and a SHA-256, or null";
|
|
5847
|
+
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`;
|
|
5848
|
+
const kinds = ARTIFACT_KINDS[platform];
|
|
5849
|
+
if (!kinds.includes(artifact.kind)) return `artifact.kind of an ${platform} build must be ${kinds.join(" or ")}`;
|
|
5850
|
+
if (!Number.isSafeInteger(artifact.byteSize) || artifact.byteSize < 0) return "artifact.byteSize must be a byte count";
|
|
5851
|
+
return null;
|
|
5852
|
+
}, "problemOf");
|
|
5853
|
+
var createNativeBuildApi = /* @__PURE__ */ __name((db, { now = Date.now } = {}) => ({
|
|
5854
|
+
async registerNativeBuild(registration) {
|
|
5855
|
+
const problem = problemOf(registration);
|
|
5856
|
+
if (problem !== null) throw new NativeBuildInputError(`Invalid native build: ${problem}.`);
|
|
5857
|
+
const scopeKey = nativeBuildScopeKey(registration);
|
|
5858
|
+
const channelId = `channel:${encodeChannelKey(registration.channel)}`;
|
|
5859
|
+
const { embeddedBundle } = registration;
|
|
5860
|
+
const row = {
|
|
5861
|
+
id: registration.id,
|
|
5862
|
+
scope_key: scopeKey,
|
|
5863
|
+
platform: registration.platform,
|
|
5864
|
+
channel_id: channelId,
|
|
5865
|
+
fingerprint_hash: registration.fingerprintHash,
|
|
5866
|
+
app_version: registration.appVersion,
|
|
5867
|
+
min_bundle_id: registration.minBundleId,
|
|
5868
|
+
artifact_kind: registration.artifact.kind,
|
|
5869
|
+
artifact_storage_uri: registration.artifact.storageUri,
|
|
5870
|
+
byte_size: registration.artifact.byteSize,
|
|
5871
|
+
git_commit_hash: registration.gitCommitHash,
|
|
5872
|
+
created_at_ms: now(),
|
|
5873
|
+
build_key: registration.buildKey,
|
|
5874
|
+
build_number: registration.buildNumber ?? null,
|
|
5875
|
+
submitted_to: null,
|
|
5876
|
+
submitted_at_ms: null,
|
|
5877
|
+
submitted_version: null
|
|
5878
|
+
};
|
|
5879
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
5880
|
+
await insertChannel(db, {
|
|
5881
|
+
id: channelId,
|
|
5882
|
+
name: registration.channel
|
|
5883
|
+
});
|
|
5884
|
+
try {
|
|
5885
|
+
await db.transaction(async (tx) => {
|
|
5886
|
+
tx.create("bundles", {
|
|
5887
|
+
id: registration.minBundleId,
|
|
5888
|
+
platform: registration.platform,
|
|
5889
|
+
git_commit_hash: registration.gitCommitHash,
|
|
5890
|
+
metadata: embeddedBundle.metadata ?? {},
|
|
5891
|
+
manifest_storage_uri: embeddedBundle.manifestStorageUri,
|
|
5892
|
+
manifest_file_hash: embeddedBundle.manifestFileHash,
|
|
5893
|
+
asset_base_storage_uri: embeddedBundle.assetBaseStorageUri
|
|
5894
|
+
});
|
|
5895
|
+
tx.create("native_builds", row);
|
|
5896
|
+
});
|
|
5897
|
+
break;
|
|
5898
|
+
} catch (error) {
|
|
5899
|
+
if (!(error instanceof DatabaseConstraintError)) throw error;
|
|
5900
|
+
if (error.reason === "exists" || error.reason === "unique") throw new DatabaseConstraintError(error.reason, "native_builds");
|
|
5901
|
+
if (attempt < 3 && error.reason === "not_found" && error.model === "channels") continue;
|
|
5902
|
+
throw error;
|
|
5903
|
+
}
|
|
5904
|
+
}
|
|
5905
|
+
return row;
|
|
5906
|
+
},
|
|
5907
|
+
async getNativeBuild(id) {
|
|
5908
|
+
const row = await db.findOne("native_builds", { id });
|
|
5909
|
+
return row === null ? null : pick2(row);
|
|
5910
|
+
},
|
|
5911
|
+
async findNativeBuild(scope, options) {
|
|
5912
|
+
const scopeKey = nativeBuildScopeKey(scope);
|
|
5913
|
+
const buildKey = options?.buildKey;
|
|
5914
|
+
if (buildKey !== void 0 && !BUILD_KEY.test(buildKey)) throw new NativeBuildInputError("Invalid native build key: it must be nbk1: and a SHA-256.");
|
|
5915
|
+
const { rows } = buildKey === void 0 ? await db.findMany("native_builds", {
|
|
5916
|
+
index: "byScope",
|
|
5917
|
+
where: { scope_key: scopeKey },
|
|
5918
|
+
order: "desc",
|
|
5919
|
+
limit: 1
|
|
5920
|
+
}) : await db.findMany("native_builds", {
|
|
5921
|
+
index: "byBuildKey",
|
|
5922
|
+
where: {
|
|
5923
|
+
scope_key: scopeKey,
|
|
5924
|
+
build_key: buildKey
|
|
5925
|
+
},
|
|
5926
|
+
order: "desc",
|
|
5927
|
+
limit: 1
|
|
5928
|
+
});
|
|
5929
|
+
return rows[0] === void 0 ? null : pick2(rows[0]);
|
|
5930
|
+
},
|
|
5931
|
+
async recordNativeBuildSubmission(id, { destination, version: version2 }) {
|
|
5932
|
+
if (version2.trim().length === 0 || version2.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.`);
|
|
5933
|
+
return db.transaction(async (tx) => {
|
|
5934
|
+
const row = await tx.findOne("native_builds", { id });
|
|
5935
|
+
if (row === null) return null;
|
|
5936
|
+
const build = pick2(row);
|
|
5937
|
+
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}.`);
|
|
5938
|
+
const set = {
|
|
5939
|
+
submitted_to: destination,
|
|
5940
|
+
submitted_at_ms: now(),
|
|
5941
|
+
submitted_version: version2
|
|
5942
|
+
};
|
|
5943
|
+
tx.update("native_builds", row, set);
|
|
5944
|
+
return {
|
|
5945
|
+
...build,
|
|
5946
|
+
...set
|
|
5947
|
+
};
|
|
5948
|
+
});
|
|
5949
|
+
},
|
|
5950
|
+
async deleteNativeBuild(id) {
|
|
5951
|
+
return db.transaction(async (tx) => {
|
|
5952
|
+
const row = await tx.findOne("native_builds", { id });
|
|
5953
|
+
if (row === null) return {
|
|
5954
|
+
deleted: false,
|
|
5955
|
+
reason: "not_found"
|
|
5956
|
+
};
|
|
5957
|
+
await tx.delete("native_builds", row);
|
|
5958
|
+
const bundle = await tx.findOne("bundles", { id: row.min_bundle_id });
|
|
5959
|
+
if (bundle !== null) await tx.delete("bundles", bundle);
|
|
5960
|
+
return {
|
|
5961
|
+
deleted: true,
|
|
5962
|
+
nativeBuild: pick2(row)
|
|
5963
|
+
};
|
|
5964
|
+
});
|
|
5965
|
+
},
|
|
5966
|
+
async listNativeBuilds(input) {
|
|
5967
|
+
const page2 = {
|
|
5968
|
+
order: input.order ?? "asc",
|
|
5969
|
+
limit: input.limit
|
|
5970
|
+
};
|
|
5971
|
+
const { rows } = input.channelId === void 0 ? await db.findMany("native_builds", {
|
|
5972
|
+
index: "all",
|
|
5973
|
+
where: {},
|
|
5974
|
+
...page2,
|
|
5975
|
+
...after(input)
|
|
5976
|
+
}) : await db.findMany("native_builds", {
|
|
5977
|
+
index: "byChannel",
|
|
5978
|
+
where: { channel_id: input.channelId },
|
|
5979
|
+
...page2,
|
|
5980
|
+
...after(input)
|
|
5981
|
+
});
|
|
5982
|
+
return rows.map(pick2);
|
|
5983
|
+
}
|
|
5984
|
+
}), "createNativeBuildApi");
|
|
5985
|
+
|
|
5650
5986
|
// ../../packages/server/dist/core/releases.mjs
|
|
5651
5987
|
var PAGE3 = 500;
|
|
5652
5988
|
var fail2 = /* @__PURE__ */ __name((code, message) => {
|
|
@@ -5929,13 +6265,14 @@ var createCoreOperations = /* @__PURE__ */ __name((db, { now = Date.now } = {})
|
|
|
5929
6265
|
}
|
|
5930
6266
|
}, "withChannels");
|
|
5931
6267
|
const deployOnce = /* @__PURE__ */ __name(async (deployments) => {
|
|
5932
|
-
const
|
|
5933
|
-
for (const { release } of deployments) if (!
|
|
6268
|
+
const channels2 = /* @__PURE__ */ new Map();
|
|
6269
|
+
for (const { release } of deployments) if (!channels2.has(release.channel)) channels2.set(release.channel, await ensureChannel(release.channel));
|
|
5934
6270
|
const platforms = /* @__PURE__ */ new Map();
|
|
5935
6271
|
for (const deployment of deployments) {
|
|
5936
6272
|
if (!("bundleId" in deployment) || platforms.has(deployment.bundleId)) continue;
|
|
5937
6273
|
const row = await db.findOne("bundles", { id: deployment.bundleId });
|
|
5938
6274
|
if (row === null) throw new DatabaseBundleNotFoundError(deployment.bundleId);
|
|
6275
|
+
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.`);
|
|
5939
6276
|
platforms.set(deployment.bundleId, toBundleRow(row).platform);
|
|
5940
6277
|
}
|
|
5941
6278
|
const updatedAtMs = now();
|
|
@@ -5944,7 +6281,7 @@ var createCoreOperations = /* @__PURE__ */ __name((db, { now = Date.now } = {})
|
|
|
5944
6281
|
const stored2 = "bundleId" in deployment;
|
|
5945
6282
|
const bundleId = stored2 ? deployment.bundleId : deployment.bundle.id;
|
|
5946
6283
|
const platform = stored2 ? platforms.get(deployment.bundleId) : deployment.bundle.platform;
|
|
5947
|
-
const scope = scopeOf(
|
|
6284
|
+
const scope = scopeOf(channels2.get(policy.channel), platform, policy.fingerprintHash);
|
|
5948
6285
|
return {
|
|
5949
6286
|
scope,
|
|
5950
6287
|
...stored2 ? {} : { bundle: {
|
|
@@ -6131,7 +6468,8 @@ var createCoreOperations = /* @__PURE__ */ __name((db, { now = Date.now } = {})
|
|
|
6131
6468
|
id
|
|
6132
6469
|
};
|
|
6133
6470
|
const { id: _id, ...set } = bundleToRow(next);
|
|
6134
|
-
|
|
6471
|
+
const counted = set.platform === current.platform || !await isBuiltInBundle(tx, id);
|
|
6472
|
+
updateBundle(tx, current, set, { counted });
|
|
6135
6473
|
if (patches !== void 0) await replaceBundlePatches(tx, current, bundleToPatchRows({
|
|
6136
6474
|
...next,
|
|
6137
6475
|
patches
|
|
@@ -6142,7 +6480,9 @@ var createCoreOperations = /* @__PURE__ */ __name((db, { now = Date.now } = {})
|
|
|
6142
6480
|
await db.transaction(async (tx) => {
|
|
6143
6481
|
for (const id of new Set(ids)) {
|
|
6144
6482
|
const current = await tx.findOne("bundles", { id });
|
|
6145
|
-
if (current
|
|
6483
|
+
if (current === null) continue;
|
|
6484
|
+
if (await isBuiltInBundle(tx, id)) throw new DatabaseRowReferencedError();
|
|
6485
|
+
await deleteBundle(tx, current);
|
|
6146
6486
|
}
|
|
6147
6487
|
});
|
|
6148
6488
|
} catch (error) {
|
|
@@ -6188,6 +6528,7 @@ var createCoreApi = /* @__PURE__ */ __name((db, storage, options = {}) => {
|
|
|
6188
6528
|
return {
|
|
6189
6529
|
...reads,
|
|
6190
6530
|
...createCoreOperations(purgingCachedRoutes(db, options.onCachedRoutesChange), options),
|
|
6531
|
+
...createNativeBuildApi(db, options),
|
|
6191
6532
|
/** One read, which checks the schema settings first. */
|
|
6192
6533
|
ready: /* @__PURE__ */ __name(async () => {
|
|
6193
6534
|
await reads.listReleaseCatalogs({ limit: 1 });
|
|
@@ -6383,6 +6724,10 @@ var answer = /* @__PURE__ */ __name(async (operation) => {
|
|
|
6383
6724
|
code: "REFERENCED",
|
|
6384
6725
|
error: error.message
|
|
6385
6726
|
}, 409);
|
|
6727
|
+
if (error instanceof DatabaseConstraintError && error.model === "native_builds") return json({
|
|
6728
|
+
code: "NATIVE_BUILD_EXISTS",
|
|
6729
|
+
error: "A native build with this ID or built-in bundle ID is already registered."
|
|
6730
|
+
}, 409);
|
|
6386
6731
|
throw error;
|
|
6387
6732
|
}
|
|
6388
6733
|
}, "answer");
|
|
@@ -6478,6 +6823,40 @@ var bundleUpdateOf = /* @__PURE__ */ __name((value, bundleId) => {
|
|
|
6478
6823
|
if (id !== void 0 && id !== bundleId) throw new HandlerBadRequestError("Bundle id mismatch");
|
|
6479
6824
|
return update;
|
|
6480
6825
|
}, "bundleUpdateOf");
|
|
6826
|
+
var NATIVE_BUILD_ARTIFACT_KINDS = /* @__PURE__ */ new Set([
|
|
6827
|
+
"apk",
|
|
6828
|
+
"aab",
|
|
6829
|
+
"ipa",
|
|
6830
|
+
"app"
|
|
6831
|
+
]);
|
|
6832
|
+
var nativeBuildScopeOf = /* @__PURE__ */ __name((url) => {
|
|
6833
|
+
const params = url.searchParams;
|
|
6834
|
+
const platform = platformOf(url);
|
|
6835
|
+
const channel = params.get("channel");
|
|
6836
|
+
const fingerprintHash = params.get("fingerprintHash");
|
|
6837
|
+
const appVersion = params.get("appVersion");
|
|
6838
|
+
if (platform === void 0 || !isText(channel) || fingerprintHash === null === (appVersion === null) || fingerprintHash === "" || appVersion === "") throw new HandlerBadRequestError("A native build lookup needs platform, channel, and fingerprintHash or appVersion.");
|
|
6839
|
+
return fingerprintHash !== null ? {
|
|
6840
|
+
platform,
|
|
6841
|
+
channel,
|
|
6842
|
+
fingerprintHash,
|
|
6843
|
+
appVersion: null
|
|
6844
|
+
} : {
|
|
6845
|
+
platform,
|
|
6846
|
+
channel,
|
|
6847
|
+
fingerprintHash: null,
|
|
6848
|
+
appVersion
|
|
6849
|
+
};
|
|
6850
|
+
}, "nativeBuildScopeOf");
|
|
6851
|
+
var refusingInvalid = /* @__PURE__ */ __name(async (operation) => {
|
|
6852
|
+
try {
|
|
6853
|
+
return await operation();
|
|
6854
|
+
} catch (error) {
|
|
6855
|
+
if (error instanceof NativeBuildInputError) throw new HandlerBadRequestError(error.message);
|
|
6856
|
+
throw error;
|
|
6857
|
+
}
|
|
6858
|
+
}, "refusingInvalid");
|
|
6859
|
+
var isNativeBuildRegistration = /* @__PURE__ */ __name((value) => isRecord3(value) && isText(value.id) && isText(value.minBundleId) && typeof value.platform === "string" && isPlatform(value.platform) && isText(value.channel) && isTextOrNull(value.fingerprintHash) && isTextOrNull(value.appVersion) && value.fingerprintHash === null !== (value.appVersion === null) && isTextOrNull(value.gitCommitHash) && isTextOrNull(value.buildKey) && (value.buildNumber === void 0 || isTextOrNull(value.buildNumber)) && isRecord3(value.artifact) && typeof value.artifact.kind === "string" && NATIVE_BUILD_ARTIFACT_KINDS.has(value.artifact.kind) && isText(value.artifact.storageUri) && Number.isSafeInteger(value.artifact.byteSize) && value.artifact.byteSize >= 0 && isRecord3(value.embeddedBundle) && isText(value.embeddedBundle.manifestStorageUri) && isText(value.embeddedBundle.manifestFileHash) && isText(value.embeddedBundle.assetBaseStorageUri) && (value.embeddedBundle.metadata === void 0 || isRecord3(value.embeddedBundle.metadata)), "isNativeBuildRegistration");
|
|
6481
6860
|
var scopeKeyOf = /* @__PURE__ */ __name((params) => decodeMaybe(requireRouteParam(params, "scopeKey")) ?? "", "scopeKeyOf");
|
|
6482
6861
|
var createAdminRouteHandlers = /* @__PURE__ */ __name(() => ({
|
|
6483
6862
|
listBundles: onCore(async (core, request) => {
|
|
@@ -6596,6 +6975,37 @@ var createAdminRouteHandlers = /* @__PURE__ */ __name(() => ({
|
|
|
6596
6975
|
}) });
|
|
6597
6976
|
}),
|
|
6598
6977
|
preflightReleaseCatalogRebuild: onCore(async (core, _request, params) => json({ data: await core.preflightReleaseCatalogRebuild(scopeKeyOf(params)) })),
|
|
6978
|
+
registerNativeBuild: onCore(async (core, request) => {
|
|
6979
|
+
const input = await body(request);
|
|
6980
|
+
if (!isNativeBuildRegistration(input)) throw new HandlerBadRequestError("Invalid native build");
|
|
6981
|
+
return json({ data: await refusingInvalid(() => core.registerNativeBuild(input)) }, 201);
|
|
6982
|
+
}),
|
|
6983
|
+
findNativeBuild: onCore(async (core, request) => {
|
|
6984
|
+
const url = new URL(request.url);
|
|
6985
|
+
const scope = nativeBuildScopeOf(url);
|
|
6986
|
+
const buildKey = url.searchParams.get("buildKey");
|
|
6987
|
+
return json({ data: await refusingInvalid(() => buildKey === null ? core.findNativeBuild(scope) : core.findNativeBuild(scope, { buildKey })) });
|
|
6988
|
+
}),
|
|
6989
|
+
getNativeBuild: onCore(async (core, _request, params) => json({ data: await core.getNativeBuild(requireRouteParam(params, "id")) })),
|
|
6990
|
+
listNativeBuilds: onCore(async (core, request) => {
|
|
6991
|
+
const url = new URL(request.url);
|
|
6992
|
+
const input = keyset(url);
|
|
6993
|
+
const channelId = url.searchParams.get("channelId");
|
|
6994
|
+
return page(await core.listNativeBuilds({
|
|
6995
|
+
...input,
|
|
6996
|
+
...channelId === null ? {} : { channelId }
|
|
6997
|
+
}), input, ({ id }) => id);
|
|
6998
|
+
}),
|
|
6999
|
+
recordNativeBuildSubmission: onCore(async (core, request, params) => {
|
|
7000
|
+
const input = await body(request);
|
|
7001
|
+
if (!isRecord3(input) || input.destination !== "testflight" && input.destination !== "play-internal" || !isText(input.version)) throw new HandlerBadRequestError("A native build submission needs destination (testflight or play-internal) and version.");
|
|
7002
|
+
const submission = {
|
|
7003
|
+
destination: input.destination,
|
|
7004
|
+
version: input.version
|
|
7005
|
+
};
|
|
7006
|
+
return json({ data: await refusingInvalid(() => core.recordNativeBuildSubmission(requireRouteParam(params, "id"), submission)) });
|
|
7007
|
+
}),
|
|
7008
|
+
deleteNativeBuild: onCore(async (core, _request, params) => json({ data: await core.deleteNativeBuild(requireRouteParam(params, "id")) })),
|
|
6599
7009
|
deleteBundles: onCore(async (core, request) => {
|
|
6600
7010
|
const { ids } = await body(request);
|
|
6601
7011
|
if (!Array.isArray(ids) || !ids.every((id) => typeof id === "string" && id.length > 0)) throw new HandlerBadRequestError("Bundle deletion needs ids");
|
|
@@ -6713,6 +7123,36 @@ var ADMIN_ROUTES = [
|
|
|
6713
7123
|
method: "GET",
|
|
6714
7124
|
path: "/base-candidates/:candidateKey",
|
|
6715
7125
|
handler: "findBaseCandidates"
|
|
7126
|
+
},
|
|
7127
|
+
{
|
|
7128
|
+
method: "POST",
|
|
7129
|
+
path: "/native-builds",
|
|
7130
|
+
handler: "registerNativeBuild"
|
|
7131
|
+
},
|
|
7132
|
+
{
|
|
7133
|
+
method: "GET",
|
|
7134
|
+
path: "/native-builds/latest",
|
|
7135
|
+
handler: "findNativeBuild"
|
|
7136
|
+
},
|
|
7137
|
+
{
|
|
7138
|
+
method: "GET",
|
|
7139
|
+
path: "/native-builds/:id",
|
|
7140
|
+
handler: "getNativeBuild"
|
|
7141
|
+
},
|
|
7142
|
+
{
|
|
7143
|
+
method: "GET",
|
|
7144
|
+
path: "/native-builds",
|
|
7145
|
+
handler: "listNativeBuilds"
|
|
7146
|
+
},
|
|
7147
|
+
{
|
|
7148
|
+
method: "POST",
|
|
7149
|
+
path: "/native-builds/:id/submission",
|
|
7150
|
+
handler: "recordNativeBuildSubmission"
|
|
7151
|
+
},
|
|
7152
|
+
{
|
|
7153
|
+
method: "DELETE",
|
|
7154
|
+
path: "/native-builds/:id",
|
|
7155
|
+
handler: "deleteNativeBuild"
|
|
6716
7156
|
}
|
|
6717
7157
|
];
|
|
6718
7158
|
|
|
@@ -8621,7 +9061,7 @@ var readUserInstallationCursor = /* @__PURE__ */ __name((value, userId) => {
|
|
|
8621
9061
|
};
|
|
8622
9062
|
}, "readUserInstallationCursor");
|
|
8623
9063
|
var compareEventNewest = /* @__PURE__ */ __name((left, right) => right.received_at_ms - left.received_at_ms || compareInsightsText(right.id, left.id), "compareEventNewest");
|
|
8624
|
-
var isAfterEventCursor = /* @__PURE__ */ __name((row,
|
|
9064
|
+
var isAfterEventCursor = /* @__PURE__ */ __name((row, after2) => row.received_at_ms < after2.receivedAtMs || row.received_at_ms === after2.receivedAtMs && compareInsightsText(row.id, after2.id) < 0, "isAfterEventCursor");
|
|
8625
9065
|
var assertEventRows = /* @__PURE__ */ __name((rows, input) => {
|
|
8626
9066
|
if (rows.length > input.limit) throw new Error("Insights database returned too many event rows.");
|
|
8627
9067
|
for (let index = 0; index < rows.length; index += 1) {
|
|
@@ -8698,7 +9138,7 @@ var toInstallationRow = /* @__PURE__ */ __name((row) => ({
|
|
|
8698
9138
|
userId: row.user_id
|
|
8699
9139
|
}), "toInstallationRow");
|
|
8700
9140
|
var defaultEventStart = /* @__PURE__ */ __name((filter, beforeReceivedAtMs) => filter.kind === "installationMovement" ? 0 : Math.max(0, beforeReceivedAtMs - EVENT_LIST_RANGE_MS), "defaultEventStart");
|
|
8701
|
-
var isInsideBounds = /* @__PURE__ */ __name(({ after }, bounds) =>
|
|
9141
|
+
var isInsideBounds = /* @__PURE__ */ __name(({ after: after2 }, bounds) => after2.receivedAtMs >= bounds.sinceMs && after2.receivedAtMs < bounds.beforeReceivedAtMs, "isInsideBounds");
|
|
8702
9142
|
var readEventBounds = /* @__PURE__ */ __name((input, cursor, filter) => {
|
|
8703
9143
|
const beforeReceivedAtMs = cursor?.beforeReceivedAtMs ?? (input.beforeReceivedAtMs === void 0 ? Date.now() : requireTimestamp(input.beforeReceivedAtMs, "event cutoff"));
|
|
8704
9144
|
if (cursor !== void 0 && input.beforeReceivedAtMs !== void 0 && input.beforeReceivedAtMs !== beforeReceivedAtMs) throw new InsightsBadRequestError("Insights cursor does not match the requested event cutoff.");
|
|
@@ -9714,7 +10154,7 @@ var getAppUsage = /* @__PURE__ */ __name(async (db, input, now, retention) => {
|
|
|
9714
10154
|
});
|
|
9715
10155
|
const versions = /* @__PURE__ */ new Map();
|
|
9716
10156
|
const platforms = /* @__PURE__ */ new Map();
|
|
9717
|
-
const
|
|
10157
|
+
const bundles2 = /* @__PURE__ */ new Map();
|
|
9718
10158
|
const countBundle2 = /* @__PURE__ */ __name((row, releaseId, builtinBundleId, count) => {
|
|
9719
10159
|
const key = JSON.stringify([
|
|
9720
10160
|
row.app_version,
|
|
@@ -9722,12 +10162,12 @@ var getAppUsage = /* @__PURE__ */ __name(async (db, input, now, retention) => {
|
|
|
9722
10162
|
releaseId,
|
|
9723
10163
|
builtinBundleId
|
|
9724
10164
|
]);
|
|
9725
|
-
|
|
10165
|
+
bundles2.set(key, {
|
|
9726
10166
|
appVersion: row.app_version,
|
|
9727
10167
|
platform: row.platform,
|
|
9728
10168
|
releaseId,
|
|
9729
10169
|
builtinBundleId,
|
|
9730
|
-
installations: (
|
|
10170
|
+
installations: (bundles2.get(key)?.installations ?? 0) + count
|
|
9731
10171
|
});
|
|
9732
10172
|
}, "countBundle");
|
|
9733
10173
|
for (const row of distribution) {
|
|
@@ -9749,7 +10189,7 @@ var getAppUsage = /* @__PURE__ */ __name(async (db, input, now, retention) => {
|
|
|
9749
10189
|
appVersions: sortedVersions.map(({ name }) => name),
|
|
9750
10190
|
versions: sortedVersions,
|
|
9751
10191
|
platforms: byInstallations(platforms),
|
|
9752
|
-
bundleDistribution: [...
|
|
10192
|
+
bundleDistribution: [...bundles2.values()].filter(({ installations }) => installations > 0),
|
|
9753
10193
|
measuredAtMs: at
|
|
9754
10194
|
};
|
|
9755
10195
|
}, "getAppUsage");
|
|
@@ -10427,10 +10867,10 @@ var validateRules = /* @__PURE__ */ __name((value, path, issue) => {
|
|
|
10427
10867
|
return;
|
|
10428
10868
|
}
|
|
10429
10869
|
case "channel": {
|
|
10430
|
-
const
|
|
10870
|
+
const channels2 = textList(raw2.channels, `${rulePath}.channels`, issue, (text) => visibleText(text, MAX_RULE_TEXT_LENGTH), `must be a channel name of 1-${MAX_RULE_TEXT_LENGTH} characters.`);
|
|
10431
10871
|
rules.push({
|
|
10432
10872
|
type,
|
|
10433
|
-
channels
|
|
10873
|
+
channels: channels2
|
|
10434
10874
|
});
|
|
10435
10875
|
return;
|
|
10436
10876
|
}
|