UNPKG

104 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/fire/firestore'), require('@datorama/akita'), require('rxjs'), require('rxjs/operators'), require('@angular/router'), require('@angular/fire/auth'), require('firebase/app')) :
3 typeof define === 'function' && define.amd ? define('akita-ng-fire', ['exports', '@angular/core', '@angular/fire/firestore', '@datorama/akita', 'rxjs', 'rxjs/operators', '@angular/router', '@angular/fire/auth', 'firebase/app'], factory) :
4 (global = global || self, factory(global['akita-ng-fire'] = {}, global.ng.core, global.ng.fire.firestore, global.akita, global.rxjs, global.rxjs.operators, global.ng.router, global.ng.fire.auth, global.firebase));
5}(this, (function (exports, core, firestore, akita, rxjs, operators, router, auth, firebase) { 'use strict';
6
7 firebase = firebase && Object.prototype.hasOwnProperty.call(firebase, 'default') ? firebase['default'] : firebase;
8
9 /*! *****************************************************************************
10 Copyright (c) Microsoft Corporation.
11
12 Permission to use, copy, modify, and/or distribute this software for any
13 purpose with or without fee is hereby granted.
14
15 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
16 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
17 AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
18 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
19 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
20 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21 PERFORMANCE OF THIS SOFTWARE.
22 ***************************************************************************** */
23 /* global Reflect, Promise */
24 var extendStatics = function (d, b) {
25 extendStatics = Object.setPrototypeOf ||
26 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
27 function (d, b) { for (var p in b)
28 if (Object.prototype.hasOwnProperty.call(b, p))
29 d[p] = b[p]; };
30 return extendStatics(d, b);
31 };
32 function __extends(d, b) {
33 extendStatics(d, b);
34 function __() { this.constructor = d; }
35 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
36 }
37 var __assign = function () {
38 __assign = Object.assign || function __assign(t) {
39 for (var s, i = 1, n = arguments.length; i < n; i++) {
40 s = arguments[i];
41 for (var p in s)
42 if (Object.prototype.hasOwnProperty.call(s, p))
43 t[p] = s[p];
44 }
45 return t;
46 };
47 return __assign.apply(this, arguments);
48 };
49 function __rest(s, e) {
50 var t = {};
51 for (var p in s)
52 if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
53 t[p] = s[p];
54 if (s != null && typeof Object.getOwnPropertySymbols === "function")
55 for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
56 if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
57 t[p[i]] = s[p[i]];
58 }
59 return t;
60 }
61 function __decorate(decorators, target, key, desc) {
62 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
63 if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
64 r = Reflect.decorate(decorators, target, key, desc);
65 else
66 for (var i = decorators.length - 1; i >= 0; i--)
67 if (d = decorators[i])
68 r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
69 return c > 3 && r && Object.defineProperty(target, key, r), r;
70 }
71 function __param(paramIndex, decorator) {
72 return function (target, key) { decorator(target, key, paramIndex); };
73 }
74 function __metadata(metadataKey, metadataValue) {
75 if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
76 return Reflect.metadata(metadataKey, metadataValue);
77 }
78 function __awaiter(thisArg, _arguments, P, generator) {
79 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
80 return new (P || (P = Promise))(function (resolve, reject) {
81 function fulfilled(value) { try {
82 step(generator.next(value));
83 }
84 catch (e) {
85 reject(e);
86 } }
87 function rejected(value) { try {
88 step(generator["throw"](value));
89 }
90 catch (e) {
91 reject(e);
92 } }
93 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
94 step((generator = generator.apply(thisArg, _arguments || [])).next());
95 });
96 }
97 function __generator(thisArg, body) {
98 var _ = { label: 0, sent: function () { if (t[0] & 1)
99 throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
100 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g;
101 function verb(n) { return function (v) { return step([n, v]); }; }
102 function step(op) {
103 if (f)
104 throw new TypeError("Generator is already executing.");
105 while (_)
106 try {
107 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done)
108 return t;
109 if (y = 0, t)
110 op = [op[0] & 2, t.value];
111 switch (op[0]) {
112 case 0:
113 case 1:
114 t = op;
115 break;
116 case 4:
117 _.label++;
118 return { value: op[1], done: false };
119 case 5:
120 _.label++;
121 y = op[1];
122 op = [0];
123 continue;
124 case 7:
125 op = _.ops.pop();
126 _.trys.pop();
127 continue;
128 default:
129 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
130 _ = 0;
131 continue;
132 }
133 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
134 _.label = op[1];
135 break;
136 }
137 if (op[0] === 6 && _.label < t[1]) {
138 _.label = t[1];
139 t = op;
140 break;
141 }
142 if (t && _.label < t[2]) {
143 _.label = t[2];
144 _.ops.push(op);
145 break;
146 }
147 if (t[2])
148 _.ops.pop();
149 _.trys.pop();
150 continue;
151 }
152 op = body.call(thisArg, _);
153 }
154 catch (e) {
155 op = [6, e];
156 y = 0;
157 }
158 finally {
159 f = t = 0;
160 }
161 if (op[0] & 5)
162 throw op[1];
163 return { value: op[0] ? op[1] : void 0, done: true };
164 }
165 }
166 var __createBinding = Object.create ? (function (o, m, k, k2) {
167 if (k2 === undefined)
168 k2 = k;
169 Object.defineProperty(o, k2, { enumerable: true, get: function () { return m[k]; } });
170 }) : (function (o, m, k, k2) {
171 if (k2 === undefined)
172 k2 = k;
173 o[k2] = m[k];
174 });
175 function __exportStar(m, o) {
176 for (var p in m)
177 if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p))
178 __createBinding(o, m, p);
179 }
180 function __values(o) {
181 var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
182 if (m)
183 return m.call(o);
184 if (o && typeof o.length === "number")
185 return {
186 next: function () {
187 if (o && i >= o.length)
188 o = void 0;
189 return { value: o && o[i++], done: !o };
190 }
191 };
192 throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
193 }
194 function __read(o, n) {
195 var m = typeof Symbol === "function" && o[Symbol.iterator];
196 if (!m)
197 return o;
198 var i = m.call(o), r, ar = [], e;
199 try {
200 while ((n === void 0 || n-- > 0) && !(r = i.next()).done)
201 ar.push(r.value);
202 }
203 catch (error) {
204 e = { error: error };
205 }
206 finally {
207 try {
208 if (r && !r.done && (m = i["return"]))
209 m.call(i);
210 }
211 finally {
212 if (e)
213 throw e.error;
214 }
215 }
216 return ar;
217 }
218 function __spread() {
219 for (var ar = [], i = 0; i < arguments.length; i++)
220 ar = ar.concat(__read(arguments[i]));
221 return ar;
222 }
223 function __spreadArrays() {
224 for (var s = 0, i = 0, il = arguments.length; i < il; i++)
225 s += arguments[i].length;
226 for (var r = Array(s), k = 0, i = 0; i < il; i++)
227 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
228 r[k] = a[j];
229 return r;
230 }
231 ;
232 function __await(v) {
233 return this instanceof __await ? (this.v = v, this) : new __await(v);
234 }
235 function __asyncGenerator(thisArg, _arguments, generator) {
236 if (!Symbol.asyncIterator)
237 throw new TypeError("Symbol.asyncIterator is not defined.");
238 var g = generator.apply(thisArg, _arguments || []), i, q = [];
239 return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
240 function verb(n) { if (g[n])
241 i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
242 function resume(n, v) { try {
243 step(g[n](v));
244 }
245 catch (e) {
246 settle(q[0][3], e);
247 } }
248 function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
249 function fulfill(value) { resume("next", value); }
250 function reject(value) { resume("throw", value); }
251 function settle(f, v) { if (f(v), q.shift(), q.length)
252 resume(q[0][0], q[0][1]); }
253 }
254 function __asyncDelegator(o) {
255 var i, p;
256 return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
257 function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; }
258 }
259 function __asyncValues(o) {
260 if (!Symbol.asyncIterator)
261 throw new TypeError("Symbol.asyncIterator is not defined.");
262 var m = o[Symbol.asyncIterator], i;
263 return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
264 function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
265 function settle(resolve, reject, d, v) { Promise.resolve(v).then(function (v) { resolve({ value: v, done: d }); }, reject); }
266 }
267 function __makeTemplateObject(cooked, raw) {
268 if (Object.defineProperty) {
269 Object.defineProperty(cooked, "raw", { value: raw });
270 }
271 else {
272 cooked.raw = raw;
273 }
274 return cooked;
275 }
276 ;
277 var __setModuleDefault = Object.create ? (function (o, v) {
278 Object.defineProperty(o, "default", { enumerable: true, value: v });
279 }) : function (o, v) {
280 o["default"] = v;
281 };
282 function __importStar(mod) {
283 if (mod && mod.__esModule)
284 return mod;
285 var result = {};
286 if (mod != null)
287 for (var k in mod)
288 if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
289 __createBinding(result, mod, k);
290 __setModuleDefault(result, mod);
291 return result;
292 }
293 function __importDefault(mod) {
294 return (mod && mod.__esModule) ? mod : { default: mod };
295 }
296 function __classPrivateFieldGet(receiver, privateMap) {
297 if (!privateMap.has(receiver)) {
298 throw new TypeError("attempted to get private field on non-instance");
299 }
300 return privateMap.get(receiver);
301 }
302 function __classPrivateFieldSet(receiver, privateMap, value) {
303 if (!privateMap.has(receiver)) {
304 throw new TypeError("attempted to set private field on non-instance");
305 }
306 privateMap.set(receiver, value);
307 return value;
308 }
309
310 // Helper to retrieve the id and path of a document in the collection
311 function getIdAndPath(options, collectionPath) {
312 var path = '';
313 var id = '';
314 if (options['id']) {
315 if (!collectionPath) {
316 throw new Error('You should provide the colletion path with the id');
317 }
318 id = options['id'];
319 path = collectionPath + "/" + id;
320 }
321 else if (options['path']) {
322 path = options['path'];
323 var part = path.split('/');
324 if (part.length % 2 !== 0) {
325 throw new Error("Path " + path + " doesn't look like a Firestore's document path");
326 }
327 id = part[part.length - 1];
328 }
329 else {
330 throw new Error("You should provide either an \"id\" OR a \"path\".");
331 }
332 return { id: id, path: path };
333 }
334
335 /** Set the loading parameter of a specific store */
336 function setLoading(storeName, loading) {
337 akita.runStoreAction(storeName, akita.StoreAction.Update, function (update) { return update({ loading: loading }); });
338 }
339 ;
340 /** Reset the store to an empty array */
341 function resetStore(storeName) {
342 akita.runStoreAction(storeName, akita.StoreAction.Update, function (update) { return update([]); });
343 }
344 ;
345 /** Set a entity as active */
346 function setActive(storeName, active) {
347 akita.runStoreAction(storeName, akita.StoreAction.Update, function (update) { return update({ active: active }); });
348 }
349 ;
350 /** Create or update one or several entities in the store */
351 function upsertStoreEntity(storeName, data, id) {
352 akita.runEntityStoreAction(storeName, akita.EntityStoreAction.UpsertEntities, function (upsert) { return upsert(id, data); });
353 }
354 /** Remove one or several entities in the store */
355 function removeStoreEntity(storeName, entityIds) {
356 akita.runEntityStoreAction(storeName, akita.EntityStoreAction.RemoveEntities, function (remove) { return remove(entityIds); });
357 }
358 /** Update one or several entities in the store */
359 function updateStoreEntity(removeAndAdd, storeName, entityIds, data) {
360 if (removeAndAdd) {
361 akita.applyTransaction(function () {
362 removeStoreEntity(storeName, entityIds);
363 upsertStoreEntity(storeName, data, entityIds);
364 });
365 }
366 else {
367 akita.runEntityStoreAction(storeName, akita.EntityStoreAction.UpdateEntities, function (update) { return update(entityIds, data); });
368 }
369 }
370 /** Sync a specific store with actions from Firestore */
371 function syncStoreFromDocAction(storeName, actions, idKey, removeAndAdd, formatFromFirestore) {
372 var e_1, _a, _b;
373 if (idKey === void 0) { idKey = 'id'; }
374 setLoading(storeName, false);
375 if (actions.length === 0) {
376 return;
377 }
378 try {
379 for (var actions_1 = __values(actions), actions_1_1 = actions_1.next(); !actions_1_1.done; actions_1_1 = actions_1.next()) {
380 var action = actions_1_1.value;
381 var id = action.payload.doc.id;
382 var entity = formatFromFirestore(action.payload.doc.data());
383 switch (action.type) {
384 case 'added': {
385 upsertStoreEntity(storeName, Object.assign((_b = {}, _b[idKey] = id, _b), entity), id);
386 break;
387 }
388 case 'removed': {
389 removeStoreEntity(storeName, id);
390 break;
391 }
392 case 'modified': {
393 updateStoreEntity(removeAndAdd, storeName, id, entity);
394 break;
395 }
396 }
397 }
398 }
399 catch (e_1_1) { e_1 = { error: e_1_1 }; }
400 finally {
401 try {
402 if (actions_1_1 && !actions_1_1.done && (_a = actions_1.return)) _a.call(actions_1);
403 }
404 finally { if (e_1) throw e_1.error; }
405 }
406 }
407 /** Sync a specific store with actions from Firestore */
408 function syncStoreFromDocActionSnapshot(storeName, action, idKey, formatFromFirestore) {
409 var _a;
410 if (idKey === void 0) { idKey = 'id'; }
411 setLoading(storeName, false);
412 var id = action.payload.id;
413 var entity = formatFromFirestore(action.payload.data());
414 if (!action.payload.exists) {
415 removeStoreEntity(storeName, id);
416 }
417 else {
418 upsertStoreEntity(storeName, Object.assign((_a = {}, _a[idKey] = id, _a), entity), id);
419 }
420 }
421
422 /**
423 * Get the store name of a store to be synced
424 */
425 function getStoreName(store, storeOptions) {
426 if (storeOptions === void 0) { storeOptions = {}; }
427 if (!store && !storeOptions.storeName) {
428 throw new Error('You should either provide a store name or inject a store instance in constructor');
429 }
430 return storeOptions.storeName || store.storeName;
431 }
432
433 /** Get the params from a path */
434 function getPathParams(path) {
435 return path.split('/')
436 .filter(function (segment) { return segment.charAt(0) === ':'; })
437 .map(function (segment) { return segment.substr(1); });
438 }
439 /**
440 * Transform a path based on the params
441 * @param path The path with params starting with "/:"
442 * @param params A map of id params
443 * @example pathWithParams('movies/:movieId/stakeholder/:shId', { movieId, shId })
444 */
445 function pathWithParams(path, params) {
446 return path.split('/').map(function (segment) {
447 if (segment.charAt(0) === ':') {
448 var key = segment.substr(1);
449 if (!params[key]) {
450 throw new Error("Required parameter " + key + " from " + path + " doesn't exist in params " + JSON.stringify(params));
451 }
452 return params[key];
453 }
454 else {
455 return segment;
456 }
457 }).join('/');
458 }
459
460 /**
461 * Same as Object.getOwnPropertyDescriptor, but recursively checks prototype chain excluding passed currentClass
462 * @param instance Instance to check on
463 * @param params Property name to check
464 * @param currentClass Checks prototype chain until this class
465 * @example getPropertyDescriptor(this, 'path', CollectionService)
466 */
467 function getPropertyDescriptor(instance, property, currentClass) {
468 if (currentClass === void 0) { currentClass = Object; }
469 var prototype = Object.getPrototypeOf(instance);
470 if (!prototype || !(prototype instanceof currentClass))
471 return;
472 return Object.getOwnPropertyDescriptor(prototype, property) || getPropertyDescriptor(prototype, property, currentClass);
473 }
474 /**
475 * Check prototype chain for a specific getter function, excluding parent class
476 * @param instance Instance of the class to check on
477 * @param parentClass Parent class of the instance
478 * @param property Property name to check
479 * @example hasChildGetter(this, CollectionService, 'path')
480 */
481 function hasChildGetter(instance, parentClass, property) {
482 var descriptor = getPropertyDescriptor(instance, property, parentClass);
483 return descriptor && descriptor.get && true;
484 }
485
486 function isArray(entityOrArray) {
487 return Array.isArray(entityOrArray);
488 }
489 /** check is an Atomic write is a transaction */
490 function isTransaction(write) {
491 return write && !!write['get'];
492 }
493 var CollectionService = /** @class */ (function () {
494 function CollectionService(store, collectionPath, db) {
495 this.store = store;
496 this.collectionPath = collectionPath;
497 // keep memory of the current ids to listen to (for syncManyDocs)
498 this.idsToListen = {};
499 if (!hasChildGetter(this, CollectionService, 'path') && !this.constructor['path'] && !this.collectionPath) {
500 throw new Error('You should provide a path to the collection');
501 }
502 try {
503 this.db = db || core.inject(firestore.AngularFirestore);
504 }
505 catch (err) {
506 throw new Error('CollectionService requires AngularFirestore.');
507 }
508 }
509 CollectionService.prototype.getPath = function (options) {
510 return (options && options.params)
511 ? pathWithParams(this.path, options.params)
512 : this.currentPath;
513 };
514 Object.defineProperty(CollectionService.prototype, "idKey", {
515 get: function () {
516 return this.constructor['idKey']
517 || this.store ? this.store.idKey : 'id';
518 },
519 enumerable: false,
520 configurable: true
521 });
522 Object.defineProperty(CollectionService.prototype, "path", {
523 /** The path to the collection in Firestore */
524 get: function () {
525 return this.constructor['path'] || this.collectionPath;
526 },
527 enumerable: false,
528 configurable: true
529 });
530 Object.defineProperty(CollectionService.prototype, "currentPath", {
531 /** A snapshot of the path */
532 get: function () {
533 if (rxjs.isObservable(this.path)) {
534 throw new Error('Cannot get a snapshot of the path if it is an Observable');
535 }
536 return this.path;
537 },
538 enumerable: false,
539 configurable: true
540 });
541 Object.defineProperty(CollectionService.prototype, "resetOnUpdate", {
542 get: function () {
543 return this.constructor['resetOnUpdate'] || false;
544 },
545 enumerable: false,
546 configurable: true
547 });
548 Object.defineProperty(CollectionService.prototype, "collection", {
549 /**
550 * The Angular Fire collection
551 * @notice If path is an observable, it becomes an observable.
552 */
553 get: function () {
554 return this.db.collection(this.currentPath);
555 },
556 enumerable: false,
557 configurable: true
558 });
559 /**
560 * Function triggered when adding/updating data to firestore
561 * @note should be overridden
562 */
563 CollectionService.prototype.formatToFirestore = function (entity) {
564 return entity;
565 };
566 /**
567 * Function triggered when getting data from firestore
568 * @note should be overridden
569 */
570 CollectionService.prototype.formatFromFirestore = function (entity) {
571 return entity;
572 };
573 CollectionService.prototype.syncCollection = function (pathOrQuery, queryOrOptions, syncOptions) {
574 var _this = this;
575 if (pathOrQuery === void 0) { pathOrQuery = this.currentPath; }
576 if (syncOptions === void 0) { syncOptions = { loading: true }; }
577 var path;
578 var queryFn;
579 // check type of pathOrQuery
580 if (typeof pathOrQuery === 'function') {
581 queryFn = pathOrQuery;
582 path = this.getPath(queryOrOptions);
583 }
584 else if (typeof pathOrQuery === 'object') {
585 syncOptions = pathOrQuery;
586 path = this.getPath(syncOptions);
587 }
588 else if (typeof pathOrQuery === 'string') {
589 path = pathOrQuery;
590 }
591 else {
592 path = this.getPath(syncOptions);
593 }
594 // check type of queryOrOptions
595 if (typeof queryOrOptions === 'function') {
596 queryFn = queryOrOptions;
597 }
598 else if (typeof queryOrOptions === 'object') {
599 syncOptions = queryOrOptions;
600 }
601 var storeName = getStoreName(this.store, syncOptions);
602 // reset has to happen before setLoading, otherwise it will also reset the loading state
603 if (syncOptions.reset) {
604 resetStore(storeName);
605 }
606 if (syncOptions.loading) {
607 setLoading(storeName, true);
608 }
609 // Start Listening
610 return this.db.collection(path, queryFn).stateChanges().pipe(akita.withTransaction(function (actions) { return syncStoreFromDocAction(storeName, actions, _this.idKey, _this.resetOnUpdate, function (entity) { return _this.formatFromFirestore(entity); }); }));
611 };
612 CollectionService.prototype.syncCollectionGroup = function (idOrQuery, queryOrOption, syncOptions) {
613 var _this = this;
614 if (idOrQuery === void 0) { idOrQuery = this.currentPath; }
615 if (syncOptions === void 0) { syncOptions = { loading: true }; }
616 var path;
617 var query;
618 if (typeof idOrQuery === 'string') {
619 path = idOrQuery;
620 }
621 else if (typeof idOrQuery === 'function') {
622 path = this.currentPath;
623 query = idOrQuery;
624 }
625 else if (typeof idOrQuery === 'object') {
626 path = this.currentPath;
627 syncOptions = idOrQuery;
628 }
629 else {
630 throw new Error('1ier parameter if either a string, a queryFn or a StoreOption');
631 }
632 if (typeof queryOrOption === 'function') {
633 query = queryOrOption;
634 }
635 else if (typeof queryOrOption === 'object') {
636 syncOptions = queryOrOption;
637 }
638 var storeName = getStoreName(this.store, syncOptions);
639 // reset has to happen before setLoading, otherwise it will also reset the loading state
640 if (syncOptions.reset) {
641 resetStore(storeName);
642 }
643 if (syncOptions.loading) {
644 setLoading(storeName, true);
645 }
646 var collectionId = path.split('/').pop();
647 return this.db.collectionGroup(collectionId, query).stateChanges().pipe(akita.withTransaction(function (actions) { return syncStoreFromDocAction(storeName, actions, _this.idKey, _this.resetOnUpdate, function (entity) { return _this.formatFromFirestore(entity); }); }));
648 };
649 CollectionService.prototype.syncManyDocs = function (ids$, syncOptions) {
650 var _this = this;
651 if (syncOptions === void 0) { syncOptions = { loading: true }; }
652 if (!rxjs.isObservable(ids$)) {
653 ids$ = rxjs.of(ids$);
654 }
655 var storeName = getStoreName(this.store, syncOptions);
656 // reset has to happen before setLoading, otherwise it will also reset the loading state
657 if (syncOptions.reset) {
658 resetStore(storeName);
659 }
660 if (syncOptions.loading) {
661 setLoading(storeName, true);
662 }
663 return ids$.pipe(operators.switchMap((function (ids) {
664 // Remove previous ids that have changed
665 var previousIds = _this.idsToListen[storeName];
666 if (previousIds) {
667 var idsToRemove = previousIds.filter(function (id) { return !ids.includes(id); });
668 removeStoreEntity(storeName, idsToRemove);
669 }
670 _this.idsToListen[storeName] = ids;
671 // Return empty array if no ids are provided
672 if (!ids.length) {
673 return rxjs.of([]);
674 }
675 // Sync all docs
676 var syncs = ids.map(function (id) {
677 var path = _this.getPath(syncOptions) + "/" + id;
678 return _this.db.doc(path).snapshotChanges();
679 });
680 return rxjs.combineLatest(syncs).pipe(operators.tap(function (actions) { return actions.map(function (action) {
681 syncStoreFromDocActionSnapshot(storeName, action, _this.idKey, function (entity) { return _this.formatFromFirestore(entity); });
682 }); }));
683 })));
684 };
685 /**
686 * Stay in sync with one document
687 * @param docOptions An object with EITHER `id` OR `path`.
688 * @note We need to use id and path because there is no way to differentiate them.
689 * @param syncOptions Options on the store to sync to
690 */
691 CollectionService.prototype.syncDoc = function (docOptions, syncOptions) {
692 var _this = this;
693 if (syncOptions === void 0) { syncOptions = { loading: false }; }
694 var storeName = getStoreName(this.store, syncOptions);
695 var collectionPath = this.getPath(syncOptions);
696 var _a = getIdAndPath(docOptions, collectionPath), id = _a.id, path = _a.path;
697 // reset has to happen before setLoading, otherwise it will also reset the loading state
698 if (syncOptions.reset) {
699 resetStore(storeName);
700 }
701 if (syncOptions.loading) {
702 setLoading(storeName, true);
703 }
704 return this.db.doc(path).valueChanges().pipe(operators.map(function (entity) {
705 var _a;
706 if (!entity) {
707 setLoading(storeName, false);
708 // note: We don't removeEntity as it would result in weird behavior
709 return undefined;
710 }
711 var data = _this.formatFromFirestore(Object.assign((_a = {}, _a[_this.idKey] = id, _a), entity));
712 upsertStoreEntity(storeName, data, id);
713 setLoading(storeName, false);
714 return data;
715 }));
716 };
717 CollectionService.prototype.syncActive = function (options, syncOptions) {
718 var _this = this;
719 var storeName = getStoreName(this.store, syncOptions);
720 if (Array.isArray(options)) {
721 return this.syncManyDocs(options, syncOptions).pipe(operators.tap(function (_) { return setActive(storeName, options); }));
722 }
723 else {
724 return this.syncDoc(options, syncOptions).pipe(operators.tap(function (entity) { return entity ? setActive(storeName, entity[_this.idKey]) : null; }));
725 }
726 };
727 CollectionService.prototype.getRef = function (idOrQuery, options) {
728 var _this = this;
729 if (options === void 0) { options = {}; }
730 var path = this.getPath(options);
731 // If path targets a collection ( odd number of segments after the split )
732 if (typeof idOrQuery === 'string') {
733 return this.db.doc(path + "/" + idOrQuery).ref;
734 }
735 if (Array.isArray(idOrQuery)) {
736 return idOrQuery.map(function (id) { return _this.db.doc(path + "/" + id).ref; });
737 }
738 else if (typeof idOrQuery === 'object') {
739 var subpath = this.getPath(idOrQuery);
740 return this.db.collection(subpath).ref;
741 }
742 else {
743 return this.db.collection(path, idOrQuery).ref;
744 }
745 };
746 CollectionService.prototype.getValue = function (idOrQuery, options) {
747 if (options === void 0) { options = {}; }
748 return __awaiter(this, void 0, void 0, function () {
749 var path, snapshot, docs, ref, snaphot, subpath, snapshot, snapshot;
750 var _a;
751 var _this = this;
752 return __generator(this, function (_b) {
753 switch (_b.label) {
754 case 0:
755 path = this.getPath(options);
756 if (!(typeof idOrQuery === 'string')) return [3 /*break*/, 2];
757 return [4 /*yield*/, this.db.doc(path + "/" + idOrQuery).ref.get()];
758 case 1:
759 snapshot = _b.sent();
760 return [2 /*return*/, snapshot.exists
761 ? this.formatFromFirestore(Object.assign(Object.assign({}, snapshot.data()), (_a = {}, _a[this.idKey] = snapshot.id, _a)))
762 : null];
763 case 2:
764 if (!Array.isArray(idOrQuery)) return [3 /*break*/, 4];
765 return [4 /*yield*/, Promise.all(idOrQuery.map(function (id) {
766 return _this.db.doc(path + "/" + id).ref.get();
767 }))];
768 case 3:
769 docs = _b.sent();
770 return [3 /*break*/, 10];
771 case 4:
772 if (!(typeof idOrQuery === 'function')) return [3 /*break*/, 6];
773 ref = this.db.collection(path).ref;
774 return [4 /*yield*/, idOrQuery(ref).get()];
775 case 5:
776 snaphot = _b.sent();
777 docs = snaphot.docs;
778 return [3 /*break*/, 10];
779 case 6:
780 if (!(typeof idOrQuery === 'object')) return [3 /*break*/, 8];
781 subpath = this.getPath(idOrQuery);
782 return [4 /*yield*/, this.db.collection(subpath).ref.get()];
783 case 7:
784 snapshot = _b.sent();
785 docs = snapshot.docs;
786 return [3 /*break*/, 10];
787 case 8: return [4 /*yield*/, this.db.collection(path, idOrQuery).ref.get()];
788 case 9:
789 snapshot = _b.sent();
790 docs = snapshot.docs;
791 _b.label = 10;
792 case 10: return [2 /*return*/, docs.filter(function (doc) { return doc.exists; })
793 .map(function (doc) {
794 var _a;
795 return Object.assign(Object.assign({}, doc.data()), (_a = {}, _a[_this.idKey] = doc.id, _a));
796 })
797 .map(function (doc) { return _this.formatFromFirestore(doc); })];
798 }
799 });
800 });
801 };
802 CollectionService.prototype.valueChanges = function (idOrQuery, options) {
803 var _this = this;
804 if (options === void 0) { options = {}; }
805 var path = this.getPath(options);
806 // If path targets a collection ( odd number of segments after the split )
807 if (typeof idOrQuery === 'string') {
808 return this.db.doc(path + "/" + idOrQuery).valueChanges().pipe(operators.map(function (doc) { return _this.formatFromFirestore(doc); }));
809 }
810 var docs$;
811 if (Array.isArray(idOrQuery)) {
812 docs$ = idOrQuery.length
813 ? rxjs.combineLatest(idOrQuery.map(function (id) { return _this.db.doc(path + "/" + id).valueChanges(); }))
814 : rxjs.of([]);
815 }
816 else if (typeof idOrQuery === 'function') {
817 docs$ = this.db.collection(path, idOrQuery).valueChanges();
818 }
819 else if (typeof idOrQuery === 'object') {
820 var subpath = this.getPath(idOrQuery);
821 docs$ = this.db.collection(subpath).valueChanges();
822 }
823 else {
824 docs$ = this.db.collection(path, idOrQuery).valueChanges();
825 }
826 return docs$.pipe(operators.map(function (docs) { return docs.map(function (doc) { return _this.formatFromFirestore(doc); }); }));
827 };
828 ///////////
829 // WRITE //
830 ///////////
831 /**
832 * Create a batch object.
833 * @note alias for `angularFirestore.firestore.batch()`
834 */
835 CollectionService.prototype.batch = function () {
836 return this.db.firestore.batch();
837 };
838 /**
839 * Run a transaction
840 * @note alias for `angularFirestore.firestore.runTransaction()`
841 */
842 CollectionService.prototype.runTransaction = function (cb) {
843 return this.db.firestore.runTransaction(function (tx) { return cb(tx); });
844 };
845 /**
846 * Create or update documents
847 * @param documents One or many documents
848 * @param options options to write the document on firestore
849 */
850 CollectionService.prototype.upsert = function (documents, options) {
851 if (options === void 0) { options = {}; }
852 return __awaiter(this, void 0, void 0, function () {
853 var doesExist, toAdd, toUpdate, documents_1, documents_1_1, doc, e_1_1;
854 var e_1, _a;
855 var _this = this;
856 return __generator(this, function (_b) {
857 switch (_b.label) {
858 case 0:
859 doesExist = function (doc) { return __awaiter(_this, void 0, void 0, function () {
860 var ref, exists;
861 return __generator(this, function (_a) {
862 switch (_a.label) {
863 case 0:
864 ref = this.getRef(doc[this.idKey]);
865 return [4 /*yield*/, (isTransaction(options.write) ? options.write.get(ref) : ref.get())];
866 case 1:
867 exists = (_a.sent()).exists;
868 return [2 /*return*/, exists];
869 }
870 });
871 }); };
872 if (!!isArray(documents)) return [3 /*break*/, 2];
873 return [4 /*yield*/, doesExist(documents)];
874 case 1: return [2 /*return*/, (_b.sent())
875 ? this.update(documents, options).then(function (_) { return documents[_this.idKey]; })
876 : this.add(documents, options)];
877 case 2:
878 toAdd = [];
879 toUpdate = [];
880 _b.label = 3;
881 case 3:
882 _b.trys.push([3, 8, 9, 10]);
883 documents_1 = __values(documents), documents_1_1 = documents_1.next();
884 _b.label = 4;
885 case 4:
886 if (!!documents_1_1.done) return [3 /*break*/, 7];
887 doc = documents_1_1.value;
888 return [4 /*yield*/, doesExist(doc)];
889 case 5:
890 (_b.sent())
891 ? toUpdate.push(doc)
892 : toAdd.push(doc);
893 _b.label = 6;
894 case 6:
895 documents_1_1 = documents_1.next();
896 return [3 /*break*/, 4];
897 case 7: return [3 /*break*/, 10];
898 case 8:
899 e_1_1 = _b.sent();
900 e_1 = { error: e_1_1 };
901 return [3 /*break*/, 10];
902 case 9:
903 try {
904 if (documents_1_1 && !documents_1_1.done && (_a = documents_1.return)) _a.call(documents_1);
905 }
906 finally { if (e_1) throw e_1.error; }
907 return [7 /*endfinally*/];
908 case 10: return [2 /*return*/, Promise.all([
909 this.add(toAdd, options),
910 this.update(toUpdate, options).then(function (_) { return toUpdate.map(function (doc) { return doc[_this.idKey]; }); })
911 ]).then(function (_a) {
912 var _b = __read(_a, 2), added = _b[0], updated = _b[1];
913 return added.concat(updated);
914 })];
915 }
916 });
917 });
918 };
919 /**
920 * Add a document or a list of document to Firestore
921 * @param docs A document or a list of document
922 * @param options options to write the document on firestore
923 */
924 CollectionService.prototype.add = function (documents, options) {
925 if (options === void 0) { options = {}; }
926 return __awaiter(this, void 0, void 0, function () {
927 var docs, _a, write, ctx, path, operations, ids;
928 var _this = this;
929 return __generator(this, function (_b) {
930 switch (_b.label) {
931 case 0:
932 docs = (Array.isArray(documents) ? documents : [documents]);
933 _a = options.write, write = _a === void 0 ? this.batch() : _a, ctx = options.ctx;
934 path = this.getPath(options);
935 operations = docs.map(function (doc) { return __awaiter(_this, void 0, void 0, function () {
936 var id, data, ref;
937 var _a;
938 return __generator(this, function (_b) {
939 switch (_b.label) {
940 case 0:
941 id = doc[this.idKey] || this.db.createId();
942 data = this.formatToFirestore(Object.assign(Object.assign({}, doc), (_a = {}, _a[this.idKey] = id, _a)));
943 ref = this.db.doc(path + "/" + id).ref;
944 write.set(ref, (data));
945 if (!this.onCreate) return [3 /*break*/, 2];
946 return [4 /*yield*/, this.onCreate(data, { write: write, ctx: ctx })];
947 case 1:
948 _b.sent();
949 _b.label = 2;
950 case 2: return [2 /*return*/, id];
951 }
952 });
953 }); });
954 return [4 /*yield*/, Promise.all(operations)];
955 case 1:
956 ids = _b.sent();
957 if (!!options.write) return [3 /*break*/, 3];
958 return [4 /*yield*/, write.commit()];
959 case 2:
960 _b.sent();
961 _b.label = 3;
962 case 3: return [2 /*return*/, Array.isArray(documents) ? ids : ids[0]];
963 }
964 });
965 });
966 };
967 /**
968 * Remove one or several document from Firestore
969 * @param id A unique or list of id representing the document
970 * @param options options to write the document on firestore
971 */
972 CollectionService.prototype.remove = function (id, options) {
973 if (options === void 0) { options = {}; }
974 return __awaiter(this, void 0, void 0, function () {
975 var _a, write, ctx, path, ids, operations;
976 var _this = this;
977 return __generator(this, function (_b) {
978 switch (_b.label) {
979 case 0:
980 _a = options.write, write = _a === void 0 ? this.batch() : _a, ctx = options.ctx;
981 path = this.getPath(options);
982 ids = Array.isArray(id) ? id : [id];
983 operations = ids.map(function (docId) { return __awaiter(_this, void 0, void 0, function () {
984 var ref;
985 return __generator(this, function (_a) {
986 switch (_a.label) {
987 case 0:
988 ref = this.db.doc(path + "/" + docId).ref;
989 write.delete(ref);
990 if (!this.onDelete) return [3 /*break*/, 2];
991 return [4 /*yield*/, this.onDelete(docId, { write: write, ctx: ctx })];
992 case 1:
993 _a.sent();
994 _a.label = 2;
995 case 2: return [2 /*return*/];
996 }
997 });
998 }); });
999 return [4 /*yield*/, Promise.all(operations)];
1000 case 1:
1001 _b.sent();
1002 // If there is no atomic write provided
1003 if (!options.write) {
1004 return [2 /*return*/, write.commit()];
1005 }
1006 return [2 /*return*/];
1007 }
1008 });
1009 });
1010 };
1011 /** Remove all document of the collection */
1012 CollectionService.prototype.removeAll = function (options) {
1013 if (options === void 0) { options = {}; }
1014 return __awaiter(this, void 0, void 0, function () {
1015 var path, snapshot, ids;
1016 return __generator(this, function (_a) {
1017 switch (_a.label) {
1018 case 0:
1019 path = this.getPath(options);
1020 return [4 /*yield*/, this.db.collection(path).ref.get()];
1021 case 1:
1022 snapshot = _a.sent();
1023 ids = snapshot.docs.map(function (doc) { return doc.id; });
1024 return [2 /*return*/, this.remove(ids, options)];
1025 }
1026 });
1027 });
1028 };
1029 CollectionService.prototype.update = function (idsOrEntity, stateFnOrWrite, options) {
1030 if (options === void 0) { options = {}; }
1031 return __awaiter(this, void 0, void 0, function () {
1032 var ids, stateFunction, getData, isEntity, isEntityArray, entityMap_1, ctx, path, _a, write_1, operations;
1033 var _this = this;
1034 return __generator(this, function (_b) {
1035 switch (_b.label) {
1036 case 0:
1037 ids = [];
1038 isEntity = function (value) {
1039 return typeof value === 'object' && value[_this.idKey];
1040 };
1041 isEntityArray = function (values) {
1042 return Array.isArray(values) && values.every(function (value) { return isEntity(value); });
1043 };
1044 if (isEntity(idsOrEntity)) {
1045 ids = [idsOrEntity[this.idKey]];
1046 getData = function () { return idsOrEntity; };
1047 options = stateFnOrWrite || {};
1048 }
1049 else if (isEntityArray(idsOrEntity)) {
1050 entityMap_1 = new Map(idsOrEntity.map(function (entity) { return [entity[_this.idKey], entity]; }));
1051 ids = Array.from(entityMap_1.keys());
1052 getData = function (docId) { return entityMap_1.get(docId); };
1053 options = stateFnOrWrite || {};
1054 }
1055 else if (typeof stateFnOrWrite === 'function') {
1056 ids = Array.isArray(idsOrEntity) ? idsOrEntity : [idsOrEntity];
1057 stateFunction = stateFnOrWrite;
1058 }
1059 else if (typeof stateFnOrWrite === 'object') {
1060 ids = Array.isArray(idsOrEntity) ? idsOrEntity : [idsOrEntity];
1061 getData = function () { return stateFnOrWrite; };
1062 }
1063 else {
1064 throw new Error('Passed parameters match none of the function signatures.');
1065 }
1066 ctx = options.ctx;
1067 path = this.getPath(options);
1068 if (!Array.isArray(ids) || !ids.length) {
1069 return [2 /*return*/];
1070 }
1071 if (!stateFunction) return [3 /*break*/, 1];
1072 return [2 /*return*/, this.db.firestore.runTransaction(function (tx) { return __awaiter(_this, void 0, void 0, function () {
1073 var operations;
1074 var _this = this;
1075 return __generator(this, function (_a) {
1076 operations = ids.map(function (id) { return __awaiter(_this, void 0, void 0, function () {
1077 var ref, snapshot, doc, data;
1078 var _a;
1079 return __generator(this, function (_b) {
1080 switch (_b.label) {
1081 case 0:
1082 ref = this.db.doc(path + "/" + id).ref;
1083 return [4 /*yield*/, tx.get(ref)];
1084 case 1:
1085 snapshot = _b.sent();
1086 doc = Object.freeze(Object.assign(Object.assign({}, snapshot.data()), (_a = {}, _a[this.idKey] = id, _a)));
1087 data = stateFunction(doc, tx);
1088 tx.update(ref, this.formatToFirestore(data));
1089 if (!this.onUpdate) return [3 /*break*/, 3];
1090 return [4 /*yield*/, this.onUpdate(data, { write: tx, ctx: ctx })];
1091 case 2:
1092 _b.sent();
1093 _b.label = 3;
1094 case 3: return [2 /*return*/, tx];
1095 }
1096 });
1097 }); });
1098 return [2 /*return*/, Promise.all(operations)];
1099 });
1100 }); })];
1101 case 1:
1102 _a = options.write, write_1 = _a === void 0 ? this.batch() : _a;
1103 operations = ids.map(function (docId) { return __awaiter(_this, void 0, void 0, function () {
1104 var doc, ref;
1105 return __generator(this, function (_a) {
1106 switch (_a.label) {
1107 case 0:
1108 doc = Object.freeze(getData(docId));
1109 if (!docId) {
1110 throw new Error("Document should have an unique id to be updated, but none was found in " + doc);
1111 }
1112 ref = this.db.doc(path + "/" + docId).ref;
1113 write_1.update(ref, this.formatToFirestore(doc));
1114 if (!this.onUpdate) return [3 /*break*/, 2];
1115 return [4 /*yield*/, this.onUpdate(doc, { write: write_1, ctx: ctx })];
1116 case 1:
1117 _a.sent();
1118 _a.label = 2;
1119 case 2: return [2 /*return*/];
1120 }
1121 });
1122 }); });
1123 return [4 /*yield*/, Promise.all(operations)];
1124 case 2:
1125 _b.sent();
1126 // If there is no atomic write provided
1127 if (!options.write) {
1128 return [2 /*return*/, write_1.commit()];
1129 }
1130 return [2 /*return*/];
1131 }
1132 });
1133 });
1134 };
1135 return CollectionService;
1136 }());
1137
1138 /** Set the configuration for the collection service */
1139 function CollectionConfig(options) {
1140 if (options === void 0) { options = {}; }
1141 return function (constructor) {
1142 Object.keys(options).forEach(function (key) { return constructor[key] = options[key]; });
1143 };
1144 }
1145
1146 var initialAuthState = {
1147 uid: null,
1148 emailVerified: undefined,
1149 profile: null,
1150 loading: false
1151 };
1152
1153 var authProviders = ['github', 'google', 'microsoft', 'facebook', 'twitter', 'email', 'apple'];
1154 /** Verify if provider is part of the list of Authentication provider provided by Firebase Auth */
1155 function isFireAuthProvider(provider) {
1156 return typeof provider === 'string' && authProviders.includes(provider);
1157 }
1158 /**
1159 * Get the custom claims of a user. If no key is provided, return the whole claims object
1160 * @param user The user object returned by Firebase Auth
1161 * @param roles Keys of the custom claims inside the claim objet
1162 */
1163 function getCustomClaims(user, roles) {
1164 return __awaiter(this, void 0, void 0, function () {
1165 var claims, keys;
1166 return __generator(this, function (_a) {
1167 switch (_a.label) {
1168 case 0: return [4 /*yield*/, user.getIdTokenResult()];
1169 case 1:
1170 claims = (_a.sent()).claims;
1171 if (!roles) {
1172 return [2 /*return*/, claims];
1173 }
1174 keys = Array.isArray(roles) ? roles : [roles];
1175 return [2 /*return*/, Object.keys(claims)
1176 .filter(function (key) { return keys.includes(key); })
1177 .reduce(function (acc, key) {
1178 acc[key] = claims[key];
1179 return acc;
1180 }, {})];
1181 }
1182 });
1183 });
1184 }
1185 /**
1186 * Get the Authentication Provider based on its name
1187 * @param provider string literal representing the name of the provider
1188 */
1189 function getAuthProvider(provider) {
1190 switch (provider) {
1191 case 'email': return new firebase.auth.EmailAuthProvider();
1192 case 'facebook': return new firebase.auth.FacebookAuthProvider();
1193 case 'github': return new firebase.auth.GithubAuthProvider();
1194 case 'google': return new firebase.auth.GoogleAuthProvider();
1195 case 'microsoft': return new firebase.auth.OAuthProvider('microsoft.com');
1196 case 'twitter': return new firebase.auth.TwitterAuthProvider();
1197 case 'apple': return new firebase.auth.OAuthProvider('apple');
1198 }
1199 }
1200 var FireAuthService = /** @class */ (function () {
1201 function FireAuthService(store, db, auth$1) {
1202 this.store = store;
1203 this.collectionPath = 'users';
1204 this.db = db || core.inject(firestore.AngularFirestore);
1205 this.auth = auth$1 || core.inject(auth.AngularFireAuth);
1206 this.collection = this.db.collection(this.path);
1207 }
1208 /**
1209 * Select the profile in the Firestore
1210 * @note can be override to point to a different place
1211 */
1212 FireAuthService.prototype.selectProfile = function (user) {
1213 return this.collection.doc(user.uid).valueChanges();
1214 };
1215 /**
1216 * Select the roles for this user. Can be in custom claims or in a Firestore collection
1217 * @param user The user given by FireAuth
1218 * @see getCustomClaims to get the custom claims out of the user
1219 * @note Can be overwritten
1220 */
1221 FireAuthService.prototype.selectRoles = function (user) {
1222 return rxjs.of(null);
1223 };
1224 /**
1225 * Function triggered when getting data from firestore
1226 * @note should be overwritten
1227 */
1228 FireAuthService.prototype.formatFromFirestore = function (user) {
1229 return user;
1230 };
1231 /**
1232 * Function triggered when adding/updating data to firestore
1233 * @note should be overwritten
1234 */
1235 FireAuthService.prototype.formatToFirestore = function (user) {
1236 return user;
1237 };
1238 /**
1239 * Function triggered when transforming a user into a profile
1240 * @param user The user object from FireAuth
1241 * @param ctx The context given on signup
1242 * @note Should be override
1243 */
1244 FireAuthService.prototype.createProfile = function (user, ctx) {
1245 return {
1246 photoURL: user.photoURL,
1247 displayName: user.displayName,
1248 };
1249 };
1250 Object.defineProperty(FireAuthService.prototype, "user", {
1251 /**
1252 * The current sign-in user (or null)
1253 * @returns a Promise in v6.*.* & a snapshot in v5.*.*
1254 */
1255 get: function () {
1256 return this.auth.currentUser;
1257 },
1258 enumerable: false,
1259 configurable: true
1260 });
1261 Object.defineProperty(FireAuthService.prototype, "idKey", {
1262 get: function () {
1263 return this.constructor['idKey'] || 'id';
1264 },
1265 enumerable: false,
1266 configurable: true
1267 });
1268 Object.defineProperty(FireAuthService.prototype, "path", {
1269 /** The path to the profile in firestore */
1270 get: function () {
1271 return this.constructor['path'] || this.collectionPath;
1272 },
1273 enumerable: false,
1274 configurable: true
1275 });
1276 /** Start listening on User */
1277 FireAuthService.prototype.sync = function () {
1278 var _this = this;
1279 return this.auth.authState.pipe(operators.switchMap(function (user) { return user ? rxjs.combineLatest([
1280 rxjs.of(user),
1281 _this.selectProfile(user),
1282 _this.selectRoles(user),
1283 ]) : rxjs.of([undefined, undefined, undefined]); }), operators.tap(function (_a) {
1284 var _b = __read(_a, 3), _c = _b[0], user = _c === void 0 ? {} : _c, userProfile = _b[1], roles = _b[2];
1285 var profile = _this.formatFromFirestore(userProfile);
1286 var uid = user.uid, emailVerified = user.emailVerified;
1287 _this.store.update({ uid: uid, emailVerified: emailVerified, profile: profile, roles: roles });
1288 }), operators.map(function (_a) {
1289 var _b = __read(_a, 3), user = _b[0], userProfile = _b[1], roles = _b[2];
1290 return user ? [user, _this.formatFromFirestore(userProfile), roles] : null;
1291 }));
1292 };
1293 /**
1294 * @description Delete user from authentication service and database
1295 * WARNING This is security sensitive operation
1296 */
1297 FireAuthService.prototype.delete = function (options) {
1298 if (options === void 0) { options = {}; }
1299 return __awaiter(this, void 0, void 0, function () {
1300 var user, _a, write, ctx, ref;
1301 return __generator(this, function (_b) {
1302 switch (_b.label) {
1303 case 0: return [4 /*yield*/, this.user];
1304 case 1:
1305 user = _b.sent();
1306 if (!user) {
1307 throw new Error('No user connected');
1308 }
1309 _a = options.write, write = _a === void 0 ? this.db.firestore.batch() : _a, ctx = options.ctx;
1310 ref = this.collection.doc(user.uid).ref;
1311 write.delete(ref);
1312 if (!this.onDelete) return [3 /*break*/, 3];
1313 return [4 /*yield*/, this.onDelete({ write: write, ctx: ctx })];
1314 case 2:
1315 _b.sent();
1316 _b.label = 3;
1317 case 3:
1318 if (!!options.write) return [3 /*break*/, 5];
1319 return [4 /*yield*/, write.commit()];
1320 case 4:
1321 _b.sent();
1322 _b.label = 5;
1323 case 5: return [2 /*return*/, user.delete()];
1324 }
1325 });
1326 });
1327 };
1328 /** Update the current profile of the authenticated user */
1329 FireAuthService.prototype.update = function (profile, options) {
1330 if (options === void 0) { options = {}; }
1331 return __awaiter(this, void 0, void 0, function () {
1332 var user, ref, _a, write, ctx;
1333 var _this = this;
1334 return __generator(this, function (_b) {
1335 switch (_b.label) {
1336 case 0: return [4 /*yield*/, this.user];
1337 case 1:
1338 user = _b.sent();
1339 if (!user.uid) {
1340 throw new Error('No user connected.');
1341 }
1342 ref = this.collection.doc(user.uid).ref;
1343 if (!(typeof profile === 'function')) return [3 /*break*/, 2];
1344 return [2 /*return*/, this.db.firestore.runTransaction(function (tx) { return __awaiter(_this, void 0, void 0, function () {
1345 var snapshot, doc, data;
1346 var _a;
1347 return __generator(this, function (_b) {
1348 switch (_b.label) {
1349 case 0: return [4 /*yield*/, tx.get(ref)];
1350 case 1:
1351 snapshot = _b.sent();
1352 doc = Object.freeze(Object.assign(Object.assign({}, snapshot.data()), (_a = {}, _a[this.idKey] = snapshot.id, _a)));
1353 data = profile(this.formatToFirestore(doc), tx);
1354 tx.update(ref, data);
1355 if (!this.onUpdate) return [3 /*break*/, 3];
1356 return [4 /*yield*/, this.onUpdate(data, { write: tx, ctx: options.ctx })];
1357 case 2:
1358 _b.sent();
1359 _b.label = 3;
1360 case 3: return [2 /*return*/, tx];
1361 }
1362 });
1363 }); })];
1364 case 2:
1365 if (!(typeof profile === 'object')) return [3 /*break*/, 5];
1366 _a = options.write, write = _a === void 0 ? this.db.firestore.batch() : _a, ctx = options.ctx;
1367 write.update(ref, this.formatToFirestore(profile));
1368 if (!this.onUpdate) return [3 /*break*/, 4];
1369 return [4 /*yield*/, this.onUpdate(profile, { write: write, ctx: ctx })];
1370 case 3:
1371 _b.sent();
1372 _b.label = 4;
1373 case 4:
1374 // If there is no atomic write provided
1375 if (!options.write) {
1376 return [2 /*return*/, write.commit()];
1377 }
1378 _b.label = 5;
1379 case 5: return [2 /*return*/];
1380 }
1381 });
1382 });
1383 };
1384 /** Create a user based on email and password */
1385 FireAuthService.prototype.signup = function (email, password, options) {
1386 if (options === void 0) { options = {}; }
1387 return __awaiter(this, void 0, void 0, function () {
1388 var cred, _a, write, ctx, profile, ref;
1389 return __generator(this, function (_b) {
1390 switch (_b.label) {
1391 case 0: return [4 /*yield*/, this.auth.createUserWithEmailAndPassword(email, password)];
1392 case 1:
1393 cred = _b.sent();
1394 _a = options.write, write = _a === void 0 ? this.db.firestore.batch() : _a, ctx = options.ctx;
1395 if (!this.onSignup) return [3 /*break*/, 3];
1396 return [4 /*yield*/, this.onSignup(cred, { write: write, ctx: ctx })];
1397 case 2:
1398 _b.sent();
1399 _b.label = 3;
1400 case 3: return [4 /*yield*/, this.createProfile(cred.user, ctx)];
1401 case 4:
1402 profile = _b.sent();
1403 ref = this.collection.doc(cred.user.uid).ref;
1404 write.set(ref, this.formatToFirestore(profile));
1405 if (!this.onCreate) return [3 /*break*/, 6];
1406 return [4 /*yield*/, this.onCreate(profile, { write: write, ctx: ctx })];
1407 case 5:
1408 _b.sent();
1409 _b.label = 6;
1410 case 6:
1411 if (!!options.write) return [3 /*break*/, 8];
1412 return [4 /*yield*/, write.commit()];
1413 case 7:
1414 _b.sent();
1415 _b.label = 8;
1416 case 8: return [2 /*return*/, cred];
1417 }
1418 });
1419 });
1420 };
1421 FireAuthService.prototype.signin = function (provider, passwordOrOptions) {
1422 return __awaiter(this, void 0, void 0, function () {
1423 var profile, cred, write, authProvider, ref, collection, document, _a, uid, emailVerified, error_1, err_1;
1424 return __generator(this, function (_b) {
1425 switch (_b.label) {
1426 case 0:
1427 this.store.setLoading(true);
1428 _b.label = 1;
1429 case 1:
1430 _b.trys.push([1, 29, , 30]);
1431 cred = void 0;
1432 write = this.db.firestore.batch();
1433 if (!!provider) return [3 /*break*/, 3];
1434 return [4 /*yield*/, this.auth.signInAnonymously()];
1435 case 2:
1436 cred = _b.sent();
1437 return [3 /*break*/, 11];
1438 case 3:
1439 if (!(passwordOrOptions && typeof provider === 'string' && typeof passwordOrOptions === 'string')) return [3 /*break*/, 5];
1440 return [4 /*yield*/, this.auth.signInWithEmailAndPassword(provider, passwordOrOptions)];
1441 case 4:
1442 cred = _b.sent();
1443 return [3 /*break*/, 11];
1444 case 5:
1445 if (!(typeof provider === 'object')) return [3 /*break*/, 7];
1446 return [4 /*yield*/, this.auth.signInWithPopup(provider)];
1447 case 6:
1448 cred = _b.sent();
1449 return [3 /*break*/, 11];
1450 case 7:
1451 if (!isFireAuthProvider(provider)) return [3 /*break*/, 9];
1452 authProvider = getAuthProvider(provider);
1453 return [4 /*yield*/, this.auth.signInWithPopup(authProvider)];
1454 case 8:
1455 cred = _b.sent();
1456 return [3 /*break*/, 11];
1457 case 9: return [4 /*yield*/, this.auth.signInWithCustomToken(provider)];
1458 case 10:
1459 cred = _b.sent();
1460 _b.label = 11;
1461 case 11:
1462 if (!cred.additionalUserInfo.isNewUser) return [3 /*break*/, 20];
1463 if (!this.onSignup) return [3 /*break*/, 13];
1464 return [4 /*yield*/, this.onSignup(cred, {})];
1465 case 12:
1466 _b.sent();
1467 _b.label = 13;
1468 case 13: return [4 /*yield*/, this.createProfile(cred.user)];
1469 case 14:
1470 profile = _b.sent();
1471 this.store.update({ profile: profile });
1472 ref = this.collection.doc(cred.user.uid).ref;
1473 write.set(ref, this.formatToFirestore(profile));
1474 if (!this.onCreate) return [3 /*break*/, 18];
1475 if (!(typeof passwordOrOptions === 'object')) return [3 /*break*/, 16];
1476 return [4 /*yield*/, this.onCreate(profile, { write: write, ctx: passwordOrOptions.ctx })];
1477 case 15:
1478 _b.sent();
1479 return [3 /*break*/, 18];
1480 case 16: return [4 /*yield*/, this.onCreate(profile, { write: write, ctx: {} })];
1481 case 17:
1482 _b.sent();
1483 _b.label = 18;
1484 case 18: return [4 /*yield*/, write.commit()];
1485 case 19:
1486 _b.sent();
1487 return [3 /*break*/, 26];
1488 case 20:
1489 _b.trys.push([20, 25, , 26]);
1490 collection = this.collection.doc(cred.user.uid);
1491 return [4 /*yield*/, collection.get().toPromise()];
1492 case 21:
1493 document = _b.sent();
1494 _a = cred.user, uid = _a.uid, emailVerified = _a.emailVerified;
1495 if (!document.exists) return [3 /*break*/, 22];
1496 profile = this.formatFromFirestore(document.data());
1497 return [3 /*break*/, 24];
1498 case 22: return [4 /*yield*/, this.createProfile(cred.user)];
1499 case 23:
1500 profile = _b.sent();
1501 write.set(collection.ref, this.formatToFirestore(profile));
1502 write.commit();
1503 _b.label = 24;
1504 case 24:
1505 this.store.update({ profile: profile, uid: uid, emailVerified: emailVerified });
1506 return [3 /*break*/, 26];
1507 case 25:
1508 error_1 = _b.sent();
1509 console.error(error_1);
1510 return [3 /*break*/, 26];
1511 case 26:
1512 if (!this.onSignin) return [3 /*break*/, 28];
1513 return [4 /*yield*/, this.onSignin(cred)];
1514 case 27:
1515 _b.sent();
1516 _b.label = 28;
1517 case 28:
1518 this.store.setLoading(false);
1519 return [2 /*return*/, cred];
1520 case 29:
1521 err_1 = _b.sent();
1522 this.store.setLoading(false);
1523 if (err_1.code === 'auth/operation-not-allowed') {
1524 console.warn('You tried to connect with a disabled auth provider. Enable it in Firebase console');
1525 }
1526 throw err_1;
1527 case 30: return [2 /*return*/];
1528 }
1529 });
1530 });
1531 };
1532 /** Signs out the current user and clear the store */
1533 FireAuthService.prototype.signOut = function () {
1534 return __awaiter(this, void 0, void 0, function () {
1535 return __generator(this, function (_a) {
1536 switch (_a.label) {
1537 case 0: return [4 /*yield*/, this.auth.signOut()];
1538 case 1:
1539 _a.sent();
1540 this.store.update(initialAuthState);
1541 if (!this.onSignout) return [3 /*break*/, 3];
1542 return [4 /*yield*/, this.onSignout()];
1543 case 2:
1544 _a.sent();
1545 _a.label = 3;
1546 case 3: return [2 /*return*/];
1547 }
1548 });
1549 });
1550 };
1551 return FireAuthService;
1552 }());
1553
1554 function CollectionGuardConfig(data) {
1555 return function (constructor) {
1556 Object.keys(data).forEach(function (key) { return (constructor[key] = data[key]); });
1557 };
1558 }
1559 var CollectionGuard = /** @class */ (function () {
1560 function CollectionGuard(service) {
1561 this.service = service;
1562 try {
1563 this.router = core.inject(router.Router);
1564 }
1565 catch (err) {
1566 throw new Error('CollectionGuard requires RouterModule to be imported');
1567 }
1568 }
1569 Object.defineProperty(CollectionGuard.prototype, "awaitSync", {
1570 // Can be override by the extended class
1571 /** Should the guard wait for connection to Firestore to complete */
1572 get: function () {
1573 return this.constructor['awaitSync'] || false;
1574 },
1575 enumerable: false,
1576 configurable: true
1577 });
1578 Object.defineProperty(CollectionGuard.prototype, "queryFn", {
1579 // Can be override by the extended class
1580 /** A queryFn to execute for sync */
1581 get: function () {
1582 return this.constructor['queryFn'];
1583 },
1584 enumerable: false,
1585 configurable: true
1586 });
1587 Object.defineProperty(CollectionGuard.prototype, "redirect", {
1588 // Can be override by the extended class
1589 /** The route to redirect to if you sync failed */
1590 get: function () {
1591 return this.constructor['redirect'];
1592 },
1593 enumerable: false,
1594 configurable: true
1595 });
1596 // Can be override by the extended class
1597 /** The method to subscribe to while route is active */
1598 CollectionGuard.prototype.sync = function (next, state) {
1599 var _a = next.data.queryFn, queryFn = _a === void 0 ? this.queryFn : _a;
1600 if (this.service instanceof FireAuthService) {
1601 return this.service.sync();
1602 }
1603 else if (this.service instanceof CollectionService) {
1604 return this.service.syncCollection(queryFn);
1605 }
1606 };
1607 CollectionGuard.prototype.canActivate = function (next, state) {
1608 var _this = this;
1609 var _a = next.data, _b = _a.redirect, redirect = _b === void 0 ? this.redirect : _b, _c = _a.awaitSync, awaitSync = _c === void 0 ? this.awaitSync : _c;
1610 return new Promise(function (res, rej) {
1611 if (awaitSync) {
1612 var unsubscribe_1 = new rxjs.Subject();
1613 _this.subscription = _this.sync(next, state).pipe(operators.takeUntil(unsubscribe_1)).subscribe({
1614 next: function (result) {
1615 if (result instanceof router.UrlTree) {
1616 return res(result);
1617 }
1618 switch (typeof result) {
1619 case 'string':
1620 unsubscribe_1.next();
1621 unsubscribe_1.complete();
1622 return res(_this.router.parseUrl(result));
1623 case 'boolean':
1624 return res(result);
1625 default:
1626 return res(true);
1627 }
1628 },
1629 error: function (err) {
1630 res(_this.router.parseUrl(redirect || ''));
1631 throw new Error(err);
1632 }
1633 });
1634 }
1635 else {
1636 _this.subscription = _this.sync(next, state).subscribe();
1637 res(true);
1638 }
1639 });
1640 };
1641 CollectionGuard.prototype.canDeactivate = function () {
1642 if (this.subscription) {
1643 this.subscription.unsubscribe();
1644 }
1645 return true;
1646 };
1647 return CollectionGuard;
1648 }());
1649
1650 /** @deprecated Use CollectionService instead */
1651 var CollectionGroupService = /** @class */ (function () {
1652 function CollectionGroupService(store) {
1653 this.store = store;
1654 try {
1655 this.db = core.inject(firestore.AngularFirestore);
1656 }
1657 catch (err) {
1658 throw new Error('CollectionGroupService requires AngularFirestore.');
1659 }
1660 }
1661 /**
1662 * Function triggered when getting data from firestore
1663 * @note should be overrided
1664 */
1665 CollectionGroupService.prototype.formatFromFirestore = function (entity) {
1666 return entity;
1667 };
1668 Object.defineProperty(CollectionGroupService.prototype, "idKey", {
1669 get: function () {
1670 if (this.store) {
1671 return this.store.idKey;
1672 }
1673 },
1674 enumerable: false,
1675 configurable: true
1676 });
1677 Object.defineProperty(CollectionGroupService.prototype, "resetOnUpdate", {
1678 get: function () {
1679 return this.constructor['resetOnUpdate'] || false;
1680 },
1681 enumerable: false,
1682 configurable: true
1683 });
1684 CollectionGroupService.prototype.syncCollection = function (queryOrOptions, storeOptions) {
1685 var _this = this;
1686 if (storeOptions === void 0) { storeOptions = { loading: true }; }
1687 var query;
1688 if (typeof queryOrOptions === 'function') {
1689 query = queryOrOptions;
1690 }
1691 else if (typeof queryOrOptions === 'object') {
1692 storeOptions = queryOrOptions;
1693 }
1694 var storeName = getStoreName(this.store, storeOptions);
1695 // reset has to happen before setLoading, otherwise it will also reset the loading state
1696 if (storeOptions.reset) {
1697 resetStore(storeName);
1698 }
1699 if (storeOptions.loading) {
1700 setLoading(storeName, true);
1701 }
1702 return this.db.collectionGroup(this.collectionId, query).stateChanges().pipe(akita.withTransaction(function (actions) { return syncStoreFromDocAction(storeName, actions, _this.idKey, _this.resetOnUpdate, function (entity) { return _this.formatFromFirestore(entity); }); }));
1703 };
1704 /** Return a snapshot of the collection group */
1705 CollectionGroupService.prototype.getValue = function (queryGroupFn) {
1706 return __awaiter(this, void 0, void 0, function () {
1707 var snapshot;
1708 var _this = this;
1709 return __generator(this, function (_a) {
1710 switch (_a.label) {
1711 case 0: return [4 /*yield*/, this.db.collectionGroup(this.collectionId, queryGroupFn).get().toPromise()];
1712 case 1:
1713 snapshot = _a.sent();
1714 return [2 /*return*/, snapshot.docs.map(function (doc) {
1715 var entity = doc.data();
1716 return _this.formatFromFirestore(entity);
1717 })];
1718 }
1719 });
1720 });
1721 };
1722 return CollectionGroupService;
1723 }());
1724
1725 function canWrite(write, roles) {
1726 return roles.some(function (role) { return role === write || role === 'write'; });
1727 }
1728 function canRead(read, roles) {
1729 return roles.some(function (role) { return role === read || role === 'write'; });
1730 }
1731 function hasRole(role, roles) {
1732 switch (role) {
1733 case 'write': return roles.includes('write');
1734 case 'create': return canWrite('create', roles);
1735 case 'delete': return canWrite('delete', roles);
1736 case 'update': return canWrite('update', roles);
1737 case 'read': return roles.includes('read');
1738 case 'get': return canRead('get', roles);
1739 case 'list': return canRead('list', roles);
1740 default: return false;
1741 }
1742 }
1743
1744 function shouldCancel(_a) {
1745 var validate = _a.validate, cancel = _a.cancel;
1746 return rxjs.race([validate.pipe(operators.map(function (_) { return false; })), cancel.pipe(operators.map(function (_) { return true; }))]);
1747 }
1748 function waitForCancel(_a) {
1749 var startWith = _a.startWith, endWith = _a.endWith, shouldValidate = _a.shouldValidate, shouldCancel = _a.shouldCancel;
1750 return __awaiter(this, void 0, void 0, function () {
1751 var cancelled;
1752 return __generator(this, function (_a) {
1753 switch (_a.label) {
1754 case 0:
1755 startWith();
1756 return [4 /*yield*/, rxjs.race([
1757 shouldValidate.pipe(operators.map(function (_) { return false; })),
1758 shouldCancel.pipe(operators.map(function (_) { return true; }))
1759 ]).toPromise()];
1760 case 1:
1761 cancelled = _a.sent();
1762 endWith(cancelled);
1763 return [2 /*return*/];
1764 }
1765 });
1766 });
1767 }
1768
1769 /**
1770 * @description Custom RxJs operator
1771 * @param redirectTo Route path to redirecto if collection is empty
1772 */
1773 function redirectIfEmpty(redirectTo) {
1774 return operators.map(function (actions) { return actions.length === 0 ? redirectTo : true; });
1775 }
1776
1777 function syncWithRouter(routerQuery) {
1778 var _this = this;
1779 if (!this['store'].resettable) {
1780 throw new Error("Store " + this['store'].storeName + " is required to be resettable for syncWithRouter to work.");
1781 }
1782 var pathParams = getPathParams(this.path);
1783 return routerQuery.selectParams().pipe(
1784 // Don't trigger change if params needed (and only them) haven't changed
1785 operators.distinctUntilChanged(function (old, next) {
1786 var paramsHaveChanged = !!pathParams.find(function (param) { return old[param] !== next[param]; });
1787 // reset store on every parameter change
1788 if (paramsHaveChanged) {
1789 _this['store'].reset();
1790 }
1791 return !paramsHaveChanged;
1792 }),
1793 // Need to filter because changes in params comes before canDeactivate
1794 operators.filter(function (params) { return pathParams.every(function (param) { return !!params[param]; }); }), operators.switchMap(function (params) { return _this.syncCollection({ params: params }); }), operators.share());
1795 }
1796
1797 var queryKeys = ['path', 'queryFn'];
1798 function getSubQueryKeys(query) {
1799 var keys = Object.keys(query);
1800 return keys.filter(function (key) { return !queryKeys.includes(key); });
1801 }
1802 function hasSubQueries(query) {
1803 return getSubQueryKeys(query).length > 0;
1804 }
1805 function getSubQuery(query, parent) {
1806 if (typeof query !== 'function') {
1807 return query;
1808 }
1809 return query(parent);
1810 }
1811 function isDocPath(path) {
1812 return path.split('/').length % 2 === 0;
1813 }
1814 /** Transform a path into a collection Query */
1815 function collection(path, queryFn) {
1816 return { path: path, queryFn: queryFn };
1817 }
1818 /** Transform a path into a doc query */
1819 function doc(path) {
1820 return { path: path };
1821 }
1822 /** Check if a value is a query */
1823 function isQuery(query) {
1824 if (typeof query === 'object' && query !== null) {
1825 return !!query['path'];
1826 }
1827 return false;
1828 }
1829
1830 /**
1831 * Sync the collection
1832 * @param this Uses this function with bind on a Collection Service
1833 * @param query The query to trigger
1834 */
1835 function syncQuery(query) {
1836 var _this = this;
1837 // If single query
1838 if (typeof query === 'string') {
1839 return isDocPath(query)
1840 ? this.syncDoc({ path: query })
1841 : this.syncCollection(query);
1842 }
1843 if (Array.isArray(query)) {
1844 return rxjs.combineLatest(query.map(function (oneQuery) { return syncQuery.call(_this, oneQuery); }));
1845 }
1846 if (!isQuery(query)) {
1847 throw new Error('Query should be either a path, a Query object or an array of Queries');
1848 }
1849 ////////////////
1850 // SUBQUERIES //
1851 ////////////////
1852 /** Listen on Child actions */
1853 var fromChildAction = function (actions, child) {
1854 var e_1, _a;
1855 var idKey = 'id'; // TODO: Improve how to
1856 var parentId = child.parentId, key = child.key;
1857 var _loop_1 = function (action) {
1858 var id = action.payload.doc.id;
1859 var data = action.payload.doc.data();
1860 switch (action.type) {
1861 case 'added': {
1862 _this['store'].update(parentId, function (entity) {
1863 var _a;
1864 return (_a = {},
1865 _a[key] = akita.arrayUpsert(entity[key] || [], id, data, idKey),
1866 _a);
1867 });
1868 break;
1869 }
1870 case 'removed': {
1871 _this['store'].update(parentId, function (entity) {
1872 var _a;
1873 return (_a = {},
1874 _a[key] = akita.arrayRemove(entity[key], id, idKey),
1875 _a);
1876 });
1877 break;
1878 }
1879 case 'modified': {
1880 _this['store'].update(parentId, function (entity) {
1881 var _a;
1882 return (_a = {},
1883 _a[key] = akita.arrayUpdate(entity[key], id, data, idKey),
1884 _a);
1885 });
1886 }
1887 }
1888 };
1889 try {
1890 for (var actions_1 = __values(actions), actions_1_1 = actions_1.next(); !actions_1_1.done; actions_1_1 = actions_1.next()) {
1891 var action = actions_1_1.value;
1892 _loop_1(action);
1893 }
1894 }
1895 catch (e_1_1) { e_1 = { error: e_1_1 }; }
1896 finally {
1897 try {
1898 if (actions_1_1 && !actions_1_1.done && (_a = actions_1.return)) _a.call(actions_1);
1899 }
1900 finally { if (e_1) throw e_1.error; }
1901 }
1902 };
1903 /**
1904 * Stay in sync a subquery of the collection
1905 * @param subQuery The sub query based on the entity
1906 * @param child An object that describe the parent
1907 */
1908 var syncSubQuery = function (subQuery, child) {
1909 var _a;
1910 var parentId = child.parentId, key = child.key;
1911 // If it's a static value
1912 // TODO : Check if subQuery is a string ???
1913 if (!isQuery(subQuery)) {
1914 var update = _this['store'].update(parentId, (_a = {}, _a[key] = subQuery, _a));
1915 return rxjs.of(update);
1916 }
1917 if (Array.isArray(subQuery)) {
1918 var syncQueries = subQuery.map(function (oneQuery) {
1919 if (isQuery(subQuery)) {
1920 var id_1 = getIdAndPath({ path: subQuery.path });
1921 return _this['db'].doc(subQuery.path).valueChanges().pipe(operators.tap(function (childDoc) {
1922 _this['store'].update(parentId, function (entity) {
1923 var _a;
1924 return (_a = {},
1925 _a[key] = akita.arrayAdd(entity[key], id_1, childDoc),
1926 _a);
1927 });
1928 }));
1929 }
1930 return syncSubQuery(oneQuery, child);
1931 });
1932 return rxjs.combineLatest(syncQueries);
1933 }
1934 if (typeof subQuery !== 'object') {
1935 throw new Error('Query should be either a path, a Query object or an array of Queries');
1936 }
1937 // Sync subquery
1938 if (isDocPath(subQuery.path)) {
1939 return _this['db'].doc(subQuery.path).valueChanges().pipe(operators.tap(function (children) {
1940 var _a;
1941 return _this['store'].update(parentId, (_a = {}, _a[key] = children, _a));
1942 }));
1943 }
1944 else {
1945 return _this['db'].collection(subQuery.path, subQuery.queryFn)
1946 .stateChanges()
1947 .pipe(akita.withTransaction(function (actions) { return fromChildAction(actions, child); }));
1948 }
1949 };
1950 /**
1951 * Get in sync with all the subqueries
1952 * @param subQueries A map of all the subqueries
1953 * @param parent The parent entity to attach the children to
1954 */
1955 var syncAllSubQueries = function (subQueries, parent) {
1956 var obs = Object.keys(subQueries)
1957 .filter(function (key) { return key !== 'path' && key !== 'queryFn'; })
1958 .map(function (key) {
1959 var queryLike = getSubQuery(subQueries[key], parent);
1960 var child = { key: key, parentId: parent[_this.idKey] };
1961 return syncSubQuery(queryLike, child);
1962 });
1963 return rxjs.combineLatest(obs);
1964 };
1965 ////////////////
1966 // MAIN QUERY //
1967 ////////////////
1968 /** Listen on action with child queries */
1969 var fromActionWithChild = function (actions, mainQuery, subscriptions) {
1970 var e_2, _a, _b;
1971 try {
1972 for (var actions_2 = __values(actions), actions_2_1 = actions_2.next(); !actions_2_1.done; actions_2_1 = actions_2.next()) {
1973 var action = actions_2_1.value;
1974 var id = action.payload.doc.id;
1975 var data = action.payload.doc.data();
1976 switch (action.type) {
1977 case 'added': {
1978 var entity = _this.formatFromFirestore(Object.assign((_b = {}, _b[_this.idKey] = id, _b), data));
1979 _this['store'].upsert(id, entity);
1980 subscriptions[id] = syncAllSubQueries(mainQuery, entity).subscribe();
1981 break;
1982 }
1983 case 'removed': {
1984 _this['store'].remove(id);
1985 subscriptions[id].unsubscribe();
1986 delete subscriptions[id];
1987 break;
1988 }
1989 case 'modified': {
1990 _this['store'].update(id, data);
1991 }
1992 }
1993 }
1994 }
1995 catch (e_2_1) { e_2 = { error: e_2_1 }; }
1996 finally {
1997 try {
1998 if (actions_2_1 && !actions_2_1.done && (_a = actions_2.return)) _a.call(actions_2);
1999 }
2000 finally { if (e_2) throw e_2.error; }
2001 }
2002 };
2003 var path = query.path, queryFn = query.queryFn;
2004 if (isDocPath(path)) {
2005 var id_2 = getIdAndPath({ path: path }).id;
2006 var subscription_1;
2007 return this['db'].doc(path).valueChanges().pipe(operators.tap(function (entity) {
2008 _this['store'].upsert(id_2, _this.formatFromFirestore(Object.assign({ id: id_2 }, entity)));
2009 if (!subscription_1) { // Subscribe only the first time
2010 subscription_1 = syncAllSubQueries(query, entity).subscribe();
2011 }
2012 }),
2013 // Stop subscription
2014 operators.finalize(function () { return subscription_1.unsubscribe(); }));
2015 }
2016 else {
2017 var subscriptions_1 = {};
2018 return this['db'].collection(path, queryFn)
2019 .stateChanges()
2020 .pipe(akita.withTransaction(function (actions) { return fromActionWithChild(actions, query, subscriptions_1); }),
2021 // Stop all subscriptions
2022 operators.finalize(function () { return Object.keys(subscriptions_1).forEach(function (id) {
2023 subscriptions_1[id].unsubscribe();
2024 delete subscriptions_1[id];
2025 }); }));
2026 }
2027 }
2028
2029 function awaitSyncQuery(query) {
2030 var _this = this;
2031 return queryChanges.call(this, query).pipe(operators.tap(function (entities) {
2032 Array.isArray(entities)
2033 ? _this['store'].upsertMany(entities)
2034 : _this['store'].upsert(entities[_this.idKey], entities);
2035 }));
2036 }
2037 /**
2038 * Listen on the changes of the documents in the query
2039 * @param query A query object to listen to
2040 */
2041 function queryChanges(query) {
2042 var _this = this;
2043 return awaitQuery.call(this, query).pipe(operators.map(function (entities) {
2044 return Array.isArray(entities)
2045 ? entities.map(function (e) { return _this.formatFromFirestore(e); })
2046 : _this.formatFromFirestore(entities);
2047 }));
2048 }
2049 function awaitQuery(query) {
2050 var _this = this;
2051 // If single query
2052 if (typeof query === 'string') {
2053 return isDocPath(query)
2054 ? this['db'].doc(query).valueChanges()
2055 : this['db'].collection(query).valueChanges({ idField: this.idKey });
2056 }
2057 if (Array.isArray(query)) {
2058 return !!query.length
2059 ? rxjs.combineLatest(query.map(function (oneQuery) { return awaitSyncQuery.call(_this, oneQuery); }))
2060 : rxjs.of(query);
2061 }
2062 if (!isQuery(query)) {
2063 return rxjs.of(query);
2064 }
2065 /**
2066 * Get the entity of one subquery
2067 * @param subQuery The subquery function or value
2068 * @param entity The parent entity
2069 */
2070 var syncSubQuery = function (subQueryFn, entity) {
2071 if (!subQueryFn) {
2072 return rxjs.throwError("Query failed");
2073 }
2074 if (typeof subQueryFn !== 'function') {
2075 return rxjs.of(subQueryFn);
2076 }
2077 return awaitQuery.call(_this, subQueryFn(entity));
2078 };
2079 /**
2080 * Get all the Entities of all subqueries
2081 * @param parentQuery The parent Query
2082 * @param entity The parent Entity
2083 */
2084 var getAllSubQueries = function (parentQuery, entity) {
2085 if (!entity) {
2086 return rxjs.throwError("Nothing found at path : " + parentQuery.path);
2087 }
2088 // There is no subqueries return the entity
2089 if (!hasSubQueries(parentQuery)) {
2090 return rxjs.of(entity);
2091 }
2092 // Get all subquery keys
2093 var subQueryKeys = getSubQueryKeys(query);
2094 // For each key get the subquery
2095 var subQueries$ = subQueryKeys.map(function (key) {
2096 return syncSubQuery(parentQuery[key], entity).pipe(operators.tap(function (subentity) { return entity[key] = subentity; }));
2097 });
2098 return !!subQueries$.length
2099 ? rxjs.combineLatest(subQueries$).pipe(operators.map(function () { return entity; }))
2100 : rxjs.of(entity);
2101 };
2102 // IF DOCUMENT
2103 var path = query.path, queryFn = query.queryFn;
2104 if (isDocPath(path)) {
2105 var id_1 = getIdAndPath({ path: path }).id;
2106 return this['db'].doc(path).valueChanges().pipe(operators.switchMap(function (entity) { return getAllSubQueries(query, entity); }), operators.map(function (entity) { return (Object.assign({ id: id_1 }, entity)); }));
2107 }
2108 // IF COLLECTION
2109 return this['db'].collection(path, queryFn)
2110 .valueChanges({ idField: this.idKey })
2111 .pipe(operators.switchMap(function (entities) {
2112 var entities$ = entities.map(function (entity) { return getAllSubQueries(query, entity); });
2113 return entities$.length ? rxjs.combineLatest(entities$) : rxjs.of([]);
2114 }));
2115 }
2116
2117 /**
2118 * @description calls cloud function
2119 * @param functions you want to make callable
2120 * @param name of the cloud function
2121 * @param params you want to set
2122 */
2123 function callFunction(functions, name, params) {
2124 return __awaiter(this, void 0, void 0, function () {
2125 var callable;
2126 return __generator(this, function (_a) {
2127 callable = functions.httpsCallable(name);
2128 return [2 /*return*/, callable(params).toPromise()];
2129 });
2130 });
2131 }
2132
2133 /*
2134 * Public API Surface of akita-ng-fire
2135 */
2136
2137 /**
2138 * Generated bundle index. Do not edit.
2139 */
2140
2141 exports.CollectionConfig = CollectionConfig;
2142 exports.CollectionGroupService = CollectionGroupService;
2143 exports.CollectionGuard = CollectionGuard;
2144 exports.CollectionGuardConfig = CollectionGuardConfig;
2145 exports.CollectionService = CollectionService;
2146 exports.FireAuthService = FireAuthService;
2147 exports.authProviders = authProviders;
2148 exports.awaitQuery = awaitQuery;
2149 exports.awaitSyncQuery = awaitSyncQuery;
2150 exports.callFunction = callFunction;
2151 exports.canRead = canRead;
2152 exports.canWrite = canWrite;
2153 exports.collection = collection;
2154 exports.doc = doc;
2155 exports.getAuthProvider = getAuthProvider;
2156 exports.getCustomClaims = getCustomClaims;
2157 exports.getIdAndPath = getIdAndPath;
2158 exports.getPathParams = getPathParams;
2159 exports.getSubQuery = getSubQuery;
2160 exports.getSubQueryKeys = getSubQueryKeys;
2161 exports.hasRole = hasRole;
2162 exports.hasSubQueries = hasSubQueries;
2163 exports.initialAuthState = initialAuthState;
2164 exports.isDocPath = isDocPath;
2165 exports.isFireAuthProvider = isFireAuthProvider;
2166 exports.isQuery = isQuery;
2167 exports.isTransaction = isTransaction;
2168 exports.pathWithParams = pathWithParams;
2169 exports.queryChanges = queryChanges;
2170 exports.queryKeys = queryKeys;
2171 exports.redirectIfEmpty = redirectIfEmpty;
2172 exports.removeStoreEntity = removeStoreEntity;
2173 exports.resetStore = resetStore;
2174 exports.setActive = setActive;
2175 exports.setLoading = setLoading;
2176 exports.shouldCancel = shouldCancel;
2177 exports.syncQuery = syncQuery;
2178 exports.syncStoreFromDocAction = syncStoreFromDocAction;
2179 exports.syncStoreFromDocActionSnapshot = syncStoreFromDocActionSnapshot;
2180 exports.syncWithRouter = syncWithRouter;
2181 exports.updateStoreEntity = updateStoreEntity;
2182 exports.upsertStoreEntity = upsertStoreEntity;
2183 exports.waitForCancel = waitForCancel;
2184
2185 Object.defineProperty(exports, '__esModule', { value: true });
2186
2187})));
2188//# sourceMappingURL=akita-ng-fire.umd.js.map