@hot-updater/azure 1.2.0-rc.12 → 1.2.0-rc.14
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/functions/index.mjs +130 -114
- package/dist/index.cjs +27 -62
- package/dist/index.d.cts +2 -3
- package/dist/index.d.mts +2 -3
- package/dist/index.mjs +27 -62
- package/package.json +6 -6
package/dist/functions/index.mjs
CHANGED
|
@@ -6104,6 +6104,10 @@ const nativeBuilds = defineTable({
|
|
|
6104
6104
|
type: "string",
|
|
6105
6105
|
maxLength: 255,
|
|
6106
6106
|
required: false
|
|
6107
|
+
},
|
|
6108
|
+
store_build: {
|
|
6109
|
+
type: "boolean",
|
|
6110
|
+
required: false
|
|
6107
6111
|
}
|
|
6108
6112
|
}, {
|
|
6109
6113
|
key: ["id"],
|
|
@@ -6116,6 +6120,11 @@ const nativeBuilds = defineTable({
|
|
|
6116
6120
|
eq: ["scope_key", "build_key"],
|
|
6117
6121
|
sort: ["id"]
|
|
6118
6122
|
},
|
|
6123
|
+
byStoreBuild: {
|
|
6124
|
+
eq: ["scope_key", "store_build"],
|
|
6125
|
+
sort: [],
|
|
6126
|
+
unique: true
|
|
6127
|
+
},
|
|
6119
6128
|
byChannel: {
|
|
6120
6129
|
eq: ["channel_id"],
|
|
6121
6130
|
sort: ["id"],
|
|
@@ -6311,10 +6320,7 @@ const LOG_TABLES = [
|
|
|
6311
6320
|
const LEASE_TABLE = "aggregate_lease";
|
|
6312
6321
|
const LEASE = { id: "compaction" };
|
|
6313
6322
|
const LEASE_MS = 3e4;
|
|
6314
|
-
const WINDOW_MS$1 =
|
|
6315
|
-
log: 6e4,
|
|
6316
|
-
memory: 15e3
|
|
6317
|
-
};
|
|
6323
|
+
const WINDOW_MS$1 = 6e4;
|
|
6318
6324
|
const LOG_PAGE = 200;
|
|
6319
6325
|
const GROUPS = {
|
|
6320
6326
|
commit: 1,
|
|
@@ -6322,7 +6328,6 @@ const GROUPS = {
|
|
|
6322
6328
|
flush: 256
|
|
6323
6329
|
};
|
|
6324
6330
|
const ATTEMPTS = 5;
|
|
6325
|
-
const MAX_BUFFERED = 1e4;
|
|
6326
6331
|
const log$1 = defineTable({
|
|
6327
6332
|
id: {
|
|
6328
6333
|
type: "string",
|
|
@@ -6423,8 +6428,8 @@ const rewrite = (change, key, current) => {
|
|
|
6423
6428
|
previous: current
|
|
6424
6429
|
};
|
|
6425
6430
|
};
|
|
6426
|
-
const compile = async (adapter, changes
|
|
6427
|
-
const keys = changes.map(({
|
|
6431
|
+
const compile = async (adapter, changes) => {
|
|
6432
|
+
const keys = changes.map(({ key }) => [...key.slice(0, -1), 0]);
|
|
6428
6433
|
const reads = /* @__PURE__ */ new Map();
|
|
6429
6434
|
changes.forEach((change, at) => {
|
|
6430
6435
|
if (rewrites(change)) reads.set(change.model, [...reads.get(change.model) ?? [], at]);
|
|
@@ -6470,7 +6475,7 @@ const encode$1 = async (changes) => {
|
|
|
6470
6475
|
const zipped = await deflate(json);
|
|
6471
6476
|
return zipped !== void 0 && zipped.length < json.length ? `z${zipped}` : json;
|
|
6472
6477
|
};
|
|
6473
|
-
const createAggregateBatches = ({ engine, adapter, schema, batching: {
|
|
6478
|
+
const createAggregateBatches = ({ engine, adapter, schema, batching: { windowMs = WINDOW_MS$1 }, now }) => {
|
|
6474
6479
|
if (!schema.models.has(LEASE_TABLE)) throw new DatabaseTransactionError("An engine that batches aggregates needs aggregateBatchingModule in its schema.");
|
|
6475
6480
|
const holder = crypto.randomUUID().replaceAll("-", "");
|
|
6476
6481
|
const table = (name) => schema.models.get(name).table;
|
|
@@ -6497,61 +6502,6 @@ const createAggregateBatches = ({ engine, adapter, schema, batching: { mode = "l
|
|
|
6497
6502
|
}
|
|
6498
6503
|
return changes;
|
|
6499
6504
|
};
|
|
6500
|
-
const apply = async (changes, shard) => {
|
|
6501
|
-
let rest = changes;
|
|
6502
|
-
let ops = [];
|
|
6503
|
-
let read = true;
|
|
6504
|
-
for (let failures = 0; rest.length > 0 && failures < ATTEMPTS;) try {
|
|
6505
|
-
if (read) ops = await compile(adapter, rest, shard);
|
|
6506
|
-
let size = rest.length;
|
|
6507
|
-
const chunk = () => ops.slice(0, size).filter(isOp);
|
|
6508
|
-
while (size > 1 && !adapter.fits(chunk())) size = Math.ceil(size / 2);
|
|
6509
|
-
let result = { ok: true };
|
|
6510
|
-
if (chunk().length > 0) result = await adapter.write(chunk()).catch((error) => {
|
|
6511
|
-
warn("flush")(error);
|
|
6512
|
-
return { ok: true };
|
|
6513
|
-
});
|
|
6514
|
-
read = !result.ok && !("retry" in result);
|
|
6515
|
-
if (result.ok) {
|
|
6516
|
-
rest = rest.slice(size);
|
|
6517
|
-
ops = ops.slice(size);
|
|
6518
|
-
failures = 0;
|
|
6519
|
-
} else {
|
|
6520
|
-
failures += 1;
|
|
6521
|
-
if (!read) await sleep$1(failures);
|
|
6522
|
-
}
|
|
6523
|
-
} catch (error) {
|
|
6524
|
-
warn("flush")(error);
|
|
6525
|
-
return rest;
|
|
6526
|
-
}
|
|
6527
|
-
return rest;
|
|
6528
|
-
};
|
|
6529
|
-
let buffer = /* @__PURE__ */ new Map();
|
|
6530
|
-
let timer;
|
|
6531
|
-
let flushing;
|
|
6532
|
-
const shards = /* @__PURE__ */ new Map();
|
|
6533
|
-
const processShard = (model) => {
|
|
6534
|
-
if (!shards.has(model)) shards.set(model, Math.floor(Math.random() * shapeOf(model).shards));
|
|
6535
|
-
return shards.get(model);
|
|
6536
|
-
};
|
|
6537
|
-
const schedule = () => {
|
|
6538
|
-
if (timer !== void 0 || buffer.size === 0) return;
|
|
6539
|
-
timer = setTimeout(() => {
|
|
6540
|
-
timer = void 0;
|
|
6541
|
-
flushBuffer();
|
|
6542
|
-
}, windowMs);
|
|
6543
|
-
timer.unref?.();
|
|
6544
|
-
};
|
|
6545
|
-
const flushBuffer = () => flushing ??= (async () => {
|
|
6546
|
-
clearTimeout(timer);
|
|
6547
|
-
timer = void 0;
|
|
6548
|
-
const pending = [...buffer.values()];
|
|
6549
|
-
buffer = /* @__PURE__ */ new Map();
|
|
6550
|
-
mergeAll(buffer, await apply(pending, processShard));
|
|
6551
|
-
})().catch(warn("flush")).finally(() => {
|
|
6552
|
-
flushing = void 0;
|
|
6553
|
-
schedule();
|
|
6554
|
-
});
|
|
6555
6505
|
let nextDue = 0;
|
|
6556
6506
|
let compacting;
|
|
6557
6507
|
const known = /* @__PURE__ */ new Map();
|
|
@@ -6673,7 +6623,7 @@ const createAggregateBatches = ({ engine, adapter, schema, batching: { mode = "l
|
|
|
6673
6623
|
more
|
|
6674
6624
|
};
|
|
6675
6625
|
};
|
|
6676
|
-
const rowsFor = async (changes) => (await compile(adapter, changes
|
|
6626
|
+
const rowsFor = async (changes) => (await compile(adapter, changes)).filter(isOp);
|
|
6677
6627
|
const applyGroup = async (held, logs) => {
|
|
6678
6628
|
for (let count = logs.length; count > 0; count = Math.floor(count / 2)) {
|
|
6679
6629
|
const batch = logs.slice(0, count);
|
|
@@ -6753,19 +6703,6 @@ const createAggregateBatches = ({ engine, adapter, schema, batching: { mode = "l
|
|
|
6753
6703
|
}))).then((pages) => pages.some((page) => page.rows.length > 0)).finally(() => {
|
|
6754
6704
|
checking = void 0;
|
|
6755
6705
|
});
|
|
6756
|
-
const committed = async (changes) => {
|
|
6757
|
-
if (mode === "log") {
|
|
6758
|
-
if (now() >= nextDue) await compact(false, GROUPS.commit);
|
|
6759
|
-
return;
|
|
6760
|
-
}
|
|
6761
|
-
mergeAll(buffer, changes.values());
|
|
6762
|
-
schedule();
|
|
6763
|
-
if (buffer.size >= MAX_BUFFERED) await flushBuffer();
|
|
6764
|
-
};
|
|
6765
|
-
const flush = async () => {
|
|
6766
|
-
await flushBuffer();
|
|
6767
|
-
if (mode === "log" && await hasPending()) await compact(true, GROUPS.flush);
|
|
6768
|
-
};
|
|
6769
6706
|
return {
|
|
6770
6707
|
async transaction(fn) {
|
|
6771
6708
|
let changes = /* @__PURE__ */ new Map();
|
|
@@ -6791,7 +6728,7 @@ const createAggregateBatches = ({ engine, adapter, schema, batching: { mode = "l
|
|
|
6791
6728
|
aggregate
|
|
6792
6729
|
});
|
|
6793
6730
|
if (failure) throw failure.error;
|
|
6794
|
-
if (
|
|
6731
|
+
if (attempt.size > 0) {
|
|
6795
6732
|
const id = `${now().toString(36).padStart(9, "0")}${holder.slice(0, 8)}${crypto.randomUUID().slice(0, 8)}`;
|
|
6796
6733
|
const shard = Math.floor(Math.random() * LOG_TABLES.length);
|
|
6797
6734
|
const merged = mergeAll(/* @__PURE__ */ new Map(), attempt.values());
|
|
@@ -6805,24 +6742,18 @@ const createAggregateBatches = ({ engine, adapter, schema, batching: { mode = "l
|
|
|
6805
6742
|
open = false;
|
|
6806
6743
|
}
|
|
6807
6744
|
});
|
|
6808
|
-
if (changes.size > 0) await
|
|
6745
|
+
if (changes.size > 0 && now() >= nextDue) await compact(false, GROUPS.commit);
|
|
6809
6746
|
return result;
|
|
6810
6747
|
},
|
|
6811
6748
|
async beforeRead(name) {
|
|
6812
6749
|
if (!isBatched(schema.models.get(name))) return;
|
|
6813
|
-
if (
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
return false;
|
|
6818
|
-
})) await compact(true, GROUPS.read);
|
|
6819
|
-
}
|
|
6750
|
+
if (await hasPending().catch((error) => {
|
|
6751
|
+
warn("read")(error);
|
|
6752
|
+
return false;
|
|
6753
|
+
})) await compact(true, GROUPS.read);
|
|
6820
6754
|
},
|
|
6821
|
-
flush
|
|
6822
|
-
|
|
6823
|
-
clearTimeout(timer);
|
|
6824
|
-
timer = void 0;
|
|
6825
|
-
await flush();
|
|
6755
|
+
async flush() {
|
|
6756
|
+
if (await hasPending()) await compact(true, GROUPS.flush);
|
|
6826
6757
|
}
|
|
6827
6758
|
};
|
|
6828
6759
|
};
|
|
@@ -7788,9 +7719,6 @@ const createDatabaseEngine = ({ batching, now = Date.now, meter, ...options }) =
|
|
|
7788
7719
|
flush: async () => {
|
|
7789
7720
|
await batches?.flush();
|
|
7790
7721
|
},
|
|
7791
|
-
dispose: async () => {
|
|
7792
|
-
await batches?.dispose();
|
|
7793
|
-
},
|
|
7794
7722
|
database(module) {
|
|
7795
7723
|
const name = (model) => module.namespace ? `${module.namespace}_${model}` : model;
|
|
7796
7724
|
return {
|
|
@@ -7858,8 +7786,7 @@ const createEngine = (database, { plugins = [], now = Date.now } = {}) => {
|
|
|
7858
7786
|
if (module === void 0) throw new HotUpdaterConfigError(`Plugin "${plugin.id}" is not one of this engine's plugins.`);
|
|
7859
7787
|
return engine.database(module);
|
|
7860
7788
|
},
|
|
7861
|
-
flush: () => engine.flush()
|
|
7862
|
-
dispose: () => engine.dispose()
|
|
7789
|
+
flush: () => engine.flush()
|
|
7863
7790
|
};
|
|
7864
7791
|
};
|
|
7865
7792
|
//#endregion
|
|
@@ -8118,7 +8045,7 @@ const createKvAdapter = ({ store, tablePrefix = "" }) => {
|
|
|
8118
8045
|
};
|
|
8119
8046
|
//#endregion
|
|
8120
8047
|
//#region ../../packages/server/dist/version.mjs
|
|
8121
|
-
const HOT_UPDATER_SERVER_VERSION = "1.2.0-rc.
|
|
8048
|
+
const HOT_UPDATER_SERVER_VERSION = "1.2.0-rc.14";
|
|
8122
8049
|
//#endregion
|
|
8123
8050
|
//#region ../../packages/server/dist/handlerVersionRoutes.mjs
|
|
8124
8051
|
const createVersionRouteHandlers = (plugins = []) => {
|
|
@@ -9226,8 +9153,12 @@ const pick = (row) => ({
|
|
|
9226
9153
|
build_number: row.build_number ?? null,
|
|
9227
9154
|
submitted_to: row.submitted_to ?? null,
|
|
9228
9155
|
submitted_at_ms: row.submitted_at_ms ?? null,
|
|
9229
|
-
submitted_version: row.submitted_version ?? null
|
|
9156
|
+
submitted_version: row.submitted_version ?? null,
|
|
9157
|
+
store_build: row.store_build === true ? true : null
|
|
9230
9158
|
});
|
|
9159
|
+
const SANDBOX_CHANNEL_PREFIX = "sandbox-";
|
|
9160
|
+
const OLDER_BUILDS_PAGE = 20;
|
|
9161
|
+
const MARK_ATTEMPTS = 8;
|
|
9231
9162
|
const SUBMISSION_DESTINATIONS = {
|
|
9232
9163
|
android: "play-internal",
|
|
9233
9164
|
ios: "testflight"
|
|
@@ -9293,7 +9224,8 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
9293
9224
|
build_number: registration.buildNumber ?? null,
|
|
9294
9225
|
submitted_to: null,
|
|
9295
9226
|
submitted_at_ms: null,
|
|
9296
|
-
submitted_version: null
|
|
9227
|
+
submitted_version: null,
|
|
9228
|
+
store_build: null
|
|
9297
9229
|
};
|
|
9298
9230
|
for (let attempt = 1;; attempt += 1) {
|
|
9299
9231
|
await insertChannel(db, {
|
|
@@ -9331,21 +9263,45 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
9331
9263
|
const scopeKey = nativeBuildScopeKey(scope);
|
|
9332
9264
|
const buildKey = options?.buildKey;
|
|
9333
9265
|
if (buildKey !== void 0 && !BUILD_KEY.test(buildKey)) throw new NativeBuildInputError("Invalid native build key: it must be nbk1: and a SHA-256.");
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
where: { scope_key: scopeKey },
|
|
9337
|
-
order: "desc",
|
|
9338
|
-
limit: 1
|
|
9339
|
-
}) : await db.findMany("native_builds", {
|
|
9340
|
-
index: "byBuildKey",
|
|
9341
|
-
where: {
|
|
9266
|
+
if (options?.storeBuild) {
|
|
9267
|
+
const marked = await db.findOne("native_builds", {
|
|
9342
9268
|
scope_key: scopeKey,
|
|
9343
|
-
|
|
9344
|
-
}
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9269
|
+
store_build: true
|
|
9270
|
+
});
|
|
9271
|
+
return marked === null ? null : pick(marked);
|
|
9272
|
+
}
|
|
9273
|
+
const read = (limit, before) => {
|
|
9274
|
+
const page = {
|
|
9275
|
+
order: "desc",
|
|
9276
|
+
limit,
|
|
9277
|
+
...before === void 0 ? {} : { range: { lt: before } }
|
|
9278
|
+
};
|
|
9279
|
+
return buildKey === void 0 ? db.findMany("native_builds", {
|
|
9280
|
+
index: "byScope",
|
|
9281
|
+
where: { scope_key: scopeKey },
|
|
9282
|
+
...page
|
|
9283
|
+
}) : db.findMany("native_builds", {
|
|
9284
|
+
index: "byBuildKey",
|
|
9285
|
+
where: {
|
|
9286
|
+
scope_key: scopeKey,
|
|
9287
|
+
build_key: buildKey
|
|
9288
|
+
},
|
|
9289
|
+
...page
|
|
9290
|
+
});
|
|
9291
|
+
};
|
|
9292
|
+
const { rows } = await read(1);
|
|
9293
|
+
const newest = rows[0];
|
|
9294
|
+
if (newest === void 0) return null;
|
|
9295
|
+
const bundleId = options?.builtInOlderThan;
|
|
9296
|
+
if (bundleId === void 0 || newest.min_bundle_id < bundleId) return pick(newest);
|
|
9297
|
+
for (let before = newest.id;;) {
|
|
9298
|
+
const { rows: older } = await read(OLDER_BUILDS_PAGE, before);
|
|
9299
|
+
const usable = older.find(({ min_bundle_id }) => min_bundle_id < bundleId);
|
|
9300
|
+
if (usable !== void 0) return pick(usable);
|
|
9301
|
+
const last = older.at(-1);
|
|
9302
|
+
if (older.length < OLDER_BUILDS_PAGE || last === void 0) return null;
|
|
9303
|
+
before = last.id;
|
|
9304
|
+
}
|
|
9349
9305
|
},
|
|
9350
9306
|
async recordNativeBuildSubmission(id, { destination, version }) {
|
|
9351
9307
|
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.`);
|
|
@@ -9366,6 +9322,49 @@ const createNativeBuildApi = (db, { now = Date.now } = {}) => ({
|
|
|
9366
9322
|
};
|
|
9367
9323
|
});
|
|
9368
9324
|
},
|
|
9325
|
+
async markNativeBuild(id, { storeBuild }) {
|
|
9326
|
+
for (let attempt = 1;; attempt += 1) try {
|
|
9327
|
+
return await db.transaction(async (tx) => {
|
|
9328
|
+
const row = await tx.findOne("native_builds", { id });
|
|
9329
|
+
if (row === null) return null;
|
|
9330
|
+
const build = pick(row);
|
|
9331
|
+
if (!storeBuild) {
|
|
9332
|
+
if (build.store_build !== null) tx.update("native_builds", row, { store_build: null });
|
|
9333
|
+
return {
|
|
9334
|
+
nativeBuild: {
|
|
9335
|
+
...build,
|
|
9336
|
+
store_build: null
|
|
9337
|
+
},
|
|
9338
|
+
previous: null
|
|
9339
|
+
};
|
|
9340
|
+
}
|
|
9341
|
+
if (decodeChannelKey(build.channel_id.slice(8)).startsWith(SANDBOX_CHANNEL_PREFIX)) throw new NativeBuildInputError("A build on a sandbox channel can't be the store build.");
|
|
9342
|
+
if (build.store_build !== null) return {
|
|
9343
|
+
nativeBuild: build,
|
|
9344
|
+
previous: null
|
|
9345
|
+
};
|
|
9346
|
+
const holder = await tx.findOne("native_builds", {
|
|
9347
|
+
scope_key: build.scope_key,
|
|
9348
|
+
store_build: true
|
|
9349
|
+
});
|
|
9350
|
+
if (holder !== null) tx.update("native_builds", holder, { store_build: null });
|
|
9351
|
+
tx.update("native_builds", row, { store_build: true });
|
|
9352
|
+
return {
|
|
9353
|
+
nativeBuild: {
|
|
9354
|
+
...build,
|
|
9355
|
+
store_build: true
|
|
9356
|
+
},
|
|
9357
|
+
previous: holder === null ? null : {
|
|
9358
|
+
...pick(holder),
|
|
9359
|
+
store_build: null
|
|
9360
|
+
}
|
|
9361
|
+
};
|
|
9362
|
+
});
|
|
9363
|
+
} catch (error) {
|
|
9364
|
+
if (attempt < MARK_ATTEMPTS && error instanceof DatabaseConstraintError && error.reason === "unique") continue;
|
|
9365
|
+
throw error;
|
|
9366
|
+
}
|
|
9367
|
+
},
|
|
9369
9368
|
async deleteNativeBuild(id) {
|
|
9370
9369
|
return db.transaction(async (tx) => {
|
|
9371
9370
|
const row = await tx.findOne("native_builds", { id });
|
|
@@ -10392,7 +10391,13 @@ const createAdminRouteHandlers = () => ({
|
|
|
10392
10391
|
const url = new URL(request.url);
|
|
10393
10392
|
const scope = nativeBuildScopeOf(url);
|
|
10394
10393
|
const buildKey = url.searchParams.get("buildKey");
|
|
10395
|
-
|
|
10394
|
+
const builtInOlderThan = url.searchParams.get("builtInOlderThan");
|
|
10395
|
+
const options = {
|
|
10396
|
+
...buildKey === null ? {} : { buildKey },
|
|
10397
|
+
...url.searchParams.get("storeBuild") === "true" ? { storeBuild: true } : {},
|
|
10398
|
+
...builtInOlderThan === null ? {} : { builtInOlderThan }
|
|
10399
|
+
};
|
|
10400
|
+
return json$2({ data: await refusingInvalid(() => Object.keys(options).length === 0 ? core.findNativeBuild(scope) : core.findNativeBuild(scope, options)) });
|
|
10396
10401
|
}),
|
|
10397
10402
|
getNativeBuild: onCore(async (core, _request, params) => json$2({ data: await core.getNativeBuild(requireRouteParam(params, "id")) })),
|
|
10398
10403
|
listNativeBuilds: onCore(async (core, request) => {
|
|
@@ -10413,6 +10418,12 @@ const createAdminRouteHandlers = () => ({
|
|
|
10413
10418
|
};
|
|
10414
10419
|
return json$2({ data: await refusingInvalid(() => core.recordNativeBuildSubmission(requireRouteParam(params, "id"), submission)) });
|
|
10415
10420
|
}),
|
|
10421
|
+
markNativeBuild: onCore(async (core, request, params) => {
|
|
10422
|
+
const input = await body$1(request);
|
|
10423
|
+
if (!isRecord$5(input) || typeof input.storeBuild !== "boolean") throw new HandlerBadRequestError("A native build's store build mark needs storeBuild, true or false.");
|
|
10424
|
+
const mark = { storeBuild: input.storeBuild };
|
|
10425
|
+
return json$2({ data: await refusingInvalid(() => core.markNativeBuild(requireRouteParam(params, "id"), mark)) });
|
|
10426
|
+
}),
|
|
10416
10427
|
deleteNativeBuild: onCore(async (core, _request, params) => json$2({ data: await core.deleteNativeBuild(requireRouteParam(params, "id")) })),
|
|
10417
10428
|
deleteBundles: onCore(async (core, request) => {
|
|
10418
10429
|
const { ids } = await body$1(request);
|
|
@@ -10557,6 +10568,11 @@ const ADMIN_ROUTES = [
|
|
|
10557
10568
|
path: "/native-builds/:id/submission",
|
|
10558
10569
|
handler: "recordNativeBuildSubmission"
|
|
10559
10570
|
},
|
|
10571
|
+
{
|
|
10572
|
+
method: "POST",
|
|
10573
|
+
path: "/native-builds/:id/store-build",
|
|
10574
|
+
handler: "markNativeBuild"
|
|
10575
|
+
},
|
|
10560
10576
|
{
|
|
10561
10577
|
method: "DELETE",
|
|
10562
10578
|
path: "/native-builds/:id",
|
package/dist/index.cjs
CHANGED
|
@@ -811,23 +811,23 @@ const NO_CREDENTIAL_ERRORS = /* @__PURE__ */ new Set([
|
|
|
811
811
|
"AuthenticationRequiredError",
|
|
812
812
|
"CredentialUnavailableError"
|
|
813
813
|
]);
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
"
|
|
818
|
-
];
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
if (
|
|
830
|
-
return
|
|
814
|
+
/** The variables `DefaultAzureCredential` signs in with first, if complete. */
|
|
815
|
+
const environmentCredentialVariables = () => {
|
|
816
|
+
const { env } = process;
|
|
817
|
+
if (!env["AZURE_TENANT_ID"] || !env["AZURE_CLIENT_ID"]) return void 0;
|
|
818
|
+
if (env["AZURE_CLIENT_SECRET"]) return "AZURE_CLIENT_SECRET";
|
|
819
|
+
if (env["AZURE_CLIENT_CERTIFICATE_PATH"]) return "AZURE_CLIENT_CERTIFICATE_PATH";
|
|
820
|
+
if (env["AZURE_USERNAME"] && env["AZURE_PASSWORD"]) return "AZURE_PASSWORD";
|
|
821
|
+
};
|
|
822
|
+
/**
|
|
823
|
+
* The one step that can fix the identity in use: `az login` changes nothing
|
|
824
|
+
* while the environment's identity comes first.
|
|
825
|
+
*/
|
|
826
|
+
const signInHint = (credential) => {
|
|
827
|
+
if (credential !== void 0) return [`Check the credential passed to ${NAME}().`];
|
|
828
|
+
const variable = environmentCredentialVariables();
|
|
829
|
+
if (variable) return [`Fix or unset ${variable}, AZURE_TENANT_ID, and AZURE_CLIENT_ID.`];
|
|
830
|
+
return process.env["CI"] ? ["Sign in with azure/login."] : ["Sign in:", "az login"];
|
|
831
831
|
};
|
|
832
832
|
/** An error of one line each, for a terminal. */
|
|
833
833
|
const failure = (lines, cause) => new Error(lines.join("\n"), cause === void 0 ? void 0 : { cause });
|
|
@@ -940,56 +940,21 @@ const keyVault = ({ credential, prefix = "hot-updater-", vaultName, vaultUrl })
|
|
|
940
940
|
const name = errorName(error);
|
|
941
941
|
const status = statusOf(error);
|
|
942
942
|
const code = innerCode(error) ?? error?.code;
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
if (
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
...SIGN_IN_HINT
|
|
950
|
-
], error);
|
|
951
|
-
if (status === 401) return failure([
|
|
952
|
-
`Azure Key Vault refused the sign-in to ${verb} a secret.`,
|
|
953
|
-
secret,
|
|
954
|
-
...detail,
|
|
955
|
-
"Sign in again.",
|
|
956
|
-
...SIGN_IN_HINT
|
|
957
|
-
], error);
|
|
958
|
-
if (code === "SecretDisabled") return failure([
|
|
959
|
-
`Azure Key Vault can't ${verb} a secret.`,
|
|
960
|
-
secret,
|
|
961
|
-
"Its current version is disabled. Enable it, or store a new value."
|
|
962
|
-
], error);
|
|
963
|
-
if (status === 403) return failure([
|
|
964
|
-
`Azure Key Vault refused to ${verb} a secret.`,
|
|
965
|
-
secret,
|
|
966
|
-
...detail,
|
|
967
|
-
`The Azure identity needs the ${role} role on the vault.`,
|
|
968
|
-
`With access policies, it needs the secret permissions ${role === READ_ROLE ? "get" : "get and set"}.`,
|
|
969
|
-
"A firewall may also refuse this network."
|
|
970
|
-
], error);
|
|
971
|
-
if (code === "ObjectIsBeingDeleted" || /being deleted/iu.test(errorMessage(error))) return failure([
|
|
972
|
-
`Azure Key Vault can't ${verb} a secret: it is being deleted.`,
|
|
973
|
-
secret,
|
|
974
|
-
"Try again in a minute."
|
|
975
|
-
], error);
|
|
943
|
+
if (NO_CREDENTIAL_ERRORS.has(name)) return failure([`${NAME} found no Azure identity.`, ...signInHint(credential)], error);
|
|
944
|
+
if (status === 401 || name === "AuthenticationError") return failure(["Azure rejected the sign-in.", ...signInHint(credential)], error);
|
|
945
|
+
if (/\bENOTFOUND\b/u.test(`${code} ${errorMessage(error)}`) && errorMessage(error).includes(vault().hostname)) return failure([`${NAME} can't find ${vault().hostname}.`, `Check ${vaultUrl === void 0 ? "vaultName" : "vaultUrl"}.`], error);
|
|
946
|
+
if (code === "SecretDisabled") return failure([`${id}'s current version is disabled. Enable it, or store a new value.`], error);
|
|
947
|
+
if (status === 403) return failure([`Azure Key Vault refused to ${verb} ${id}.`, code === "ForbiddenByFirewall" ? "Its firewall refuses this network." : code === "ForbiddenByPolicy" ? `The identity needs the secret permissions ${role === READ_ROLE ? "get" : "get and set"}.` : `The identity needs the ${role} role on the vault.`], error);
|
|
948
|
+
if (code === "ObjectIsBeingDeleted" || /being deleted/iu.test(errorMessage(error))) return failure([`${id} is being deleted. Try again in a minute.`], error);
|
|
976
949
|
if (status === 409 || code === "ObjectIsDeletedButRecoverable") {
|
|
977
950
|
const vaultName = vaultLabel();
|
|
978
951
|
return failure([
|
|
979
|
-
|
|
980
|
-
secret
|
|
981
|
-
|
|
982
|
-
`az keyvault secret recover --vault-name ${vaultName} \\`,
|
|
983
|
-
` --name ${id}`,
|
|
984
|
-
`az keyvault secret purge --vault-name ${vaultName} \\`,
|
|
985
|
-
` --name ${id}`
|
|
952
|
+
`${id} was deleted. Recover it, or purge it:`,
|
|
953
|
+
`az keyvault secret recover --vault-name ${vaultName} --name ${id}`,
|
|
954
|
+
`az keyvault secret purge --vault-name ${vaultName} --name ${id}`
|
|
986
955
|
], error);
|
|
987
956
|
}
|
|
988
|
-
return failure([
|
|
989
|
-
`Azure Key Vault failed to ${verb} a secret.`,
|
|
990
|
-
secret,
|
|
991
|
-
...detail
|
|
992
|
-
], error);
|
|
957
|
+
return failure([`Azure Key Vault failed to ${verb} ${id}.`, `${code || name || "Error"}${status ? ` (${status})` : ""}: ${errorMessage(error)}.`], error);
|
|
993
958
|
};
|
|
994
959
|
const read = async (client, id) => {
|
|
995
960
|
let value;
|
package/dist/index.d.cts
CHANGED
|
@@ -58,9 +58,8 @@ type CosmosDatabaseConfig = Omit<CosmosClientOptions, "consistencyLevel" | "conn
|
|
|
58
58
|
readonly containerId?: string;
|
|
59
59
|
/**
|
|
60
60
|
* Cosmos DB bills each item a write touches, so Insights' aggregates are
|
|
61
|
-
* batched: through log rows a compaction merges (the default)
|
|
62
|
-
*
|
|
63
|
-
* each event.
|
|
61
|
+
* batched: through log rows a compaction merges (the default). `false` commits
|
|
62
|
+
* them with each event.
|
|
64
63
|
*/
|
|
65
64
|
readonly aggregateBatching?: EngineDatabase["aggregateBatching"] | false;
|
|
66
65
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -58,9 +58,8 @@ type CosmosDatabaseConfig = Omit<CosmosClientOptions, "consistencyLevel" | "conn
|
|
|
58
58
|
readonly containerId?: string;
|
|
59
59
|
/**
|
|
60
60
|
* Cosmos DB bills each item a write touches, so Insights' aggregates are
|
|
61
|
-
* batched: through log rows a compaction merges (the default)
|
|
62
|
-
*
|
|
63
|
-
* each event.
|
|
61
|
+
* batched: through log rows a compaction merges (the default). `false` commits
|
|
62
|
+
* them with each event.
|
|
64
63
|
*/
|
|
65
64
|
readonly aggregateBatching?: EngineDatabase["aggregateBatching"] | false;
|
|
66
65
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -810,23 +810,23 @@ const NO_CREDENTIAL_ERRORS = /* @__PURE__ */ new Set([
|
|
|
810
810
|
"AuthenticationRequiredError",
|
|
811
811
|
"CredentialUnavailableError"
|
|
812
812
|
]);
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
"
|
|
817
|
-
];
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
if (
|
|
829
|
-
return
|
|
813
|
+
/** The variables `DefaultAzureCredential` signs in with first, if complete. */
|
|
814
|
+
const environmentCredentialVariables = () => {
|
|
815
|
+
const { env } = process;
|
|
816
|
+
if (!env["AZURE_TENANT_ID"] || !env["AZURE_CLIENT_ID"]) return void 0;
|
|
817
|
+
if (env["AZURE_CLIENT_SECRET"]) return "AZURE_CLIENT_SECRET";
|
|
818
|
+
if (env["AZURE_CLIENT_CERTIFICATE_PATH"]) return "AZURE_CLIENT_CERTIFICATE_PATH";
|
|
819
|
+
if (env["AZURE_USERNAME"] && env["AZURE_PASSWORD"]) return "AZURE_PASSWORD";
|
|
820
|
+
};
|
|
821
|
+
/**
|
|
822
|
+
* The one step that can fix the identity in use: `az login` changes nothing
|
|
823
|
+
* while the environment's identity comes first.
|
|
824
|
+
*/
|
|
825
|
+
const signInHint = (credential) => {
|
|
826
|
+
if (credential !== void 0) return [`Check the credential passed to ${NAME}().`];
|
|
827
|
+
const variable = environmentCredentialVariables();
|
|
828
|
+
if (variable) return [`Fix or unset ${variable}, AZURE_TENANT_ID, and AZURE_CLIENT_ID.`];
|
|
829
|
+
return process.env["CI"] ? ["Sign in with azure/login."] : ["Sign in:", "az login"];
|
|
830
830
|
};
|
|
831
831
|
/** An error of one line each, for a terminal. */
|
|
832
832
|
const failure = (lines, cause) => new Error(lines.join("\n"), cause === void 0 ? void 0 : { cause });
|
|
@@ -939,56 +939,21 @@ const keyVault = ({ credential, prefix = "hot-updater-", vaultName, vaultUrl })
|
|
|
939
939
|
const name = errorName(error);
|
|
940
940
|
const status = statusOf(error);
|
|
941
941
|
const code = innerCode(error) ?? error?.code;
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
if (
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
...SIGN_IN_HINT
|
|
949
|
-
], error);
|
|
950
|
-
if (status === 401) return failure([
|
|
951
|
-
`Azure Key Vault refused the sign-in to ${verb} a secret.`,
|
|
952
|
-
secret,
|
|
953
|
-
...detail,
|
|
954
|
-
"Sign in again.",
|
|
955
|
-
...SIGN_IN_HINT
|
|
956
|
-
], error);
|
|
957
|
-
if (code === "SecretDisabled") return failure([
|
|
958
|
-
`Azure Key Vault can't ${verb} a secret.`,
|
|
959
|
-
secret,
|
|
960
|
-
"Its current version is disabled. Enable it, or store a new value."
|
|
961
|
-
], error);
|
|
962
|
-
if (status === 403) return failure([
|
|
963
|
-
`Azure Key Vault refused to ${verb} a secret.`,
|
|
964
|
-
secret,
|
|
965
|
-
...detail,
|
|
966
|
-
`The Azure identity needs the ${role} role on the vault.`,
|
|
967
|
-
`With access policies, it needs the secret permissions ${role === READ_ROLE ? "get" : "get and set"}.`,
|
|
968
|
-
"A firewall may also refuse this network."
|
|
969
|
-
], error);
|
|
970
|
-
if (code === "ObjectIsBeingDeleted" || /being deleted/iu.test(errorMessage(error))) return failure([
|
|
971
|
-
`Azure Key Vault can't ${verb} a secret: it is being deleted.`,
|
|
972
|
-
secret,
|
|
973
|
-
"Try again in a minute."
|
|
974
|
-
], error);
|
|
942
|
+
if (NO_CREDENTIAL_ERRORS.has(name)) return failure([`${NAME} found no Azure identity.`, ...signInHint(credential)], error);
|
|
943
|
+
if (status === 401 || name === "AuthenticationError") return failure(["Azure rejected the sign-in.", ...signInHint(credential)], error);
|
|
944
|
+
if (/\bENOTFOUND\b/u.test(`${code} ${errorMessage(error)}`) && errorMessage(error).includes(vault().hostname)) return failure([`${NAME} can't find ${vault().hostname}.`, `Check ${vaultUrl === void 0 ? "vaultName" : "vaultUrl"}.`], error);
|
|
945
|
+
if (code === "SecretDisabled") return failure([`${id}'s current version is disabled. Enable it, or store a new value.`], error);
|
|
946
|
+
if (status === 403) return failure([`Azure Key Vault refused to ${verb} ${id}.`, code === "ForbiddenByFirewall" ? "Its firewall refuses this network." : code === "ForbiddenByPolicy" ? `The identity needs the secret permissions ${role === READ_ROLE ? "get" : "get and set"}.` : `The identity needs the ${role} role on the vault.`], error);
|
|
947
|
+
if (code === "ObjectIsBeingDeleted" || /being deleted/iu.test(errorMessage(error))) return failure([`${id} is being deleted. Try again in a minute.`], error);
|
|
975
948
|
if (status === 409 || code === "ObjectIsDeletedButRecoverable") {
|
|
976
949
|
const vaultName = vaultLabel();
|
|
977
950
|
return failure([
|
|
978
|
-
|
|
979
|
-
secret
|
|
980
|
-
|
|
981
|
-
`az keyvault secret recover --vault-name ${vaultName} \\`,
|
|
982
|
-
` --name ${id}`,
|
|
983
|
-
`az keyvault secret purge --vault-name ${vaultName} \\`,
|
|
984
|
-
` --name ${id}`
|
|
951
|
+
`${id} was deleted. Recover it, or purge it:`,
|
|
952
|
+
`az keyvault secret recover --vault-name ${vaultName} --name ${id}`,
|
|
953
|
+
`az keyvault secret purge --vault-name ${vaultName} --name ${id}`
|
|
985
954
|
], error);
|
|
986
955
|
}
|
|
987
|
-
return failure([
|
|
988
|
-
`Azure Key Vault failed to ${verb} a secret.`,
|
|
989
|
-
secret,
|
|
990
|
-
...detail
|
|
991
|
-
], error);
|
|
956
|
+
return failure([`Azure Key Vault failed to ${verb} ${id}.`, `${code || name || "Error"}${status ? ` (${status})` : ""}: ${errorMessage(error)}.`], error);
|
|
992
957
|
};
|
|
993
958
|
const read = async (client, id) => {
|
|
994
959
|
let value;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/azure",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.2.0-rc.
|
|
4
|
+
"version": "1.2.0-rc.14",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.23.4 || ^24.22.1 || >=26.12.1"
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"@azure/identity": "4.14.0",
|
|
45
45
|
"@azure/keyvault-secrets": "4.11.2",
|
|
46
46
|
"@azure/storage-blob": "12.34.0",
|
|
47
|
-
"@hot-updater/cli-tools": "1.2.0-rc.
|
|
48
|
-
"@hot-updater/
|
|
49
|
-
"@hot-updater/
|
|
50
|
-
"@hot-updater/
|
|
47
|
+
"@hot-updater/cli-tools": "1.2.0-rc.14",
|
|
48
|
+
"@hot-updater/protocol": "1.2.0-rc.14",
|
|
49
|
+
"@hot-updater/server": "1.2.0-rc.14",
|
|
50
|
+
"@hot-updater/plugin-core": "1.2.0-rc.14"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@azure/functions": "4.16.5",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"execa": "9.5.2",
|
|
56
56
|
"jszip": "^3.10.1",
|
|
57
57
|
"vitest": "4.1.4",
|
|
58
|
-
"@hot-updater/test-utils": "1.2.0-rc.
|
|
58
|
+
"@hot-updater/test-utils": "1.2.0-rc.14"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "tsdown",
|