@hot-updater/aws 1.0.0-rc.46 → 1.0.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/init/index.cjs +1 -1
- package/dist/init/index.mjs +1 -1
- package/dist/lambda/index.cjs +26 -62
- package/package.json +7 -7
package/dist/init/index.cjs
CHANGED
|
@@ -7800,7 +7800,7 @@ var DynamoDBTableSchemaError = class extends Error {
|
|
|
7800
7800
|
tableName;
|
|
7801
7801
|
name = "DynamoDBTableSchemaError";
|
|
7802
7802
|
constructor(tableName) {
|
|
7803
|
-
super(`DynamoDB table "${tableName}" does not match the Hot Updater schema: a pk and sk key and no secondary index.
|
|
7803
|
+
super(`DynamoDB table "${tableName}" does not match the Hot Updater schema: a pk and sk key and no secondary index. Delete the table and rerun init to create it again.`);
|
|
7804
7804
|
this.tableName = tableName;
|
|
7805
7805
|
}
|
|
7806
7806
|
};
|
package/dist/init/index.mjs
CHANGED
|
@@ -7795,7 +7795,7 @@ var DynamoDBTableSchemaError = class extends Error {
|
|
|
7795
7795
|
tableName;
|
|
7796
7796
|
name = "DynamoDBTableSchemaError";
|
|
7797
7797
|
constructor(tableName) {
|
|
7798
|
-
super(`DynamoDB table "${tableName}" does not match the Hot Updater schema: a pk and sk key and no secondary index.
|
|
7798
|
+
super(`DynamoDB table "${tableName}" does not match the Hot Updater schema: a pk and sk key and no secondary index. Delete the table and rerun init to create it again.`);
|
|
7799
7799
|
this.tableName = tableName;
|
|
7800
7800
|
}
|
|
7801
7801
|
};
|
package/dist/lambda/index.cjs
CHANGED
|
@@ -1154,29 +1154,23 @@ function validateReleases(releases, strategy) {
|
|
|
1154
1154
|
if (seenIds.has(release.id)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Duplicate Release ID: ${release.id}`);
|
|
1155
1155
|
seenIds.add(release.id);
|
|
1156
1156
|
if (release.strategy !== strategy) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has the wrong strategy`);
|
|
1157
|
-
if (release.
|
|
1157
|
+
if (release.bundleId === null) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has no Bundle`);
|
|
1158
1158
|
if (release.targetCohorts.length > 100) throw new ReleaseCatalogCompilationError("TARGET_COHORT_LIMIT", `Release ${release.id} exceeds 100 target cohorts`);
|
|
1159
1159
|
if (strategy === "APP_VERSION" && (release.targetAppVersion === null || release.fingerprintHash !== null)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has an invalid app-version target`);
|
|
1160
1160
|
if (strategy === "FINGERPRINT" && (release.fingerprintHash === null || release.targetAppVersion !== null)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has an invalid fingerprint target`);
|
|
1161
1161
|
}
|
|
1162
|
-
return enabled
|
|
1162
|
+
return enabled;
|
|
1163
1163
|
}
|
|
1164
1164
|
function collectFrontierReleaseIds(releases) {
|
|
1165
1165
|
if (releases.every((release) => normalizeRolloutCohortCount(release.rolloutCohortCount) >= 1e3)) {
|
|
1166
1166
|
const retained = /* @__PURE__ */ new Set();
|
|
1167
1167
|
const bundleIds = /* @__PURE__ */ new Set();
|
|
1168
|
-
let embedded = false;
|
|
1169
1168
|
for (const release of releases) {
|
|
1170
|
-
if (release.
|
|
1171
|
-
if (!embedded) {
|
|
1172
|
-
embedded = true;
|
|
1173
|
-
retained.add(release.id);
|
|
1174
|
-
}
|
|
1175
|
-
} else if (release.bundleId !== null && bundleIds.size < 11 && !bundleIds.has(release.bundleId)) {
|
|
1169
|
+
if (release.bundleId !== null && !bundleIds.has(release.bundleId)) {
|
|
1176
1170
|
bundleIds.add(release.bundleId);
|
|
1177
1171
|
retained.add(release.id);
|
|
1178
1172
|
}
|
|
1179
|
-
if (
|
|
1173
|
+
if (bundleIds.size >= 11) break;
|
|
1180
1174
|
}
|
|
1181
1175
|
return retained;
|
|
1182
1176
|
}
|
|
@@ -1193,7 +1187,6 @@ function collectFrontierReleaseIds(releases) {
|
|
|
1193
1187
|
normalized,
|
|
1194
1188
|
numeric: normalized === void 0 ? null : getNumericCohortValue(normalized),
|
|
1195
1189
|
bundleIds: /* @__PURE__ */ new Set(),
|
|
1196
|
-
embedded: false,
|
|
1197
1190
|
complete: false
|
|
1198
1191
|
};
|
|
1199
1192
|
});
|
|
@@ -1218,16 +1211,11 @@ function collectFrontierReleaseIds(releases) {
|
|
|
1218
1211
|
for (const index of eligibleStateIndexes) {
|
|
1219
1212
|
const state = cohortStates[index];
|
|
1220
1213
|
if (state.complete) continue;
|
|
1221
|
-
if (release.
|
|
1222
|
-
if (!state.embedded) {
|
|
1223
|
-
state.embedded = true;
|
|
1224
|
-
retained.add(release.id);
|
|
1225
|
-
}
|
|
1226
|
-
} else if (release.bundleId !== null && state.bundleIds.size < 11 && !state.bundleIds.has(release.bundleId)) {
|
|
1214
|
+
if (release.bundleId !== null && !state.bundleIds.has(release.bundleId)) {
|
|
1227
1215
|
state.bundleIds.add(release.bundleId);
|
|
1228
1216
|
retained.add(release.id);
|
|
1229
1217
|
}
|
|
1230
|
-
if (state.
|
|
1218
|
+
if (state.bundleIds.size >= 11) {
|
|
1231
1219
|
state.complete = true;
|
|
1232
1220
|
incompleteStateCount -= 1;
|
|
1233
1221
|
}
|
|
@@ -1412,12 +1400,11 @@ function projectCompiledCatalog(catalog, appVersion) {
|
|
|
1412
1400
|
}
|
|
1413
1401
|
function projectCompiledRollbackCatalog(catalog, appVersion) {
|
|
1414
1402
|
let indexes;
|
|
1415
|
-
if (catalog.strategy === "FINGERPRINT") indexes = catalog.rollbackReleaseIndexes
|
|
1403
|
+
if (catalog.strategy === "FINGERPRINT") indexes = catalog.rollbackReleaseIndexes;
|
|
1416
1404
|
else {
|
|
1417
1405
|
const canonicalVersion = appVersion && canonicalizeAppVersion(appVersion);
|
|
1418
1406
|
if (!canonicalVersion) return [];
|
|
1419
|
-
|
|
1420
|
-
indexes = segment?.rollbackReleaseIndexes ?? segment?.releaseIndexes ?? [];
|
|
1407
|
+
indexes = catalog.segments.find((segment) => versionInSegment(canonicalVersion, segment))?.rollbackReleaseIndexes ?? [];
|
|
1421
1408
|
}
|
|
1422
1409
|
return indexes.map((index) => catalog.releaseDescriptors[index]);
|
|
1423
1410
|
}
|
|
@@ -1939,7 +1926,7 @@ const SETTINGS_TABLE = {
|
|
|
1939
1926
|
const ENGINE_SCHEMA_KEY = "schema.engine";
|
|
1940
1927
|
const CORE_SCHEMA_KEY = "schema.core";
|
|
1941
1928
|
const stored = ({ key, expected, found }) => `"${key}" is ${found === null ? "missing" : `"${found}"`}; expected "${expected}"`;
|
|
1942
|
-
const settingMessage = (adapterName, { key, expected, found }) => `Hot Updater schema setting "${key}" for ${adapterName} is ${found === null ? "missing" : `"${found}"`}; expected "${expected}".
|
|
1929
|
+
const settingMessage = (adapterName, { key, expected, found }) => `Hot Updater schema setting "${key}" for ${adapterName} is ${found === null ? "missing" : `"${found}"`}; expected "${expected}". Run \`hot-updater db migrate\`.`;
|
|
1943
1930
|
/** The plugin a `schema.<id>` setting belongs to; undefined for core's and the engine's. */
|
|
1944
1931
|
const pluginOf = ({ key }) => key.startsWith("schema.") && key !== "schema.engine" && key !== CORE_SCHEMA_KEY ? key.slice(7) : void 0;
|
|
1945
1932
|
const listed = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1)}`;
|
|
@@ -2057,26 +2044,13 @@ const METER = Symbol.for("@hot-updater/plugin-core/read-meter");
|
|
|
2057
2044
|
const readMeterOf = (database) => typeof database === "object" && database !== null ? database[METER] : void 0;
|
|
2058
2045
|
//#endregion
|
|
2059
2046
|
//#region ../plugin-core/dist/engine/db/schemaSettings.mjs
|
|
2060
|
-
/** Refuses a database with `schema.core` but no `schema.engine`: it predates the engine. */
|
|
2061
|
-
const assertEngineDatabase = (adapterName, stored) => {
|
|
2062
|
-
if (stored.get("schema.core") && !stored.get("schema.engine")) throw new HotUpdaterSchemaMigrationRequiredError(adapterName, String(stored.get("schema.core").value), {
|
|
2063
|
-
key: ENGINE_SCHEMA_KEY,
|
|
2064
|
-
expected: "1",
|
|
2065
|
-
found: null
|
|
2066
|
-
});
|
|
2067
|
-
};
|
|
2068
2047
|
const storedSettings = async (adapter, keys) => {
|
|
2069
2048
|
const rows = await readSettings(adapter, keys);
|
|
2070
2049
|
return new Map(keys.map((key, position) => [key, rows[position] ?? null]));
|
|
2071
2050
|
};
|
|
2072
|
-
/** Writes the settings rows after every table exists
|
|
2051
|
+
/** Writes the settings rows after every table exists. */
|
|
2073
2052
|
const writeSchemaSettings = async (adapter, adapterName, settings) => {
|
|
2074
|
-
const stored = await storedSettings(adapter,
|
|
2075
|
-
"schema.core",
|
|
2076
|
-
ENGINE_SCHEMA_KEY,
|
|
2077
|
-
...Object.keys(settings)
|
|
2078
|
-
])]);
|
|
2079
|
-
assertEngineDatabase(adapterName, stored);
|
|
2053
|
+
const stored = await storedSettings(adapter, Object.keys(settings));
|
|
2080
2054
|
const ops = Object.entries(settings).flatMap(([key, value]) => {
|
|
2081
2055
|
const row = stored.get(key);
|
|
2082
2056
|
if (row === null || row === void 0) return [{
|
|
@@ -2099,19 +2073,11 @@ const writeSchemaSettings = async (adapter, adapterName, settings) => {
|
|
|
2099
2073
|
}];
|
|
2100
2074
|
});
|
|
2101
2075
|
if (ops.length === 0) return;
|
|
2102
|
-
if (!(await adapter.write(ops)).ok) throw new Error(
|
|
2076
|
+
if (!(await adapter.write(ops)).ok) throw new Error(`${adapterName}: the schema settings changed during the migration; run it again.`);
|
|
2103
2077
|
};
|
|
2104
|
-
/**
|
|
2105
|
-
* Creates or updates every table, then writes the settings rows last. A
|
|
2106
|
-
* pre-engine database is refused before any table changes.
|
|
2107
|
-
*/
|
|
2078
|
+
/** Creates or updates every table, then writes the settings rows last. */
|
|
2108
2079
|
const migrateSchema = async (adapter, adapterName, tables, settings) => {
|
|
2109
2080
|
if (adapter.migrations === void 0) throw new Error(`${adapterName} creates its tables with its own tooling; write only the settings rows.`);
|
|
2110
|
-
const stored = await storedSettings(adapter, ["schema.core", ENGINE_SCHEMA_KEY]).catch((error) => {
|
|
2111
|
-
if (!isMissingSchemaError(error)) throw error;
|
|
2112
|
-
return /* @__PURE__ */ new Map();
|
|
2113
|
-
});
|
|
2114
|
-
assertEngineDatabase(adapterName, stored);
|
|
2115
2081
|
await adapter.migrations.apply([...tables, SETTINGS_TABLE]);
|
|
2116
2082
|
await writeSchemaSettings(adapter, adapterName, settings);
|
|
2117
2083
|
};
|
|
@@ -2403,15 +2369,14 @@ const storedSchemaVersion = (stored) => {
|
|
|
2403
2369
|
const version = stored?.get("schema.core") ?? stored?.get("version");
|
|
2404
2370
|
return typeof version === "string" ? version : void 0;
|
|
2405
2371
|
};
|
|
2406
|
-
/**
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
} : void 0);
|
|
2372
|
+
/**
|
|
2373
|
+
* A v0 database, whose settings hold `version` and no `schema.engine`, is
|
|
2374
|
+
* recreated, never converted.
|
|
2375
|
+
*/
|
|
2376
|
+
const refuseV0Database = (adapterName, stored) => {
|
|
2377
|
+
const version = stored.get("version");
|
|
2378
|
+
if (typeof version !== "string" || stored.has("schema.engine")) return;
|
|
2379
|
+
throw new HotUpdaterSchemaMigrationRequiredError(adapterName, version);
|
|
2415
2380
|
};
|
|
2416
2381
|
//#endregion
|
|
2417
2382
|
//#region ../plugin-core/dist/engine/database/cursor.mjs
|
|
@@ -3474,13 +3439,13 @@ const resolveSchema = (modules) => {
|
|
|
3474
3439
|
* `hot-updater db migrate` for a store whose adapter creates its own tables
|
|
3475
3440
|
* (MongoDB and the key-value stores): the tables and indexes, then the
|
|
3476
3441
|
* settings rows, applied when a setting is missing or different, and refused
|
|
3477
|
-
* on a database
|
|
3442
|
+
* on a v0 database.
|
|
3478
3443
|
*/
|
|
3479
3444
|
const createEngineMigrator = (options) => {
|
|
3480
3445
|
const getVersion = async () => storedSchemaVersion(await options.readSettings());
|
|
3481
3446
|
const makeResult = async (migrate = {}) => {
|
|
3482
3447
|
const stored = await options.readSettings();
|
|
3483
|
-
|
|
3448
|
+
refuseV0Database(options.adapterName, stored);
|
|
3484
3449
|
if (Object.entries(options.settings).every(([key, value]) => stored.get(key) === value)) return getEmptyMigrationResult();
|
|
3485
3450
|
return {
|
|
3486
3451
|
operations: [...options.schema.tables.map((table) => ({
|
|
@@ -4585,7 +4550,7 @@ const createKvAdapter = ({ store, tablePrefix = "" }) => {
|
|
|
4585
4550
|
};
|
|
4586
4551
|
//#endregion
|
|
4587
4552
|
//#region ../../packages/server/dist/version.mjs
|
|
4588
|
-
const HOT_UPDATER_SERVER_VERSION = "1.0.0
|
|
4553
|
+
const HOT_UPDATER_SERVER_VERSION = "1.0.0";
|
|
4589
4554
|
//#endregion
|
|
4590
4555
|
//#region ../../packages/server/dist/handlerVersionRoutes.mjs
|
|
4591
4556
|
/**
|
|
@@ -5274,7 +5239,7 @@ const baseBundleIdsOf = (row, range, bundleId, limit) => {
|
|
|
5274
5239
|
for (const { descriptor, index } of newestFirst) {
|
|
5275
5240
|
if (ids.length >= limit) break;
|
|
5276
5241
|
const id = descriptor.bundleId;
|
|
5277
|
-
if (serving.has(index) &&
|
|
5242
|
+
if (serving.has(index) && id !== null && id < bundleId && !ids.includes(id)) ids.push(id);
|
|
5278
5243
|
}
|
|
5279
5244
|
return ids;
|
|
5280
5245
|
};
|
|
@@ -6040,7 +6005,7 @@ const createCoreOperations = (db, { now = Date.now } = {}) => {
|
|
|
6040
6005
|
}),
|
|
6041
6006
|
promoteRelease: (input) => withChannels(async () => {
|
|
6042
6007
|
const checkSource = (source) => {
|
|
6043
|
-
if (source.
|
|
6008
|
+
if (source.bundle_id === null) throw new ReleaseManagementError("TARGET_RELEASE_INVALID", "Only a Bundle Release can be promoted.");
|
|
6044
6009
|
};
|
|
6045
6010
|
checkSource((await db.transaction((tx) => loadTarget(tx, input))).release);
|
|
6046
6011
|
const targetChannel = input.targetChannel.trim();
|
|
@@ -6239,7 +6204,6 @@ const fail = (message) => {
|
|
|
6239
6204
|
const isRecord$6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6240
6205
|
const checkPlugin = (value, at) => {
|
|
6241
6206
|
if (!isRecord$6(value)) return fail(`${at} is not a plugin.`);
|
|
6242
|
-
if ("kind" in value) fail(`${at} has a kind; core is built in and is never passed as a plugin.`);
|
|
6243
6207
|
const unknown = Object.keys(value).find((key) => !PLUGIN_KEYS.has(key));
|
|
6244
6208
|
if (unknown !== void 0) fail(`${at} has an unknown key "${unknown}".`);
|
|
6245
6209
|
const plugin = value;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/aws",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"es-toolkit": "^1.32.0",
|
|
46
46
|
"execa": "9.5.2",
|
|
47
47
|
"mime": "^4.0.4",
|
|
48
|
-
"@hot-updater/protocol": "1.0.0
|
|
49
|
-
"@hot-updater/test-utils": "1.0.0
|
|
50
|
-
"@hot-updater/mock": "1.0.0
|
|
48
|
+
"@hot-updater/protocol": "1.0.0",
|
|
49
|
+
"@hot-updater/test-utils": "1.0.0",
|
|
50
|
+
"@hot-updater/mock": "1.0.0"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@aws-sdk/client-cloudfront": "3.1066.0",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"@aws-sdk/lib-dynamodb": "3.1066.0",
|
|
64
64
|
"@aws-sdk/s3-request-presigner": "3.1066.0",
|
|
65
65
|
"hono": "4.12.34",
|
|
66
|
-
"@hot-updater/
|
|
67
|
-
"@hot-updater/
|
|
68
|
-
"@hot-updater/
|
|
66
|
+
"@hot-updater/plugin-core": "1.0.0",
|
|
67
|
+
"@hot-updater/server": "1.0.0",
|
|
68
|
+
"@hot-updater/cli-tools": "1.0.0"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
71
|
"build": "tsdown",
|