@dereekb/oauth-resource 14.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +185 -0
  3. package/express/index.d.ts +1 -0
  4. package/express/index.esm.js +321 -0
  5. package/express/package.json +29 -0
  6. package/express/src/index.d.ts +1 -0
  7. package/express/src/lib/bearer.middleware.d.ts +78 -0
  8. package/express/src/lib/index.d.ts +2 -0
  9. package/express/src/lib/well-known.router.d.ts +35 -0
  10. package/firebase/index.d.ts +1 -0
  11. package/firebase/index.esm.js +1924 -0
  12. package/firebase/package.json +27 -0
  13. package/firebase/src/index.d.ts +1 -0
  14. package/firebase/src/lib/firestore/firestore.sdk-identity.d.ts +165 -0
  15. package/firebase/src/lib/firestore/index.d.ts +1 -0
  16. package/firebase/src/lib/index.d.ts +2 -0
  17. package/firebase/src/lib/session/firebase-client.config.d.ts +86 -0
  18. package/firebase/src/lib/session/firebase-user-session.d.ts +223 -0
  19. package/firebase/src/lib/session/firebase-user-session.pool.d.ts +168 -0
  20. package/firebase/src/lib/session/firestore-session.cache.d.ts +79 -0
  21. package/firebase/src/lib/session/firestore-session.client.d.ts +149 -0
  22. package/firebase/src/lib/session/index.d.ts +5 -0
  23. package/index.d.ts +1 -0
  24. package/index.esm.js +1066 -0
  25. package/package.json +53 -0
  26. package/src/index.d.ts +1 -0
  27. package/src/lib/auth/index.d.ts +1 -0
  28. package/src/lib/auth/oauth.resource.auth.d.ts +55 -0
  29. package/src/lib/challenge/bearer.challenge.d.ts +73 -0
  30. package/src/lib/challenge/index.d.ts +1 -0
  31. package/src/lib/error/index.d.ts +1 -0
  32. package/src/lib/error/oauth.resource.error.d.ts +76 -0
  33. package/src/lib/index.d.ts +6 -0
  34. package/src/lib/issuer/index.d.ts +1 -0
  35. package/src/lib/issuer/issuer.profile.d.ts +98 -0
  36. package/src/lib/metadata/index.d.ts +1 -0
  37. package/src/lib/metadata/protected-resource.metadata.d.ts +64 -0
  38. package/src/lib/verify/index.d.ts +1 -0
  39. package/src/lib/verify/verify.bearer.d.ts +95 -0
@@ -0,0 +1,1924 @@
1
+ import { createRequire } from 'node:module';
2
+ import { dirname } from 'node:path';
3
+ import { Firestore, getFirestore, connectFirestoreEmulator } from 'firebase/firestore';
4
+ import { initializeApp, deleteApp, getApps } from 'firebase/app';
5
+ import { initializeAppCheck, CustomProvider } from 'firebase/app-check';
6
+ import { signInWithCustomToken, getAuth, connectAuthEmulator } from 'firebase/auth';
7
+ import { clientFirebaseFirestoreContextFactory } from '@dereekb/firebase';
8
+ import { BaseError } from 'make-error';
9
+ import { expirationDetails } from '@dereekb/util';
10
+
11
+ function _define_property$3(obj, key, value) {
12
+ if (key in obj) {
13
+ Object.defineProperty(obj, key, {
14
+ value: value,
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true
18
+ });
19
+ } else obj[key] = value;
20
+ return obj;
21
+ }
22
+ function _instanceof$2(left, right) {
23
+ "@swc/helpers - instanceof";
24
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
25
+ return !!right[Symbol.hasInstance](left);
26
+ } else return left instanceof right;
27
+ }
28
+ function _object_spread$2(target) {
29
+ for(var i = 1; i < arguments.length; i++){
30
+ var source = arguments[i] != null ? arguments[i] : {};
31
+ var ownKeys = Object.keys(source);
32
+ if (typeof Object.getOwnPropertySymbols === "function") {
33
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
34
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
35
+ }));
36
+ }
37
+ ownKeys.forEach(function(key) {
38
+ _define_property$3(target, key, source[key]);
39
+ });
40
+ }
41
+ return target;
42
+ }
43
+ function ownKeys$2(object, enumerableOnly) {
44
+ var keys = Object.keys(object);
45
+ if (Object.getOwnPropertySymbols) {
46
+ var symbols = Object.getOwnPropertySymbols(object);
47
+ keys.push.apply(keys, symbols);
48
+ }
49
+ return keys;
50
+ }
51
+ function _object_spread_props$2(target, source) {
52
+ source = source != null ? source : {};
53
+ if (Object.getOwnPropertyDescriptors) Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
54
+ else {
55
+ ownKeys$2(Object(source)).forEach(function(key) {
56
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
57
+ });
58
+ }
59
+ return target;
60
+ }
61
+ /**
62
+ * The `drivers.firestoreDriverIdentifier` a client-SDK `FirestoreContext` reports.
63
+ *
64
+ * `clientFirebaseFirestoreContextFactory` stamps this; the server's
65
+ * `googleCloudFirestoreContextFactory` stamps `@google-cloud/firestore` instead. Comparing against
66
+ * it is how {@link inspectFirebaseClientFirestoreIdentity} tells an admin-SDK context that reached
67
+ * the client-SDK read path apart from a genuinely broken client one.
68
+ */ var CLIENT_FIRESTORE_DRIVER_IDENTIFIER = '@firebase/firestore';
69
+ /**
70
+ * Default {@link InspectFirebaseClientFirestoreIdentityInput.consumerName}.
71
+ */ var DEFAULT_FIRESTORE_IDENTITY_CONSUMER_NAME = '@dereekb/oauth-resource/firebase';
72
+ /**
73
+ * Checks that a session's Firestore handle is one THIS copy of the client SDK will accept, and
74
+ * reports where every consumer resolved the SDK from.
75
+ *
76
+ * Exists because `collection()` refuses any non-`Firestore` first argument with one message —
77
+ * `Expected first argument to collection() to be a CollectionReference, a DocumentReference or
78
+ * FirebaseFirestore` — that names neither the model, nor the collection, nor which of the four
79
+ * distinct causes ({@link FirebaseClientFirestoreIdentityProblem}) produced it.
80
+ *
81
+ * Lives in this package rather than in a consumer because the `instanceof Firestore` brand check
82
+ * must be performed by the module that CONSTRUCTS the handle, and that module is
83
+ * `openFirebaseUserSession`.
84
+ *
85
+ * The `instanceof` test is the load-bearing one and it is deliberately NOT structural: an identity
86
+ * check across the package boundary is the only thing that can detect a duplicated
87
+ * `@firebase/firestore`, which is exactly what a structural check would hide.
88
+ *
89
+ * @param input - The session's `firestoreContext`, when one was built, plus the consumer's
90
+ * resolution root.
91
+ * @returns The provenance + brand-check report.
92
+ */ function inspectFirebaseClientFirestoreIdentity(input) {
93
+ var _input_firestoreContext, _input_firestoreContext_drivers, _input_firestoreContext1;
94
+ var firestore = (_input_firestoreContext = input.firestoreContext) === null || _input_firestoreContext === void 0 ? void 0 : _input_firestoreContext.firestore;
95
+ var driverIdentifier = (_input_firestoreContext1 = input.firestoreContext) === null || _input_firestoreContext1 === void 0 ? void 0 : (_input_firestoreContext_drivers = _input_firestoreContext1.drivers) === null || _input_firestoreContext_drivers === void 0 ? void 0 : _input_firestoreContext_drivers.firestoreDriverIdentifier;
96
+ var firestoreDriverIdentifier = typeof driverIdentifier === 'string' ? driverIdentifier : undefined;
97
+ var firestorePresent = firestore != null;
98
+ var firestoreIsSdkInstance = _instanceof$2(firestore, Firestore);
99
+ var firestoreConstructor = firestoreConstructorName(firestore);
100
+ var modules = resolveFirebaseClientFirestoreSdkModules(input.consumerRequire);
101
+ var sdkDuplicated = modules.sdkFromConsumer.packageDir != null && modules.sdkFromDbxFirebase.packageDir != null && modules.sdkFromConsumer.packageDir !== modules.sdkFromDbxFirebase.packageDir;
102
+ var problem;
103
+ if (!firestorePresent) {
104
+ problem = 'no-firestore-handle';
105
+ } else if (firestoreDriverIdentifier != null && firestoreDriverIdentifier !== CLIENT_FIRESTORE_DRIVER_IDENTIFIER) {
106
+ problem = 'unexpected-driver';
107
+ } else if (!firestoreIsSdkInstance) {
108
+ problem = sdkDuplicated ? 'duplicated-firestore-sdk' : 'foreign-firestore-instance';
109
+ }
110
+ return _object_spread_props$2(_object_spread$2(_object_spread_props$2(_object_spread$2({
111
+ ok: problem == null
112
+ }, problem == null ? {} : {
113
+ problem: problem
114
+ }), {
115
+ firestorePresent: firestorePresent,
116
+ firestoreIsSdkInstance: firestoreIsSdkInstance
117
+ }), firestoreConstructor == null ? {} : {
118
+ firestoreConstructor: firestoreConstructor
119
+ }, firestoreDriverIdentifier == null ? {} : {
120
+ firestoreDriverIdentifier: firestoreDriverIdentifier
121
+ }, modules), {
122
+ sdkDuplicated: sdkDuplicated
123
+ });
124
+ }
125
+ /**
126
+ * The actionable next step for a failed {@link inspectFirebaseClientFirestoreIdentity}, keyed on
127
+ * which problem was found.
128
+ *
129
+ * Each branch names the fix rather than the symptom — a raw SDK sentence sends an operator looking at
130
+ * security rules and App Check, neither of which is ever the cause here.
131
+ *
132
+ * @param report - The report to describe.
133
+ * @param overrides - Per-problem replacement text, for a consumer whose operator-facing wording
134
+ * names its own remediation (`re-run with --verbose`, `rebuild the CLI`, …).
135
+ * @param consumerName - Names the consumer in the duplicated-SDK text. Defaults to
136
+ * {@link DEFAULT_FIRESTORE_IDENTITY_CONSUMER_NAME}.
137
+ * @returns The suggestion, or `undefined` when the report passed.
138
+ *
139
+ * @__NO_SIDE_EFFECTS__
140
+ */ function firebaseClientFirestoreIdentitySuggestion(report, overrides, consumerName) {
141
+ var consumer = consumerName || DEFAULT_FIRESTORE_IDENTITY_CONSUMER_NAME;
142
+ var override = report.problem == null ? undefined : overrides === null || overrides === void 0 ? void 0 : overrides[report.problem];
143
+ var result;
144
+ if (override == null) {
145
+ switch(report.problem){
146
+ case 'no-firestore-handle':
147
+ result = "The session produced no Firestore handle, so every collection reference built from it fails. This is a wiring fault, not a rules or App Check fault — verify the handshake completed and that ".concat(consumer, " resolves ONE `@dereekb/firebase`.");
148
+ break;
149
+ case 'unexpected-driver':
150
+ result = 'The `FirestoreContext` reports driver "'.concat(report.firestoreDriverIdentifier, '", not "').concat(CLIENT_FIRESTORE_DRIVER_IDENTIFIER, '". An admin-SDK context reached the client-SDK read path — build the context with `clientFirebaseFirestoreContextFactory`, not `googleCloudFirestoreContextFactory`.');
151
+ break;
152
+ case 'duplicated-firestore-sdk':
153
+ result = "Two copies of `@firebase/firestore` are loaded — ".concat(consumer, " resolves ").concat(report.sdkFromConsumer.packageDir, ", `@dereekb/firebase` resolves ").concat(report.sdkFromDbxFirebase.packageDir, " — so the handle one minted fails the other's brand check. Dedupe the install (`npm ls @firebase/firestore`) and rebuild.");
154
+ break;
155
+ case 'foreign-firestore-instance':
156
+ var _report_firestoreConstructor;
157
+ result = "The session's handle (`".concat((_report_firestoreConstructor = report.firestoreConstructor) !== null && _report_firestoreConstructor !== void 0 ? _report_firestoreConstructor : 'unknown', "`) is not an instance of the loaded SDK's `Firestore`, and only one copy of `@firebase/firestore` is installed. The likeliest cause is a STALE build artifact running against a newer `@dereekb/*` in `node_modules` — rebuild ").concat(consumer, "'s consumer and re-run.");
158
+ break;
159
+ default:
160
+ result = undefined;
161
+ break;
162
+ }
163
+ } else {
164
+ result = override;
165
+ }
166
+ return result;
167
+ }
168
+ // MARK: Internal
169
+ /**
170
+ * The handle's constructor name, when it has one.
171
+ *
172
+ * @param firestore - The candidate handle.
173
+ * @returns The constructor name, or `undefined`.
174
+ */ function firestoreConstructorName(firestore) {
175
+ var _firestore_constructor;
176
+ var name = firestore == null ? undefined : (_firestore_constructor = firestore.constructor) === null || _firestore_constructor === void 0 ? void 0 : _firestore_constructor.name;
177
+ return typeof name === 'string' ? name : undefined;
178
+ }
179
+ /**
180
+ * Resolves `@firebase/firestore` from both the consumer and `@dereekb/firebase`, plus the `firebase`
181
+ * umbrella version.
182
+ *
183
+ * `createRequire` rather than `import.meta.resolve` because the resolution has to be performed
184
+ * relative to ANOTHER package's location (`@dereekb/firebase`'s entry file) to answer the question
185
+ * that matters — whether the two consumers see the same copy.
186
+ *
187
+ * @param consumerRequire - The consumer's own `require`. Defaults to this package's.
188
+ * @returns The two module identities and the umbrella version, each degrading to an `error` field
189
+ * rather than throwing: this runs inside a diagnostic, and a failed resolution is itself a finding.
190
+ */ function resolveFirebaseClientFirestoreSdkModules(consumerRequire) {
191
+ var requireFromConsumer = consumerRequire !== null && consumerRequire !== void 0 ? consumerRequire : createRequire(import.meta.url);
192
+ var sdkFromConsumer = resolveFirestoreSdkIdentity(requireFromConsumer);
193
+ var sdkFromDbxFirebase;
194
+ try {
195
+ sdkFromDbxFirebase = resolveFirestoreSdkIdentity(createRequire(requireFromConsumer.resolve('@dereekb/firebase')));
196
+ } catch (e) {
197
+ sdkFromDbxFirebase = {
198
+ error: _instanceof$2(e, Error) ? e.message : String(e)
199
+ };
200
+ }
201
+ var firebaseVersion;
202
+ try {
203
+ firebaseVersion = readPackageVersion(requireFromConsumer, 'firebase/package.json');
204
+ } catch (unused) {
205
+ // the umbrella version is context for the report, not a finding on its own
206
+ firebaseVersion = undefined;
207
+ }
208
+ return _object_spread$2({
209
+ sdkFromConsumer: sdkFromConsumer,
210
+ sdkFromDbxFirebase: sdkFromDbxFirebase
211
+ }, firebaseVersion == null ? {} : {
212
+ firebaseVersion: firebaseVersion
213
+ });
214
+ }
215
+ /**
216
+ * Resolves `@firebase/firestore`'s package directory + version through one `require`.
217
+ *
218
+ * Resolves `package.json` rather than the package entry because the entry differs per export
219
+ * condition (`index.node.cjs.js` vs `index.node.mjs`), which would report two copies where there is
220
+ * one. The `package.json` path is condition-independent, so its directory is a stable identity.
221
+ *
222
+ * @param resolver - The `require` to resolve through.
223
+ * @returns The module identity, with `error` set when resolution failed.
224
+ */ function resolveFirestoreSdkIdentity(resolver) {
225
+ var result;
226
+ try {
227
+ var packageJsonPath = resolver.resolve('@firebase/firestore/package.json');
228
+ var version = readPackageVersion(resolver, '@firebase/firestore/package.json');
229
+ result = _object_spread$2({
230
+ packageDir: dirname(packageJsonPath)
231
+ }, version == null ? {} : {
232
+ version: version
233
+ });
234
+ } catch (e) {
235
+ result = {
236
+ error: _instanceof$2(e, Error) ? e.message : String(e)
237
+ };
238
+ }
239
+ return result;
240
+ }
241
+ /**
242
+ * Reads a resolvable `package.json`'s `version`.
243
+ *
244
+ * @param resolver - The `require` to load through.
245
+ * @param specifier - The `package.json` specifier.
246
+ * @returns The version string, or `undefined` when absent.
247
+ */ function readPackageVersion(resolver, specifier) {
248
+ var contents = resolver(specifier);
249
+ return typeof contents.version === 'string' ? contents.version : undefined;
250
+ }
251
+
252
+ /**
253
+ * Default host used for Firebase emulator connections when a {@link FirebaseClientEmulatorsConfig}
254
+ * omits one.
255
+ */ var DEFAULT_FIREBASE_CLIENT_EMULATOR_HOST = 'localhost';
256
+ /**
257
+ * Returns true when the config carries the minimum Firebase client values needed to open a
258
+ * user-scoped Firestore session.
259
+ *
260
+ * Deliberately a standalone predicate rather than a field of a wider "config is complete" check: a
261
+ * host's Firebase config is optional, and folding it into a general completeness check would break
262
+ * every consumer that only talks to the HTTP API.
263
+ *
264
+ * @param firebase - The Firebase client config, if any.
265
+ * @returns `true` when `apiKey`, `projectId`, and `appId` are all present and non-empty.
266
+ */ function isFirebaseClientConfigComplete(firebase) {
267
+ return Boolean((firebase === null || firebase === void 0 ? void 0 : firebase.apiKey) && (firebase === null || firebase === void 0 ? void 0 : firebase.projectId) && (firebase === null || firebase === void 0 ? void 0 : firebase.appId));
268
+ }
269
+ /**
270
+ * Returns true when the config's emulator targets are present and active.
271
+ *
272
+ * @param firebase - The Firebase client config, if any.
273
+ * @returns `true` when emulators are configured and not explicitly disabled.
274
+ */ function firebaseClientEmulatorsInUse(firebase) {
275
+ var emulators = firebase === null || firebase === void 0 ? void 0 : firebase.emulators;
276
+ return Boolean(emulators && emulators.useEmulators !== false && (emulators.authPort != null || emulators.firestorePort != null));
277
+ }
278
+
279
+ function _assert_this_initialized(self) {
280
+ if (self === void 0) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
281
+ return self;
282
+ }
283
+ function asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, key, arg) {
284
+ try {
285
+ var info = gen[key](arg);
286
+ var value = info.value;
287
+ } catch (error) {
288
+ reject(error);
289
+ return;
290
+ }
291
+ if (info.done) resolve(value);
292
+ else Promise.resolve(value).then(_next, _throw);
293
+ }
294
+ function _async_to_generator$2(fn) {
295
+ return function() {
296
+ var self = this, args = arguments;
297
+ return new Promise(function(resolve, reject) {
298
+ var gen = fn.apply(self, args);
299
+ function _next(value) {
300
+ asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, "next", value);
301
+ }
302
+ function _throw(err) {
303
+ asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, "throw", err);
304
+ }
305
+ _next(undefined);
306
+ });
307
+ };
308
+ }
309
+ function _call_super(_this, derived, args) {
310
+ derived = _get_prototype_of(derived);
311
+ return _possible_constructor_return(_this, _is_native_reflect_construct() ? Reflect.construct(derived, args || [], _get_prototype_of(_this).constructor) : derived.apply(_this, args));
312
+ }
313
+ function _class_call_check(instance, Constructor) {
314
+ if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
315
+ }
316
+ function _define_property$2(obj, key, value) {
317
+ if (key in obj) {
318
+ Object.defineProperty(obj, key, {
319
+ value: value,
320
+ enumerable: true,
321
+ configurable: true,
322
+ writable: true
323
+ });
324
+ } else obj[key] = value;
325
+ return obj;
326
+ }
327
+ function _get_prototype_of(o) {
328
+ _get_prototype_of = Object.setPrototypeOf ? Object.getPrototypeOf : function getPrototypeOf(o) {
329
+ return o.__proto__ || Object.getPrototypeOf(o);
330
+ };
331
+ return _get_prototype_of(o);
332
+ }
333
+ function _inherits(subClass, superClass) {
334
+ if (typeof superClass !== "function" && superClass !== null) {
335
+ throw new TypeError("Super expression must either be null or a function");
336
+ }
337
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
338
+ constructor: {
339
+ value: subClass,
340
+ writable: true,
341
+ configurable: true
342
+ }
343
+ });
344
+ if (superClass) _set_prototype_of(subClass, superClass);
345
+ }
346
+ function _instanceof$1(left, right) {
347
+ "@swc/helpers - instanceof";
348
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
349
+ return !!right[Symbol.hasInstance](left);
350
+ } else return left instanceof right;
351
+ }
352
+ function _is_native_reflect_construct() {
353
+ try {
354
+ var result = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
355
+ } catch (_) {}
356
+ return (_is_native_reflect_construct = function() {
357
+ return !!result;
358
+ })();
359
+ }
360
+ function _possible_constructor_return(self, call) {
361
+ if (call && (_type_of(call) === "object" || typeof call === "function")) return call;
362
+ return _assert_this_initialized(self);
363
+ }
364
+ function _set_prototype_of(o, p) {
365
+ _set_prototype_of = Object.setPrototypeOf || function setPrototypeOf(o, p) {
366
+ o.__proto__ = p;
367
+ return o;
368
+ };
369
+ return _set_prototype_of(o, p);
370
+ }
371
+ function _ts_generator$2(thisArg, body) {
372
+ var f, y, t, _ = {
373
+ label: 0,
374
+ sent: function() {
375
+ if (t[0] & 1) throw t[1];
376
+ return t[1];
377
+ },
378
+ trys: [],
379
+ ops: []
380
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
381
+ return d(g, "next", {
382
+ value: verb(0)
383
+ }), d(g, "throw", {
384
+ value: verb(1)
385
+ }), d(g, "return", {
386
+ value: verb(2)
387
+ }), typeof Symbol === "function" && d(g, Symbol.iterator, {
388
+ value: function() {
389
+ return this;
390
+ }
391
+ }), g;
392
+ function verb(n) {
393
+ return function(v) {
394
+ return step([
395
+ n,
396
+ v
397
+ ]);
398
+ };
399
+ }
400
+ function step(op) {
401
+ if (f) throw new TypeError("Generator is already executing.");
402
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
403
+ 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) return t;
404
+ if (y = 0, t) op = [
405
+ op[0] & 2,
406
+ t.value
407
+ ];
408
+ switch(op[0]){
409
+ case 0:
410
+ case 1:
411
+ t = op;
412
+ break;
413
+ case 4:
414
+ _.label++;
415
+ return {
416
+ value: op[1],
417
+ done: false
418
+ };
419
+ case 5:
420
+ _.label++;
421
+ y = op[1];
422
+ op = [
423
+ 0
424
+ ];
425
+ continue;
426
+ case 7:
427
+ op = _.ops.pop();
428
+ _.trys.pop();
429
+ continue;
430
+ default:
431
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
432
+ _ = 0;
433
+ continue;
434
+ }
435
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
436
+ _.label = op[1];
437
+ break;
438
+ }
439
+ if (op[0] === 6 && _.label < t[1]) {
440
+ _.label = t[1];
441
+ t = op;
442
+ break;
443
+ }
444
+ if (t && _.label < t[2]) {
445
+ _.label = t[2];
446
+ _.ops.push(op);
447
+ break;
448
+ }
449
+ if (t[2]) _.ops.pop();
450
+ _.trys.pop();
451
+ continue;
452
+ }
453
+ op = body.call(thisArg, _);
454
+ } catch (e) {
455
+ op = [
456
+ 6,
457
+ e
458
+ ];
459
+ y = 0;
460
+ } finally{
461
+ f = t = 0;
462
+ }
463
+ if (op[0] & 5) throw op[1];
464
+ return {
465
+ value: op[0] ? op[1] : void 0,
466
+ done: true
467
+ };
468
+ }
469
+ }
470
+ function _type_of(obj) {
471
+ "@swc/helpers - typeof";
472
+ return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
473
+ }
474
+ /**
475
+ * Path (relative to the API base URL) of the user-scoped Firestore session endpoint served by
476
+ * `@dereekb/firebase-server`'s `SessionApiController`.
477
+ *
478
+ * Duplicated here rather than imported because importing it from `@dereekb/firebase-server` would be
479
+ * a dependency CYCLE: `firebase-server` already depends on `@dereekb/oauth-resource`
480
+ * (`packages/firebase-server/oidc/src/lib/service/oidc.service.ts`). The source of truth is
481
+ * `packages/firebase-server/src/lib/nest/controller/session/session.api.config.ts`.
482
+ */ var FIRESTORE_SESSION_API_PATH = "/session/firestore";
483
+ /**
484
+ * Error raised when a user-scoped Firestore session cannot be obtained.
485
+ *
486
+ * Consumers that already have their own error type do not have to catch and re-wrap this one: pass a
487
+ * {@link FirestoreSessionErrorFactory} and the session functions throw that type instead — the same
488
+ * seam `OAuthResourceErrorFactory` provides for verification.
489
+ */ var FirestoreSessionError = /*#__PURE__*/ function(BaseError) {
490
+ _inherits(FirestoreSessionError, BaseError);
491
+ function FirestoreSessionError(input) {
492
+ _class_call_check(this, FirestoreSessionError);
493
+ var _this;
494
+ _this = _call_super(this, FirestoreSessionError, [
495
+ input.message
496
+ ]), _define_property$2(_this, "code", void 0), _define_property$2(_this, "status", void 0), _define_property$2(_this, "suggestion", void 0), _define_property$2(_this, "details", void 0);
497
+ _this.code = input.code;
498
+ if (input.status != null) {
499
+ _this.status = input.status;
500
+ }
501
+ if (input.suggestion != null) {
502
+ _this.suggestion = input.suggestion;
503
+ }
504
+ if (input.details != null) {
505
+ _this.details = input.details;
506
+ }
507
+ return _this;
508
+ }
509
+ return FirestoreSessionError;
510
+ }(BaseError);
511
+ /**
512
+ * Default {@link FirestoreSessionErrorFactory}, producing a {@link FirestoreSessionError}.
513
+ *
514
+ * @param input - The code, message, status, suggestion, and details of the failure.
515
+ * @returns The error to throw.
516
+ */ var defaultFirestoreSessionErrorFactory = function defaultFirestoreSessionErrorFactory(input) {
517
+ return new FirestoreSessionError(input);
518
+ };
519
+ /**
520
+ * Returns true when the input is a {@link FirestoreSessionError}.
521
+ *
522
+ * @param error - The value to test.
523
+ * @returns Whether the value is a {@link FirestoreSessionError}.
524
+ */ function isFirestoreSessionError(error) {
525
+ return _instanceof$1(error, FirestoreSessionError);
526
+ }
527
+ /**
528
+ * Maps an HTTP status from the mint endpoint onto a {@link FirestoreSessionErrorCode}.
529
+ *
530
+ * @param status - The response status.
531
+ * @returns The matching error code.
532
+ * @__NO_SIDE_EFFECTS__
533
+ */ function firestoreSessionErrorCodeForStatus(status) {
534
+ var result;
535
+ if (status === 401) {
536
+ result = 'unauthorized';
537
+ } else if (status === 403) {
538
+ result = 'forbidden';
539
+ } else if (status === 404) {
540
+ result = 'not_found';
541
+ } else if (status >= 500) {
542
+ result = 'unavailable';
543
+ } else {
544
+ result = 'invalid_response';
545
+ }
546
+ return result;
547
+ }
548
+ // MARK: Fetch
549
+ /**
550
+ * Composes the session endpoint's absolute URL from an API base URL.
551
+ *
552
+ * @param apiBaseUrl - The API base URL, with or without a trailing slash.
553
+ * @returns The absolute `/session/firestore` URL.
554
+ * @__NO_SIDE_EFFECTS__
555
+ */ function firestoreSessionUrl(apiBaseUrl) {
556
+ return "".concat(trimSlash(apiBaseUrl)).concat(FIRESTORE_SESSION_API_PATH);
557
+ }
558
+ /**
559
+ * Fetches a user-scoped Firestore session from the API with a Bearer access token.
560
+ *
561
+ * The endpoint is admin-only and additionally gated on the `session.firestore` OIDC scope, so a 403
562
+ * here usually means the token's user is not an admin or the token was issued without that scope.
563
+ *
564
+ * @param input - The API target, access token, and optional fetch/error overrides.
565
+ * @returns The parsed {@link FirestoreSessionCredentials}.
566
+ * @throws {FirestoreSessionError} (or the `errorFactory`'s type) When the endpoint answers non-2xx or returns an unusable body.
567
+ */ function fetchFirestoreSession(input) {
568
+ return _async_to_generator$2(function() {
569
+ var _input_errorFactory, _input_fetcher, errorFactory, url, fetchImpl, res, text, body, credentials;
570
+ return _ts_generator$2(this, function(_state) {
571
+ switch(_state.label){
572
+ case 0:
573
+ errorFactory = (_input_errorFactory = input.errorFactory) !== null && _input_errorFactory !== void 0 ? _input_errorFactory : defaultFirestoreSessionErrorFactory;
574
+ url = firestoreSessionUrl(input.apiBaseUrl);
575
+ fetchImpl = (_input_fetcher = input.fetcher) !== null && _input_fetcher !== void 0 ? _input_fetcher : fetch;
576
+ return [
577
+ 4,
578
+ fetchImpl(url, {
579
+ method: 'GET',
580
+ headers: {
581
+ Accept: 'application/json',
582
+ Authorization: "Bearer ".concat(input.accessToken)
583
+ }
584
+ })
585
+ ];
586
+ case 1:
587
+ res = _state.sent();
588
+ return [
589
+ 4,
590
+ res.text()
591
+ ];
592
+ case 2:
593
+ text = _state.sent();
594
+ if (text.length > 0) {
595
+ try {
596
+ body = JSON.parse(text);
597
+ } catch (unused) {
598
+ body = text;
599
+ }
600
+ }
601
+ if (!res.ok) {
602
+ throw errorFactory({
603
+ message: "firestore session request failed: ".concat(extractMessage(body, text, res)),
604
+ code: firestoreSessionErrorCodeForStatus(res.status),
605
+ status: res.status,
606
+ suggestion: res.status === 401 || res.status === 403 ? '`/session/firestore` is admin-only and additionally gated on the `session.firestore` OIDC scope. The presented access token must belong to an admin user and carry that scope.' : 'Verify the API exposes `/session/firestore` (the `@dereekb/firebase-server` session module must be registered and `/api/session` added to the OIDC `protectedPaths`).'
607
+ });
608
+ }
609
+ credentials = body;
610
+ if (!(credentials === null || credentials === void 0 ? void 0 : credentials.customToken)) {
611
+ throw errorFactory({
612
+ message: 'firestore session response did not include a customToken.',
613
+ code: 'invalid_response',
614
+ status: res.status,
615
+ suggestion: 'Verify the API is running a `@dereekb/firebase-server` version that serves `/session/firestore`.'
616
+ });
617
+ }
618
+ return [
619
+ 2,
620
+ credentials
621
+ ];
622
+ }
623
+ });
624
+ })();
625
+ }
626
+ /**
627
+ * The most descriptive message available for a failed response.
628
+ *
629
+ * @param body - The parsed body, when one parsed.
630
+ * @param fallback - The raw response text.
631
+ * @param res - The response, for its status line.
632
+ * @returns The message to report.
633
+ */ function extractMessage(body, fallback, res) {
634
+ var bodyMessage = (typeof body === "undefined" ? "undefined" : _type_of(body)) === 'object' && body && 'message' in body ? body.message : undefined;
635
+ var messageString = typeof bodyMessage === 'string' ? bodyMessage : undefined;
636
+ return messageString !== null && messageString !== void 0 ? messageString : fallback || "".concat(res.status, " ").concat(res.statusText);
637
+ }
638
+ /**
639
+ * Removes a single trailing slash from a URL.
640
+ *
641
+ * @param url - The URL to trim.
642
+ * @returns The URL with no trailing slash.
643
+ */ function trimSlash(url) {
644
+ return url.endsWith('/') ? url.slice(0, -1) : url;
645
+ }
646
+
647
+ /**
648
+ * Hard ceiling on how long a minted user-scoped Firestore session may be reused, regardless of what
649
+ * the API reported in `expiresAt`.
650
+ *
651
+ * One hour, because that is the Firebase ceiling the credentials themselves sit under: a custom
652
+ * token is exchangeable for one hour, and the ID token it mints lives one hour. Holding a session
653
+ * past that buys nothing — the sign-in would fail — and re-minting is one HTTP round-trip.
654
+ */ var FIRESTORE_SESSION_MAX_CACHE_MS = 60 * 60 * 1000;
655
+ /**
656
+ * Default skew/latency buffer applied when deciding whether a cached session is still usable.
657
+ */ var FIRESTORE_SESSION_EXPIRY_BUFFER_MS = 60000;
658
+ /**
659
+ * Resolves the epoch-millis instant at which a cached session stops being usable.
660
+ *
661
+ * The effective expiry is the EARLIER of the API-reported `expiresAt` and
662
+ * {@link FIRESTORE_SESSION_MAX_CACHE_MS} past the write. Taking the earlier of the two means a
663
+ * server that reports an over-long (or unparsable) window still cannot push a session past the
664
+ * Firebase credential ceiling.
665
+ *
666
+ * @param entry - The cached entry.
667
+ * @returns The effective expiry in unix epoch milliseconds.
668
+ *
669
+ * @__NO_SIDE_EFFECTS__
670
+ */ function firestoreSessionEntryExpiresAt(entry) {
671
+ var ceiling = entry.cachedAt + FIRESTORE_SESSION_MAX_CACHE_MS;
672
+ var reported = Date.parse(entry.session.expiresAt);
673
+ return Number.isFinite(reported) ? Math.min(reported, ceiling) : ceiling;
674
+ }
675
+ /**
676
+ * Returns true when the cached session is at or near its effective expiry.
677
+ *
678
+ * @param entry - The cached entry (`null`/`undefined` is treated as expired).
679
+ * @param nowMs - The current time in unix epoch milliseconds. Defaults to `Date.now()`.
680
+ * @param bufferMs - Skew/latency buffer; the entry is treated as expired this far ahead of its effective expiry.
681
+ * @returns `true` when the entry is unusable, otherwise `false`.
682
+ */ function isFirestoreSessionExpired(entry) {
683
+ var nowMs = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : Date.now(), bufferMs = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : FIRESTORE_SESSION_EXPIRY_BUFFER_MS;
684
+ return entry == null || expirationDetails({
685
+ expiresFromDate: firestoreSessionEntryExpiresAt(entry),
686
+ expiresIn: -bufferMs,
687
+ now: new Date(nowMs)
688
+ }).hasExpired();
689
+ }
690
+
691
+ function asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, key, arg) {
692
+ try {
693
+ var info = gen[key](arg);
694
+ var value = info.value;
695
+ } catch (error) {
696
+ reject(error);
697
+ return;
698
+ }
699
+ if (info.done) resolve(value);
700
+ else Promise.resolve(value).then(_next, _throw);
701
+ }
702
+ function _async_to_generator$1(fn) {
703
+ return function() {
704
+ var self = this, args = arguments;
705
+ return new Promise(function(resolve, reject) {
706
+ var gen = fn.apply(self, args);
707
+ function _next(value) {
708
+ asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "next", value);
709
+ }
710
+ function _throw(err) {
711
+ asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "throw", err);
712
+ }
713
+ _next(undefined);
714
+ });
715
+ };
716
+ }
717
+ function _define_property$1(obj, key, value) {
718
+ if (key in obj) {
719
+ Object.defineProperty(obj, key, {
720
+ value: value,
721
+ enumerable: true,
722
+ configurable: true,
723
+ writable: true
724
+ });
725
+ } else obj[key] = value;
726
+ return obj;
727
+ }
728
+ function _instanceof(left, right) {
729
+ "@swc/helpers - instanceof";
730
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
731
+ return !!right[Symbol.hasInstance](left);
732
+ } else return left instanceof right;
733
+ }
734
+ function _object_spread$1(target) {
735
+ for(var i = 1; i < arguments.length; i++){
736
+ var source = arguments[i] != null ? arguments[i] : {};
737
+ var ownKeys = Object.keys(source);
738
+ if (typeof Object.getOwnPropertySymbols === "function") {
739
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
740
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
741
+ }));
742
+ }
743
+ ownKeys.forEach(function(key) {
744
+ _define_property$1(target, key, source[key]);
745
+ });
746
+ }
747
+ return target;
748
+ }
749
+ function ownKeys$1(object, enumerableOnly) {
750
+ var keys = Object.keys(object);
751
+ if (Object.getOwnPropertySymbols) {
752
+ var symbols = Object.getOwnPropertySymbols(object);
753
+ keys.push.apply(keys, symbols);
754
+ }
755
+ return keys;
756
+ }
757
+ function _object_spread_props$1(target, source) {
758
+ source = source != null ? source : {};
759
+ if (Object.getOwnPropertyDescriptors) Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
760
+ else {
761
+ ownKeys$1(Object(source)).forEach(function(key) {
762
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
763
+ });
764
+ }
765
+ return target;
766
+ }
767
+ function _ts_generator$1(thisArg, body) {
768
+ var f, y, t, _ = {
769
+ label: 0,
770
+ sent: function() {
771
+ if (t[0] & 1) throw t[1];
772
+ return t[1];
773
+ },
774
+ trys: [],
775
+ ops: []
776
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
777
+ return d(g, "next", {
778
+ value: verb(0)
779
+ }), d(g, "throw", {
780
+ value: verb(1)
781
+ }), d(g, "return", {
782
+ value: verb(2)
783
+ }), typeof Symbol === "function" && d(g, Symbol.iterator, {
784
+ value: function() {
785
+ return this;
786
+ }
787
+ }), g;
788
+ function verb(n) {
789
+ return function(v) {
790
+ return step([
791
+ n,
792
+ v
793
+ ]);
794
+ };
795
+ }
796
+ function step(op) {
797
+ if (f) throw new TypeError("Generator is already executing.");
798
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
799
+ 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) return t;
800
+ if (y = 0, t) op = [
801
+ op[0] & 2,
802
+ t.value
803
+ ];
804
+ switch(op[0]){
805
+ case 0:
806
+ case 1:
807
+ t = op;
808
+ break;
809
+ case 4:
810
+ _.label++;
811
+ return {
812
+ value: op[1],
813
+ done: false
814
+ };
815
+ case 5:
816
+ _.label++;
817
+ y = op[1];
818
+ op = [
819
+ 0
820
+ ];
821
+ continue;
822
+ case 7:
823
+ op = _.ops.pop();
824
+ _.trys.pop();
825
+ continue;
826
+ default:
827
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
828
+ _ = 0;
829
+ continue;
830
+ }
831
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
832
+ _.label = op[1];
833
+ break;
834
+ }
835
+ if (op[0] === 6 && _.label < t[1]) {
836
+ _.label = t[1];
837
+ t = op;
838
+ break;
839
+ }
840
+ if (t && _.label < t[2]) {
841
+ _.label = t[2];
842
+ _.ops.push(op);
843
+ break;
844
+ }
845
+ if (t[2]) _.ops.pop();
846
+ _.trys.pop();
847
+ continue;
848
+ }
849
+ op = body.call(thisArg, _);
850
+ } catch (e) {
851
+ op = [
852
+ 6,
853
+ e
854
+ ];
855
+ y = 0;
856
+ } finally{
857
+ f = t = 0;
858
+ }
859
+ if (op[0] & 5) throw op[1];
860
+ return {
861
+ value: op[0] ? op[1] : void 0,
862
+ done: true
863
+ };
864
+ }
865
+ }
866
+ // MARK: App Name
867
+ /**
868
+ * Separator between the segments of a {@link firebaseUserSessionAppName}.
869
+ *
870
+ * `::` rather than `-` because a Firebase project id is `[a-z0-9-]+`, so a single `-` makes
871
+ * `(ns='srv', scope='a-b', uid='c')` and `(ns='srv', scope='a', uid='b-c')` collide on `srv-a-b-c`.
872
+ * `:` cannot appear in a project id, so `::` is collision-free for any namespace that does not itself
873
+ * contain `::`.
874
+ */ var FIREBASE_USER_SESSION_KEY_SEPARATOR = '::';
875
+ /**
876
+ * The base Firebase app name a user-scoped session registers.
877
+ *
878
+ * A DERIVED name rather than a side registry is what lets {@link closeFirebaseUserSessionApps} find
879
+ * every app this owner opened from `getApps()` alone — no registry to keep in sync, and idempotent by
880
+ * construction.
881
+ *
882
+ * Note this is the BASE name: {@link openFirebaseUserSession} appends `#2`, `#3`, … when the base is
883
+ * already taken, because a session that mints its own credentials must never reuse an existing app
884
+ * registration (see the note on {@link openFirebaseUserSession}).
885
+ *
886
+ * @param input - The namespace, scope, and uid the session targets.
887
+ * @returns The base Firebase app name.
888
+ * @__NO_SIDE_EFFECTS__
889
+ */ function firebaseUserSessionAppName(input) {
890
+ return [
891
+ input.namespace,
892
+ input.scope,
893
+ input.uid
894
+ ].join(FIREBASE_USER_SESSION_KEY_SEPARATOR);
895
+ }
896
+ /**
897
+ * The `getApps()` name prefix every session opened under a namespace shares.
898
+ *
899
+ * @param namespace - The namespace to sweep.
900
+ * @returns The app-name prefix.
901
+ * @__NO_SIDE_EFFECTS__
902
+ */ function firebaseUserSessionNamespacePrefix(namespace) {
903
+ return "".concat(namespace).concat(FIREBASE_USER_SESSION_KEY_SEPARATOR);
904
+ }
905
+ /**
906
+ * Opens a direct Firestore connection as the user the presented access token belongs to.
907
+ *
908
+ * Steps, in a strict order:
909
+ *
910
+ * 0. When a `credentialsCache` is supplied, reuse the cached credential envelope if it is still live.
911
+ * Sessions are cached for up to an hour (see {@link FIRESTORE_SESSION_MAX_CACHE_MS}), which is the
912
+ * ceiling the Firebase credentials themselves sit under. A hit skips step 1 only — the Firebase
913
+ * app is per-session, so the sign-in in step 5 always runs.
914
+ * 1. `GET <apiBaseUrl>/session/firestore` for a custom token + App Check attestation.
915
+ * 2. `initializeApp` under the first FREE name derived from {@link firebaseUserSessionAppName}.
916
+ *
917
+ * **Never reuse an existing registration.** `initializeAppCheck` on an app whose App Check
918
+ * provider is already initialized silently returns the EXISTING instance when
919
+ * `CustomProvider.isEqual` matches — and `isEqual` compares `getToken.toString()`, the source text
920
+ * of the arrow function, which is byte-identical across two closures built at this call site over
921
+ * different tokens. Reusing an app therefore keeps the FIRST attestation and drops the freshly
922
+ * minted one on the floor, so requests go out under a stale (eventually expired) App Check token.
923
+ * App reuse is the POOL's responsibility, at the session-object level, never at the app-registry
924
+ * level.
925
+ * 3. `initializeAppCheck` with a `CustomProvider` handing back the server-minted token. **This must
926
+ * happen before any other Firebase call** — `dbx-firebase`'s provider documents the same
927
+ * constraint: "App Check must be initialized before any Firebase request goes out, otherwise
928
+ * requests are sent without an App Check token and are rejected in production." Skipped when the
929
+ * config targets emulators (which do not verify attestations) or when the API minted no token.
930
+ * 4. `getAuth` / `getFirestore`, connecting each to its emulator when configured.
931
+ * 5. `signInWithCustomToken`. The user's stored custom claims land at the top level of the exchanged
932
+ * ID token, so `request.auth.token.<claim>` reads in security rules behave exactly as in the app.
933
+ *
934
+ * There is deliberately NO fallback to an Admin-SDK context — the whole point is that rules stay in
935
+ * force, so a failure here throws.
936
+ *
937
+ * @param input - The namespace, Firebase client config, API target, and access token.
938
+ * @returns The live {@link FirebaseUserSession}.
939
+ * @throws {FirestoreSessionError} (or the `errorFactory`'s type) When the config is incomplete, or any step of the handshake fails.
940
+ */ function openFirebaseUserSession(input) {
941
+ return _async_to_generator$1(function() {
942
+ var _input_maxSessionAgeMs, _input_errorFactory, _input_now, _ref, _ref1, _firebase_emulators, _firebase_emulators1, _firebase_emulators2, namespace, firebase, apiBaseUrl, accessToken, fetcher, credentialsCache, cacheKey, _input_refreshCredentials, refreshCredentials, maxSessionAgeMs, errorFactory, now, scope, readKey, cached, _tmp, liveCached, fromCache, credentials, _tmp1, writeKey, appName, app, createdAt, useEmulators, appCheckToken, expireTimeMillis, emulatorHost, auth, firestore, e;
943
+ return _ts_generator$1(this, function(_state) {
944
+ switch(_state.label){
945
+ case 0:
946
+ namespace = input.namespace, firebase = input.firebase, apiBaseUrl = input.apiBaseUrl, accessToken = input.accessToken, fetcher = input.fetcher, credentialsCache = input.credentialsCache, cacheKey = input.cacheKey, _input_refreshCredentials = input.refreshCredentials, refreshCredentials = _input_refreshCredentials === void 0 ? false : _input_refreshCredentials;
947
+ maxSessionAgeMs = (_input_maxSessionAgeMs = input.maxSessionAgeMs) !== null && _input_maxSessionAgeMs !== void 0 ? _input_maxSessionAgeMs : FIRESTORE_SESSION_MAX_CACHE_MS;
948
+ errorFactory = (_input_errorFactory = input.errorFactory) !== null && _input_errorFactory !== void 0 ? _input_errorFactory : defaultFirestoreSessionErrorFactory;
949
+ now = (_input_now = input.now) !== null && _input_now !== void 0 ? _input_now : function() {
950
+ return Date.now();
951
+ };
952
+ if (!isFirebaseClientConfigComplete(firebase)) {
953
+ throw errorFactory({
954
+ code: 'invalid_config',
955
+ message: 'No complete Firebase client config was supplied, so a user-scoped Firestore session cannot be opened.',
956
+ suggestion: "Set `apiKey`, `projectId`, and `appId` on the Firebase client config. Copy them from the app's Firebase web app config."
957
+ });
958
+ }
959
+ scope = input.scope || firebase.projectId;
960
+ readKey = (_ref = cacheKey !== null && cacheKey !== void 0 ? cacheKey : input.uid) !== null && _ref !== void 0 ? _ref : undefined;
961
+ if (!(credentialsCache && !refreshCredentials && readKey != null)) return [
962
+ 3,
963
+ 2
964
+ ];
965
+ return [
966
+ 4,
967
+ credentialsCache.get(readKey)
968
+ ];
969
+ case 1:
970
+ _tmp = _state.sent();
971
+ return [
972
+ 3,
973
+ 3
974
+ ];
975
+ case 2:
976
+ _tmp = undefined;
977
+ _state.label = 3;
978
+ case 3:
979
+ cached = _tmp;
980
+ liveCached = isFirestoreSessionExpired(cached, now()) ? undefined : cached;
981
+ fromCache = liveCached != null;
982
+ if (!((_ref1 = liveCached === null || liveCached === void 0 ? void 0 : liveCached.session) !== null && _ref1 !== void 0)) return [
983
+ 3,
984
+ 4
985
+ ];
986
+ _tmp1 = _ref1;
987
+ return [
988
+ 3,
989
+ 6
990
+ ];
991
+ case 4:
992
+ return [
993
+ 4,
994
+ fetchFirestoreSession({
995
+ apiBaseUrl: apiBaseUrl,
996
+ accessToken: accessToken,
997
+ fetcher: fetcher,
998
+ errorFactory: errorFactory
999
+ })
1000
+ ];
1001
+ case 5:
1002
+ _tmp1 = _state.sent();
1003
+ _state.label = 6;
1004
+ case 6:
1005
+ credentials = _tmp1;
1006
+ if (input.uid != null && credentials.uid !== input.uid) {
1007
+ throw errorFactory({
1008
+ code: 'invalid_response',
1009
+ message: 'The session endpoint minted credentials for uid "'.concat(credentials.uid, '" but "').concat(input.uid, '" was requested.'),
1010
+ suggestion: "The mint endpoint always mints for the presented token's own `auth.uid`. A mismatch means the access token does not belong to the requested user."
1011
+ });
1012
+ }
1013
+ writeKey = cacheKey !== null && cacheKey !== void 0 ? cacheKey : credentials.uid;
1014
+ if (!(!fromCache && credentialsCache)) return [
1015
+ 3,
1016
+ 8
1017
+ ];
1018
+ return [
1019
+ 4,
1020
+ credentialsCache.set(writeKey, {
1021
+ session: credentials,
1022
+ cachedAt: now(),
1023
+ uid: credentials.uid
1024
+ })
1025
+ ];
1026
+ case 7:
1027
+ _state.sent();
1028
+ _state.label = 8;
1029
+ case 8:
1030
+ appName = nextFreeFirebaseAppName(firebaseUserSessionAppName({
1031
+ namespace: namespace,
1032
+ scope: scope,
1033
+ uid: credentials.uid
1034
+ }));
1035
+ app = initializeApp({
1036
+ apiKey: firebase.apiKey,
1037
+ authDomain: firebase.authDomain,
1038
+ projectId: firebase.projectId,
1039
+ appId: firebase.appId
1040
+ }, appName);
1041
+ createdAt = now();
1042
+ useEmulators = firebaseClientEmulatorsInUse(firebase);
1043
+ // App Check must be registered on the app before any Firebase request goes out. Mirrors
1044
+ // `createDbxFirebaseAppCheck`, which likewise disables App Check whenever emulators are in use.
1045
+ if (credentials.appCheckToken && !useEmulators) {
1046
+ appCheckToken = credentials.appCheckToken;
1047
+ expireTimeMillis = expireTimeMillisFromCredentials(credentials, now());
1048
+ initializeAppCheck(app, {
1049
+ provider: new CustomProvider({
1050
+ getToken: function getToken() {
1051
+ return _async_to_generator$1(function() {
1052
+ return _ts_generator$1(this, function(_state) {
1053
+ return [
1054
+ 2,
1055
+ {
1056
+ token: appCheckToken,
1057
+ expireTimeMillis: expireTimeMillis
1058
+ }
1059
+ ];
1060
+ });
1061
+ })();
1062
+ }
1063
+ }),
1064
+ // the token is minted per-session by the API; there is no local attestation to refresh against
1065
+ isTokenAutoRefreshEnabled: false
1066
+ });
1067
+ }
1068
+ emulatorHost = ((_firebase_emulators = firebase.emulators) === null || _firebase_emulators === void 0 ? void 0 : _firebase_emulators.host) || DEFAULT_FIREBASE_CLIENT_EMULATOR_HOST;
1069
+ auth = getAuth(app);
1070
+ if (useEmulators && ((_firebase_emulators1 = firebase.emulators) === null || _firebase_emulators1 === void 0 ? void 0 : _firebase_emulators1.authPort) != null) {
1071
+ connectAuthEmulator(auth, "http://".concat(emulatorHost, ":").concat(firebase.emulators.authPort), {
1072
+ disableWarnings: true
1073
+ });
1074
+ }
1075
+ firestore = getFirestore(app);
1076
+ if (useEmulators && ((_firebase_emulators2 = firebase.emulators) === null || _firebase_emulators2 === void 0 ? void 0 : _firebase_emulators2.firestorePort) != null) {
1077
+ connectFirestoreEmulator(firestore, emulatorHost, firebase.emulators.firestorePort);
1078
+ }
1079
+ _state.label = 9;
1080
+ case 9:
1081
+ _state.trys.push([
1082
+ 9,
1083
+ 11,
1084
+ ,
1085
+ 15
1086
+ ]);
1087
+ return [
1088
+ 4,
1089
+ signInWithCustomToken(auth, credentials.customToken)
1090
+ ];
1091
+ case 10:
1092
+ _state.sent();
1093
+ return [
1094
+ 3,
1095
+ 15
1096
+ ];
1097
+ case 11:
1098
+ e = _state.sent();
1099
+ // The app created on THIS attempt is torn down explicitly rather than left to the namespace
1100
+ // sweep: every open registers its OWN app (see step 2), so a caller that retries — or a pool
1101
+ // whose config is broken and fails once per request — would otherwise accumulate one dead app,
1102
+ // with its refresh timer and network stack, per failed attempt.
1103
+ return [
1104
+ 4,
1105
+ closeFirebaseUserSessionApp(app)
1106
+ ];
1107
+ case 12:
1108
+ _state.sent();
1109
+ if (!(fromCache && credentialsCache)) return [
1110
+ 3,
1111
+ 14
1112
+ ];
1113
+ return [
1114
+ 4,
1115
+ credentialsCache.remove(writeKey)
1116
+ ];
1117
+ case 13:
1118
+ _state.sent();
1119
+ return [
1120
+ 2,
1121
+ openFirebaseUserSession(_object_spread_props$1(_object_spread$1({}, input), {
1122
+ refreshCredentials: true
1123
+ }))
1124
+ ];
1125
+ case 14:
1126
+ throw errorFactory({
1127
+ code: 'unauthorized',
1128
+ message: "Failed to sign in with the minted custom token: ".concat(_instanceof(e, Error) ? e.message : String(e)),
1129
+ suggestion: 'Verify `firebase.projectId`/`firebase.apiKey` match the project the API mints tokens for.'
1130
+ });
1131
+ case 15:
1132
+ return [
1133
+ 2,
1134
+ {
1135
+ uid: credentials.uid,
1136
+ credentials: credentials,
1137
+ fromCache: fromCache,
1138
+ appName: appName,
1139
+ app: app,
1140
+ auth: auth,
1141
+ firestore: firestore,
1142
+ firestoreContext: clientFirebaseFirestoreContextFactory(firestore),
1143
+ createdAt: createdAt,
1144
+ expiresAt: firebaseUserSessionExpiresAt(credentials, createdAt, maxSessionAgeMs)
1145
+ }
1146
+ ];
1147
+ }
1148
+ });
1149
+ })();
1150
+ }
1151
+ // MARK: Teardown
1152
+ /**
1153
+ * Tears down a session opened by {@link openFirebaseUserSession}.
1154
+ *
1155
+ * Required, not optional hygiene. A signed-in `Auth` runs a token-refresh timer and a live
1156
+ * `Firestore` holds open handles, so both leak per user without teardown — and in a CLI-shaped
1157
+ * process they keep the Node event loop alive indefinitely, so the process never exits.
1158
+ *
1159
+ * `deleteApp` is the single call that covers it — it disposes every registered component, which for
1160
+ * Firestore runs the same shutdown `terminate()` does, and for Auth stops the token-refresh timer.
1161
+ *
1162
+ * Deliberately tolerant: teardown normally runs after the caller's result is already produced, so a
1163
+ * failure here must not change the outcome.
1164
+ *
1165
+ * @param session - The session to close.
1166
+ */ function closeFirebaseUserSession(session) {
1167
+ return _async_to_generator$1(function() {
1168
+ return _ts_generator$1(this, function(_state) {
1169
+ switch(_state.label){
1170
+ case 0:
1171
+ return [
1172
+ 4,
1173
+ closeFirebaseUserSessionApp(session.app)
1174
+ ];
1175
+ case 1:
1176
+ _state.sent();
1177
+ return [
1178
+ 2
1179
+ ];
1180
+ }
1181
+ });
1182
+ })();
1183
+ }
1184
+ /**
1185
+ * Deletes one Firebase app, swallowing any failure.
1186
+ *
1187
+ * @param app - The app to delete.
1188
+ */ function closeFirebaseUserSessionApp(app) {
1189
+ return _async_to_generator$1(function() {
1190
+ return _ts_generator$1(this, function(_state) {
1191
+ switch(_state.label){
1192
+ case 0:
1193
+ _state.trys.push([
1194
+ 0,
1195
+ 2,
1196
+ ,
1197
+ 3
1198
+ ]);
1199
+ return [
1200
+ 4,
1201
+ deleteApp(app)
1202
+ ];
1203
+ case 1:
1204
+ _state.sent();
1205
+ return [
1206
+ 3,
1207
+ 3
1208
+ ];
1209
+ case 2:
1210
+ _state.sent();
1211
+ return [
1212
+ 3,
1213
+ 3
1214
+ ];
1215
+ case 3:
1216
+ return [
1217
+ 2
1218
+ ];
1219
+ }
1220
+ });
1221
+ })();
1222
+ }
1223
+ /**
1224
+ * Deletes every still-live Firebase app opened under a namespace, whether or not a session was
1225
+ * handed back.
1226
+ *
1227
+ * The last line of defence against a leak. {@link closeFirebaseUserSession} covers the normal path,
1228
+ * but it needs a session to be handed to it, and three cases never produce one:
1229
+ *
1230
+ * - a handshake that fails AFTER `initializeApp` — a rejected custom token, a failed App Check
1231
+ * registration — throws, so the caller that catches it has an initialized app and no session;
1232
+ * - a caller that opens its own session outside a pool owns its own teardown, and forgetting it
1233
+ * leaks;
1234
+ * - an owner orphaned mid-invocation carries the only reference to its session.
1235
+ *
1236
+ * `getApps()` already tracks every live app and `deleteApp` removes it from that list, so the app
1237
+ * names {@link firebaseUserSessionAppName} derives are enough to find them again — no side registry
1238
+ * to keep in sync, and idempotent by construction.
1239
+ *
1240
+ * @param input - The function inputs.
1241
+ * @param input.namespace - The namespace whose apps should be closed. Apps belonging to other
1242
+ * Firebase consumers in the same process are left alone.
1243
+ * @returns Resolves once every matching app has been deleted.
1244
+ */ function closeFirebaseUserSessionApps(input) {
1245
+ return _async_to_generator$1(function() {
1246
+ var prefix, apps;
1247
+ return _ts_generator$1(this, function(_state) {
1248
+ switch(_state.label){
1249
+ case 0:
1250
+ prefix = firebaseUserSessionNamespacePrefix(input.namespace);
1251
+ apps = getApps().filter(function(x) {
1252
+ return x.name.startsWith(prefix);
1253
+ });
1254
+ return [
1255
+ 4,
1256
+ Promise.all(apps.map(function(app) {
1257
+ return closeFirebaseUserSessionApp(app);
1258
+ }))
1259
+ ];
1260
+ case 1:
1261
+ _state.sent();
1262
+ return [
1263
+ 2
1264
+ ];
1265
+ }
1266
+ });
1267
+ })();
1268
+ }
1269
+ // MARK: Internal
1270
+ /**
1271
+ * The first unregistered app name at or after `base`, trying `base`, `base#2`, `base#3`, ….
1272
+ *
1273
+ * @param base - The derived base app name.
1274
+ * @returns A name no live app currently holds.
1275
+ */ function nextFreeFirebaseAppName(base) {
1276
+ var taken = new Set(getApps().map(function(x) {
1277
+ return x.name;
1278
+ }));
1279
+ var result = base;
1280
+ var attempt = 1;
1281
+ while(taken.has(result)){
1282
+ attempt += 1;
1283
+ result = "".concat(base, "#").concat(attempt);
1284
+ }
1285
+ return result;
1286
+ }
1287
+ /**
1288
+ * The session's effective expiry: the EARLIER of the API-reported `expiresAt` and the age ceiling.
1289
+ *
1290
+ * Mirrors `firestoreSessionEntryExpiresAt` — a server reporting an over-long (or unparsable) window
1291
+ * still cannot push a session past the Firebase credential ceiling.
1292
+ *
1293
+ * @param credentials - The minted envelope.
1294
+ * @param createdAt - When the session was opened, in unix epoch milliseconds.
1295
+ * @param maxSessionAgeMs - The age ceiling.
1296
+ * @returns The effective expiry in unix epoch milliseconds.
1297
+ */ function firebaseUserSessionExpiresAt(credentials, createdAt, maxSessionAgeMs) {
1298
+ var ceiling = createdAt + maxSessionAgeMs;
1299
+ var reported = Date.parse(credentials.expiresAt);
1300
+ return Number.isFinite(reported) ? Math.min(reported, ceiling) : ceiling;
1301
+ }
1302
+ /**
1303
+ * Resolves the App Check token's expiry from the credential envelope, falling back to a short window
1304
+ * when the API returned an unparsable `expiresAt`.
1305
+ *
1306
+ * @param credentials - The credential envelope returned by the API.
1307
+ * @param nowMs - The current time in unix epoch milliseconds.
1308
+ * @returns The epoch-millis expiry to advertise to the App Check `CustomProvider`.
1309
+ */ function expireTimeMillisFromCredentials(credentials, nowMs) {
1310
+ var parsed = Date.parse(credentials.expiresAt);
1311
+ return Number.isFinite(parsed) ? parsed : nowMs + 30 * 60 * 1000;
1312
+ }
1313
+
1314
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
1315
+ try {
1316
+ var info = gen[key](arg);
1317
+ var value = info.value;
1318
+ } catch (error) {
1319
+ reject(error);
1320
+ return;
1321
+ }
1322
+ if (info.done) resolve(value);
1323
+ else Promise.resolve(value).then(_next, _throw);
1324
+ }
1325
+ function _async_to_generator(fn) {
1326
+ return function() {
1327
+ var self = this, args = arguments;
1328
+ return new Promise(function(resolve, reject) {
1329
+ var gen = fn.apply(self, args);
1330
+ function _next(value) {
1331
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
1332
+ }
1333
+ function _throw(err) {
1334
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
1335
+ }
1336
+ _next(undefined);
1337
+ });
1338
+ };
1339
+ }
1340
+ function _define_property(obj, key, value) {
1341
+ if (key in obj) {
1342
+ Object.defineProperty(obj, key, {
1343
+ value: value,
1344
+ enumerable: true,
1345
+ configurable: true,
1346
+ writable: true
1347
+ });
1348
+ } else obj[key] = value;
1349
+ return obj;
1350
+ }
1351
+ function _object_spread(target) {
1352
+ for(var i = 1; i < arguments.length; i++){
1353
+ var source = arguments[i] != null ? arguments[i] : {};
1354
+ var ownKeys = Object.keys(source);
1355
+ if (typeof Object.getOwnPropertySymbols === "function") {
1356
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
1357
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
1358
+ }));
1359
+ }
1360
+ ownKeys.forEach(function(key) {
1361
+ _define_property(target, key, source[key]);
1362
+ });
1363
+ }
1364
+ return target;
1365
+ }
1366
+ function ownKeys(object, enumerableOnly) {
1367
+ var keys = Object.keys(object);
1368
+ if (Object.getOwnPropertySymbols) {
1369
+ var symbols = Object.getOwnPropertySymbols(object);
1370
+ keys.push.apply(keys, symbols);
1371
+ }
1372
+ return keys;
1373
+ }
1374
+ function _object_spread_props(target, source) {
1375
+ source = source != null ? source : {};
1376
+ if (Object.getOwnPropertyDescriptors) Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
1377
+ else {
1378
+ ownKeys(Object(source)).forEach(function(key) {
1379
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
1380
+ });
1381
+ }
1382
+ return target;
1383
+ }
1384
+ function _ts_generator(thisArg, body) {
1385
+ var f, y, t, _ = {
1386
+ label: 0,
1387
+ sent: function() {
1388
+ if (t[0] & 1) throw t[1];
1389
+ return t[1];
1390
+ },
1391
+ trys: [],
1392
+ ops: []
1393
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
1394
+ return d(g, "next", {
1395
+ value: verb(0)
1396
+ }), d(g, "throw", {
1397
+ value: verb(1)
1398
+ }), d(g, "return", {
1399
+ value: verb(2)
1400
+ }), typeof Symbol === "function" && d(g, Symbol.iterator, {
1401
+ value: function() {
1402
+ return this;
1403
+ }
1404
+ }), g;
1405
+ function verb(n) {
1406
+ return function(v) {
1407
+ return step([
1408
+ n,
1409
+ v
1410
+ ]);
1411
+ };
1412
+ }
1413
+ function step(op) {
1414
+ if (f) throw new TypeError("Generator is already executing.");
1415
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
1416
+ 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) return t;
1417
+ if (y = 0, t) op = [
1418
+ op[0] & 2,
1419
+ t.value
1420
+ ];
1421
+ switch(op[0]){
1422
+ case 0:
1423
+ case 1:
1424
+ t = op;
1425
+ break;
1426
+ case 4:
1427
+ _.label++;
1428
+ return {
1429
+ value: op[1],
1430
+ done: false
1431
+ };
1432
+ case 5:
1433
+ _.label++;
1434
+ y = op[1];
1435
+ op = [
1436
+ 0
1437
+ ];
1438
+ continue;
1439
+ case 7:
1440
+ op = _.ops.pop();
1441
+ _.trys.pop();
1442
+ continue;
1443
+ default:
1444
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
1445
+ _ = 0;
1446
+ continue;
1447
+ }
1448
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
1449
+ _.label = op[1];
1450
+ break;
1451
+ }
1452
+ if (op[0] === 6 && _.label < t[1]) {
1453
+ _.label = t[1];
1454
+ t = op;
1455
+ break;
1456
+ }
1457
+ if (t && _.label < t[2]) {
1458
+ _.label = t[2];
1459
+ _.ops.push(op);
1460
+ break;
1461
+ }
1462
+ if (t[2]) _.ops.pop();
1463
+ _.trys.pop();
1464
+ continue;
1465
+ }
1466
+ op = body.call(thisArg, _);
1467
+ } catch (e) {
1468
+ op = [
1469
+ 6,
1470
+ e
1471
+ ];
1472
+ y = 0;
1473
+ } finally{
1474
+ f = t = 0;
1475
+ }
1476
+ if (op[0] & 5) throw op[1];
1477
+ return {
1478
+ value: op[0] ? op[1] : void 0,
1479
+ done: true
1480
+ };
1481
+ }
1482
+ }
1483
+ /**
1484
+ * Default {@link FirebaseUserSessionPoolConfig.maxSessions}.
1485
+ *
1486
+ * Bounds *concurrently distinct users*, not total users. Each entry is a full Firebase app: one
1487
+ * `Firestore` with its network stack and one `Auth` with a refresh timer. 32 is deliberate — high
1488
+ * enough that a normal request mix never evicts, low enough that an unbounded-uid caller cannot
1489
+ * exhaust the process.
1490
+ */ var DEFAULT_FIREBASE_USER_SESSION_POOL_SIZE = 32;
1491
+ /**
1492
+ * Creates a per-`(scope, uid)` Firebase session pool with an explicit cap and TTL/LRU eviction.
1493
+ *
1494
+ * Exists because {@link openFirebaseUserSession} alone does not generalize past one user per process:
1495
+ * a signed-in `Auth` runs a token-refresh timer and a live `Firestore` holds handles, so a server
1496
+ * holding N concurrent user sessions leaks both per user without an owner. The pool is that owner.
1497
+ *
1498
+ * ### Why the TTL is driven by App Check, not Auth
1499
+ *
1500
+ * A signed-in `Auth` refreshes its ID token indefinitely — a Firebase refresh token does not expire
1501
+ * on an hourly clock. The App Check token does not refresh: it is minted once by the API and
1502
+ * `isTokenAutoRefreshEnabled` is `false`, because there is no local attestation to refresh against.
1503
+ * So the session's real ceiling is the envelope's `expiresAt` (documented by the endpoint as "the
1504
+ * earliest expiry among its credentials"), floored by {@link FirebaseUserSessionPoolConfig.maxSessionAgeMs}.
1505
+ * Tearing the whole app down on expiry and re-minting costs one round trip; keeping it alive costs a
1506
+ * silently unattested connection.
1507
+ *
1508
+ * ### Why a lease rather than a bare getter
1509
+ *
1510
+ * Reference counting is what makes eviction safe. Evicting an entry someone is mid-query on turns a
1511
+ * capacity event into a user-visible failure, so the pool never tears down a leased session.
1512
+ *
1513
+ * @param config - The pool configuration.
1514
+ * @returns The pool.
1515
+ */ function firebaseUserSessionPool(config) {
1516
+ var _config_errorFactory, _config_openSession, _config_now, _config_maxSessions, _config_maxSessionAgeMs, _config_refreshSkewMs;
1517
+ var namespace = config.namespace, firebase = config.firebase, apiBaseUrl = config.apiBaseUrl, fetcher = config.fetcher, credentialsCache = config.credentialsCache;
1518
+ var errorFactory = (_config_errorFactory = config.errorFactory) !== null && _config_errorFactory !== void 0 ? _config_errorFactory : defaultFirestoreSessionErrorFactory;
1519
+ var openSessionImpl = (_config_openSession = config.openSession) !== null && _config_openSession !== void 0 ? _config_openSession : openFirebaseUserSession;
1520
+ var now = (_config_now = config.now) !== null && _config_now !== void 0 ? _config_now : function() {
1521
+ return Date.now();
1522
+ };
1523
+ var scope = config.scope || firebase.projectId || namespace;
1524
+ var maxSessions = (_config_maxSessions = config.maxSessions) !== null && _config_maxSessions !== void 0 ? _config_maxSessions : DEFAULT_FIREBASE_USER_SESSION_POOL_SIZE;
1525
+ var maxSessionAgeMs = (_config_maxSessionAgeMs = config.maxSessionAgeMs) !== null && _config_maxSessionAgeMs !== void 0 ? _config_maxSessionAgeMs : FIRESTORE_SESSION_MAX_CACHE_MS;
1526
+ var refreshSkewMs = (_config_refreshSkewMs = config.refreshSkewMs) !== null && _config_refreshSkewMs !== void 0 ? _config_refreshSkewMs : FIRESTORE_SESSION_EXPIRY_BUFFER_MS;
1527
+ var onEvent = config.onEvent;
1528
+ var entries = new Map();
1529
+ var closed = false;
1530
+ function emit(type, entry, error) {
1531
+ onEvent === null || onEvent === void 0 ? void 0 : onEvent(_object_spread({
1532
+ type: type,
1533
+ uid: entry.uid,
1534
+ appName: entry.appName,
1535
+ size: entries.size
1536
+ }, error === undefined ? {} : {
1537
+ error: error
1538
+ }));
1539
+ }
1540
+ /**
1541
+ * Removes the entry from the map when it is still the live one for its key.
1542
+ *
1543
+ * Guarded on identity because a replacement entry may already have been inserted for the same key
1544
+ * (the expiry path detaches and immediately re-opens), and deleting blindly would drop the new one.
1545
+ *
1546
+ * @param entry - The entry to remove.
1547
+ */ function detach(entry) {
1548
+ if (entries.get(entry.key) === entry) {
1549
+ entries.delete(entry.key);
1550
+ }
1551
+ }
1552
+ function teardown(entry) {
1553
+ return _async_to_generator(function() {
1554
+ var session, error;
1555
+ return _ts_generator(this, function(_state) {
1556
+ switch(_state.label){
1557
+ case 0:
1558
+ _state.trys.push([
1559
+ 0,
1560
+ 2,
1561
+ ,
1562
+ 3
1563
+ ]);
1564
+ return [
1565
+ 4,
1566
+ entry.loading
1567
+ ];
1568
+ case 1:
1569
+ session = _state.sent();
1570
+ return [
1571
+ 3,
1572
+ 3
1573
+ ];
1574
+ case 2:
1575
+ _state.sent();
1576
+ // an open that never produced a session has nothing to tear down; the rejection was already
1577
+ // surfaced to the caller that awaited it
1578
+ session = undefined;
1579
+ return [
1580
+ 3,
1581
+ 3
1582
+ ];
1583
+ case 3:
1584
+ if (!(session != null)) return [
1585
+ 3,
1586
+ 7
1587
+ ];
1588
+ entry.appName = session.appName;
1589
+ _state.label = 4;
1590
+ case 4:
1591
+ _state.trys.push([
1592
+ 4,
1593
+ 6,
1594
+ ,
1595
+ 7
1596
+ ]);
1597
+ // `deleteApp` directly rather than `closeFirebaseUserSession`, which deliberately swallows:
1598
+ // a long-lived pool wants a failed teardown OBSERVABLE — it means an app whose refresh timer
1599
+ // and network stack are still running — even though it must never be fatal.
1600
+ return [
1601
+ 4,
1602
+ deleteApp(session.app)
1603
+ ];
1604
+ case 5:
1605
+ _state.sent();
1606
+ emit('closed', entry);
1607
+ return [
1608
+ 3,
1609
+ 7
1610
+ ];
1611
+ case 6:
1612
+ error = _state.sent();
1613
+ emit('teardown-failed', entry, error);
1614
+ return [
1615
+ 3,
1616
+ 7
1617
+ ];
1618
+ case 7:
1619
+ return [
1620
+ 2
1621
+ ];
1622
+ }
1623
+ });
1624
+ })();
1625
+ }
1626
+ /**
1627
+ * Tears the entry down now when it is idle, otherwise marks it for teardown on its last release.
1628
+ *
1629
+ * @param entry - The detached entry.
1630
+ * @returns The teardown promise when one started, otherwise `undefined`.
1631
+ */ function teardownWhenIdle(entry) {
1632
+ var result;
1633
+ if (entry.leases === 0) {
1634
+ result = teardown(entry);
1635
+ } else {
1636
+ entry.pendingTeardown = true;
1637
+ }
1638
+ return result;
1639
+ }
1640
+ /**
1641
+ * Brings the pool back to its cap after an insert.
1642
+ *
1643
+ * The cap is a TARGET, not a hard ceiling. When every entry is leased there is nothing safe to
1644
+ * evict, so the pool admits anyway and marks the LRU entry for teardown-on-release: refusing a user
1645
+ * because other users are busy is worse than a brief overshoot, and killing a live handle is worse
1646
+ * than both. The overshoot is bounded by the host's own request concurrency.
1647
+ *
1648
+ * @param admitted - The entry just inserted, which is never the one given up.
1649
+ */ function enforceCapacity(admitted) {
1650
+ while(entries.size > maxSessions){
1651
+ var idle = leastRecentlyUsed(function(x) {
1652
+ return x.leases === 0 && !x.pendingTeardown && x !== admitted;
1653
+ });
1654
+ if (idle == null) {
1655
+ var lru = leastRecentlyUsed(function(x) {
1656
+ return !x.pendingTeardown && x !== admitted;
1657
+ });
1658
+ if (lru == null) {
1659
+ // every entry is leased AND already marked; there is nothing further to give up
1660
+ emit('over-capacity', admitted);
1661
+ break;
1662
+ } else {
1663
+ lru.pendingTeardown = true;
1664
+ detach(lru);
1665
+ emit('over-capacity', lru);
1666
+ }
1667
+ } else {
1668
+ detach(idle);
1669
+ emit('evicted', idle);
1670
+ void teardown(idle);
1671
+ }
1672
+ }
1673
+ }
1674
+ function leastRecentlyUsed(predicate) {
1675
+ var result;
1676
+ entries.forEach(function(entry) {
1677
+ if (predicate(entry) && (result == null || entry.lastUsedAt < result.lastUsedAt)) {
1678
+ result = entry;
1679
+ }
1680
+ });
1681
+ return result;
1682
+ }
1683
+ function openEntry(input, key, openedAt) {
1684
+ var entry = {
1685
+ key: key,
1686
+ uid: input.uid,
1687
+ appName: key,
1688
+ leases: 0,
1689
+ lastUsedAt: openedAt,
1690
+ pendingTeardown: false,
1691
+ loading: openSessionImpl(_object_spread_props(_object_spread({
1692
+ namespace: namespace,
1693
+ scope: scope,
1694
+ firebase: firebase,
1695
+ apiBaseUrl: apiBaseUrl,
1696
+ accessToken: input.accessToken,
1697
+ uid: input.uid,
1698
+ fetcher: fetcher,
1699
+ errorFactory: errorFactory,
1700
+ credentialsCache: credentialsCache,
1701
+ cacheKey: key
1702
+ }, input.refreshCredentials == null ? {} : {
1703
+ refreshCredentials: input.refreshCredentials
1704
+ }), {
1705
+ maxSessionAgeMs: maxSessionAgeMs,
1706
+ now: now
1707
+ }))
1708
+ };
1709
+ entry.loading.then(function(session) {
1710
+ entry.appName = session.appName;
1711
+ // the ceiling is re-derived from the POOL's clock so it holds whatever the opener reported —
1712
+ // an envelope claiming a six-hour window is still capped at maxSessionAgeMs
1713
+ entry.expiresAt = Math.min(session.expiresAt, openedAt + maxSessionAgeMs);
1714
+ }, function() {
1715
+ // Cache only on success: a rejected open is removed outright so the next acquisition retries
1716
+ // rather than latching onto the failure.
1717
+ detach(entry);
1718
+ });
1719
+ return entry;
1720
+ }
1721
+ function isExpired(entry, nowMs) {
1722
+ return entry.expiresAt != null && entry.expiresAt - nowMs <= refreshSkewMs;
1723
+ }
1724
+ function leaseFor(entry, session) {
1725
+ var released = false;
1726
+ return {
1727
+ session: session,
1728
+ release: function release() {
1729
+ if (!released) {
1730
+ released = true;
1731
+ entry.leases -= 1;
1732
+ entry.lastUsedAt = now();
1733
+ if (entry.pendingTeardown && entry.leases === 0) {
1734
+ void teardown(entry);
1735
+ }
1736
+ }
1737
+ }
1738
+ };
1739
+ }
1740
+ function acquire(input) {
1741
+ return _async_to_generator(function() {
1742
+ var nowMs, key, existing, entry, acquired, session, e;
1743
+ return _ts_generator(this, function(_state) {
1744
+ switch(_state.label){
1745
+ case 0:
1746
+ if (closed) {
1747
+ throw errorFactory({
1748
+ code: 'unavailable',
1749
+ message: 'The Firebase user session pool is closed.',
1750
+ suggestion: 'Do not reuse a pool after `close()`; build a new one.'
1751
+ });
1752
+ }
1753
+ nowMs = now();
1754
+ key = firebaseUserSessionAppName({
1755
+ namespace: namespace,
1756
+ scope: scope,
1757
+ uid: input.uid
1758
+ });
1759
+ existing = entries.get(key);
1760
+ if (existing != null && !existing.pendingTeardown && !isExpired(existing, nowMs)) {
1761
+ entry = existing;
1762
+ // leases + lastUsedAt are bumped SYNCHRONOUSLY, before any await, so a concurrent eviction
1763
+ // cannot tear down a session that is about to be handed out
1764
+ entry.leases += 1;
1765
+ entry.lastUsedAt = nowMs;
1766
+ emit('reused', entry);
1767
+ } else {
1768
+ if (existing != null) {
1769
+ detach(existing);
1770
+ emit('expired', existing);
1771
+ void teardownWhenIdle(existing);
1772
+ }
1773
+ entry = openEntry(input, key, nowMs);
1774
+ entry.leases += 1;
1775
+ entries.set(key, entry);
1776
+ emit('opened', entry);
1777
+ enforceCapacity(entry);
1778
+ }
1779
+ acquired = entry;
1780
+ _state.label = 1;
1781
+ case 1:
1782
+ _state.trys.push([
1783
+ 1,
1784
+ 3,
1785
+ ,
1786
+ 4
1787
+ ]);
1788
+ return [
1789
+ 4,
1790
+ acquired.loading
1791
+ ];
1792
+ case 2:
1793
+ session = _state.sent();
1794
+ return [
1795
+ 3,
1796
+ 4
1797
+ ];
1798
+ case 3:
1799
+ e = _state.sent();
1800
+ acquired.leases -= 1;
1801
+ throw e;
1802
+ case 4:
1803
+ return [
1804
+ 2,
1805
+ leaseFor(acquired, session)
1806
+ ];
1807
+ }
1808
+ });
1809
+ })();
1810
+ }
1811
+ return {
1812
+ openSession: acquire,
1813
+ useSession: function useSession(input, fn) {
1814
+ return _async_to_generator(function() {
1815
+ var lease;
1816
+ return _ts_generator(this, function(_state) {
1817
+ switch(_state.label){
1818
+ case 0:
1819
+ return [
1820
+ 4,
1821
+ acquire(input)
1822
+ ];
1823
+ case 1:
1824
+ lease = _state.sent();
1825
+ _state.label = 2;
1826
+ case 2:
1827
+ _state.trys.push([
1828
+ 2,
1829
+ ,
1830
+ 4,
1831
+ 5
1832
+ ]);
1833
+ return [
1834
+ 4,
1835
+ fn(lease.session)
1836
+ ];
1837
+ case 3:
1838
+ return [
1839
+ 2,
1840
+ _state.sent()
1841
+ ];
1842
+ case 4:
1843
+ lease.release();
1844
+ return [
1845
+ 7
1846
+ ];
1847
+ case 5:
1848
+ return [
1849
+ 2
1850
+ ];
1851
+ }
1852
+ });
1853
+ })();
1854
+ },
1855
+ closeSession: function closeSession(uid) {
1856
+ return _async_to_generator(function() {
1857
+ var entry;
1858
+ return _ts_generator(this, function(_state) {
1859
+ switch(_state.label){
1860
+ case 0:
1861
+ entry = entries.get(firebaseUserSessionAppName({
1862
+ namespace: namespace,
1863
+ scope: scope,
1864
+ uid: uid
1865
+ }));
1866
+ if (!(entry != null)) return [
1867
+ 3,
1868
+ 2
1869
+ ];
1870
+ detach(entry);
1871
+ return [
1872
+ 4,
1873
+ teardownWhenIdle(entry)
1874
+ ];
1875
+ case 1:
1876
+ _state.sent();
1877
+ _state.label = 2;
1878
+ case 2:
1879
+ return [
1880
+ 2
1881
+ ];
1882
+ }
1883
+ });
1884
+ })();
1885
+ },
1886
+ close: function close() {
1887
+ return _async_to_generator(function() {
1888
+ var all;
1889
+ return _ts_generator(this, function(_state) {
1890
+ switch(_state.label){
1891
+ case 0:
1892
+ closed = true;
1893
+ all = Array.from(entries.values());
1894
+ entries.clear();
1895
+ return [
1896
+ 4,
1897
+ Promise.all(all.map(function(entry) {
1898
+ return teardown(entry);
1899
+ }))
1900
+ ];
1901
+ case 1:
1902
+ _state.sent();
1903
+ return [
1904
+ 2
1905
+ ];
1906
+ }
1907
+ });
1908
+ })();
1909
+ },
1910
+ stats: function stats() {
1911
+ var leased = 0;
1912
+ entries.forEach(function(entry) {
1913
+ leased += entry.leases;
1914
+ });
1915
+ return {
1916
+ size: entries.size,
1917
+ leased: leased,
1918
+ maxSessions: maxSessions
1919
+ };
1920
+ }
1921
+ };
1922
+ }
1923
+
1924
+ export { CLIENT_FIRESTORE_DRIVER_IDENTIFIER, DEFAULT_FIREBASE_CLIENT_EMULATOR_HOST, DEFAULT_FIREBASE_USER_SESSION_POOL_SIZE, DEFAULT_FIRESTORE_IDENTITY_CONSUMER_NAME, FIREBASE_USER_SESSION_KEY_SEPARATOR, FIRESTORE_SESSION_API_PATH, FIRESTORE_SESSION_EXPIRY_BUFFER_MS, FIRESTORE_SESSION_MAX_CACHE_MS, FirestoreSessionError, closeFirebaseUserSession, closeFirebaseUserSessionApp, closeFirebaseUserSessionApps, defaultFirestoreSessionErrorFactory, fetchFirestoreSession, firebaseClientEmulatorsInUse, firebaseClientFirestoreIdentitySuggestion, firebaseUserSessionAppName, firebaseUserSessionNamespacePrefix, firebaseUserSessionPool, firestoreSessionEntryExpiresAt, firestoreSessionErrorCodeForStatus, firestoreSessionUrl, inspectFirebaseClientFirestoreIdentity, isFirebaseClientConfigComplete, isFirestoreSessionError, isFirestoreSessionExpired, openFirebaseUserSession };