@hot-updater/firebase 1.0.0-rc.45 → 1.0.0-rc.47
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/firebase/functions/index.cjs +71 -113
- package/dist/init/index.cjs +15 -28
- package/dist/init/index.mjs +16 -29
- package/package.json +7 -7
|
@@ -216,6 +216,13 @@ const isDatabaseJsonValue = (value) => {
|
|
|
216
216
|
const isDatabaseJsonObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && isDatabaseJsonValue(value);
|
|
217
217
|
const isDatabaseMetadataObject = (value) => isDatabaseJsonObject(value) && (!Object.hasOwn(value, "app_version") || typeof value["app_version"] === "string");
|
|
218
218
|
//#endregion
|
|
219
|
+
//#region ../plugin-core/dist/databaseMetadata.mjs
|
|
220
|
+
const bundleMetadataToRow = (metadata) => {
|
|
221
|
+
if (metadata === void 0) return {};
|
|
222
|
+
if (!isDatabaseMetadataObject(metadata)) throw new DatabaseAdapterInputError("invalid-data");
|
|
223
|
+
return metadata;
|
|
224
|
+
};
|
|
225
|
+
//#endregion
|
|
219
226
|
//#region ../../packages/protocol/dist/index.mjs
|
|
220
227
|
const LETTER_DASH_NUMBER$2 = "[a-zA-Z0-9-]";
|
|
221
228
|
const NUMERIC_IDENTIFIER$2 = String.raw`0|[1-9]\d*`;
|
|
@@ -329,7 +336,6 @@ function canonicalizeAppVersion(appVersion) {
|
|
|
329
336
|
const version = coerce$1(appVersion);
|
|
330
337
|
return version ? normalizeFull(version) : null;
|
|
331
338
|
}
|
|
332
|
-
const stripBundleArtifactMetadata = (metadata) => metadata;
|
|
333
339
|
const getManifestStorageUri = (bundle) => bundle.manifestStorageUri;
|
|
334
340
|
const getManifestFileHash = (bundle) => bundle.manifestFileHash;
|
|
335
341
|
const getAssetBaseStorageUri = (bundle) => bundle.assetBaseStorageUri;
|
|
@@ -576,14 +582,6 @@ const NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
|
576
582
|
const UUID_V7_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
577
583
|
const isUUIDv7 = (value) => typeof value === "string" && UUID_V7_PATTERN.test(value);
|
|
578
584
|
//#endregion
|
|
579
|
-
//#region ../plugin-core/dist/databaseMetadata.mjs
|
|
580
|
-
const bundleMetadataToRow = (metadata) => {
|
|
581
|
-
const value = stripBundleArtifactMetadata(metadata);
|
|
582
|
-
if (value === void 0) return {};
|
|
583
|
-
if (!isDatabaseMetadataObject(value)) throw new DatabaseAdapterInputError("invalid-data");
|
|
584
|
-
return value;
|
|
585
|
-
};
|
|
586
|
-
//#endregion
|
|
587
585
|
//#region ../plugin-core/dist/databaseRows.mjs
|
|
588
586
|
const releaseRowToRelease = (row) => ({
|
|
589
587
|
id: row.id,
|
|
@@ -641,7 +639,7 @@ const rowToBundle = (row, patchRows = []) => {
|
|
|
641
639
|
id: row.id,
|
|
642
640
|
platform: row.platform,
|
|
643
641
|
gitCommitHash: row.git_commit_hash,
|
|
644
|
-
metadata:
|
|
642
|
+
metadata: row.metadata,
|
|
645
643
|
manifestStorageUri: row.manifest_storage_uri,
|
|
646
644
|
manifestFileHash: row.manifest_file_hash,
|
|
647
645
|
assetBaseStorageUri: row.asset_base_storage_uri,
|
|
@@ -649,6 +647,35 @@ const rowToBundle = (row, patchRows = []) => {
|
|
|
649
647
|
};
|
|
650
648
|
};
|
|
651
649
|
//#endregion
|
|
650
|
+
//#region ../plugin-core/dist/uuidv7.mjs
|
|
651
|
+
const extractTimestampFromUUIDv7 = (uuid) => {
|
|
652
|
+
if (!isUUIDv7(uuid)) throw new TypeError("Expected a canonical lowercase UUIDv7");
|
|
653
|
+
const timestampHex = uuid.split("-").join("").slice(0, 12);
|
|
654
|
+
return Number.parseInt(timestampHex, 16);
|
|
655
|
+
};
|
|
656
|
+
function createUUIDv7FromTimestampHex(timestampHex) {
|
|
657
|
+
const randomBytes = /* @__PURE__ */ new Uint8Array(10);
|
|
658
|
+
crypto.getRandomValues(randomBytes);
|
|
659
|
+
const randomHex = Array.from(randomBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
660
|
+
const randA = randomHex.slice(0, 3);
|
|
661
|
+
const randBHex = randomHex.slice(3, 19);
|
|
662
|
+
const versionAndRandA = `7${randA}`;
|
|
663
|
+
const variantAndFirstRandB = (128 | parseInt(randBHex.slice(0, 2), 16) & 63).toString(16).padStart(2, "0");
|
|
664
|
+
return [
|
|
665
|
+
timestampHex.slice(0, 8),
|
|
666
|
+
timestampHex.slice(8, 12),
|
|
667
|
+
versionAndRandA,
|
|
668
|
+
variantAndFirstRandB + randBHex.slice(2, 4),
|
|
669
|
+
randBHex.slice(4, 16)
|
|
670
|
+
].join("-");
|
|
671
|
+
}
|
|
672
|
+
const createUUIDv7 = () => createUUIDv7FromTimestampHex(Date.now().toString(16).padStart(12, "0"));
|
|
673
|
+
const createUUIDv7After = (floor, nowMs = Date.now()) => {
|
|
674
|
+
const timestamp = Math.max(nowMs, floor === null ? 0 : extractTimestampFromUUIDv7(floor) + 1);
|
|
675
|
+
if (!Number.isSafeInteger(timestamp) || timestamp < 0 || timestamp >= 2 ** 48) throw new RangeError("UUIDv7 timestamp is out of range");
|
|
676
|
+
return createUUIDv7FromTimestampHex(timestamp.toString(16).padStart(12, "0"));
|
|
677
|
+
};
|
|
678
|
+
//#endregion
|
|
652
679
|
//#region ../plugin-core/dist/node_modules/.pnpm/verkit@0.4.0/node_modules/verkit/dist/comparison-DenM3wCn.mjs
|
|
653
680
|
const LETTER_DASH_NUMBER$1 = "[a-zA-Z0-9-]";
|
|
654
681
|
const NUMERIC_IDENTIFIER$1 = String.raw`0|[1-9]\d*`;
|
|
@@ -995,42 +1022,6 @@ function coerce(value, options = {}) {
|
|
|
995
1022
|
return tryParse(`${major}.${match[3] || "0"}.${match[4] || "0"}${options.includePrerelease && match[5] ? `-${match[5]}` : ""}${options.includePrerelease && match[6] ? `+${match[6]}` : ""}`, options);
|
|
996
1023
|
}
|
|
997
1024
|
//#endregion
|
|
998
|
-
//#region ../plugin-core/dist/semverSatisfies.mjs
|
|
999
|
-
const semverSatisfies = (targetAppVersion, currentVersion) => {
|
|
1000
|
-
const currentCoerce = coerce(currentVersion);
|
|
1001
|
-
if (!currentCoerce) return false;
|
|
1002
|
-
return satisfies(currentCoerce, targetAppVersion);
|
|
1003
|
-
};
|
|
1004
|
-
//#endregion
|
|
1005
|
-
//#region ../plugin-core/dist/uuidv7.mjs
|
|
1006
|
-
const extractTimestampFromUUIDv7 = (uuid) => {
|
|
1007
|
-
if (!isUUIDv7(uuid)) throw new TypeError("Expected a canonical lowercase UUIDv7");
|
|
1008
|
-
const timestampHex = uuid.split("-").join("").slice(0, 12);
|
|
1009
|
-
return Number.parseInt(timestampHex, 16);
|
|
1010
|
-
};
|
|
1011
|
-
function createUUIDv7FromTimestampHex(timestampHex) {
|
|
1012
|
-
const randomBytes = /* @__PURE__ */ new Uint8Array(10);
|
|
1013
|
-
crypto.getRandomValues(randomBytes);
|
|
1014
|
-
const randomHex = Array.from(randomBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1015
|
-
const randA = randomHex.slice(0, 3);
|
|
1016
|
-
const randBHex = randomHex.slice(3, 19);
|
|
1017
|
-
const versionAndRandA = `7${randA}`;
|
|
1018
|
-
const variantAndFirstRandB = (128 | parseInt(randBHex.slice(0, 2), 16) & 63).toString(16).padStart(2, "0");
|
|
1019
|
-
return [
|
|
1020
|
-
timestampHex.slice(0, 8),
|
|
1021
|
-
timestampHex.slice(8, 12),
|
|
1022
|
-
versionAndRandA,
|
|
1023
|
-
variantAndFirstRandB + randBHex.slice(2, 4),
|
|
1024
|
-
randBHex.slice(4, 16)
|
|
1025
|
-
].join("-");
|
|
1026
|
-
}
|
|
1027
|
-
const createUUIDv7 = () => createUUIDv7FromTimestampHex(Date.now().toString(16).padStart(12, "0"));
|
|
1028
|
-
const createUUIDv7After = (floor, nowMs = Date.now()) => {
|
|
1029
|
-
const timestamp = Math.max(nowMs, floor === null ? 0 : extractTimestampFromUUIDv7(floor) + 1);
|
|
1030
|
-
if (!Number.isSafeInteger(timestamp) || timestamp < 0 || timestamp >= 2 ** 48) throw new RangeError("UUIDv7 timestamp is out of range");
|
|
1031
|
-
return createUUIDv7FromTimestampHex(timestamp.toString(16).padStart(12, "0"));
|
|
1032
|
-
};
|
|
1033
|
-
//#endregion
|
|
1034
1025
|
//#region ../plugin-core/dist/releaseCatalogCompiler.mjs
|
|
1035
1026
|
var ReleaseCatalogCompilationError = class extends Error {
|
|
1036
1027
|
code;
|
|
@@ -1138,29 +1129,23 @@ function validateReleases(releases, strategy) {
|
|
|
1138
1129
|
if (seenIds.has(release.id)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Duplicate Release ID: ${release.id}`);
|
|
1139
1130
|
seenIds.add(release.id);
|
|
1140
1131
|
if (release.strategy !== strategy) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has the wrong strategy`);
|
|
1141
|
-
if (release.
|
|
1132
|
+
if (release.bundleId === null) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has no Bundle`);
|
|
1142
1133
|
if (release.targetCohorts.length > 100) throw new ReleaseCatalogCompilationError("TARGET_COHORT_LIMIT", `Release ${release.id} exceeds 100 target cohorts`);
|
|
1143
1134
|
if (strategy === "APP_VERSION" && (release.targetAppVersion === null || release.fingerprintHash !== null)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has an invalid app-version target`);
|
|
1144
1135
|
if (strategy === "FINGERPRINT" && (release.fingerprintHash === null || release.targetAppVersion !== null)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has an invalid fingerprint target`);
|
|
1145
1136
|
}
|
|
1146
|
-
return enabled
|
|
1137
|
+
return enabled;
|
|
1147
1138
|
}
|
|
1148
1139
|
function collectFrontierReleaseIds(releases) {
|
|
1149
1140
|
if (releases.every((release) => normalizeRolloutCohortCount(release.rolloutCohortCount) >= 1e3)) {
|
|
1150
1141
|
const retained = /* @__PURE__ */ new Set();
|
|
1151
1142
|
const bundleIds = /* @__PURE__ */ new Set();
|
|
1152
|
-
let embedded = false;
|
|
1153
1143
|
for (const release of releases) {
|
|
1154
|
-
if (release.
|
|
1155
|
-
if (!embedded) {
|
|
1156
|
-
embedded = true;
|
|
1157
|
-
retained.add(release.id);
|
|
1158
|
-
}
|
|
1159
|
-
} else if (release.bundleId !== null && bundleIds.size < 11 && !bundleIds.has(release.bundleId)) {
|
|
1144
|
+
if (release.bundleId !== null && !bundleIds.has(release.bundleId)) {
|
|
1160
1145
|
bundleIds.add(release.bundleId);
|
|
1161
1146
|
retained.add(release.id);
|
|
1162
1147
|
}
|
|
1163
|
-
if (
|
|
1148
|
+
if (bundleIds.size >= 11) break;
|
|
1164
1149
|
}
|
|
1165
1150
|
return retained;
|
|
1166
1151
|
}
|
|
@@ -1177,7 +1162,6 @@ function collectFrontierReleaseIds(releases) {
|
|
|
1177
1162
|
normalized,
|
|
1178
1163
|
numeric: normalized === void 0 ? null : getNumericCohortValue(normalized),
|
|
1179
1164
|
bundleIds: /* @__PURE__ */ new Set(),
|
|
1180
|
-
embedded: false,
|
|
1181
1165
|
complete: false
|
|
1182
1166
|
};
|
|
1183
1167
|
});
|
|
@@ -1202,16 +1186,11 @@ function collectFrontierReleaseIds(releases) {
|
|
|
1202
1186
|
for (const index of eligibleStateIndexes) {
|
|
1203
1187
|
const state = cohortStates[index];
|
|
1204
1188
|
if (state.complete) continue;
|
|
1205
|
-
if (release.
|
|
1206
|
-
if (!state.embedded) {
|
|
1207
|
-
state.embedded = true;
|
|
1208
|
-
retained.add(release.id);
|
|
1209
|
-
}
|
|
1210
|
-
} else if (release.bundleId !== null && state.bundleIds.size < 11 && !state.bundleIds.has(release.bundleId)) {
|
|
1189
|
+
if (release.bundleId !== null && !state.bundleIds.has(release.bundleId)) {
|
|
1211
1190
|
state.bundleIds.add(release.bundleId);
|
|
1212
1191
|
retained.add(release.id);
|
|
1213
1192
|
}
|
|
1214
|
-
if (state.
|
|
1193
|
+
if (state.bundleIds.size >= 11) {
|
|
1215
1194
|
state.complete = true;
|
|
1216
1195
|
incompleteStateCount -= 1;
|
|
1217
1196
|
}
|
|
@@ -1396,12 +1375,11 @@ function projectCompiledCatalog(catalog, appVersion) {
|
|
|
1396
1375
|
}
|
|
1397
1376
|
function projectCompiledRollbackCatalog(catalog, appVersion) {
|
|
1398
1377
|
let indexes;
|
|
1399
|
-
if (catalog.strategy === "FINGERPRINT") indexes = catalog.rollbackReleaseIndexes
|
|
1378
|
+
if (catalog.strategy === "FINGERPRINT") indexes = catalog.rollbackReleaseIndexes;
|
|
1400
1379
|
else {
|
|
1401
1380
|
const canonicalVersion = appVersion && canonicalizeAppVersion(appVersion);
|
|
1402
1381
|
if (!canonicalVersion) return [];
|
|
1403
|
-
|
|
1404
|
-
indexes = segment?.rollbackReleaseIndexes ?? segment?.releaseIndexes ?? [];
|
|
1382
|
+
indexes = catalog.segments.find((segment) => versionInSegment(canonicalVersion, segment))?.rollbackReleaseIndexes ?? [];
|
|
1405
1383
|
}
|
|
1406
1384
|
return indexes.map((index) => catalog.releaseDescriptors[index]);
|
|
1407
1385
|
}
|
|
@@ -1426,6 +1404,13 @@ var ReleaseCatalogMutationError = class extends Error {
|
|
|
1426
1404
|
}
|
|
1427
1405
|
};
|
|
1428
1406
|
//#endregion
|
|
1407
|
+
//#region ../plugin-core/dist/semverSatisfies.mjs
|
|
1408
|
+
const semverSatisfies = (targetAppVersion, currentVersion) => {
|
|
1409
|
+
const currentCoerce = coerce(currentVersion);
|
|
1410
|
+
if (!currentCoerce) return false;
|
|
1411
|
+
return satisfies(currentCoerce, targetAppVersion);
|
|
1412
|
+
};
|
|
1413
|
+
//#endregion
|
|
1429
1414
|
//#region ../plugin-core/dist/serverPlugin/definePlugin.mjs
|
|
1430
1415
|
/** Declares a plugin; Hot Updater's own plugins and third-party ones use the same contract. */
|
|
1431
1416
|
const definePlugin = (plugin) => plugin;
|
|
@@ -1916,7 +1901,7 @@ const SETTINGS_TABLE = {
|
|
|
1916
1901
|
const ENGINE_SCHEMA_KEY = "schema.engine";
|
|
1917
1902
|
const CORE_SCHEMA_KEY = "schema.core";
|
|
1918
1903
|
const stored = ({ key, expected, found }) => `"${key}" is ${found === null ? "missing" : `"${found}"`}; expected "${expected}"`;
|
|
1919
|
-
const settingMessage = (adapterName, { key, expected, found }) => `Hot Updater schema setting "${key}" for ${adapterName} is ${found === null ? "missing" : `"${found}"`}; expected "${expected}".
|
|
1904
|
+
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\`.`;
|
|
1920
1905
|
/** The plugin a `schema.<id>` setting belongs to; undefined for core's and the engine's. */
|
|
1921
1906
|
const pluginOf = ({ key }) => key.startsWith("schema.") && key !== "schema.engine" && key !== CORE_SCHEMA_KEY ? key.slice(7) : void 0;
|
|
1922
1907
|
const listed = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1)}`;
|
|
@@ -2034,26 +2019,13 @@ const METER = Symbol.for("@hot-updater/plugin-core/read-meter");
|
|
|
2034
2019
|
const readMeterOf = (database) => typeof database === "object" && database !== null ? database[METER] : void 0;
|
|
2035
2020
|
//#endregion
|
|
2036
2021
|
//#region ../plugin-core/dist/engine/db/schemaSettings.mjs
|
|
2037
|
-
/** Refuses a database with `schema.core` but no `schema.engine`: it predates the engine. */
|
|
2038
|
-
const assertEngineDatabase = (adapterName, stored) => {
|
|
2039
|
-
if (stored.get("schema.core") && !stored.get("schema.engine")) throw new HotUpdaterSchemaMigrationRequiredError(adapterName, String(stored.get("schema.core").value), {
|
|
2040
|
-
key: ENGINE_SCHEMA_KEY,
|
|
2041
|
-
expected: "1",
|
|
2042
|
-
found: null
|
|
2043
|
-
});
|
|
2044
|
-
};
|
|
2045
2022
|
const storedSettings = async (adapter, keys) => {
|
|
2046
2023
|
const rows = await readSettings(adapter, keys);
|
|
2047
2024
|
return new Map(keys.map((key, position) => [key, rows[position] ?? null]));
|
|
2048
2025
|
};
|
|
2049
|
-
/** Writes the settings rows after every table exists
|
|
2026
|
+
/** Writes the settings rows after every table exists. */
|
|
2050
2027
|
const writeSchemaSettings = async (adapter, adapterName, settings) => {
|
|
2051
|
-
const stored = await storedSettings(adapter,
|
|
2052
|
-
"schema.core",
|
|
2053
|
-
ENGINE_SCHEMA_KEY,
|
|
2054
|
-
...Object.keys(settings)
|
|
2055
|
-
])]);
|
|
2056
|
-
assertEngineDatabase(adapterName, stored);
|
|
2028
|
+
const stored = await storedSettings(adapter, Object.keys(settings));
|
|
2057
2029
|
const ops = Object.entries(settings).flatMap(([key, value]) => {
|
|
2058
2030
|
const row = stored.get(key);
|
|
2059
2031
|
if (row === null || row === void 0) return [{
|
|
@@ -2076,19 +2048,11 @@ const writeSchemaSettings = async (adapter, adapterName, settings) => {
|
|
|
2076
2048
|
}];
|
|
2077
2049
|
});
|
|
2078
2050
|
if (ops.length === 0) return;
|
|
2079
|
-
if (!(await adapter.write(ops)).ok) throw new Error(
|
|
2051
|
+
if (!(await adapter.write(ops)).ok) throw new Error(`${adapterName}: the schema settings changed during the migration; run it again.`);
|
|
2080
2052
|
};
|
|
2081
|
-
/**
|
|
2082
|
-
* Creates or updates every table, then writes the settings rows last. A
|
|
2083
|
-
* pre-engine database is refused before any table changes.
|
|
2084
|
-
*/
|
|
2053
|
+
/** Creates or updates every table, then writes the settings rows last. */
|
|
2085
2054
|
const migrateSchema = async (adapter, adapterName, tables, settings) => {
|
|
2086
2055
|
if (adapter.migrations === void 0) throw new Error(`${adapterName} creates its tables with its own tooling; write only the settings rows.`);
|
|
2087
|
-
const stored = await storedSettings(adapter, ["schema.core", ENGINE_SCHEMA_KEY]).catch((error) => {
|
|
2088
|
-
if (!isMissingSchemaError(error)) throw error;
|
|
2089
|
-
return /* @__PURE__ */ new Map();
|
|
2090
|
-
});
|
|
2091
|
-
assertEngineDatabase(adapterName, stored);
|
|
2092
2056
|
await adapter.migrations.apply([...tables, SETTINGS_TABLE]);
|
|
2093
2057
|
await writeSchemaSettings(adapter, adapterName, settings);
|
|
2094
2058
|
};
|
|
@@ -2373,9 +2337,6 @@ const getEmptyMigrationResult = () => ({
|
|
|
2373
2337
|
execute: async () => {},
|
|
2374
2338
|
getSQL: () => ""
|
|
2375
2339
|
});
|
|
2376
|
-
const assertSupportedMigrationMode = (options) => {
|
|
2377
|
-
if (options.mode === "from-database") throw new Error("Hot Updater migrations support only mode: 'from-schema'.");
|
|
2378
|
-
};
|
|
2379
2340
|
//#endregion
|
|
2380
2341
|
//#region ../plugin-core/dist/engine/db/settingsMigrator.mjs
|
|
2381
2342
|
/** `schema.core`, or a v0 database's `version`. */
|
|
@@ -2383,15 +2344,14 @@ const storedSchemaVersion = (stored) => {
|
|
|
2383
2344
|
const version = stored?.get("schema.core") ?? stored?.get("version");
|
|
2384
2345
|
return typeof version === "string" ? version : void 0;
|
|
2385
2346
|
};
|
|
2386
|
-
/**
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
} : void 0);
|
|
2347
|
+
/**
|
|
2348
|
+
* A v0 database, whose settings hold `version` and no `schema.engine`, is
|
|
2349
|
+
* recreated, never converted.
|
|
2350
|
+
*/
|
|
2351
|
+
const refuseV0Database = (adapterName, stored) => {
|
|
2352
|
+
const version = stored.get("version");
|
|
2353
|
+
if (typeof version !== "string" || stored.has("schema.engine")) return;
|
|
2354
|
+
throw new HotUpdaterSchemaMigrationRequiredError(adapterName, version);
|
|
2395
2355
|
};
|
|
2396
2356
|
//#endregion
|
|
2397
2357
|
//#region ../plugin-core/dist/engine/database/cursor.mjs
|
|
@@ -3454,14 +3414,13 @@ const resolveSchema = (modules) => {
|
|
|
3454
3414
|
* `hot-updater db migrate` for a store whose adapter creates its own tables
|
|
3455
3415
|
* (MongoDB and the key-value stores): the tables and indexes, then the
|
|
3456
3416
|
* settings rows, applied when a setting is missing or different, and refused
|
|
3457
|
-
* on a database
|
|
3417
|
+
* on a v0 database.
|
|
3458
3418
|
*/
|
|
3459
3419
|
const createEngineMigrator = (options) => {
|
|
3460
3420
|
const getVersion = async () => storedSchemaVersion(await options.readSettings());
|
|
3461
3421
|
const makeResult = async (migrate = {}) => {
|
|
3462
|
-
assertSupportedMigrationMode(migrate);
|
|
3463
3422
|
const stored = await options.readSettings();
|
|
3464
|
-
|
|
3423
|
+
refuseV0Database(options.adapterName, stored);
|
|
3465
3424
|
if (Object.entries(options.settings).every(([key, value]) => stored.get(key) === value)) return getEmptyMigrationResult();
|
|
3466
3425
|
return {
|
|
3467
3426
|
operations: [...options.schema.tables.map((table) => ({
|
|
@@ -4566,7 +4525,7 @@ const createKvAdapter = ({ store, tablePrefix = "" }) => {
|
|
|
4566
4525
|
};
|
|
4567
4526
|
//#endregion
|
|
4568
4527
|
//#region ../../packages/server/dist/version.mjs
|
|
4569
|
-
const HOT_UPDATER_SERVER_VERSION = "1.0.0-rc.
|
|
4528
|
+
const HOT_UPDATER_SERVER_VERSION = "1.0.0-rc.47";
|
|
4570
4529
|
//#endregion
|
|
4571
4530
|
//#region ../../packages/server/dist/handlerVersionRoutes.mjs
|
|
4572
4531
|
/**
|
|
@@ -5255,7 +5214,7 @@ const baseBundleIdsOf = (row, range, bundleId, limit) => {
|
|
|
5255
5214
|
for (const { descriptor, index } of newestFirst) {
|
|
5256
5215
|
if (ids.length >= limit) break;
|
|
5257
5216
|
const id = descriptor.bundleId;
|
|
5258
|
-
if (serving.has(index) &&
|
|
5217
|
+
if (serving.has(index) && id !== null && id < bundleId && !ids.includes(id)) ids.push(id);
|
|
5259
5218
|
}
|
|
5260
5219
|
return ids;
|
|
5261
5220
|
};
|
|
@@ -6021,7 +5980,7 @@ const createCoreOperations = (db, { now = Date.now } = {}) => {
|
|
|
6021
5980
|
}),
|
|
6022
5981
|
promoteRelease: (input) => withChannels(async () => {
|
|
6023
5982
|
const checkSource = (source) => {
|
|
6024
|
-
if (source.
|
|
5983
|
+
if (source.bundle_id === null) throw new ReleaseManagementError("TARGET_RELEASE_INVALID", "Only a Bundle Release can be promoted.");
|
|
6025
5984
|
};
|
|
6026
5985
|
checkSource((await db.transaction((tx) => loadTarget(tx, input))).release);
|
|
6027
5986
|
const targetChannel = input.targetChannel.trim();
|
|
@@ -6220,7 +6179,6 @@ const fail = (message) => {
|
|
|
6220
6179
|
const isRecord$6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6221
6180
|
const checkPlugin = (value, at) => {
|
|
6222
6181
|
if (!isRecord$6(value)) return fail(`${at} is not a plugin.`);
|
|
6223
|
-
if ("kind" in value) fail(`${at} has a kind; core is built in and is never passed as a plugin.`);
|
|
6224
6182
|
const unknown = Object.keys(value).find((key) => !PLUGIN_KEYS.has(key));
|
|
6225
6183
|
if (unknown !== void 0) fail(`${at} has an unknown key "${unknown}".`);
|
|
6226
6184
|
const plugin = value;
|
|
@@ -7112,7 +7070,7 @@ const databaseOf = (value) => {
|
|
|
7112
7070
|
if (isAdapter(value.adapter)) return value;
|
|
7113
7071
|
if ("core" in value && "fetchAdmin" in value) throw new HotUpdaterConfigError("standaloneRepository reaches a server's admin API from the CLI and console. createHotUpdater needs the server's own database, such as kyselyAdapter(...).");
|
|
7114
7072
|
}
|
|
7115
|
-
throw new HotUpdaterConfigError("database must be a Hot Updater
|
|
7073
|
+
throw new HotUpdaterConfigError("database must be a Hot Updater database adapter, such as kyselyAdapter(...) or postgres(...).");
|
|
7116
7074
|
};
|
|
7117
7075
|
/** The configured storage, when it is a storage adapter. */
|
|
7118
7076
|
const storageOf = (value) => {
|
package/dist/init/index.cjs
CHANGED
|
@@ -6706,17 +6706,6 @@ const FIREBASE_V1_FUNCTION_NAME = "hot-updater-v1";
|
|
|
6706
6706
|
* the data, so item partitions are table names with no prefix.
|
|
6707
6707
|
*/
|
|
6708
6708
|
const FIREBASE_V1_COLLECTION = "hot_updater_v1";
|
|
6709
|
-
/**
|
|
6710
|
-
* The collections a 1.0 release candidate wrote before the storage engine.
|
|
6711
|
-
* Init reads them only to refuse such a database.
|
|
6712
|
-
*/
|
|
6713
|
-
const FIREBASE_PRE_ENGINE_COLLECTIONS = [
|
|
6714
|
-
"hot_updater_v1_private_settings",
|
|
6715
|
-
"hot_updater_v1_bundles",
|
|
6716
|
-
"hot_updater_v1_bundle_patches",
|
|
6717
|
-
"hot_updater_v1_channels",
|
|
6718
|
-
"hot_updater_v1_release_catalogs"
|
|
6719
|
-
];
|
|
6720
6709
|
//#endregion
|
|
6721
6710
|
//#region src/firestoreStore.ts
|
|
6722
6711
|
/**
|
|
@@ -6954,11 +6943,9 @@ const inputFirebaseApplicationCredentials = async ({ applicationCredentials, non
|
|
|
6954
6943
|
const ENGINE_SETTING = "schema.engine";
|
|
6955
6944
|
/**
|
|
6956
6945
|
* v1 once the storage engine's schema settings are written; incompatible
|
|
6957
|
-
* when
|
|
6958
|
-
* version is recorded; otherwise fresh.
|
|
6946
|
+
* when another engine version is recorded; otherwise fresh.
|
|
6959
6947
|
*/
|
|
6960
|
-
const resolveFirebaseInfrastructureState = ({ engine
|
|
6961
|
-
if (preEngineData) return "incompatible";
|
|
6948
|
+
const resolveFirebaseInfrastructureState = ({ engine }) => {
|
|
6962
6949
|
if (engine === void 0) return "fresh";
|
|
6963
6950
|
return engine === _hot_updater_plugin_core.coreSettings[ENGINE_SETTING] ? "v1" : "incompatible";
|
|
6964
6951
|
};
|
|
@@ -6974,13 +6961,8 @@ const assertFirebaseInfrastructureCanInitialize = async ({ applicationCredential
|
|
|
6974
6961
|
projectId
|
|
6975
6962
|
}, `hot-updater-init-${projectId}-${Date.now()}`);
|
|
6976
6963
|
try {
|
|
6977
|
-
const
|
|
6978
|
-
|
|
6979
|
-
const preEngine = await Promise.all(FIREBASE_PRE_ENGINE_COLLECTIONS.map((name) => db.collection(name).limit(1).get()));
|
|
6980
|
-
if (resolveFirebaseInfrastructureState({
|
|
6981
|
-
engine: setting.exists ? setting.get("row.value") : void 0,
|
|
6982
|
-
preEngineData: preEngine.some((snapshot) => !snapshot.empty)
|
|
6983
|
-
}) === "incompatible") throw new _hot_updater_cli_tools.InitError(`Firebase v1 infrastructure in project ${projectId} holds data from a 1.0 release candidate or an unsupported database version. Use a new Firebase project, or delete the hot_updater_v1_* collections, then rerun init.`);
|
|
6964
|
+
const setting = await (0, firebase_admin_firestore.getFirestore)(app).collection(FIREBASE_V1_COLLECTION).doc(firebaseEngineSettingDocumentId()).get();
|
|
6965
|
+
if (resolveFirebaseInfrastructureState({ engine: setting.exists ? setting.get("row.value") : void 0 }) === "incompatible") throw new _hot_updater_cli_tools.InitError(`Firebase v1 infrastructure in project ${projectId} records an unsupported database version. Use a new Firebase project, or delete the ${FIREBASE_V1_COLLECTION} collection, then rerun init.`);
|
|
6984
6966
|
} catch (error) {
|
|
6985
6967
|
if (error instanceof _hot_updater_cli_tools.InitError) throw error;
|
|
6986
6968
|
const code = typeof error === "object" && error !== null ? Reflect.get(error, "code") : void 0;
|
|
@@ -6995,12 +6977,17 @@ const assertFirebaseFunctionCanInitialize = async ({ fetchImpl, functions }) =>
|
|
|
6995
6977
|
for (const existingFunction of existingFunctions) {
|
|
6996
6978
|
if (!existingFunction.uri) throw new _hot_updater_cli_tools.InitError(`Could not verify the Firebase infrastructure generation at Function ${FIREBASE_V1_FUNCTION_NAME}: endpoint URL was not reported.`);
|
|
6997
6979
|
const versionUrl = new URL("version", `${existingFunction.uri.replace(/\/$/u, "")}/`).toString();
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
6980
|
+
try {
|
|
6981
|
+
await (0, _hot_updater_cli_tools.assertInfrastructureGenerationAtUrl)({
|
|
6982
|
+
fetchImpl,
|
|
6983
|
+
provider: "Firebase",
|
|
6984
|
+
resource: `Function ${FIREBASE_V1_FUNCTION_NAME}`,
|
|
6985
|
+
versionUrl
|
|
6986
|
+
});
|
|
6987
|
+
} catch (error) {
|
|
6988
|
+
if (!(error instanceof _hot_updater_cli_tools.LegacyInfrastructureError)) throw error;
|
|
6989
|
+
throw new _hot_updater_cli_tools.InitError(`Function ${FIREBASE_V1_FUNCTION_NAME}, which init deploys, already exists in this Firebase project and is incompatible: its /version does not report Hot Updater infrastructure generation 1. Delete the function or use another Firebase project, then rerun init. The existing function was not changed.`);
|
|
6990
|
+
}
|
|
7004
6991
|
}
|
|
7005
6992
|
};
|
|
7006
6993
|
//#endregion
|
package/dist/init/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import { ConfigBuilder, HOT_UPDATER_SERVER_PACKAGE_VERSION_ENV, InitError, MissingInitInputsError, assembleServer, assertInfrastructureGenerationAtUrl, assertInitProviderInputs, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, printAppSetup, provisionClientCredential, readHotUpdaterInitEnv, resolveHotUpdaterServerVersion, resolveInitProviderInput, resolvePackageVersion, transformEnv, writeHotUpdaterFiles } from "@hot-updater/cli-tools";
|
|
4
|
+
import { ConfigBuilder, HOT_UPDATER_SERVER_PACKAGE_VERSION_ENV, InitError, LegacyInfrastructureError, MissingInitInputsError, assembleServer, assertInfrastructureGenerationAtUrl, assertInitProviderInputs, confirmInitInputPersistence, copyDirToTmp, createHotUpdaterConfigScaffoldFromBuilder, getHotUpdaterInitInputEnv, getInitProviderEnvVars, getInitProviderTextPromptValues, link, makeEnv, p, printAppSetup, provisionClientCredential, readHotUpdaterInitEnv, resolveHotUpdaterServerVersion, resolveInitProviderInput, resolvePackageVersion, transformEnv, writeHotUpdaterFiles } from "@hot-updater/cli-tools";
|
|
5
5
|
import { apiKeys, insights, remoteConfig } from "@hot-updater/server/plugins";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
|
|
@@ -6699,17 +6699,6 @@ const FIREBASE_V1_FUNCTION_NAME = "hot-updater-v1";
|
|
|
6699
6699
|
* the data, so item partitions are table names with no prefix.
|
|
6700
6700
|
*/
|
|
6701
6701
|
const FIREBASE_V1_COLLECTION = "hot_updater_v1";
|
|
6702
|
-
/**
|
|
6703
|
-
* The collections a 1.0 release candidate wrote before the storage engine.
|
|
6704
|
-
* Init reads them only to refuse such a database.
|
|
6705
|
-
*/
|
|
6706
|
-
const FIREBASE_PRE_ENGINE_COLLECTIONS = [
|
|
6707
|
-
"hot_updater_v1_private_settings",
|
|
6708
|
-
"hot_updater_v1_bundles",
|
|
6709
|
-
"hot_updater_v1_bundle_patches",
|
|
6710
|
-
"hot_updater_v1_channels",
|
|
6711
|
-
"hot_updater_v1_release_catalogs"
|
|
6712
|
-
];
|
|
6713
6702
|
//#endregion
|
|
6714
6703
|
//#region src/firestoreStore.ts
|
|
6715
6704
|
/**
|
|
@@ -6947,11 +6936,9 @@ const inputFirebaseApplicationCredentials = async ({ applicationCredentials, non
|
|
|
6947
6936
|
const ENGINE_SETTING = "schema.engine";
|
|
6948
6937
|
/**
|
|
6949
6938
|
* v1 once the storage engine's schema settings are written; incompatible
|
|
6950
|
-
* when
|
|
6951
|
-
* version is recorded; otherwise fresh.
|
|
6939
|
+
* when another engine version is recorded; otherwise fresh.
|
|
6952
6940
|
*/
|
|
6953
|
-
const resolveFirebaseInfrastructureState = ({ engine
|
|
6954
|
-
if (preEngineData) return "incompatible";
|
|
6941
|
+
const resolveFirebaseInfrastructureState = ({ engine }) => {
|
|
6955
6942
|
if (engine === void 0) return "fresh";
|
|
6956
6943
|
return engine === coreSettings[ENGINE_SETTING] ? "v1" : "incompatible";
|
|
6957
6944
|
};
|
|
@@ -6967,13 +6954,8 @@ const assertFirebaseInfrastructureCanInitialize = async ({ applicationCredential
|
|
|
6967
6954
|
projectId
|
|
6968
6955
|
}, `hot-updater-init-${projectId}-${Date.now()}`);
|
|
6969
6956
|
try {
|
|
6970
|
-
const
|
|
6971
|
-
|
|
6972
|
-
const preEngine = await Promise.all(FIREBASE_PRE_ENGINE_COLLECTIONS.map((name) => db.collection(name).limit(1).get()));
|
|
6973
|
-
if (resolveFirebaseInfrastructureState({
|
|
6974
|
-
engine: setting.exists ? setting.get("row.value") : void 0,
|
|
6975
|
-
preEngineData: preEngine.some((snapshot) => !snapshot.empty)
|
|
6976
|
-
}) === "incompatible") throw new InitError(`Firebase v1 infrastructure in project ${projectId} holds data from a 1.0 release candidate or an unsupported database version. Use a new Firebase project, or delete the hot_updater_v1_* collections, then rerun init.`);
|
|
6957
|
+
const setting = await getFirestore(app).collection(FIREBASE_V1_COLLECTION).doc(firebaseEngineSettingDocumentId()).get();
|
|
6958
|
+
if (resolveFirebaseInfrastructureState({ engine: setting.exists ? setting.get("row.value") : void 0 }) === "incompatible") throw new InitError(`Firebase v1 infrastructure in project ${projectId} records an unsupported database version. Use a new Firebase project, or delete the ${FIREBASE_V1_COLLECTION} collection, then rerun init.`);
|
|
6977
6959
|
} catch (error) {
|
|
6978
6960
|
if (error instanceof InitError) throw error;
|
|
6979
6961
|
const code = typeof error === "object" && error !== null ? Reflect.get(error, "code") : void 0;
|
|
@@ -6988,12 +6970,17 @@ const assertFirebaseFunctionCanInitialize = async ({ fetchImpl, functions }) =>
|
|
|
6988
6970
|
for (const existingFunction of existingFunctions) {
|
|
6989
6971
|
if (!existingFunction.uri) throw new InitError(`Could not verify the Firebase infrastructure generation at Function ${FIREBASE_V1_FUNCTION_NAME}: endpoint URL was not reported.`);
|
|
6990
6972
|
const versionUrl = new URL("version", `${existingFunction.uri.replace(/\/$/u, "")}/`).toString();
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6973
|
+
try {
|
|
6974
|
+
await assertInfrastructureGenerationAtUrl({
|
|
6975
|
+
fetchImpl,
|
|
6976
|
+
provider: "Firebase",
|
|
6977
|
+
resource: `Function ${FIREBASE_V1_FUNCTION_NAME}`,
|
|
6978
|
+
versionUrl
|
|
6979
|
+
});
|
|
6980
|
+
} catch (error) {
|
|
6981
|
+
if (!(error instanceof LegacyInfrastructureError)) throw error;
|
|
6982
|
+
throw new InitError(`Function ${FIREBASE_V1_FUNCTION_NAME}, which init deploys, already exists in this Firebase project and is incompatible: its /version does not report Hot Updater infrastructure generation 1. Delete the function or use another Firebase project, then rerun init. The existing function was not changed.`);
|
|
6983
|
+
}
|
|
6997
6984
|
}
|
|
6998
6985
|
};
|
|
6999
6986
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/firebase",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.47",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"types": "dist/index.d.cts",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"hono": "4.12.34",
|
|
40
|
-
"@hot-updater/
|
|
41
|
-
"@hot-updater/
|
|
42
|
-
"@hot-updater/
|
|
43
|
-
"@hot-updater/server": "1.0.0-rc.
|
|
40
|
+
"@hot-updater/plugin-core": "1.0.0-rc.47",
|
|
41
|
+
"@hot-updater/cli-tools": "1.0.0-rc.47",
|
|
42
|
+
"@hot-updater/protocol": "1.0.0-rc.47",
|
|
43
|
+
"@hot-updater/server": "1.0.0-rc.47"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"firebase-tools": "15.29.0",
|
|
56
56
|
"fkill": "^9.0.0",
|
|
57
57
|
"mime": "^4.0.4",
|
|
58
|
-
"@hot-updater/mock": "1.0.0-rc.
|
|
59
|
-
"@hot-updater/test-utils": "1.0.0-rc.
|
|
58
|
+
"@hot-updater/mock": "1.0.0-rc.47",
|
|
59
|
+
"@hot-updater/test-utils": "1.0.0-rc.47"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"firebase-admin": "*",
|