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