@hot-updater/firebase 1.0.0-rc.0 → 1.0.0-rc.2
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 +1255 -841
- package/dist/firebase/public/firestore.indexes.json +27 -37
- package/dist/iac/index.cjs +374 -173
- package/dist/iac/index.d.cts +1 -5
- package/dist/iac/index.d.mts +1 -5
- package/dist/iac/index.mjs +368 -167
- package/dist/index.cjs +183 -43
- package/dist/index.d.cts +2 -4
- package/dist/index.d.mts +1 -3
- package/dist/index.mjs +184 -44
- package/package.json +6 -6
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { createDatabasePlugin, createStorageKeyBuilder, createStoragePlugin, createStorageUri, isDatabaseMetadataObject, parseStorageUri } from "@hot-updater/plugin-core";
|
|
1
|
+
import { compareInsightsText, createDatabasePlugin, createStorageKeyBuilder, createStoragePlugin, createStorageUri, isDatabaseMetadataObject, parseStorageUri } from "@hot-updater/plugin-core";
|
|
2
2
|
import { createDatabasePluginAdapter } from "@hot-updater/plugin-core/internal";
|
|
3
3
|
import { getApp, getApps, initializeApp } from "firebase-admin/app";
|
|
4
4
|
import { getFirestore } from "firebase-admin/firestore";
|
|
5
5
|
import { getStorage } from "firebase-admin/storage";
|
|
6
6
|
//#region src/firebaseDatabaseParserShared.ts
|
|
7
7
|
var FirebaseDatabaseDataError = class extends Error {
|
|
8
|
+
source;
|
|
8
9
|
name = "FirebaseDatabaseDataError";
|
|
9
10
|
constructor(source) {
|
|
10
11
|
super(`Invalid Firebase database data at "${source}".`);
|
|
@@ -116,6 +117,24 @@ const parseFirebaseBundleEventRow = (value, source) => {
|
|
|
116
117
|
received_at_ms: number(property(input, "received_at_ms"), source)
|
|
117
118
|
};
|
|
118
119
|
};
|
|
120
|
+
const parseFirebaseInsightsInstallationRow = (value, source) => {
|
|
121
|
+
const input = record(value, source);
|
|
122
|
+
const type = string(property(input, "type"), source);
|
|
123
|
+
if (type !== "UPDATE_APPLIED" && type !== "RECOVERED" && type !== "RELEASE_ADOPTED" && type !== "UNCHANGED") throw new FirebaseDatabaseDataError(source);
|
|
124
|
+
return {
|
|
125
|
+
id: string(property(input, "id"), source),
|
|
126
|
+
type,
|
|
127
|
+
install_id: string(property(input, "install_id"), source),
|
|
128
|
+
user_id: nullableString(property(input, "user_id"), source),
|
|
129
|
+
username: nullableString(property(input, "username"), source),
|
|
130
|
+
to_bundle_id: string(property(input, "to_bundle_id"), source),
|
|
131
|
+
platform: platform(property(input, "platform"), source),
|
|
132
|
+
app_version: string(property(input, "app_version"), source),
|
|
133
|
+
channel: string(property(input, "channel"), source),
|
|
134
|
+
cohort: string(property(input, "cohort"), source),
|
|
135
|
+
received_at_ms: number(property(input, "received_at_ms"), source)
|
|
136
|
+
};
|
|
137
|
+
};
|
|
119
138
|
const parseFirebaseApiKeyRow = (value, source) => {
|
|
120
139
|
const input = record(value, source);
|
|
121
140
|
const role = string(property(input, "role"), source);
|
|
@@ -196,13 +215,15 @@ const matchesCondition = (row, condition) => {
|
|
|
196
215
|
switch (condition.operator ?? "eq") {
|
|
197
216
|
case "eq": {
|
|
198
217
|
if (typeof expected !== "string") return actual === expected;
|
|
199
|
-
const
|
|
218
|
+
const mode = "mode" in condition ? condition.mode : void 0;
|
|
219
|
+
const comparison = normalizeStringComparison(actual, expected, mode);
|
|
200
220
|
return comparison !== null && comparison[0] === comparison[1];
|
|
201
221
|
}
|
|
202
222
|
case "ne": {
|
|
203
223
|
if (actual === null || actual === void 0) return false;
|
|
204
224
|
if (typeof expected !== "string") return actual !== expected;
|
|
205
|
-
const
|
|
225
|
+
const mode = "mode" in condition ? condition.mode : void 0;
|
|
226
|
+
const comparison = normalizeStringComparison(actual, expected, mode);
|
|
206
227
|
return comparison === null || comparison[0] !== comparison[1];
|
|
207
228
|
}
|
|
208
229
|
case "gt":
|
|
@@ -227,17 +248,20 @@ const matchesCondition = (row, condition) => {
|
|
|
227
248
|
}
|
|
228
249
|
case "contains": {
|
|
229
250
|
if (typeof expected !== "string") return false;
|
|
230
|
-
const
|
|
251
|
+
const mode = "mode" in condition ? condition.mode : void 0;
|
|
252
|
+
const comparison = normalizeStringComparison(actual, expected, mode);
|
|
231
253
|
return comparison?.[0].includes(comparison[1]) ?? false;
|
|
232
254
|
}
|
|
233
255
|
case "starts_with": {
|
|
234
256
|
if (typeof expected !== "string") return false;
|
|
235
|
-
const
|
|
257
|
+
const mode = "mode" in condition ? condition.mode : void 0;
|
|
258
|
+
const comparison = normalizeStringComparison(actual, expected, mode);
|
|
236
259
|
return comparison?.[0].startsWith(comparison[1]) ?? false;
|
|
237
260
|
}
|
|
238
261
|
case "ends_with": {
|
|
239
262
|
if (typeof expected !== "string") return false;
|
|
240
|
-
const
|
|
263
|
+
const mode = "mode" in condition ? condition.mode : void 0;
|
|
264
|
+
const comparison = normalizeStringComparison(actual, expected, mode);
|
|
241
265
|
return comparison?.[0].endsWith(comparison[1]) ?? false;
|
|
242
266
|
}
|
|
243
267
|
}
|
|
@@ -282,6 +306,7 @@ const queryFirebaseDatabaseRows = (rows, input) => {
|
|
|
282
306
|
//#endregion
|
|
283
307
|
//#region src/firebaseDatabaseState.ts
|
|
284
308
|
var FirebaseDatabaseConstraintError = class extends Error {
|
|
309
|
+
constraint;
|
|
285
310
|
name = "FirebaseDatabaseConstraintError";
|
|
286
311
|
constructor(constraint) {
|
|
287
312
|
super(`Firebase database constraint failed: ${constraint}`);
|
|
@@ -292,6 +317,7 @@ const cloneFirebaseDatabaseSnapshot = (snapshot) => ({
|
|
|
292
317
|
bundles: new Map(snapshot.bundles),
|
|
293
318
|
bundlePatches: new Map(snapshot.bundlePatches),
|
|
294
319
|
bundleEvents: new Map(snapshot.bundleEvents),
|
|
320
|
+
bundleInstallations: new Map(snapshot.bundleInstallations),
|
|
295
321
|
channels: new Map(snapshot.channels),
|
|
296
322
|
apiKeys: new Map(snapshot.apiKeys),
|
|
297
323
|
releaseCatalogs: new Map(snapshot.releaseCatalogs),
|
|
@@ -321,6 +347,13 @@ const createFirebaseDatabaseState = (snapshot) => ({
|
|
|
321
347
|
requireUnique(snapshot.bundleEvents, input.data.id, input.model);
|
|
322
348
|
snapshot.bundleEvents.set(input.data.id, input.data);
|
|
323
349
|
return input.data;
|
|
350
|
+
case "bundle_installations": {
|
|
351
|
+
const current = snapshot.bundleInstallations.get(input.data.install_id);
|
|
352
|
+
if (current && input.onConflict === "ignore") return current;
|
|
353
|
+
if (current) throw new FirebaseDatabaseConstraintError("bundle_installations.install_id.unique");
|
|
354
|
+
snapshot.bundleInstallations.set(input.data.install_id, input.data);
|
|
355
|
+
return input.data;
|
|
356
|
+
}
|
|
324
357
|
case "releases":
|
|
325
358
|
requireUnique(snapshot.releases, input.data.id, input.model);
|
|
326
359
|
if (!snapshot.channels.has(input.data.channel_id)) throw new FirebaseDatabaseConstraintError("releases.channel_id.foreign-key");
|
|
@@ -350,6 +383,16 @@ const createFirebaseDatabaseState = (snapshot) => ({
|
|
|
350
383
|
}
|
|
351
384
|
},
|
|
352
385
|
async update(input) {
|
|
386
|
+
if (input.model === "bundle_installations") {
|
|
387
|
+
const current = [...snapshot.bundleInstallations.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where));
|
|
388
|
+
if (!current) return null;
|
|
389
|
+
const updated = {
|
|
390
|
+
...current,
|
|
391
|
+
...input.update
|
|
392
|
+
};
|
|
393
|
+
snapshot.bundleInstallations.set(current.install_id, updated);
|
|
394
|
+
return updated;
|
|
395
|
+
}
|
|
353
396
|
if (input.model === "api_keys") {
|
|
354
397
|
const current = [...snapshot.apiKeys.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where));
|
|
355
398
|
if (!current) return null;
|
|
@@ -411,6 +454,8 @@ const createFirebaseDatabaseState = (snapshot) => ({
|
|
|
411
454
|
case "bundles": return distinctCount([...snapshot.bundles.values()].filter((row) => matchesFirebaseDatabaseWhere(row, input.where)), input.distinct);
|
|
412
455
|
case "bundle_patches": return distinctCount([...snapshot.bundlePatches.values()].filter((row) => matchesFirebaseDatabaseWhere(row, input.where)), input.distinct);
|
|
413
456
|
case "releases": return distinctCount([...snapshot.releases.values()].filter((row) => matchesFirebaseDatabaseWhere(row, input.where)), input.distinct);
|
|
457
|
+
case "bundle_installations": return distinctCount([...snapshot.bundleInstallations.values()].filter((row) => matchesFirebaseDatabaseWhere(row, input.where)), input.distinct);
|
|
458
|
+
case "bundle_events": return distinctCount([...snapshot.bundleEvents.values()].filter((row) => matchesFirebaseDatabaseWhere(row, input.where)), input.distinct);
|
|
414
459
|
}
|
|
415
460
|
},
|
|
416
461
|
async findOne(input) {
|
|
@@ -421,6 +466,7 @@ const createFirebaseDatabaseState = (snapshot) => ({
|
|
|
421
466
|
case "bundle_patches": return [...snapshot.bundlePatches.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where)) ?? null;
|
|
422
467
|
case "releases": return [...snapshot.releases.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where)) ?? null;
|
|
423
468
|
case "release_catalogs": return [...snapshot.releaseCatalogs.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where)) ?? null;
|
|
469
|
+
case "bundle_installations": return [...snapshot.bundleInstallations.values()].find((row) => matchesFirebaseDatabaseWhere(row, input.where)) ?? null;
|
|
424
470
|
}
|
|
425
471
|
},
|
|
426
472
|
async findMany(input) {
|
|
@@ -428,6 +474,7 @@ const createFirebaseDatabaseState = (snapshot) => ({
|
|
|
428
474
|
case "bundles": return queryFirebaseDatabaseRows([...snapshot.bundles.values()], input);
|
|
429
475
|
case "bundle_patches": return queryFirebaseDatabaseRows([...snapshot.bundlePatches.values()], input);
|
|
430
476
|
case "bundle_events": return queryFirebaseDatabaseRows([...snapshot.bundleEvents.values()], input);
|
|
477
|
+
case "bundle_installations": return queryFirebaseDatabaseRows([...snapshot.bundleInstallations.values()], input);
|
|
431
478
|
case "channels": return queryFirebaseDatabaseRows([...snapshot.channels.values()], input);
|
|
432
479
|
case "api_keys": return queryFirebaseDatabaseRows([...snapshot.apiKeys.values()], input);
|
|
433
480
|
case "releases": return queryFirebaseDatabaseRows([...snapshot.releases.values()], input);
|
|
@@ -440,6 +487,7 @@ const createFirebaseDatabaseState = (snapshot) => ({
|
|
|
440
487
|
const FIREBASE_V1_COLLECTION_NAMES = {
|
|
441
488
|
apiKeys: "hot_updater_v1_api_keys",
|
|
442
489
|
bundleEvents: "hot_updater_v1_bundle_events",
|
|
490
|
+
bundleInstallations: "hot_updater_v1_bundle_installations",
|
|
443
491
|
bundlePatches: "hot_updater_v1_bundle_patches",
|
|
444
492
|
bundles: "hot_updater_v1_bundles",
|
|
445
493
|
channels: "hot_updater_v1_channels",
|
|
@@ -450,6 +498,7 @@ const FIREBASE_V1_COLLECTION_NAMES = {
|
|
|
450
498
|
//#endregion
|
|
451
499
|
//#region src/firebaseDatabasePersistence.ts
|
|
452
500
|
var FirebaseDatabaseAdapterVersionError = class extends Error {
|
|
501
|
+
version;
|
|
453
502
|
name = "FirebaseDatabaseAdapterVersionError";
|
|
454
503
|
constructor(version) {
|
|
455
504
|
super(`Unsupported Firebase database adapter version: ${String(version)}`);
|
|
@@ -460,6 +509,7 @@ const createFirebaseDatabaseCollections = (db) => ({
|
|
|
460
509
|
bundles: db.collection(FIREBASE_V1_COLLECTION_NAMES.bundles),
|
|
461
510
|
bundlePatches: db.collection(FIREBASE_V1_COLLECTION_NAMES.bundlePatches),
|
|
462
511
|
bundleEvents: db.collection(FIREBASE_V1_COLLECTION_NAMES.bundleEvents),
|
|
512
|
+
bundleInstallations: db.collection(FIREBASE_V1_COLLECTION_NAMES.bundleInstallations),
|
|
463
513
|
channels: db.collection(FIREBASE_V1_COLLECTION_NAMES.channels),
|
|
464
514
|
apiKeys: db.collection(FIREBASE_V1_COLLECTION_NAMES.apiKeys),
|
|
465
515
|
releaseCatalogs: db.collection(FIREBASE_V1_COLLECTION_NAMES.releaseCatalogs),
|
|
@@ -468,14 +518,15 @@ const createFirebaseDatabaseCollections = (db) => ({
|
|
|
468
518
|
});
|
|
469
519
|
const firebaseChannelDocumentId = (name) => `name_${Buffer.from(name, "utf8").toString("base64url")}`;
|
|
470
520
|
const firebaseChannelIdDocumentId = (id) => `channel_id_${Buffer.from(id, "utf8").toString("base64url")}`;
|
|
521
|
+
const firebaseInstallationDocumentId = (id) => `install_${Buffer.from(id, "utf8").toString("base64url")}`;
|
|
471
522
|
const requireFirebaseDocumentKey = (model, documentId, row) => {
|
|
472
|
-
if (documentId !== ("id" in row ? row.id : row.scope_key)) throw new FirebaseDatabaseConstraintError(`${model}.id.document-key`);
|
|
523
|
+
if (documentId !== (model === "bundle_installations" ? firebaseInstallationDocumentId(row.install_id) : "id" in row ? row.id : row.scope_key)) throw new FirebaseDatabaseConstraintError(`${model}.id.document-key`);
|
|
473
524
|
return row;
|
|
474
525
|
};
|
|
475
526
|
const documentMap = (model, documents) => {
|
|
476
527
|
const rows = /* @__PURE__ */ new Map();
|
|
477
528
|
for (const { row } of documents) {
|
|
478
|
-
const key = "id" in row ? row.id : row.scope_key;
|
|
529
|
+
const key = model === "bundle_installations" ? row.install_id : "id" in row ? row.id : row.scope_key;
|
|
479
530
|
if (rows.has(key)) throw new FirebaseDatabaseConstraintError(`${model}.id.unique`);
|
|
480
531
|
rows.set(key, row);
|
|
481
532
|
}
|
|
@@ -490,10 +541,6 @@ const patchMap = (snapshot) => documentMap("bundle_patches", snapshot.docs.map((
|
|
|
490
541
|
document,
|
|
491
542
|
row: parseFirebasePatchRow(document.data(), `bundle_patches/${document.id}`)
|
|
492
543
|
})));
|
|
493
|
-
const eventMap = (snapshot) => documentMap("bundle_events", snapshot.docs.map((document) => ({
|
|
494
|
-
document,
|
|
495
|
-
row: parseFirebaseBundleEventRow(document.data(), `bundle_events/${document.id}`)
|
|
496
|
-
})));
|
|
497
544
|
const channelMap = (snapshot) => {
|
|
498
545
|
const rows = /* @__PURE__ */ new Map();
|
|
499
546
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -524,18 +571,18 @@ const toSnapshot = (documents) => {
|
|
|
524
571
|
return {
|
|
525
572
|
bundles: bundleMap(documents[0]),
|
|
526
573
|
bundlePatches: patchMap(documents[1]),
|
|
527
|
-
bundleEvents:
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
574
|
+
bundleEvents: /* @__PURE__ */ new Map(),
|
|
575
|
+
bundleInstallations: /* @__PURE__ */ new Map(),
|
|
576
|
+
channels: channelMap(documents[2]),
|
|
577
|
+
apiKeys: apiKeyMap(documents[3]),
|
|
578
|
+
releases: releaseMap(documents[4]),
|
|
579
|
+
releaseCatalogs: releaseCatalogMap(documents[5])
|
|
532
580
|
};
|
|
533
581
|
};
|
|
534
582
|
const loadFirebaseDatabaseSnapshot = async (collections) => {
|
|
535
|
-
const [bundles, patches,
|
|
583
|
+
const [bundles, patches, channels, apiKeys, releases, releaseCatalogs] = await Promise.all([
|
|
536
584
|
collections.bundles.get(),
|
|
537
585
|
collections.bundlePatches.get(),
|
|
538
|
-
collections.bundleEvents.get(),
|
|
539
586
|
collections.channels.get(),
|
|
540
587
|
collections.apiKeys.get(),
|
|
541
588
|
collections.releases.get(),
|
|
@@ -544,7 +591,6 @@ const loadFirebaseDatabaseSnapshot = async (collections) => {
|
|
|
544
591
|
return toSnapshot([
|
|
545
592
|
bundles,
|
|
546
593
|
patches,
|
|
547
|
-
events,
|
|
548
594
|
channels,
|
|
549
595
|
apiKeys,
|
|
550
596
|
releases,
|
|
@@ -552,10 +598,9 @@ const loadFirebaseDatabaseSnapshot = async (collections) => {
|
|
|
552
598
|
]);
|
|
553
599
|
};
|
|
554
600
|
const loadFirebaseTransactionSnapshot = async (transaction, collections) => {
|
|
555
|
-
const [bundles, patches,
|
|
601
|
+
const [bundles, patches, channels, apiKeys, releases, releaseCatalogs] = await Promise.all([
|
|
556
602
|
transaction.get(collections.bundles),
|
|
557
603
|
transaction.get(collections.bundlePatches),
|
|
558
|
-
transaction.get(collections.bundleEvents),
|
|
559
604
|
transaction.get(collections.channels),
|
|
560
605
|
transaction.get(collections.apiKeys),
|
|
561
606
|
transaction.get(collections.releases),
|
|
@@ -564,7 +609,6 @@ const loadFirebaseTransactionSnapshot = async (transaction, collections) => {
|
|
|
564
609
|
return toSnapshot([
|
|
565
610
|
bundles,
|
|
566
611
|
patches,
|
|
567
|
-
events,
|
|
568
612
|
channels,
|
|
569
613
|
apiKeys,
|
|
570
614
|
releases,
|
|
@@ -590,13 +634,6 @@ const persistFirebaseDatabaseSnapshot = ({ transaction, collections, before, aft
|
|
|
590
634
|
after: after.bundlePatches,
|
|
591
635
|
documentId: (row) => row.id
|
|
592
636
|
});
|
|
593
|
-
persistCollection({
|
|
594
|
-
transaction,
|
|
595
|
-
collection: collections.bundleEvents,
|
|
596
|
-
before: before.bundleEvents,
|
|
597
|
-
after: after.bundleEvents,
|
|
598
|
-
documentId: (row) => row.id
|
|
599
|
-
});
|
|
600
637
|
persistCollection({
|
|
601
638
|
transaction,
|
|
602
639
|
collection: collections.channels,
|
|
@@ -628,7 +665,7 @@ const persistFirebaseDatabaseSnapshot = ({ transaction, collections, before, aft
|
|
|
628
665
|
documentId: (row) => row.scope_key
|
|
629
666
|
});
|
|
630
667
|
};
|
|
631
|
-
const migrateFirebaseDatabase = async (
|
|
668
|
+
const migrateFirebaseDatabase = async (collections) => {
|
|
632
669
|
const versionDocument = collections.settings.doc("database_adapter_version");
|
|
633
670
|
const version = await versionDocument.get();
|
|
634
671
|
const adapterVersion = version.data()?.version;
|
|
@@ -639,7 +676,9 @@ const migrateFirebaseDatabase = async (_db, collections) => {
|
|
|
639
676
|
collections.bundlePatches.limit(1).get(),
|
|
640
677
|
collections.channels.limit(1).get(),
|
|
641
678
|
collections.releases.limit(1).get(),
|
|
642
|
-
collections.releaseCatalogs.limit(1).get()
|
|
679
|
+
collections.releaseCatalogs.limit(1).get(),
|
|
680
|
+
collections.bundleInstallations.limit(1).get(),
|
|
681
|
+
collections.bundleEvents.limit(1).get()
|
|
643
682
|
])).some((snapshot) => !snapshot.empty)) throw new FirebaseDatabaseAdapterVersionError("v0");
|
|
644
683
|
try {
|
|
645
684
|
await versionDocument.create({ version: 4 });
|
|
@@ -654,13 +693,40 @@ const exactId = (input) => {
|
|
|
654
693
|
const [condition] = input.where;
|
|
655
694
|
return condition.field === "id" && (condition.operator === void 0 || condition.operator === "eq") && typeof condition.value === "string" ? condition.value : void 0;
|
|
656
695
|
};
|
|
696
|
+
const exactInstallId = (input) => {
|
|
697
|
+
const condition = input.where?.find(({ field, operator }) => field === "install_id" && (operator === void 0 || operator === "eq"));
|
|
698
|
+
return typeof condition?.value === "string" ? condition.value : void 0;
|
|
699
|
+
};
|
|
700
|
+
const firestoreOperator = (operator) => {
|
|
701
|
+
switch (operator ?? "eq") {
|
|
702
|
+
case "eq": return "==";
|
|
703
|
+
case "ne": return "!=";
|
|
704
|
+
case "gt": return ">";
|
|
705
|
+
case "gte": return ">=";
|
|
706
|
+
case "lt": return "<";
|
|
707
|
+
case "lte": return "<=";
|
|
708
|
+
case "in": return "in";
|
|
709
|
+
case "not_in": return "not-in";
|
|
710
|
+
default: return;
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
const applyFirebaseWhere = (initial, where) => {
|
|
714
|
+
let query = initial;
|
|
715
|
+
for (const condition of where) {
|
|
716
|
+
const operator = firestoreOperator(condition.operator);
|
|
717
|
+
if (condition.connector === "OR" || operator === void 0) throw new FirebaseDatabaseConstraintError("query.unsupported");
|
|
718
|
+
query = query.where(condition.field, operator, condition.value);
|
|
719
|
+
}
|
|
720
|
+
return query;
|
|
721
|
+
};
|
|
657
722
|
const firebaseDatabase = (config) => {
|
|
658
|
-
const
|
|
659
|
-
const
|
|
723
|
+
const implementation = (() => {
|
|
724
|
+
const app = getApps().length ? getApp() : initializeApp(config);
|
|
725
|
+
const db = getFirestore(app);
|
|
660
726
|
const collections = createFirebaseDatabaseCollections(db);
|
|
661
727
|
let migration;
|
|
662
728
|
const ensureMigrated = () => {
|
|
663
|
-
migration ??= migrateFirebaseDatabase(
|
|
729
|
+
migration ??= migrateFirebaseDatabase(collections).catch((error) => {
|
|
664
730
|
migration = void 0;
|
|
665
731
|
throw error;
|
|
666
732
|
});
|
|
@@ -683,14 +749,74 @@ const firebaseDatabase = (config) => {
|
|
|
683
749
|
};
|
|
684
750
|
const read = async (operation) => {
|
|
685
751
|
await ensureMigrated();
|
|
686
|
-
|
|
752
|
+
const snapshot = await loadFirebaseDatabaseSnapshot(collections);
|
|
753
|
+
return operation(createFirebaseDatabaseState(snapshot));
|
|
687
754
|
};
|
|
688
755
|
return {
|
|
689
|
-
|
|
690
|
-
|
|
756
|
+
recordInsights: async ({ event, installation }) => {
|
|
757
|
+
await ensureMigrated();
|
|
758
|
+
await db.runTransaction(async (transaction) => {
|
|
759
|
+
const eventReference = collections.bundleEvents.doc(event.id);
|
|
760
|
+
const installationReference = collections.bundleInstallations.doc(firebaseInstallationDocumentId(installation.install_id));
|
|
761
|
+
const [storedEvent, storedInstallation] = await transaction.getAll(eventReference, installationReference);
|
|
762
|
+
if (storedEvent.exists) return;
|
|
763
|
+
const current = storedInstallation.exists ? requireFirebaseDocumentKey("bundle_installations", storedInstallation.id, parseFirebaseInsightsInstallationRow(storedInstallation.data(), `bundle_installations/${storedInstallation.id}`)) : null;
|
|
764
|
+
transaction.create(eventReference, event);
|
|
765
|
+
if (current === null || installation.received_at_ms > current.received_at_ms || installation.received_at_ms === current.received_at_ms && compareInsightsText(installation.id, current.id) > 0) transaction.set(installationReference, installation);
|
|
766
|
+
});
|
|
767
|
+
},
|
|
768
|
+
create: async (input) => {
|
|
769
|
+
if (input.model !== "bundle_events" && input.model !== "bundle_installations") return mutate((database) => database.create(input));
|
|
770
|
+
await ensureMigrated();
|
|
771
|
+
return db.runTransaction(async (transaction) => {
|
|
772
|
+
const collection = input.model === "bundle_events" ? collections.bundleEvents : collections.bundleInstallations;
|
|
773
|
+
const documentId = input.model === "bundle_events" ? input.data.id : firebaseInstallationDocumentId(input.data.install_id);
|
|
774
|
+
const reference = collection.doc(documentId);
|
|
775
|
+
const document = await transaction.get(reference);
|
|
776
|
+
if (document.exists) {
|
|
777
|
+
const row = input.model === "bundle_events" ? requireFirebaseDocumentKey("bundle_events", document.id, parseFirebaseBundleEventRow(document.data(), `bundle_events/${document.id}`)) : requireFirebaseDocumentKey("bundle_installations", document.id, parseFirebaseInsightsInstallationRow(document.data(), `bundle_installations/${document.id}`));
|
|
778
|
+
if (input.onConflict === "ignore") return row;
|
|
779
|
+
throw new FirebaseDatabaseConstraintError(`${input.model}.id.unique`);
|
|
780
|
+
}
|
|
781
|
+
transaction.create(reference, input.data);
|
|
782
|
+
return input.data;
|
|
783
|
+
});
|
|
784
|
+
},
|
|
785
|
+
update: async (input) => {
|
|
786
|
+
if (input.model !== "bundle_installations") return mutate((database) => database.update(input));
|
|
787
|
+
const installId = exactInstallId(input);
|
|
788
|
+
if (installId === void 0) return mutate((database) => database.update(input));
|
|
789
|
+
await ensureMigrated();
|
|
790
|
+
return db.runTransaction(async (transaction) => {
|
|
791
|
+
const reference = collections.bundleInstallations.doc(firebaseInstallationDocumentId(installId));
|
|
792
|
+
const document = await transaction.get(reference);
|
|
793
|
+
if (!document.exists) return null;
|
|
794
|
+
const current = requireFirebaseDocumentKey("bundle_installations", document.id, parseFirebaseInsightsInstallationRow(document.data(), `bundle_installations/${document.id}`));
|
|
795
|
+
if (!matchesFirebaseDatabaseWhere(current, input.where)) return null;
|
|
796
|
+
const updated = {
|
|
797
|
+
...current,
|
|
798
|
+
...input.update
|
|
799
|
+
};
|
|
800
|
+
transaction.set(reference, updated, { merge: true });
|
|
801
|
+
return updated;
|
|
802
|
+
});
|
|
803
|
+
},
|
|
691
804
|
delete: (input) => mutate((database) => database.delete(input)),
|
|
692
|
-
count: (input) =>
|
|
805
|
+
count: async (input) => {
|
|
806
|
+
if (input.model !== "bundle_installations" && input.model !== "bundle_events") return read((database) => database.count(input));
|
|
807
|
+
await ensureMigrated();
|
|
808
|
+
let query = applyFirebaseWhere(input.model === "bundle_events" ? collections.bundleEvents : collections.bundleInstallations, input.where ?? []);
|
|
809
|
+
if (input.model === "bundle_events") query = query.orderBy("received_at_ms", "desc").orderBy("id", "desc");
|
|
810
|
+
return (await query.count().get()).data().count;
|
|
811
|
+
},
|
|
693
812
|
findOne: async (input) => {
|
|
813
|
+
if (input.model === "bundle_installations") {
|
|
814
|
+
const installId = exactInstallId(input);
|
|
815
|
+
if (installId === void 0) return read((database) => database.findOne(input));
|
|
816
|
+
await ensureMigrated();
|
|
817
|
+
const document = await collections.bundleInstallations.doc(firebaseInstallationDocumentId(installId)).get();
|
|
818
|
+
return document.exists ? requireFirebaseDocumentKey("bundle_installations", document.id, parseFirebaseInsightsInstallationRow(document.data(), `bundle_installations/${document.id}`)) : null;
|
|
819
|
+
}
|
|
694
820
|
const id = exactId(input);
|
|
695
821
|
if (id === void 0) return read((database) => database.findOne(input));
|
|
696
822
|
await ensureMigrated();
|
|
@@ -711,9 +837,21 @@ const firebaseDatabase = (config) => {
|
|
|
711
837
|
}
|
|
712
838
|
},
|
|
713
839
|
findMany: async (input) => {
|
|
714
|
-
if (input.model
|
|
715
|
-
|
|
716
|
-
|
|
840
|
+
if (input.model === "bundle_events" || input.model === "bundle_installations") {
|
|
841
|
+
await ensureMigrated();
|
|
842
|
+
const collection = input.model === "bundle_events" ? collections.bundleEvents : collections.bundleInstallations;
|
|
843
|
+
let query = applyFirebaseWhere(collection, input.where ?? []);
|
|
844
|
+
for (const order of input.orderBy ?? []) {
|
|
845
|
+
if (order.nulls !== void 0) throw new FirebaseDatabaseConstraintError("query.unsupported");
|
|
846
|
+
query = query.orderBy(order.field, order.direction);
|
|
847
|
+
}
|
|
848
|
+
return (await query.offset(input.offset).limit(input.limit).get()).docs.map((document) => input.model === "bundle_events" ? requireFirebaseDocumentKey("bundle_events", document.id, parseFirebaseBundleEventRow(document.data(), `bundle_events/${document.id}`)) : requireFirebaseDocumentKey("bundle_installations", document.id, parseFirebaseInsightsInstallationRow(document.data(), `bundle_installations/${document.id}`)));
|
|
849
|
+
}
|
|
850
|
+
if (input.model === "channels") {
|
|
851
|
+
await ensureMigrated();
|
|
852
|
+
return queryFirebaseDatabaseRows(await loadFirebaseChannels(collections), input);
|
|
853
|
+
}
|
|
854
|
+
return read((database) => database.findMany(input));
|
|
717
855
|
},
|
|
718
856
|
insertChannel: async (input) => {
|
|
719
857
|
await ensureMigrated();
|
|
@@ -765,7 +903,8 @@ const firebaseDatabase = (config) => {
|
|
|
765
903
|
},
|
|
766
904
|
transaction: (callback) => mutate(callback)
|
|
767
905
|
};
|
|
768
|
-
})()
|
|
906
|
+
})();
|
|
907
|
+
const adapter = createDatabasePluginAdapter("firebaseDatabase", implementation);
|
|
769
908
|
return createDatabasePlugin({
|
|
770
909
|
name: "firebaseDatabase",
|
|
771
910
|
models: adapter.models,
|
|
@@ -775,7 +914,8 @@ const firebaseDatabase = (config) => {
|
|
|
775
914
|
//#endregion
|
|
776
915
|
//#region src/firebaseStorage.ts
|
|
777
916
|
const firebaseStorage = (config) => {
|
|
778
|
-
const
|
|
917
|
+
const app = getApps().length ? getApp() : initializeApp(config);
|
|
918
|
+
const bucket = getStorage(app).bucket(config.storageBucket);
|
|
779
919
|
const getStorageKey = createStorageKeyBuilder(config.basePath);
|
|
780
920
|
const parseAndValidate = (storageUri) => {
|
|
781
921
|
const parsed = parseStorageUri(storageUri, "gs");
|
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.2",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"types": "dist/index.d.cts",
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"hono": "4.12.34",
|
|
46
|
-
"@hot-updater/cli-tools": "1.0.0-rc.
|
|
46
|
+
"@hot-updater/cli-tools": "1.0.0-rc.2",
|
|
47
47
|
"@hot-updater/core": "1.0.0-rc.0",
|
|
48
|
-
"@hot-updater/plugin-core": "1.0.0-rc.
|
|
49
|
-
"@hot-updater/server": "1.0.0-rc.
|
|
48
|
+
"@hot-updater/plugin-core": "1.0.0-rc.2",
|
|
49
|
+
"@hot-updater/server": "1.0.0-rc.2"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
"firebase-tools": "^13.32.0",
|
|
62
62
|
"fkill": "^9.0.0",
|
|
63
63
|
"mime": "^4.0.4",
|
|
64
|
-
"@hot-updater/mock": "1.0.0-rc.
|
|
65
|
-
"@hot-updater/test-utils": "1.0.0-rc.
|
|
64
|
+
"@hot-updater/mock": "1.0.0-rc.2",
|
|
65
|
+
"@hot-updater/test-utils": "1.0.0-rc.2"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"firebase-admin": "*",
|