@hot-updater/firebase 0.36.6 → 1.0.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/firebase/functions/index.cjs +5368 -3796
- package/dist/firebase/public/firestore.indexes.json +17 -7
- package/dist/iac/index.cjs +953 -34
- package/dist/iac/index.mjs +957 -39
- package/dist/index.cjs +821 -345
- package/dist/index.d.cts +10 -6
- package/dist/index.d.mts +10 -6
- package/dist/index.mjs +822 -322
- package/package.json +7 -8
package/dist/iac/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@ fs = __toESM(fs);
|
|
|
27
27
|
let path = require("path");
|
|
28
28
|
path = __toESM(path);
|
|
29
29
|
let _hot_updater_cli_tools = require("@hot-updater/cli-tools");
|
|
30
|
+
let _hot_updater_server = require("@hot-updater/server");
|
|
30
31
|
let node_url = require("node:url");
|
|
31
32
|
let node_child_process = require("node:child_process");
|
|
32
33
|
let node_string_decoder = require("node:string_decoder");
|
|
@@ -46,6 +47,12 @@ let node_fs = require("node:fs");
|
|
|
46
47
|
let node_stream_promises = require("node:stream/promises");
|
|
47
48
|
let node_stream = require("node:stream");
|
|
48
49
|
let node_buffer = require("node:buffer");
|
|
50
|
+
let firebase_admin_app = require("firebase-admin/app");
|
|
51
|
+
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
52
|
+
let _hot_updater_plugin_core_internal = require("@hot-updater/plugin-core/internal");
|
|
53
|
+
let firebase_admin_firestore = require("firebase-admin/firestore");
|
|
54
|
+
let fs_promises = require("fs/promises");
|
|
55
|
+
fs_promises = __toESM(fs_promises);
|
|
49
56
|
let node_fs_promises = require("node:fs/promises");
|
|
50
57
|
node_fs_promises = __toESM(node_fs_promises);
|
|
51
58
|
//#region ../../node_modules/.pnpm/es-toolkit@1.47.0/node_modules/es-toolkit/dist/_internal/compareValues.mjs
|
|
@@ -1323,7 +1330,7 @@ const handleCommand = (filePath, rawArguments, rawOptions) => {
|
|
|
1323
1330
|
var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1324
1331
|
module.exports = isexe;
|
|
1325
1332
|
isexe.sync = sync;
|
|
1326
|
-
var fs$
|
|
1333
|
+
var fs$6 = require("fs");
|
|
1327
1334
|
function checkPathExt(path, options) {
|
|
1328
1335
|
var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
|
|
1329
1336
|
if (!pathext) return true;
|
|
@@ -1340,12 +1347,12 @@ var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
1340
1347
|
return checkPathExt(path, options);
|
|
1341
1348
|
}
|
|
1342
1349
|
function isexe(path, options, cb) {
|
|
1343
|
-
fs$
|
|
1350
|
+
fs$6.stat(path, function(er, stat) {
|
|
1344
1351
|
cb(er, er ? false : checkStat(stat, path, options));
|
|
1345
1352
|
});
|
|
1346
1353
|
}
|
|
1347
1354
|
function sync(path, options) {
|
|
1348
|
-
return checkStat(fs$
|
|
1355
|
+
return checkStat(fs$6.statSync(path), path, options);
|
|
1349
1356
|
}
|
|
1350
1357
|
}));
|
|
1351
1358
|
//#endregion
|
|
@@ -1353,14 +1360,14 @@ var require_windows = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
1353
1360
|
var require_mode = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1354
1361
|
module.exports = isexe;
|
|
1355
1362
|
isexe.sync = sync;
|
|
1356
|
-
var fs$
|
|
1363
|
+
var fs$5 = require("fs");
|
|
1357
1364
|
function isexe(path, options, cb) {
|
|
1358
|
-
fs$
|
|
1365
|
+
fs$5.stat(path, function(er, stat) {
|
|
1359
1366
|
cb(er, er ? false : checkStat(stat, options));
|
|
1360
1367
|
});
|
|
1361
1368
|
}
|
|
1362
1369
|
function sync(path, options) {
|
|
1363
|
-
return checkStat(fs$
|
|
1370
|
+
return checkStat(fs$5.statSync(path), options);
|
|
1364
1371
|
}
|
|
1365
1372
|
function checkStat(stat, options) {
|
|
1366
1373
|
return stat.isFile() && checkMode(stat, options);
|
|
@@ -1575,16 +1582,16 @@ var require_shebang_command = /* @__PURE__ */ __commonJSMin(((exports, module) =
|
|
|
1575
1582
|
//#endregion
|
|
1576
1583
|
//#region ../../node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js
|
|
1577
1584
|
var require_readShebang = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1578
|
-
const fs$
|
|
1585
|
+
const fs$4 = require("fs");
|
|
1579
1586
|
const shebangCommand = require_shebang_command();
|
|
1580
1587
|
function readShebang(command) {
|
|
1581
1588
|
const size = 150;
|
|
1582
1589
|
const buffer = Buffer.alloc(size);
|
|
1583
1590
|
let fd;
|
|
1584
1591
|
try {
|
|
1585
|
-
fd = fs$
|
|
1586
|
-
fs$
|
|
1587
|
-
fs$
|
|
1592
|
+
fd = fs$4.openSync(command, "r");
|
|
1593
|
+
fs$4.readSync(fd, buffer, 0, size, 0);
|
|
1594
|
+
fs$4.closeSync(fd);
|
|
1588
1595
|
} catch (e) {}
|
|
1589
1596
|
return shebangCommand(buffer.toString());
|
|
1590
1597
|
}
|
|
@@ -6712,6 +6719,777 @@ createExeca(mapNode);
|
|
|
6712
6719
|
createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
|
|
6713
6720
|
const { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExport();
|
|
6714
6721
|
//#endregion
|
|
6722
|
+
//#region src/firebaseDatabaseParserShared.ts
|
|
6723
|
+
var FirebaseDatabaseDataError = class extends Error {
|
|
6724
|
+
name = "FirebaseDatabaseDataError";
|
|
6725
|
+
constructor(source) {
|
|
6726
|
+
super(`Invalid Firebase database data at "${source}".`);
|
|
6727
|
+
this.source = source;
|
|
6728
|
+
}
|
|
6729
|
+
};
|
|
6730
|
+
const record = (value, source) => {
|
|
6731
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new FirebaseDatabaseDataError(source);
|
|
6732
|
+
return value;
|
|
6733
|
+
};
|
|
6734
|
+
const property = (value, key) => Reflect.get(value, key);
|
|
6735
|
+
const string = (value, source) => {
|
|
6736
|
+
if (typeof value !== "string") throw new FirebaseDatabaseDataError(source);
|
|
6737
|
+
return value;
|
|
6738
|
+
};
|
|
6739
|
+
const nullableString = (value, source) => {
|
|
6740
|
+
if (value === null || value === void 0) return null;
|
|
6741
|
+
return string(value, source);
|
|
6742
|
+
};
|
|
6743
|
+
const boolean = (value, source) => {
|
|
6744
|
+
if (typeof value !== "boolean") throw new FirebaseDatabaseDataError(source);
|
|
6745
|
+
return value;
|
|
6746
|
+
};
|
|
6747
|
+
const number = (value, source) => {
|
|
6748
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new FirebaseDatabaseDataError(source);
|
|
6749
|
+
return value;
|
|
6750
|
+
};
|
|
6751
|
+
const byteSize = (value, source) => {
|
|
6752
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new FirebaseDatabaseDataError(source);
|
|
6753
|
+
return value;
|
|
6754
|
+
};
|
|
6755
|
+
const stringArray = (value, source) => {
|
|
6756
|
+
if (value === null || value === void 0) return null;
|
|
6757
|
+
if (!Array.isArray(value)) throw new FirebaseDatabaseDataError(source);
|
|
6758
|
+
return value.map((item) => string(item, source));
|
|
6759
|
+
};
|
|
6760
|
+
const platform = (value, source) => {
|
|
6761
|
+
if (value === "android" || value === "ios") return value;
|
|
6762
|
+
throw new FirebaseDatabaseDataError(source);
|
|
6763
|
+
};
|
|
6764
|
+
//#endregion
|
|
6765
|
+
//#region src/firebaseDatabaseParser.ts
|
|
6766
|
+
const metadata = (value, source) => {
|
|
6767
|
+
if (!(0, _hot_updater_plugin_core.isDatabaseMetadataObject)(value)) throw new FirebaseDatabaseDataError(source);
|
|
6768
|
+
return value;
|
|
6769
|
+
};
|
|
6770
|
+
const requiredNullableString = (input, key, source) => {
|
|
6771
|
+
if (!Object.hasOwn(input, key)) throw new FirebaseDatabaseDataError(source);
|
|
6772
|
+
return nullableString(property(input, key), source);
|
|
6773
|
+
};
|
|
6774
|
+
const parseFirebaseBundleRow = (value, source) => {
|
|
6775
|
+
const input = record(value, source);
|
|
6776
|
+
return {
|
|
6777
|
+
id: string(property(input, "id"), source),
|
|
6778
|
+
platform: platform(property(input, "platform"), source),
|
|
6779
|
+
file_hash: string(property(input, "file_hash"), source),
|
|
6780
|
+
git_commit_hash: nullableString(property(input, "git_commit_hash"), source),
|
|
6781
|
+
storage_uri: string(property(input, "storage_uri"), source),
|
|
6782
|
+
archive_byte_size: byteSize(property(input, "archive_byte_size"), source),
|
|
6783
|
+
metadata: metadata(property(input, "metadata"), source),
|
|
6784
|
+
manifest_storage_uri: nullableString(property(input, "manifest_storage_uri"), source),
|
|
6785
|
+
manifest_file_hash: nullableString(property(input, "manifest_file_hash"), source),
|
|
6786
|
+
asset_base_storage_uri: nullableString(property(input, "asset_base_storage_uri"), source)
|
|
6787
|
+
};
|
|
6788
|
+
};
|
|
6789
|
+
const parseFirebaseChannelRow = (value, source) => {
|
|
6790
|
+
const input = record(value, source);
|
|
6791
|
+
return {
|
|
6792
|
+
id: string(property(input, "id"), source),
|
|
6793
|
+
name: string(property(input, "name"), source)
|
|
6794
|
+
};
|
|
6795
|
+
};
|
|
6796
|
+
const parseFirebasePatchRow = (value, source) => {
|
|
6797
|
+
const input = record(value, source);
|
|
6798
|
+
return {
|
|
6799
|
+
id: string(property(input, "id"), source),
|
|
6800
|
+
bundle_id: string(property(input, "bundle_id"), source),
|
|
6801
|
+
base_bundle_id: string(property(input, "base_bundle_id"), source),
|
|
6802
|
+
base_file_hash: string(property(input, "base_file_hash"), source),
|
|
6803
|
+
patch_file_hash: string(property(input, "patch_file_hash"), source),
|
|
6804
|
+
patch_storage_uri: string(property(input, "patch_storage_uri"), source),
|
|
6805
|
+
byte_size: byteSize(property(input, "byte_size"), source),
|
|
6806
|
+
order_index: number(property(input, "order_index"), source)
|
|
6807
|
+
};
|
|
6808
|
+
};
|
|
6809
|
+
const parseFirebaseBundleEventRow = (value, source) => {
|
|
6810
|
+
const input = record(value, source);
|
|
6811
|
+
const type = string(property(input, "type"), source);
|
|
6812
|
+
const fromBundleId = requiredNullableString(input, "from_bundle_id", source);
|
|
6813
|
+
const updateStrategy = requiredNullableString(input, "update_strategy", source);
|
|
6814
|
+
if (!((type === "UPDATE_APPLIED" || type === "RECOVERED" || type === "RELEASE_ADOPTED") && typeof fromBundleId === "string" && (updateStrategy === "fingerprint" || updateStrategy === "appVersion") || type === "UNCHANGED" && fromBundleId === null && updateStrategy === null)) throw new FirebaseDatabaseDataError(source);
|
|
6815
|
+
return {
|
|
6816
|
+
id: string(property(input, "id"), source),
|
|
6817
|
+
type,
|
|
6818
|
+
install_id: string(property(input, "install_id"), source),
|
|
6819
|
+
user_id: nullableString(property(input, "user_id"), source),
|
|
6820
|
+
username: nullableString(property(input, "username"), source),
|
|
6821
|
+
from_release_id: requiredNullableString(input, "from_release_id", source),
|
|
6822
|
+
from_bundle_id: fromBundleId,
|
|
6823
|
+
to_release_id: requiredNullableString(input, "to_release_id", source),
|
|
6824
|
+
to_bundle_id: string(property(input, "to_bundle_id"), source),
|
|
6825
|
+
platform: platform(property(input, "platform"), source),
|
|
6826
|
+
app_version: string(property(input, "app_version"), source),
|
|
6827
|
+
channel: string(property(input, "channel"), source),
|
|
6828
|
+
cohort: string(property(input, "cohort"), source),
|
|
6829
|
+
update_strategy: updateStrategy,
|
|
6830
|
+
fingerprint_hash: nullableString(property(input, "fingerprint_hash"), source),
|
|
6831
|
+
sdk_version: nullableString(property(input, "sdk_version"), source),
|
|
6832
|
+
received_at_ms: number(property(input, "received_at_ms"), source)
|
|
6833
|
+
};
|
|
6834
|
+
};
|
|
6835
|
+
const parseFirebaseApiKeyRow = (value, source) => {
|
|
6836
|
+
const input = record(value, source);
|
|
6837
|
+
const role = string(property(input, "role"), source);
|
|
6838
|
+
if (role !== "client") throw new FirebaseDatabaseDataError(source);
|
|
6839
|
+
const revokedAt = property(input, "revoked_at_ms");
|
|
6840
|
+
return {
|
|
6841
|
+
id: string(property(input, "id"), source),
|
|
6842
|
+
hash: string(property(input, "hash"), source),
|
|
6843
|
+
name: string(property(input, "name"), source),
|
|
6844
|
+
prefix: string(property(input, "prefix"), source),
|
|
6845
|
+
role,
|
|
6846
|
+
created_at_ms: number(property(input, "created_at_ms"), source),
|
|
6847
|
+
revoked_at_ms: revokedAt === null ? null : number(revokedAt, source)
|
|
6848
|
+
};
|
|
6849
|
+
};
|
|
6850
|
+
const parseFirebaseReleaseRow = (value, source) => {
|
|
6851
|
+
const input = record(value, source);
|
|
6852
|
+
const kind = string(property(input, "kind"), source);
|
|
6853
|
+
const strategy = string(property(input, "strategy"), source);
|
|
6854
|
+
const operation = string(property(input, "operation"), source);
|
|
6855
|
+
const targetCohorts = stringArray(property(input, "target_cohorts"), source);
|
|
6856
|
+
if (kind !== "BUNDLE" && kind !== "EMBEDDED" || strategy !== "APP_VERSION" && strategy !== "FINGERPRINT" || operation !== "DEPLOY" && operation !== "PROMOTE" && operation !== "ROLLBACK" || targetCohorts === null) throw new FirebaseDatabaseDataError(source);
|
|
6857
|
+
return {
|
|
6858
|
+
id: string(property(input, "id"), source),
|
|
6859
|
+
revision: number(property(input, "revision"), source),
|
|
6860
|
+
scope_key: string(property(input, "scope_key"), source),
|
|
6861
|
+
channel_id: string(property(input, "channel_id"), source),
|
|
6862
|
+
platform: platform(property(input, "platform"), source),
|
|
6863
|
+
kind,
|
|
6864
|
+
bundle_id: nullableString(property(input, "bundle_id"), source),
|
|
6865
|
+
strategy,
|
|
6866
|
+
target_app_version: nullableString(property(input, "target_app_version"), source),
|
|
6867
|
+
fingerprint_hash: nullableString(property(input, "fingerprint_hash"), source),
|
|
6868
|
+
enabled: boolean(property(input, "enabled"), source),
|
|
6869
|
+
should_force_update: boolean(property(input, "should_force_update"), source),
|
|
6870
|
+
message: nullableString(property(input, "message"), source),
|
|
6871
|
+
rollout_cohort_count: number(property(input, "rollout_cohort_count"), source),
|
|
6872
|
+
target_cohorts: targetCohorts,
|
|
6873
|
+
operation,
|
|
6874
|
+
source_release_id: nullableString(property(input, "source_release_id"), source),
|
|
6875
|
+
created_at_ms: number(property(input, "created_at_ms"), source),
|
|
6876
|
+
updated_at_ms: number(property(input, "updated_at_ms"), source)
|
|
6877
|
+
};
|
|
6878
|
+
};
|
|
6879
|
+
const parseFirebaseReleaseCatalogRow = (value, source) => {
|
|
6880
|
+
const input = record(value, source);
|
|
6881
|
+
const strategy = string(property(input, "strategy"), source);
|
|
6882
|
+
if (strategy !== "APP_VERSION" && strategy !== "FINGERPRINT") throw new FirebaseDatabaseDataError(source);
|
|
6883
|
+
return {
|
|
6884
|
+
scope_key: string(property(input, "scope_key"), source),
|
|
6885
|
+
catalog_id: string(property(input, "catalog_id"), source),
|
|
6886
|
+
strategy,
|
|
6887
|
+
channel_id: string(property(input, "channel_id"), source),
|
|
6888
|
+
channel_key: string(property(input, "channel_key"), source),
|
|
6889
|
+
platform: platform(property(input, "platform"), source),
|
|
6890
|
+
fingerprint_hash: nullableString(property(input, "fingerprint_hash"), source),
|
|
6891
|
+
generation: number(property(input, "generation"), source),
|
|
6892
|
+
payload: string(property(input, "payload"), source),
|
|
6893
|
+
catalog_hash: string(property(input, "catalog_hash"), source),
|
|
6894
|
+
byte_size: number(property(input, "byte_size"), source),
|
|
6895
|
+
is_tombstone: boolean(property(input, "is_tombstone"), source),
|
|
6896
|
+
updated_at_ms: number(property(input, "updated_at_ms"), source)
|
|
6897
|
+
};
|
|
6898
|
+
};
|
|
6899
|
+
//#endregion
|
|
6900
|
+
//#region src/firebaseDatabaseQuery.ts
|
|
6901
|
+
const compare = (left, right) => {
|
|
6902
|
+
if (typeof left === "number" && typeof right === "number") return left - right;
|
|
6903
|
+
return String(left).localeCompare(String(right));
|
|
6904
|
+
};
|
|
6905
|
+
const normalizeStringComparison = (actual, expected, mode) => {
|
|
6906
|
+
if (typeof actual !== "string") return null;
|
|
6907
|
+
return mode === "insensitive" ? [actual.toLocaleLowerCase(), expected.toLocaleLowerCase()] : [actual, expected];
|
|
6908
|
+
};
|
|
6909
|
+
const matchesCondition = (row, condition) => {
|
|
6910
|
+
const actual = Reflect.get(row, condition.field);
|
|
6911
|
+
const expected = condition.value;
|
|
6912
|
+
switch (condition.operator ?? "eq") {
|
|
6913
|
+
case "eq": {
|
|
6914
|
+
if (typeof expected !== "string") return actual === expected;
|
|
6915
|
+
const comparison = normalizeStringComparison(actual, expected, "mode" in condition ? condition.mode : void 0);
|
|
6916
|
+
return comparison !== null && comparison[0] === comparison[1];
|
|
6917
|
+
}
|
|
6918
|
+
case "ne": {
|
|
6919
|
+
if (actual === null || actual === void 0) return false;
|
|
6920
|
+
if (typeof expected !== "string") return actual !== expected;
|
|
6921
|
+
const comparison = normalizeStringComparison(actual, expected, "mode" in condition ? condition.mode : void 0);
|
|
6922
|
+
return comparison === null || comparison[0] !== comparison[1];
|
|
6923
|
+
}
|
|
6924
|
+
case "gt":
|
|
6925
|
+
if (actual === null || actual === void 0) return false;
|
|
6926
|
+
return compare(actual, expected) > 0;
|
|
6927
|
+
case "gte":
|
|
6928
|
+
if (actual === null || actual === void 0) return false;
|
|
6929
|
+
return compare(actual, expected) >= 0;
|
|
6930
|
+
case "lt":
|
|
6931
|
+
if (actual === null || actual === void 0) return false;
|
|
6932
|
+
return compare(actual, expected) < 0;
|
|
6933
|
+
case "lte":
|
|
6934
|
+
if (actual === null || actual === void 0) return false;
|
|
6935
|
+
return compare(actual, expected) <= 0;
|
|
6936
|
+
case "in":
|
|
6937
|
+
if (!Array.isArray(expected)) return false;
|
|
6938
|
+
return expected.some((candidate) => candidate === actual);
|
|
6939
|
+
case "not_in": {
|
|
6940
|
+
if (!Array.isArray(expected)) return false;
|
|
6941
|
+
const values = expected;
|
|
6942
|
+
return values.length === 0 || actual !== null && actual !== void 0 && values.every((candidate) => candidate !== actual);
|
|
6943
|
+
}
|
|
6944
|
+
case "contains": {
|
|
6945
|
+
if (typeof expected !== "string") return false;
|
|
6946
|
+
const comparison = normalizeStringComparison(actual, expected, "mode" in condition ? condition.mode : void 0);
|
|
6947
|
+
return comparison?.[0].includes(comparison[1]) ?? false;
|
|
6948
|
+
}
|
|
6949
|
+
case "starts_with": {
|
|
6950
|
+
if (typeof expected !== "string") return false;
|
|
6951
|
+
const comparison = normalizeStringComparison(actual, expected, "mode" in condition ? condition.mode : void 0);
|
|
6952
|
+
return comparison?.[0].startsWith(comparison[1]) ?? false;
|
|
6953
|
+
}
|
|
6954
|
+
case "ends_with": {
|
|
6955
|
+
if (typeof expected !== "string") return false;
|
|
6956
|
+
const comparison = normalizeStringComparison(actual, expected, "mode" in condition ? condition.mode : void 0);
|
|
6957
|
+
return comparison?.[0].endsWith(comparison[1]) ?? false;
|
|
6958
|
+
}
|
|
6959
|
+
}
|
|
6960
|
+
};
|
|
6961
|
+
const matchesFirebaseDatabaseWhere = (row, where) => {
|
|
6962
|
+
const first = where?.[0];
|
|
6963
|
+
if (!first) return true;
|
|
6964
|
+
let result = matchesCondition(row, first);
|
|
6965
|
+
for (const condition of where.slice(1)) {
|
|
6966
|
+
const current = matchesCondition(row, condition);
|
|
6967
|
+
result = condition.connector === "OR" ? result || current : result && current;
|
|
6968
|
+
}
|
|
6969
|
+
return result;
|
|
6970
|
+
};
|
|
6971
|
+
const queryFirebaseDatabaseRows = (rows, input) => {
|
|
6972
|
+
const filtered = rows.filter((row) => matchesFirebaseDatabaseWhere(row, input.where));
|
|
6973
|
+
const orderBy = input.orderBy;
|
|
6974
|
+
if (orderBy !== void 0) filtered.sort((left, right) => {
|
|
6975
|
+
for (const clause of orderBy) {
|
|
6976
|
+
const leftValue = Reflect.get(left, clause.field);
|
|
6977
|
+
const rightValue = Reflect.get(right, clause.field);
|
|
6978
|
+
if (leftValue === rightValue) continue;
|
|
6979
|
+
if (clause.nulls !== void 0 && (leftValue === null || rightValue === null)) {
|
|
6980
|
+
const nullComparison = leftValue === null ? -1 : 1;
|
|
6981
|
+
return clause.nulls === "first" ? nullComparison : -nullComparison;
|
|
6982
|
+
}
|
|
6983
|
+
const comparison = compare(leftValue, rightValue);
|
|
6984
|
+
if (comparison !== 0) return clause.direction === "asc" ? comparison : -comparison;
|
|
6985
|
+
}
|
|
6986
|
+
return 0;
|
|
6987
|
+
});
|
|
6988
|
+
const distinctOn = input.distinctOn;
|
|
6989
|
+
if (distinctOn === void 0) return filtered.slice(input.offset, input.offset + input.limit);
|
|
6990
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6991
|
+
return filtered.filter((row) => {
|
|
6992
|
+
const key = JSON.stringify(distinctOn.fields.map((field) => Reflect.get(row, field)));
|
|
6993
|
+
if (seen.has(key)) return false;
|
|
6994
|
+
seen.add(key);
|
|
6995
|
+
return true;
|
|
6996
|
+
}).slice(input.offset, input.offset + input.limit);
|
|
6997
|
+
};
|
|
6998
|
+
//#endregion
|
|
6999
|
+
//#region src/firebaseDatabaseState.ts
|
|
7000
|
+
var FirebaseDatabaseConstraintError = class extends Error {
|
|
7001
|
+
name = "FirebaseDatabaseConstraintError";
|
|
7002
|
+
constructor(constraint) {
|
|
7003
|
+
super(`Firebase database constraint failed: ${constraint}`);
|
|
7004
|
+
this.constraint = constraint;
|
|
7005
|
+
}
|
|
7006
|
+
};
|
|
7007
|
+
const cloneFirebaseDatabaseSnapshot = (snapshot) => ({
|
|
7008
|
+
bundles: new Map(snapshot.bundles),
|
|
7009
|
+
bundlePatches: new Map(snapshot.bundlePatches),
|
|
7010
|
+
bundleEvents: new Map(snapshot.bundleEvents),
|
|
7011
|
+
channels: new Map(snapshot.channels),
|
|
7012
|
+
apiKeys: new Map(snapshot.apiKeys),
|
|
7013
|
+
releaseCatalogs: new Map(snapshot.releaseCatalogs),
|
|
7014
|
+
releases: new Map(snapshot.releases)
|
|
7015
|
+
});
|
|
7016
|
+
const requireUnique = (rows, id, model) => {
|
|
7017
|
+
if (rows.has(id)) throw new FirebaseDatabaseConstraintError(`${model}.id.unique`);
|
|
7018
|
+
};
|
|
7019
|
+
const distinctCount = (rows, fields) => {
|
|
7020
|
+
if (fields === void 0) return rows.length;
|
|
7021
|
+
return new Set(rows.map((row) => JSON.stringify(fields.map((field) => Reflect.get(row, field))))).size;
|
|
7022
|
+
};
|
|
7023
|
+
const createFirebaseDatabaseState = (snapshot) => ({
|
|
7024
|
+
async create(input) {
|
|
7025
|
+
switch (input.model) {
|
|
7026
|
+
case "bundles":
|
|
7027
|
+
requireUnique(snapshot.bundles, input.data.id, input.model);
|
|
7028
|
+
snapshot.bundles.set(input.data.id, input.data);
|
|
7029
|
+
return input.data;
|
|
7030
|
+
case "bundle_patches":
|
|
7031
|
+
requireUnique(snapshot.bundlePatches, input.data.id, input.model);
|
|
7032
|
+
if (!snapshot.bundles.has(input.data.bundle_id)) throw new FirebaseDatabaseConstraintError("bundle_patches.bundle_id.foreign-key");
|
|
7033
|
+
if (!snapshot.bundles.has(input.data.base_bundle_id)) throw new FirebaseDatabaseConstraintError("bundle_patches.base_bundle_id.foreign-key");
|
|
7034
|
+
snapshot.bundlePatches.set(input.data.id, input.data);
|
|
7035
|
+
return input.data;
|
|
7036
|
+
case "bundle_events":
|
|
7037
|
+
requireUnique(snapshot.bundleEvents, input.data.id, input.model);
|
|
7038
|
+
snapshot.bundleEvents.set(input.data.id, input.data);
|
|
7039
|
+
return input.data;
|
|
7040
|
+
case "releases":
|
|
7041
|
+
requireUnique(snapshot.releases, input.data.id, input.model);
|
|
7042
|
+
if (!snapshot.channels.has(input.data.channel_id)) throw new FirebaseDatabaseConstraintError("releases.channel_id.foreign-key");
|
|
7043
|
+
if (input.data.bundle_id !== null && !snapshot.bundles.has(input.data.bundle_id)) throw new FirebaseDatabaseConstraintError("releases.bundle_id.foreign-key");
|
|
7044
|
+
snapshot.releases.set(input.data.id, input.data);
|
|
7045
|
+
return input.data;
|
|
7046
|
+
case "release_catalogs":
|
|
7047
|
+
if (snapshot.releaseCatalogs.has(input.data.scope_key)) throw new FirebaseDatabaseConstraintError("release_catalogs.scope_key.unique");
|
|
7048
|
+
snapshot.releaseCatalogs.set(input.data.scope_key, input.data);
|
|
7049
|
+
return input.data;
|
|
7050
|
+
case "channels": {
|
|
7051
|
+
const existing = [...snapshot.channels.values()].find(({ name }) => name === input.data.name);
|
|
7052
|
+
if (existing && input.onConflict === "ignore") return existing;
|
|
7053
|
+
requireUnique(snapshot.channels, input.data.id, input.model);
|
|
7054
|
+
if (existing) throw new FirebaseDatabaseConstraintError("channels.name.unique");
|
|
7055
|
+
snapshot.channels.set(input.data.id, input.data);
|
|
7056
|
+
return input.data;
|
|
7057
|
+
}
|
|
7058
|
+
case "api_keys": {
|
|
7059
|
+
const existing = [...snapshot.apiKeys.values()].find(({ hash }) => hash === input.data.hash);
|
|
7060
|
+
if (existing && input.onConflict === "ignore") return existing;
|
|
7061
|
+
requireUnique(snapshot.apiKeys, input.data.id, input.model);
|
|
7062
|
+
if (existing) throw new FirebaseDatabaseConstraintError("api_keys.hash.unique");
|
|
7063
|
+
snapshot.apiKeys.set(input.data.id, input.data);
|
|
7064
|
+
return input.data;
|
|
7065
|
+
}
|
|
7066
|
+
}
|
|
7067
|
+
},
|
|
7068
|
+
async update(input) {
|
|
7069
|
+
if (input.model === "api_keys") {
|
|
7070
|
+
const current = [...snapshot.apiKeys.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where));
|
|
7071
|
+
if (!current) return null;
|
|
7072
|
+
const updated = {
|
|
7073
|
+
...current,
|
|
7074
|
+
...input.update
|
|
7075
|
+
};
|
|
7076
|
+
snapshot.apiKeys.set(current.id, updated);
|
|
7077
|
+
return updated;
|
|
7078
|
+
}
|
|
7079
|
+
if (input.model === "releases") {
|
|
7080
|
+
const current = [...snapshot.releases.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where));
|
|
7081
|
+
if (!current) return null;
|
|
7082
|
+
const updated = {
|
|
7083
|
+
...current,
|
|
7084
|
+
...input.update
|
|
7085
|
+
};
|
|
7086
|
+
snapshot.releases.set(current.id, updated);
|
|
7087
|
+
return updated;
|
|
7088
|
+
}
|
|
7089
|
+
if (input.model === "release_catalogs") {
|
|
7090
|
+
const current = [...snapshot.releaseCatalogs.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where));
|
|
7091
|
+
if (!current) return null;
|
|
7092
|
+
const updated = {
|
|
7093
|
+
...current,
|
|
7094
|
+
...input.update
|
|
7095
|
+
};
|
|
7096
|
+
snapshot.releaseCatalogs.set(current.scope_key, updated);
|
|
7097
|
+
return updated;
|
|
7098
|
+
}
|
|
7099
|
+
const current = [...snapshot.bundles.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where));
|
|
7100
|
+
if (!current) return null;
|
|
7101
|
+
const updated = {
|
|
7102
|
+
...current,
|
|
7103
|
+
...input.update
|
|
7104
|
+
};
|
|
7105
|
+
snapshot.bundles.set(current.id, updated);
|
|
7106
|
+
return updated;
|
|
7107
|
+
},
|
|
7108
|
+
async delete(input) {
|
|
7109
|
+
if (input.model === "channels") {
|
|
7110
|
+
for (const row of snapshot.channels.values()) if (matchesFirebaseDatabaseWhere(row, input.where)) snapshot.channels.delete(row.id);
|
|
7111
|
+
return;
|
|
7112
|
+
}
|
|
7113
|
+
if (input.model === "bundle_patches") {
|
|
7114
|
+
for (const row of snapshot.bundlePatches.values()) if (matchesFirebaseDatabaseWhere(row, input.where)) snapshot.bundlePatches.delete(row.id);
|
|
7115
|
+
return;
|
|
7116
|
+
}
|
|
7117
|
+
if (input.model === "releases") {
|
|
7118
|
+
for (const row of snapshot.releases.values()) if (matchesFirebaseDatabaseWhere(row, input.where)) snapshot.releases.delete(row.id);
|
|
7119
|
+
return;
|
|
7120
|
+
}
|
|
7121
|
+
const removedIds = new Set([...snapshot.bundles.values()].filter((row) => matchesFirebaseDatabaseWhere(row, input.where)).map(({ id }) => id));
|
|
7122
|
+
for (const id of removedIds) snapshot.bundles.delete(id);
|
|
7123
|
+
for (const patch of snapshot.bundlePatches.values()) if (removedIds.has(patch.bundle_id) || removedIds.has(patch.base_bundle_id)) snapshot.bundlePatches.delete(patch.id);
|
|
7124
|
+
},
|
|
7125
|
+
async count(input) {
|
|
7126
|
+
switch (input.model) {
|
|
7127
|
+
case "bundles": return distinctCount([...snapshot.bundles.values()].filter((row) => matchesFirebaseDatabaseWhere(row, input.where)), input.distinct);
|
|
7128
|
+
case "bundle_patches": return distinctCount([...snapshot.bundlePatches.values()].filter((row) => matchesFirebaseDatabaseWhere(row, input.where)), input.distinct);
|
|
7129
|
+
case "releases": return distinctCount([...snapshot.releases.values()].filter((row) => matchesFirebaseDatabaseWhere(row, input.where)), input.distinct);
|
|
7130
|
+
}
|
|
7131
|
+
},
|
|
7132
|
+
async findOne(input) {
|
|
7133
|
+
switch (input.model) {
|
|
7134
|
+
case "bundles": return [...snapshot.bundles.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where)) ?? null;
|
|
7135
|
+
case "api_keys": return [...snapshot.apiKeys.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where)) ?? null;
|
|
7136
|
+
case "channels": return [...snapshot.channels.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where)) ?? null;
|
|
7137
|
+
case "bundle_patches": return [...snapshot.bundlePatches.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where)) ?? null;
|
|
7138
|
+
case "releases": return [...snapshot.releases.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where)) ?? null;
|
|
7139
|
+
case "release_catalogs": return [...snapshot.releaseCatalogs.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where)) ?? null;
|
|
7140
|
+
}
|
|
7141
|
+
},
|
|
7142
|
+
async findMany(input) {
|
|
7143
|
+
switch (input.model) {
|
|
7144
|
+
case "bundles": return queryFirebaseDatabaseRows([...snapshot.bundles.values()], input);
|
|
7145
|
+
case "bundle_patches": return queryFirebaseDatabaseRows([...snapshot.bundlePatches.values()], input);
|
|
7146
|
+
case "bundle_events": return queryFirebaseDatabaseRows([...snapshot.bundleEvents.values()], input);
|
|
7147
|
+
case "channels": return queryFirebaseDatabaseRows([...snapshot.channels.values()], input);
|
|
7148
|
+
case "api_keys": return queryFirebaseDatabaseRows([...snapshot.apiKeys.values()], input);
|
|
7149
|
+
case "releases": return queryFirebaseDatabaseRows([...snapshot.releases.values()], input);
|
|
7150
|
+
case "release_catalogs": return queryFirebaseDatabaseRows([...snapshot.releaseCatalogs.values()], input);
|
|
7151
|
+
}
|
|
7152
|
+
}
|
|
7153
|
+
});
|
|
7154
|
+
//#endregion
|
|
7155
|
+
//#region src/firebaseInfrastructureNames.ts
|
|
7156
|
+
const FIREBASE_V1_FUNCTION_NAME = "hot-updater-v1";
|
|
7157
|
+
const FIREBASE_V1_COLLECTION_NAMES = {
|
|
7158
|
+
apiKeys: "hot_updater_v1_api_keys",
|
|
7159
|
+
bundleEvents: "hot_updater_v1_bundle_events",
|
|
7160
|
+
bundlePatches: "hot_updater_v1_bundle_patches",
|
|
7161
|
+
bundles: "hot_updater_v1_bundles",
|
|
7162
|
+
channels: "hot_updater_v1_channels",
|
|
7163
|
+
releaseCatalogs: "hot_updater_v1_release_catalogs",
|
|
7164
|
+
releases: "hot_updater_v1_releases",
|
|
7165
|
+
settings: "hot_updater_v1_private_settings"
|
|
7166
|
+
};
|
|
7167
|
+
//#endregion
|
|
7168
|
+
//#region src/firebaseDatabasePersistence.ts
|
|
7169
|
+
var FirebaseDatabaseAdapterVersionError = class extends Error {
|
|
7170
|
+
name = "FirebaseDatabaseAdapterVersionError";
|
|
7171
|
+
constructor(version) {
|
|
7172
|
+
super(`Unsupported Firebase database adapter version: ${String(version)}`);
|
|
7173
|
+
this.version = version;
|
|
7174
|
+
}
|
|
7175
|
+
};
|
|
7176
|
+
const createFirebaseDatabaseCollections = (db) => ({
|
|
7177
|
+
bundles: db.collection(FIREBASE_V1_COLLECTION_NAMES.bundles),
|
|
7178
|
+
bundlePatches: db.collection(FIREBASE_V1_COLLECTION_NAMES.bundlePatches),
|
|
7179
|
+
bundleEvents: db.collection(FIREBASE_V1_COLLECTION_NAMES.bundleEvents),
|
|
7180
|
+
channels: db.collection(FIREBASE_V1_COLLECTION_NAMES.channels),
|
|
7181
|
+
apiKeys: db.collection(FIREBASE_V1_COLLECTION_NAMES.apiKeys),
|
|
7182
|
+
releaseCatalogs: db.collection(FIREBASE_V1_COLLECTION_NAMES.releaseCatalogs),
|
|
7183
|
+
releases: db.collection(FIREBASE_V1_COLLECTION_NAMES.releases),
|
|
7184
|
+
settings: db.collection(FIREBASE_V1_COLLECTION_NAMES.settings)
|
|
7185
|
+
});
|
|
7186
|
+
const firebaseChannelDocumentId = (name) => `name_${Buffer.from(name, "utf8").toString("base64url")}`;
|
|
7187
|
+
const firebaseChannelIdDocumentId = (id) => `channel_id_${Buffer.from(id, "utf8").toString("base64url")}`;
|
|
7188
|
+
const requireFirebaseDocumentKey = (model, documentId, row) => {
|
|
7189
|
+
if (documentId !== ("id" in row ? row.id : row.scope_key)) throw new FirebaseDatabaseConstraintError(`${model}.id.document-key`);
|
|
7190
|
+
return row;
|
|
7191
|
+
};
|
|
7192
|
+
const documentMap = (model, documents) => {
|
|
7193
|
+
const rows = /* @__PURE__ */ new Map();
|
|
7194
|
+
for (const { row } of documents) {
|
|
7195
|
+
const key = "id" in row ? row.id : row.scope_key;
|
|
7196
|
+
if (rows.has(key)) throw new FirebaseDatabaseConstraintError(`${model}.id.unique`);
|
|
7197
|
+
rows.set(key, row);
|
|
7198
|
+
}
|
|
7199
|
+
for (const { document, row } of documents) requireFirebaseDocumentKey(model, document.id, row);
|
|
7200
|
+
return rows;
|
|
7201
|
+
};
|
|
7202
|
+
const bundleMap = (snapshot) => documentMap("bundles", snapshot.docs.map((document) => ({
|
|
7203
|
+
document,
|
|
7204
|
+
row: parseFirebaseBundleRow(document.data(), `bundles/${document.id}`)
|
|
7205
|
+
})));
|
|
7206
|
+
const patchMap = (snapshot) => documentMap("bundle_patches", snapshot.docs.map((document) => ({
|
|
7207
|
+
document,
|
|
7208
|
+
row: parseFirebasePatchRow(document.data(), `bundle_patches/${document.id}`)
|
|
7209
|
+
})));
|
|
7210
|
+
const eventMap = (snapshot) => documentMap("bundle_events", snapshot.docs.map((document) => ({
|
|
7211
|
+
document,
|
|
7212
|
+
row: parseFirebaseBundleEventRow(document.data(), `bundle_events/${document.id}`)
|
|
7213
|
+
})));
|
|
7214
|
+
const channelMap = (snapshot) => {
|
|
7215
|
+
const rows = /* @__PURE__ */ new Map();
|
|
7216
|
+
const names = /* @__PURE__ */ new Set();
|
|
7217
|
+
for (const document of snapshot.docs) {
|
|
7218
|
+
const row = parseFirebaseChannelRow(document.data(), `channels/${document.id}`);
|
|
7219
|
+
if (document.id !== firebaseChannelDocumentId(row.name)) throw new FirebaseDatabaseConstraintError("channels.name.document-key");
|
|
7220
|
+
if (rows.has(row.id)) throw new FirebaseDatabaseConstraintError("channels.id.unique");
|
|
7221
|
+
if (names.has(row.name)) throw new FirebaseDatabaseConstraintError("channels.name.unique");
|
|
7222
|
+
rows.set(row.id, row);
|
|
7223
|
+
names.add(row.name);
|
|
7224
|
+
}
|
|
7225
|
+
return rows;
|
|
7226
|
+
};
|
|
7227
|
+
const loadFirebaseChannels = async (collections) => [...channelMap(await collections.channels.get()).values()];
|
|
7228
|
+
const apiKeyMap = (snapshot) => documentMap("api_keys", snapshot.docs.map((document) => ({
|
|
7229
|
+
document,
|
|
7230
|
+
row: parseFirebaseApiKeyRow(document.data(), `api_keys/${document.id}`)
|
|
7231
|
+
})));
|
|
7232
|
+
const releaseMap = (snapshot) => documentMap("releases", snapshot.docs.map((document) => ({
|
|
7233
|
+
document,
|
|
7234
|
+
row: parseFirebaseReleaseRow(document.data(), `releases/${document.id}`)
|
|
7235
|
+
})));
|
|
7236
|
+
const releaseCatalogMap = (snapshot) => documentMap("release_catalogs", snapshot.docs.map((document) => ({
|
|
7237
|
+
document,
|
|
7238
|
+
row: parseFirebaseReleaseCatalogRow(document.data(), `release_catalogs/${document.id}`)
|
|
7239
|
+
})));
|
|
7240
|
+
const toSnapshot = (documents) => {
|
|
7241
|
+
return {
|
|
7242
|
+
bundles: bundleMap(documents[0]),
|
|
7243
|
+
bundlePatches: patchMap(documents[1]),
|
|
7244
|
+
bundleEvents: eventMap(documents[2]),
|
|
7245
|
+
channels: channelMap(documents[3]),
|
|
7246
|
+
apiKeys: apiKeyMap(documents[4]),
|
|
7247
|
+
releases: releaseMap(documents[5]),
|
|
7248
|
+
releaseCatalogs: releaseCatalogMap(documents[6])
|
|
7249
|
+
};
|
|
7250
|
+
};
|
|
7251
|
+
const loadFirebaseDatabaseSnapshot = async (collections) => {
|
|
7252
|
+
const [bundles, patches, events, channels, apiKeys, releases, releaseCatalogs] = await Promise.all([
|
|
7253
|
+
collections.bundles.get(),
|
|
7254
|
+
collections.bundlePatches.get(),
|
|
7255
|
+
collections.bundleEvents.get(),
|
|
7256
|
+
collections.channels.get(),
|
|
7257
|
+
collections.apiKeys.get(),
|
|
7258
|
+
collections.releases.get(),
|
|
7259
|
+
collections.releaseCatalogs.get()
|
|
7260
|
+
]);
|
|
7261
|
+
return toSnapshot([
|
|
7262
|
+
bundles,
|
|
7263
|
+
patches,
|
|
7264
|
+
events,
|
|
7265
|
+
channels,
|
|
7266
|
+
apiKeys,
|
|
7267
|
+
releases,
|
|
7268
|
+
releaseCatalogs
|
|
7269
|
+
]);
|
|
7270
|
+
};
|
|
7271
|
+
const loadFirebaseTransactionSnapshot = async (transaction, collections) => {
|
|
7272
|
+
const [bundles, patches, events, channels, apiKeys, releases, releaseCatalogs] = await Promise.all([
|
|
7273
|
+
transaction.get(collections.bundles),
|
|
7274
|
+
transaction.get(collections.bundlePatches),
|
|
7275
|
+
transaction.get(collections.bundleEvents),
|
|
7276
|
+
transaction.get(collections.channels),
|
|
7277
|
+
transaction.get(collections.apiKeys),
|
|
7278
|
+
transaction.get(collections.releases),
|
|
7279
|
+
transaction.get(collections.releaseCatalogs)
|
|
7280
|
+
]);
|
|
7281
|
+
return toSnapshot([
|
|
7282
|
+
bundles,
|
|
7283
|
+
patches,
|
|
7284
|
+
events,
|
|
7285
|
+
channels,
|
|
7286
|
+
apiKeys,
|
|
7287
|
+
releases,
|
|
7288
|
+
releaseCatalogs
|
|
7289
|
+
]);
|
|
7290
|
+
};
|
|
7291
|
+
const persistCollection = ({ transaction, collection, before, after, documentId }) => {
|
|
7292
|
+
for (const [id, row] of before) if (!after.has(id)) transaction.delete(collection.doc(documentId(row)));
|
|
7293
|
+
for (const [id, row] of after) if (JSON.stringify(before.get(id)) !== JSON.stringify(row)) transaction.set(collection.doc(documentId(row)), row, { merge: true });
|
|
7294
|
+
};
|
|
7295
|
+
const persistFirebaseDatabaseSnapshot = ({ transaction, collections, before, after }) => {
|
|
7296
|
+
persistCollection({
|
|
7297
|
+
transaction,
|
|
7298
|
+
collection: collections.bundles,
|
|
7299
|
+
before: before.bundles,
|
|
7300
|
+
after: after.bundles,
|
|
7301
|
+
documentId: (row) => row.id
|
|
7302
|
+
});
|
|
7303
|
+
persistCollection({
|
|
7304
|
+
transaction,
|
|
7305
|
+
collection: collections.bundlePatches,
|
|
7306
|
+
before: before.bundlePatches,
|
|
7307
|
+
after: after.bundlePatches,
|
|
7308
|
+
documentId: (row) => row.id
|
|
7309
|
+
});
|
|
7310
|
+
persistCollection({
|
|
7311
|
+
transaction,
|
|
7312
|
+
collection: collections.bundleEvents,
|
|
7313
|
+
before: before.bundleEvents,
|
|
7314
|
+
after: after.bundleEvents,
|
|
7315
|
+
documentId: (row) => row.id
|
|
7316
|
+
});
|
|
7317
|
+
persistCollection({
|
|
7318
|
+
transaction,
|
|
7319
|
+
collection: collections.channels,
|
|
7320
|
+
before: before.channels,
|
|
7321
|
+
after: after.channels,
|
|
7322
|
+
documentId: (row) => firebaseChannelDocumentId(row.name)
|
|
7323
|
+
});
|
|
7324
|
+
for (const [id] of before.channels) if (!after.channels.has(id)) transaction.delete(collections.settings.doc(firebaseChannelIdDocumentId(id)));
|
|
7325
|
+
for (const [id, row] of after.channels) if (JSON.stringify(before.channels.get(id)) !== JSON.stringify(row)) transaction.set(collections.settings.doc(firebaseChannelIdDocumentId(id)), row);
|
|
7326
|
+
persistCollection({
|
|
7327
|
+
transaction,
|
|
7328
|
+
collection: collections.apiKeys,
|
|
7329
|
+
before: before.apiKeys,
|
|
7330
|
+
after: after.apiKeys,
|
|
7331
|
+
documentId: (row) => row.id
|
|
7332
|
+
});
|
|
7333
|
+
persistCollection({
|
|
7334
|
+
transaction,
|
|
7335
|
+
collection: collections.releases,
|
|
7336
|
+
before: before.releases,
|
|
7337
|
+
after: after.releases,
|
|
7338
|
+
documentId: (row) => row.id
|
|
7339
|
+
});
|
|
7340
|
+
persistCollection({
|
|
7341
|
+
transaction,
|
|
7342
|
+
collection: collections.releaseCatalogs,
|
|
7343
|
+
before: before.releaseCatalogs,
|
|
7344
|
+
after: after.releaseCatalogs,
|
|
7345
|
+
documentId: (row) => row.scope_key
|
|
7346
|
+
});
|
|
7347
|
+
};
|
|
7348
|
+
const migrateFirebaseDatabase = async (_db, collections) => {
|
|
7349
|
+
const versionDocument = collections.settings.doc("database_adapter_version");
|
|
7350
|
+
const version = await versionDocument.get();
|
|
7351
|
+
const adapterVersion = version.data()?.version;
|
|
7352
|
+
if (adapterVersion === 4) return;
|
|
7353
|
+
if (version.exists) throw new FirebaseDatabaseAdapterVersionError(adapterVersion);
|
|
7354
|
+
if ((await Promise.all([
|
|
7355
|
+
collections.bundles.limit(1).get(),
|
|
7356
|
+
collections.bundlePatches.limit(1).get(),
|
|
7357
|
+
collections.channels.limit(1).get(),
|
|
7358
|
+
collections.releases.limit(1).get(),
|
|
7359
|
+
collections.releaseCatalogs.limit(1).get()
|
|
7360
|
+
])).some((snapshot) => !snapshot.empty)) throw new FirebaseDatabaseAdapterVersionError("v0");
|
|
7361
|
+
try {
|
|
7362
|
+
await versionDocument.create({ version: 4 });
|
|
7363
|
+
} catch (error) {
|
|
7364
|
+
if ((await versionDocument.get()).data()?.version !== 4) throw error;
|
|
7365
|
+
}
|
|
7366
|
+
};
|
|
7367
|
+
//#endregion
|
|
7368
|
+
//#region src/firebaseDatabase.ts
|
|
7369
|
+
const exactId = (input) => {
|
|
7370
|
+
if (input.where?.length !== 1) return void 0;
|
|
7371
|
+
const [condition] = input.where;
|
|
7372
|
+
return condition.field === "id" && (condition.operator === void 0 || condition.operator === "eq") && typeof condition.value === "string" ? condition.value : void 0;
|
|
7373
|
+
};
|
|
7374
|
+
const firebaseDatabase = (config) => {
|
|
7375
|
+
const adapter = (0, _hot_updater_plugin_core_internal.createDatabasePluginAdapter)("firebaseDatabase", (() => {
|
|
7376
|
+
const db = (0, firebase_admin_firestore.getFirestore)((0, firebase_admin_app.getApps)().length ? (0, firebase_admin_app.getApp)() : (0, firebase_admin_app.initializeApp)(config));
|
|
7377
|
+
const collections = createFirebaseDatabaseCollections(db);
|
|
7378
|
+
let migration;
|
|
7379
|
+
const ensureMigrated = () => {
|
|
7380
|
+
migration ??= migrateFirebaseDatabase(db, collections).catch((error) => {
|
|
7381
|
+
migration = void 0;
|
|
7382
|
+
throw error;
|
|
7383
|
+
});
|
|
7384
|
+
return migration;
|
|
7385
|
+
};
|
|
7386
|
+
const mutate = async (operation) => {
|
|
7387
|
+
await ensureMigrated();
|
|
7388
|
+
return db.runTransaction(async (transaction) => {
|
|
7389
|
+
const before = await loadFirebaseTransactionSnapshot(transaction, collections);
|
|
7390
|
+
const after = cloneFirebaseDatabaseSnapshot(before);
|
|
7391
|
+
const result = await operation(createFirebaseDatabaseState(after));
|
|
7392
|
+
persistFirebaseDatabaseSnapshot({
|
|
7393
|
+
transaction,
|
|
7394
|
+
collections,
|
|
7395
|
+
before,
|
|
7396
|
+
after
|
|
7397
|
+
});
|
|
7398
|
+
return result;
|
|
7399
|
+
});
|
|
7400
|
+
};
|
|
7401
|
+
const read = async (operation) => {
|
|
7402
|
+
await ensureMigrated();
|
|
7403
|
+
return operation(createFirebaseDatabaseState(await loadFirebaseDatabaseSnapshot(collections)));
|
|
7404
|
+
};
|
|
7405
|
+
return {
|
|
7406
|
+
create: (input) => mutate((database) => database.create(input)),
|
|
7407
|
+
update: (input) => mutate((database) => database.update(input)),
|
|
7408
|
+
delete: (input) => mutate((database) => database.delete(input)),
|
|
7409
|
+
count: (input) => read((database) => database.count(input)),
|
|
7410
|
+
findOne: async (input) => {
|
|
7411
|
+
const id = exactId(input);
|
|
7412
|
+
if (id === void 0) return read((database) => database.findOne(input));
|
|
7413
|
+
await ensureMigrated();
|
|
7414
|
+
switch (input.model) {
|
|
7415
|
+
case "bundles": {
|
|
7416
|
+
const document = await collections.bundles.doc(id).get();
|
|
7417
|
+
return document.exists ? requireFirebaseDocumentKey("bundles", document.id, parseFirebaseBundleRow(document.data(), `bundles/${document.id}`)) : null;
|
|
7418
|
+
}
|
|
7419
|
+
case "bundle_patches": {
|
|
7420
|
+
const document = await collections.bundlePatches.doc(id).get();
|
|
7421
|
+
return document.exists ? requireFirebaseDocumentKey("bundle_patches", document.id, parseFirebasePatchRow(document.data(), `bundle_patches/${document.id}`)) : null;
|
|
7422
|
+
}
|
|
7423
|
+
case "api_keys": {
|
|
7424
|
+
const document = await collections.apiKeys.doc(id).get();
|
|
7425
|
+
return document.exists ? requireFirebaseDocumentKey("api_keys", document.id, parseFirebaseApiKeyRow(document.data(), `api_keys/${document.id}`)) : null;
|
|
7426
|
+
}
|
|
7427
|
+
default: return read((database) => database.findOne(input));
|
|
7428
|
+
}
|
|
7429
|
+
},
|
|
7430
|
+
findMany: async (input) => {
|
|
7431
|
+
if (input.model !== "channels") return read((database) => database.findMany(input));
|
|
7432
|
+
await ensureMigrated();
|
|
7433
|
+
return queryFirebaseDatabaseRows(await loadFirebaseChannels(collections), input);
|
|
7434
|
+
},
|
|
7435
|
+
insertChannel: async (input) => {
|
|
7436
|
+
await ensureMigrated();
|
|
7437
|
+
return db.runTransaction(async (transaction) => {
|
|
7438
|
+
const reference = collections.channels.doc(firebaseChannelDocumentId(input.row.name));
|
|
7439
|
+
const idReference = collections.settings.doc(firebaseChannelIdDocumentId(input.row.id));
|
|
7440
|
+
const [document, idDocument] = await transaction.getAll(reference, idReference);
|
|
7441
|
+
if (idDocument.exists) {
|
|
7442
|
+
const row = parseFirebaseChannelRow(idDocument.data(), `${FIREBASE_V1_COLLECTION_NAMES.settings}/${idDocument.id}`);
|
|
7443
|
+
if (row.id !== input.row.id || row.name !== input.row.name) throw new FirebaseDatabaseConstraintError("channels.id.registry");
|
|
7444
|
+
}
|
|
7445
|
+
if (idDocument.exists && !document.exists) throw new FirebaseDatabaseConstraintError("channels.id.unique");
|
|
7446
|
+
if (document.exists) {
|
|
7447
|
+
const row = parseFirebaseChannelRow(document.data(), `channels/${document.id}`);
|
|
7448
|
+
if (document.id !== firebaseChannelDocumentId(row.name)) throw new FirebaseDatabaseConstraintError("channels.name.document-key");
|
|
7449
|
+
return {
|
|
7450
|
+
row,
|
|
7451
|
+
inserted: false
|
|
7452
|
+
};
|
|
7453
|
+
}
|
|
7454
|
+
transaction.create(reference, input.row);
|
|
7455
|
+
transaction.create(idReference, input.row);
|
|
7456
|
+
return {
|
|
7457
|
+
row: input.row,
|
|
7458
|
+
inserted: true
|
|
7459
|
+
};
|
|
7460
|
+
});
|
|
7461
|
+
},
|
|
7462
|
+
deleteChannel: async ({ id }) => {
|
|
7463
|
+
await ensureMigrated();
|
|
7464
|
+
return db.runTransaction(async (transaction) => {
|
|
7465
|
+
const idReference = collections.settings.doc(firebaseChannelIdDocumentId(id));
|
|
7466
|
+
const idDocument = await transaction.get(idReference);
|
|
7467
|
+
if (!idDocument.exists) return {
|
|
7468
|
+
deleted: false,
|
|
7469
|
+
reason: "not_found"
|
|
7470
|
+
};
|
|
7471
|
+
const row = parseFirebaseChannelRow(idDocument.data(), `${FIREBASE_V1_COLLECTION_NAMES.settings}/${idDocument.id}`);
|
|
7472
|
+
const reference = collections.channels.doc(firebaseChannelDocumentId(row.name));
|
|
7473
|
+
if (!(await transaction.get(reference)).exists || row.id !== id) throw new FirebaseDatabaseConstraintError("channels.id.registry");
|
|
7474
|
+
if (!(await transaction.get(collections.releases.where("channel_id", "==", id).limit(1))).empty) return {
|
|
7475
|
+
deleted: false,
|
|
7476
|
+
reason: "not_empty"
|
|
7477
|
+
};
|
|
7478
|
+
transaction.delete(reference);
|
|
7479
|
+
transaction.delete(idReference);
|
|
7480
|
+
return { deleted: true };
|
|
7481
|
+
});
|
|
7482
|
+
},
|
|
7483
|
+
transaction: (callback) => mutate(callback)
|
|
7484
|
+
};
|
|
7485
|
+
})());
|
|
7486
|
+
return (0, _hot_updater_plugin_core.createDatabasePlugin)({
|
|
7487
|
+
name: "firebaseDatabase",
|
|
7488
|
+
models: adapter.models,
|
|
7489
|
+
commit: adapter.commit
|
|
7490
|
+
});
|
|
7491
|
+
};
|
|
7492
|
+
//#endregion
|
|
6715
7493
|
//#region iac/init/index.ts
|
|
6716
7494
|
const isFirebaseRegion = (value) => value !== void 0 && /^[a-z]+(?:-[a-z]+)+[0-9]+$/.test(value);
|
|
6717
7495
|
const isFirebaseProjectId = (value) => value !== void 0 && /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/.test(value);
|
|
@@ -6765,6 +7543,53 @@ const inputFirebaseApplicationCredentials = async ({ applicationCredentials, non
|
|
|
6765
7543
|
return credentialsPath || void 0;
|
|
6766
7544
|
};
|
|
6767
7545
|
//#endregion
|
|
7546
|
+
//#region iac/firebaseInfrastructureState.ts
|
|
7547
|
+
const resolveFirebaseInfrastructureState = ({ adapterVersion, hasData }) => {
|
|
7548
|
+
if (adapterVersion === 4) return "v1";
|
|
7549
|
+
if (adapterVersion !== void 0 || hasData) return "incompatible";
|
|
7550
|
+
return "fresh";
|
|
7551
|
+
};
|
|
7552
|
+
const assertFirebaseInfrastructureCanInitialize = async ({ applicationCredentials, projectId }) => {
|
|
7553
|
+
const app = (0, firebase_admin_app.initializeApp)({
|
|
7554
|
+
credential: applicationCredentials ? (0, firebase_admin_app.cert)(JSON.parse(await fs_promises.default.readFile(applicationCredentials, "utf8"))) : (0, firebase_admin_app.applicationDefault)(),
|
|
7555
|
+
projectId
|
|
7556
|
+
}, `hot-updater-init-${projectId}-${Date.now()}`);
|
|
7557
|
+
try {
|
|
7558
|
+
const db = (0, firebase_admin_firestore.getFirestore)(app);
|
|
7559
|
+
const version = await db.collection(FIREBASE_V1_COLLECTION_NAMES.settings).doc("database_adapter_version").get();
|
|
7560
|
+
const collections = await Promise.all([
|
|
7561
|
+
FIREBASE_V1_COLLECTION_NAMES.bundles,
|
|
7562
|
+
FIREBASE_V1_COLLECTION_NAMES.bundlePatches,
|
|
7563
|
+
FIREBASE_V1_COLLECTION_NAMES.channels,
|
|
7564
|
+
FIREBASE_V1_COLLECTION_NAMES.releaseCatalogs
|
|
7565
|
+
].map((name) => db.collection(name).limit(1).get()));
|
|
7566
|
+
if (resolveFirebaseInfrastructureState({
|
|
7567
|
+
adapterVersion: version.exists ? version.data()?.version : void 0,
|
|
7568
|
+
hasData: collections.some((snapshot) => !snapshot.empty)
|
|
7569
|
+
}) === "incompatible") throw new _hot_updater_cli_tools.InitError(`Firebase v1 infrastructure in project ${projectId} is incomplete or uses an unsupported database version.`);
|
|
7570
|
+
} catch (error) {
|
|
7571
|
+
if (error instanceof _hot_updater_cli_tools.InitError) throw error;
|
|
7572
|
+
const code = typeof error === "object" && error !== null ? Reflect.get(error, "code") : void 0;
|
|
7573
|
+
if (code === 5 || code === "not-found") return;
|
|
7574
|
+
throw error;
|
|
7575
|
+
} finally {
|
|
7576
|
+
await (0, firebase_admin_app.deleteApp)(app);
|
|
7577
|
+
}
|
|
7578
|
+
};
|
|
7579
|
+
const assertFirebaseFunctionCanInitialize = async ({ fetchImpl, functions }) => {
|
|
7580
|
+
const existingFunctions = functions.filter(({ id }) => id === FIREBASE_V1_FUNCTION_NAME);
|
|
7581
|
+
for (const existingFunction of existingFunctions) {
|
|
7582
|
+
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.`);
|
|
7583
|
+
const versionUrl = new URL("version", `${existingFunction.uri.replace(/\/$/u, "")}/`).toString();
|
|
7584
|
+
await (0, _hot_updater_cli_tools.assertInfrastructureGenerationAtUrl)({
|
|
7585
|
+
fetchImpl,
|
|
7586
|
+
provider: "Firebase",
|
|
7587
|
+
resource: `Function ${FIREBASE_V1_FUNCTION_NAME}`,
|
|
7588
|
+
versionUrl
|
|
7589
|
+
});
|
|
7590
|
+
}
|
|
7591
|
+
};
|
|
7592
|
+
//#endregion
|
|
6768
7593
|
//#region iac/firebaseInitInputs.ts
|
|
6769
7594
|
const resolveFirebaseCredentialsPath = (credentialsPath, { cwd = process.cwd(), homeDir = node_os.default.homedir(), pathApi = node_path.default }) => {
|
|
6770
7595
|
const homeRelativePath = credentialsPath === "~" ? "" : credentialsPath.startsWith("~/") || credentialsPath.startsWith("~\\") ? credentialsPath.slice(2) : void 0;
|
|
@@ -6894,7 +7719,7 @@ const resolveFirebaseRegion = async ({ cwd, discoverExistingProject = true, nonI
|
|
|
6894
7719
|
if (functionsList.exitCode === 0) {
|
|
6895
7720
|
const parsed = JSON.parse(functionsList.stdout);
|
|
6896
7721
|
if (typeof parsed === "object" && parsed !== null && "result" in parsed && Array.isArray(parsed.result)) {
|
|
6897
|
-
for (const entry of parsed.result) if (typeof entry === "object" && entry !== null && "id" in entry && entry.id === "hot-updater" && "region" in entry && typeof entry.region === "string") {
|
|
7722
|
+
for (const entry of parsed.result) if (typeof entry === "object" && entry !== null && "id" in entry && entry.id === "hot-updater-v1" && "region" in entry && typeof entry.region === "string") {
|
|
6898
7723
|
discoveredRegion = entry.region;
|
|
6899
7724
|
break;
|
|
6900
7725
|
}
|
|
@@ -7216,10 +8041,16 @@ function App() {
|
|
|
7216
8041
|
return null; // Replace with your app root
|
|
7217
8042
|
}
|
|
7218
8043
|
|
|
7219
|
-
|
|
8044
|
+
HotUpdater.init({
|
|
7220
8045
|
baseURL: "%%source%%",
|
|
7221
|
-
|
|
7222
|
-
|
|
8046
|
+
requestHeaders: {
|
|
8047
|
+
"x-api-key": %%apiKey%%,
|
|
8048
|
+
},
|
|
8049
|
+
});
|
|
8050
|
+
|
|
8051
|
+
// Call HotUpdater.checkForUpdate({ updateStrategy: "appVersion" })
|
|
8052
|
+
// when your app is ready to check.
|
|
8053
|
+
export default App;`;
|
|
7223
8054
|
const getFirebaseRuntimePackageInfo = () => {
|
|
7224
8055
|
const firebasePackageRoot = path.default.dirname(require.resolve("@hot-updater/firebase/package.json"));
|
|
7225
8056
|
return {
|
|
@@ -7240,6 +8071,71 @@ const syncFunctionsPackageJson = async (functionsDir) => {
|
|
|
7240
8071
|
await fs.default.promises.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
7241
8072
|
return runtimePackageInfo;
|
|
7242
8073
|
};
|
|
8074
|
+
const commandErrorMessage = (error) => {
|
|
8075
|
+
if (error instanceof ExecaError) {
|
|
8076
|
+
const output = error.stderr || error.stdout;
|
|
8077
|
+
if (output !== void 0 && output !== null) {
|
|
8078
|
+
const text = String(output).trim();
|
|
8079
|
+
if (!text) return error.message;
|
|
8080
|
+
try {
|
|
8081
|
+
const parsed = JSON.parse(text);
|
|
8082
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
8083
|
+
const message = Reflect.get(parsed, "error");
|
|
8084
|
+
if (typeof message === "string") return message;
|
|
8085
|
+
}
|
|
8086
|
+
} catch {
|
|
8087
|
+
return text;
|
|
8088
|
+
}
|
|
8089
|
+
return text;
|
|
8090
|
+
}
|
|
8091
|
+
}
|
|
8092
|
+
return error instanceof Error ? error.message : String(error);
|
|
8093
|
+
};
|
|
8094
|
+
const ensureCloudFunctionsApiEnabled = async (projectId, cliEnv) => {
|
|
8095
|
+
await _hot_updater_cli_tools.p.tasks([{
|
|
8096
|
+
title: "Checking Cloud Functions API",
|
|
8097
|
+
task: async () => {
|
|
8098
|
+
try {
|
|
8099
|
+
await execa("gcloud", [
|
|
8100
|
+
"services",
|
|
8101
|
+
"enable",
|
|
8102
|
+
"cloudfunctions.googleapis.com",
|
|
8103
|
+
`--project=${projectId}`,
|
|
8104
|
+
"--quiet"
|
|
8105
|
+
], { env: cliEnv });
|
|
8106
|
+
} catch (error) {
|
|
8107
|
+
throw new _hot_updater_cli_tools.InitError(`Could not enable the Cloud Functions API for Firebase project ${projectId}: ${commandErrorMessage(error)}`);
|
|
8108
|
+
}
|
|
8109
|
+
return "Cloud Functions API is enabled";
|
|
8110
|
+
}
|
|
8111
|
+
}]);
|
|
8112
|
+
};
|
|
8113
|
+
const listFirebaseFunctions = async (cwd, projectId, nonInteractive, cliEnv) => {
|
|
8114
|
+
let functionsList;
|
|
8115
|
+
try {
|
|
8116
|
+
functionsList = await execa("npx", [
|
|
8117
|
+
"firebase",
|
|
8118
|
+
"functions:list",
|
|
8119
|
+
"--json",
|
|
8120
|
+
"--project",
|
|
8121
|
+
projectId,
|
|
8122
|
+
...nonInteractive ? ["--non-interactive"] : []
|
|
8123
|
+
], {
|
|
8124
|
+
cwd,
|
|
8125
|
+
env: cliEnv
|
|
8126
|
+
});
|
|
8127
|
+
} catch (error) {
|
|
8128
|
+
throw new _hot_updater_cli_tools.InitError(`Could not list Firebase Functions for project ${projectId}: ${commandErrorMessage(error)}. Run npx firebase functions:list --project ${projectId} --debug for details.`);
|
|
8129
|
+
}
|
|
8130
|
+
let body;
|
|
8131
|
+
try {
|
|
8132
|
+
body = JSON.parse(functionsList.stdout);
|
|
8133
|
+
} catch {
|
|
8134
|
+
throw new _hot_updater_cli_tools.InitError("Firebase functions list response was invalid.");
|
|
8135
|
+
}
|
|
8136
|
+
if (typeof body !== "object" || body === null || !Array.isArray(Reflect.get(body, "result"))) throw new _hot_updater_cli_tools.InitError("Firebase functions list response was invalid.");
|
|
8137
|
+
return Reflect.get(body, "result");
|
|
8138
|
+
};
|
|
7243
8139
|
function normalizeIndex(index) {
|
|
7244
8140
|
return {
|
|
7245
8141
|
collectionGroup: index.collectionGroup,
|
|
@@ -7304,7 +8200,7 @@ const deployFunctions = async (cwd, nonInteractive = false, cliEnv) => {
|
|
|
7304
8200
|
"firebase",
|
|
7305
8201
|
"deploy",
|
|
7306
8202
|
"--only",
|
|
7307
|
-
|
|
8203
|
+
`functions:${FIREBASE_V1_FUNCTION_NAME}`,
|
|
7308
8204
|
...nonInteractive ? ["--non-interactive"] : []
|
|
7309
8205
|
], {
|
|
7310
8206
|
cwd,
|
|
@@ -7317,21 +8213,26 @@ const deployFunctions = async (cwd, nonInteractive = false, cliEnv) => {
|
|
|
7317
8213
|
process.exit(1);
|
|
7318
8214
|
}
|
|
7319
8215
|
};
|
|
7320
|
-
const printTemplate = async (projectId, region, cliEnv) => {
|
|
8216
|
+
const printTemplate = async (apiKey, projectId, region, cliEnv) => {
|
|
7321
8217
|
try {
|
|
7322
|
-
const
|
|
8218
|
+
const describedFunction = await execa("gcloud", [
|
|
7323
8219
|
"functions",
|
|
7324
8220
|
"describe",
|
|
7325
|
-
|
|
8221
|
+
FIREBASE_V1_FUNCTION_NAME,
|
|
7326
8222
|
"--project",
|
|
7327
8223
|
projectId,
|
|
7328
8224
|
"--region",
|
|
7329
8225
|
region,
|
|
7330
8226
|
"--format=json"
|
|
7331
8227
|
], { env: cliEnv });
|
|
7332
|
-
const
|
|
7333
|
-
|
|
7334
|
-
_hot_updater_cli_tools.p.note((0, _hot_updater_cli_tools.transformTemplate)(SOURCE_TEMPLATE, {
|
|
8228
|
+
const functionUrl = JSON.parse(describedFunction.stdout).serviceConfig?.uri;
|
|
8229
|
+
if (!functionUrl) throw new _hot_updater_cli_tools.InitError(`Firebase Function ${FIREBASE_V1_FUNCTION_NAME} did not report an endpoint URL.`);
|
|
8230
|
+
_hot_updater_cli_tools.p.note((0, _hot_updater_cli_tools.transformTemplate)(SOURCE_TEMPLATE, {
|
|
8231
|
+
apiKey: JSON.stringify(apiKey),
|
|
8232
|
+
source: functionUrl
|
|
8233
|
+
}));
|
|
8234
|
+
_hot_updater_cli_tools.p.note((0, _hot_updater_cli_tools.formatApiKeyNote)(apiKey), "API Key");
|
|
8235
|
+
_hot_updater_cli_tools.p.log.message("Store this API key separately in a secure place.");
|
|
7335
8236
|
} catch (error) {
|
|
7336
8237
|
if (error instanceof ExecaError) _hot_updater_cli_tools.p.log.error(error.stderr || error.stdout || error.message);
|
|
7337
8238
|
else if (error instanceof Error) _hot_updater_cli_tools.p.log.error(error.message);
|
|
@@ -7350,7 +8251,8 @@ const runInit = async ({ build, envFile }) => {
|
|
|
7350
8251
|
const nonInteractive = envFile !== void 0;
|
|
7351
8252
|
const initEnvSources = await (0, _hot_updater_cli_tools.readHotUpdaterInitEnv)(process.cwd(), envFile);
|
|
7352
8253
|
const { managedEnv } = initEnvSources;
|
|
7353
|
-
const
|
|
8254
|
+
const initInputEnv = (0, _hot_updater_cli_tools.getHotUpdaterInitInputEnv)(initEnvSources, nonInteractive);
|
|
8255
|
+
const savedInputs = resolveFirebaseInitInputs(initInputEnv);
|
|
7354
8256
|
assertFirebaseNonInteractiveInputs(savedInputs, nonInteractive);
|
|
7355
8257
|
let applicationCredentials = savedInputs.applicationCredentials;
|
|
7356
8258
|
const cliEnv = nonInteractive ? getFirebaseCliEnv(applicationCredentials) : void 0;
|
|
@@ -7371,6 +8273,14 @@ const runInit = async ({ build, envFile }) => {
|
|
|
7371
8273
|
});
|
|
7372
8274
|
return cliEnv;
|
|
7373
8275
|
});
|
|
8276
|
+
if (initializeVariable.status === "ready") {
|
|
8277
|
+
await assertFirebaseInfrastructureCanInitialize({
|
|
8278
|
+
applicationCredentials,
|
|
8279
|
+
projectId: initializeVariable.projectId
|
|
8280
|
+
});
|
|
8281
|
+
await ensureCloudFunctionsApiEnabled(initializeVariable.projectId, cliEnv);
|
|
8282
|
+
await assertFirebaseFunctionCanInitialize({ functions: await listFirebaseFunctions(tmpDir, initializeVariable.projectId, nonInteractive, cliEnv) });
|
|
8283
|
+
}
|
|
7374
8284
|
const currentRegion = await resolveFirebaseRegion({
|
|
7375
8285
|
cwd: tmpDir,
|
|
7376
8286
|
discoverExistingProject: initializeVariable.status === "ready",
|
|
@@ -7427,22 +8337,31 @@ const runInit = async ({ build, envFile }) => {
|
|
|
7427
8337
|
}
|
|
7428
8338
|
}]);
|
|
7429
8339
|
await deployFirestore(tmpDir, nonInteractive, cliEnv);
|
|
8340
|
+
const credential = applicationCredentials ? (0, firebase_admin_app.cert)(JSON.parse(await fs.default.promises.readFile(applicationCredentials, "utf-8"))) : (0, firebase_admin_app.applicationDefault)();
|
|
8341
|
+
const existingApps = new Set((0, firebase_admin_app.getApps)());
|
|
8342
|
+
const databasePlugin = firebaseDatabase({
|
|
8343
|
+
credential,
|
|
8344
|
+
projectId: initializeVariable.projectId
|
|
8345
|
+
});
|
|
8346
|
+
let apiKey;
|
|
8347
|
+
try {
|
|
8348
|
+
apiKey = (await (0, _hot_updater_server.provisionApiKey)({
|
|
8349
|
+
apiKeys: databasePlugin.models.apiKeys,
|
|
8350
|
+
existingApiKey: initInputEnv.HOT_UPDATER_API_KEY,
|
|
8351
|
+
name: "Firebase init"
|
|
8352
|
+
})).apiKey;
|
|
8353
|
+
await (0, _hot_updater_cli_tools.makeEnv)({ HOT_UPDATER_API_KEY: apiKey });
|
|
8354
|
+
} finally {
|
|
8355
|
+
await databasePlugin.dispose?.();
|
|
8356
|
+
await Promise.all((0, firebase_admin_app.getApps)().filter((app) => !existingApps.has(app)).map((app) => (0, firebase_admin_app.deleteApp)(app)));
|
|
8357
|
+
}
|
|
7430
8358
|
await deployFunctions(tmpDir, nonInteractive, cliEnv);
|
|
7431
8359
|
await _hot_updater_cli_tools.p.tasks([{
|
|
7432
8360
|
title: "Check IAM policy",
|
|
7433
8361
|
async task(message) {
|
|
7434
|
-
const
|
|
7435
|
-
"firebase",
|
|
7436
|
-
"functions:list",
|
|
7437
|
-
"--json",
|
|
7438
|
-
...nonInteractive ? ["--non-interactive"] : []
|
|
7439
|
-
], {
|
|
7440
|
-
cwd: tmpDir,
|
|
7441
|
-
env: cliEnv
|
|
7442
|
-
});
|
|
7443
|
-
const account = (JSON.parse(functionsList.stdout).result || []).find((fn) => fn.id === "hot-updater")?.serviceAccount;
|
|
8362
|
+
const account = (await listFirebaseFunctions(tmpDir, initializeVariable.projectId, nonInteractive, cliEnv)).find((fn) => fn.id === FIREBASE_V1_FUNCTION_NAME)?.serviceAccount;
|
|
7444
8363
|
if (!account) {
|
|
7445
|
-
_hot_updater_cli_tools.p.log.error(
|
|
8364
|
+
_hot_updater_cli_tools.p.log.error(`${FIREBASE_V1_FUNCTION_NAME} function not found`);
|
|
7446
8365
|
await removeTmpDir();
|
|
7447
8366
|
process.exit(1);
|
|
7448
8367
|
}
|
|
@@ -7479,7 +8398,7 @@ const runInit = async ({ build, envFile }) => {
|
|
|
7479
8398
|
await removeTmpDir();
|
|
7480
8399
|
process.exit(1);
|
|
7481
8400
|
}
|
|
7482
|
-
await printTemplate(initializeVariable.projectId, currentRegion, cliEnv);
|
|
8401
|
+
await printTemplate(apiKey, initializeVariable.projectId, currentRegion, cliEnv);
|
|
7483
8402
|
await removeTmpDir();
|
|
7484
8403
|
_hot_updater_cli_tools.p.log.message(`Next step: ${(0, _hot_updater_cli_tools.link)("https://hot-updater.dev/docs/managed/firebase#step-3-generated-configurations")}`);
|
|
7485
8404
|
if (!applicationCredentials) _hot_updater_cli_tools.p.log.message("Next step: Change GOOGLE_APPLICATION_CREDENTIALS=your-credentials.json in .env.hotupdater");
|