@emmett-community/emmett-google-services-bootstrap 0.3.0 → 0.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.
- package/README.md +74 -1
- package/dist/ServiceBootstrap-Cn1LJQgC.d.cts +336 -0
- package/dist/ServiceBootstrap-Cn1LJQgC.d.ts +336 -0
- package/dist/chunk-G3V6OOZL.cjs +919 -0
- package/dist/chunk-G3V6OOZL.cjs.map +1 -0
- package/dist/chunk-XUV26TX4.js +919 -0
- package/dist/chunk-XUV26TX4.js.map +1 -0
- package/dist/index.cjs +52 -949
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -301
- package/dist/index.d.ts +18 -301
- package/dist/index.js +32 -892
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +49 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +31 -0
- package/dist/testing.d.ts +31 -0
- package/dist/testing.js +49 -0
- package/dist/testing.js.map +1 -0
- package/package.json +15 -5
|
@@ -0,0 +1,919 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;// src/firebase/FirebaseInitializer.ts
|
|
2
|
+
var _firebaseadmin = require('firebase-admin'); var _firebaseadmin2 = _interopRequireDefault(_firebaseadmin);
|
|
3
|
+
var normalizeContext = (data) => {
|
|
4
|
+
if (data === void 0 || data === null) return {};
|
|
5
|
+
if (typeof data === "object" && !Array.isArray(data)) {
|
|
6
|
+
return { ...data };
|
|
7
|
+
}
|
|
8
|
+
return { data };
|
|
9
|
+
};
|
|
10
|
+
var normalizeErrorContext = (error) => {
|
|
11
|
+
if (error === void 0 || error === null) return {};
|
|
12
|
+
if (error instanceof Error) return { err: error };
|
|
13
|
+
if (typeof error === "object" && !Array.isArray(error)) {
|
|
14
|
+
return { ...error };
|
|
15
|
+
}
|
|
16
|
+
return { err: error };
|
|
17
|
+
};
|
|
18
|
+
var safeLog = {
|
|
19
|
+
debug: (logger, msg, data) => {
|
|
20
|
+
if (!logger) return;
|
|
21
|
+
logger.debug(normalizeContext(data), msg);
|
|
22
|
+
},
|
|
23
|
+
info: (logger, msg, data) => {
|
|
24
|
+
if (!logger) return;
|
|
25
|
+
logger.info(normalizeContext(data), msg);
|
|
26
|
+
},
|
|
27
|
+
warn: (logger, msg, data) => {
|
|
28
|
+
if (!logger) return;
|
|
29
|
+
logger.warn(normalizeContext(data), msg);
|
|
30
|
+
},
|
|
31
|
+
error: (logger, msg, error) => {
|
|
32
|
+
if (!logger) return;
|
|
33
|
+
logger.error(normalizeErrorContext(error), msg);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var FirebaseInitializer = class {
|
|
37
|
+
|
|
38
|
+
constructor(options) {
|
|
39
|
+
this.options = options;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns true if emulator hosts are configured.
|
|
43
|
+
*/
|
|
44
|
+
isEmulatorMode() {
|
|
45
|
+
return Boolean(
|
|
46
|
+
this.options.firestoreEmulatorHost || this.options.databaseEmulatorHost || process.env.FIRESTORE_EMULATOR_HOST || process.env.FIREBASE_DATABASE_EMULATOR_HOST
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Initialize Firebase Admin and return service handles.
|
|
51
|
+
*/
|
|
52
|
+
async initialize() {
|
|
53
|
+
const {
|
|
54
|
+
projectId,
|
|
55
|
+
databaseURL,
|
|
56
|
+
firestoreEmulatorHost,
|
|
57
|
+
databaseEmulatorHost,
|
|
58
|
+
logger
|
|
59
|
+
} = this.options;
|
|
60
|
+
if (firestoreEmulatorHost && !process.env.FIRESTORE_EMULATOR_HOST) {
|
|
61
|
+
process.env.FIRESTORE_EMULATOR_HOST = firestoreEmulatorHost;
|
|
62
|
+
}
|
|
63
|
+
if (databaseEmulatorHost && !process.env.FIREBASE_DATABASE_EMULATOR_HOST) {
|
|
64
|
+
process.env.FIREBASE_DATABASE_EMULATOR_HOST = databaseEmulatorHost;
|
|
65
|
+
}
|
|
66
|
+
const resolvedDatabaseURL = _nullishCoalesce(databaseURL, () => ( this.getDatabaseURL(projectId, databaseEmulatorHost)));
|
|
67
|
+
const firebaseAdmin = _nullishCoalesce(this.options._firebaseAdmin, () => ( _firebaseadmin2.default));
|
|
68
|
+
const app = this.getOrCreateApp(firebaseAdmin, projectId, resolvedDatabaseURL);
|
|
69
|
+
safeLog.info(logger, "Firebase initialized", {
|
|
70
|
+
projectId,
|
|
71
|
+
databaseURL: resolvedDatabaseURL,
|
|
72
|
+
emulatorMode: this.isEmulatorMode()
|
|
73
|
+
});
|
|
74
|
+
const firestore = app.firestore();
|
|
75
|
+
const database = app.database();
|
|
76
|
+
const auth = app.auth();
|
|
77
|
+
if (firestoreEmulatorHost) {
|
|
78
|
+
firestore.settings({
|
|
79
|
+
host: firestoreEmulatorHost,
|
|
80
|
+
ssl: false
|
|
81
|
+
});
|
|
82
|
+
safeLog.info(logger, "Firestore emulator configured", {
|
|
83
|
+
host: firestoreEmulatorHost
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return { app, firestore, database, auth };
|
|
87
|
+
}
|
|
88
|
+
getOrCreateApp(firebaseAdmin, projectId, databaseURL) {
|
|
89
|
+
const apps = (_nullishCoalesce(firebaseAdmin.apps, () => ( []))).filter(
|
|
90
|
+
(app) => app !== null
|
|
91
|
+
);
|
|
92
|
+
if (apps.length > 0 && firebaseAdmin.app) {
|
|
93
|
+
safeLog.debug(this.options.logger, "Reusing existing Firebase app");
|
|
94
|
+
return firebaseAdmin.app();
|
|
95
|
+
}
|
|
96
|
+
safeLog.debug(this.options.logger, "Creating Firebase app");
|
|
97
|
+
return firebaseAdmin.initializeApp(
|
|
98
|
+
{
|
|
99
|
+
projectId,
|
|
100
|
+
databaseURL
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
getDatabaseURL(projectId, emulatorHost) {
|
|
105
|
+
if (emulatorHost) {
|
|
106
|
+
return `http://${emulatorHost}?ns=${projectId}`;
|
|
107
|
+
}
|
|
108
|
+
return `https://${projectId}-default-rtdb.firebaseio.com`;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/pubsub/PubSubInitializer.ts
|
|
113
|
+
var _pubsub2 = require('@google-cloud/pubsub');
|
|
114
|
+
var normalizeContext2 = (data) => {
|
|
115
|
+
if (data === void 0 || data === null) return {};
|
|
116
|
+
if (typeof data === "object" && !Array.isArray(data)) {
|
|
117
|
+
return { ...data };
|
|
118
|
+
}
|
|
119
|
+
return { data };
|
|
120
|
+
};
|
|
121
|
+
var normalizeErrorContext2 = (error) => {
|
|
122
|
+
if (error === void 0 || error === null) return {};
|
|
123
|
+
if (error instanceof Error) return { err: error };
|
|
124
|
+
if (typeof error === "object" && !Array.isArray(error)) {
|
|
125
|
+
return { ...error };
|
|
126
|
+
}
|
|
127
|
+
return { err: error };
|
|
128
|
+
};
|
|
129
|
+
var safeLog2 = {
|
|
130
|
+
debug: (logger, msg, data) => {
|
|
131
|
+
if (!logger) return;
|
|
132
|
+
logger.debug(normalizeContext2(data), msg);
|
|
133
|
+
},
|
|
134
|
+
info: (logger, msg, data) => {
|
|
135
|
+
if (!logger) return;
|
|
136
|
+
logger.info(normalizeContext2(data), msg);
|
|
137
|
+
},
|
|
138
|
+
warn: (logger, msg, data) => {
|
|
139
|
+
if (!logger) return;
|
|
140
|
+
logger.warn(normalizeContext2(data), msg);
|
|
141
|
+
},
|
|
142
|
+
error: (logger, msg, error) => {
|
|
143
|
+
if (!logger) return;
|
|
144
|
+
logger.error(normalizeErrorContext2(error), msg);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var PubSubInitializer = class {
|
|
148
|
+
|
|
149
|
+
constructor(options) {
|
|
150
|
+
this.options = options;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Returns true if emulator host is configured.
|
|
154
|
+
*/
|
|
155
|
+
isEmulatorMode() {
|
|
156
|
+
return Boolean(
|
|
157
|
+
this.options.emulatorHost || process.env.PUBSUB_EMULATOR_HOST
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Initialize PubSub client or return injected instance.
|
|
162
|
+
*/
|
|
163
|
+
async initialize() {
|
|
164
|
+
const { projectId, emulatorHost, logger } = this.options;
|
|
165
|
+
if (emulatorHost && !process.env.PUBSUB_EMULATOR_HOST) {
|
|
166
|
+
process.env.PUBSUB_EMULATOR_HOST = emulatorHost;
|
|
167
|
+
}
|
|
168
|
+
if (this.options._pubsub) {
|
|
169
|
+
safeLog2.info(logger, "Using provided PubSub client", {
|
|
170
|
+
projectId,
|
|
171
|
+
emulatorHost: _nullishCoalesce(emulatorHost, () => ( process.env.PUBSUB_EMULATOR_HOST))
|
|
172
|
+
});
|
|
173
|
+
return this.options._pubsub;
|
|
174
|
+
}
|
|
175
|
+
const pubsub = new (0, _pubsub2.PubSub)({ projectId });
|
|
176
|
+
safeLog2.info(logger, "PubSub initialized", {
|
|
177
|
+
projectId,
|
|
178
|
+
emulatorHost: _nullishCoalesce(emulatorHost, () => ( process.env.PUBSUB_EMULATOR_HOST)),
|
|
179
|
+
emulatorMode: this.isEmulatorMode()
|
|
180
|
+
});
|
|
181
|
+
return pubsub;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// src/eventstore/EventStoreFactory.ts
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
var _emmettgooglefirestore = require('@emmett-community/emmett-google-firestore');
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
var _emmettgooglerealtimedb = require('@emmett-community/emmett-google-realtime-db');
|
|
193
|
+
var createEventStore = (options) => {
|
|
194
|
+
const { firestore, database, projections, logger, collections } = options;
|
|
195
|
+
const observability = logger ? { logger } : void 0;
|
|
196
|
+
const baseEventStore = _emmettgooglefirestore.asEventStore.call(void 0,
|
|
197
|
+
_emmettgooglefirestore.getFirestoreEventStore.call(void 0, firestore, {
|
|
198
|
+
observability,
|
|
199
|
+
collections
|
|
200
|
+
})
|
|
201
|
+
);
|
|
202
|
+
if (projections && projections.length > 0) {
|
|
203
|
+
return _emmettgooglerealtimedb.wireRealtimeDBProjections.call(void 0, {
|
|
204
|
+
eventStore: baseEventStore,
|
|
205
|
+
database,
|
|
206
|
+
projections,
|
|
207
|
+
observability
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return baseEventStore;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// src/lifecycle/DependencyChecker.ts
|
|
214
|
+
var DependencyTimeoutError = class extends Error {
|
|
215
|
+
constructor(timeoutMs) {
|
|
216
|
+
super(`Dependency check timed out after ${timeoutMs}ms`);
|
|
217
|
+
this.name = "DependencyTimeoutError";
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
var normalizeErrorContext3 = (error) => {
|
|
221
|
+
if (error === void 0 || error === null) return {};
|
|
222
|
+
if (error instanceof Error) return { err: error };
|
|
223
|
+
if (typeof error === "object" && !Array.isArray(error)) {
|
|
224
|
+
return { ...error };
|
|
225
|
+
}
|
|
226
|
+
return { err: error };
|
|
227
|
+
};
|
|
228
|
+
var withTimeout = async (promise, timeoutMs) => {
|
|
229
|
+
if (!timeoutMs || timeoutMs <= 0) return promise;
|
|
230
|
+
let timeoutId;
|
|
231
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
232
|
+
timeoutId = setTimeout(() => {
|
|
233
|
+
reject(new DependencyTimeoutError(timeoutMs));
|
|
234
|
+
}, timeoutMs);
|
|
235
|
+
});
|
|
236
|
+
try {
|
|
237
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
238
|
+
} finally {
|
|
239
|
+
if (timeoutId) {
|
|
240
|
+
clearTimeout(timeoutId);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
var startDependency = async (dependency, startFn, options) => {
|
|
245
|
+
const { logger, successMessage, timeoutMs } = _nullishCoalesce(options, () => ( {}));
|
|
246
|
+
const startTime = Date.now();
|
|
247
|
+
try {
|
|
248
|
+
await withTimeout(startFn(), timeoutMs);
|
|
249
|
+
_optionalChain([logger, 'optionalAccess', _2 => _2.info, 'call', _3 => _3({ dependency, durationMs: Date.now() - startTime }, successMessage)]);
|
|
250
|
+
return true;
|
|
251
|
+
} catch (error) {
|
|
252
|
+
_optionalChain([logger, 'optionalAccess', _4 => _4.warn, 'call', _5 => _5(
|
|
253
|
+
{ dependency, durationMs: Date.now() - startTime, ...normalizeErrorContext3(error) },
|
|
254
|
+
"Unable to reach dependency"
|
|
255
|
+
)]);
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
var DependencyChecker = class {
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
constructor(options) {
|
|
263
|
+
this.logger = _optionalChain([options, 'optionalAccess', _6 => _6.logger]);
|
|
264
|
+
this.defaultTimeoutMs = _optionalChain([options, 'optionalAccess', _7 => _7.defaultTimeoutMs]);
|
|
265
|
+
}
|
|
266
|
+
async check(dependency) {
|
|
267
|
+
const start = Date.now();
|
|
268
|
+
const timeoutMs = _nullishCoalesce(dependency.timeoutMs, () => ( this.defaultTimeoutMs));
|
|
269
|
+
try {
|
|
270
|
+
await withTimeout(dependency.check(), timeoutMs);
|
|
271
|
+
if (dependency.successMessage) {
|
|
272
|
+
_optionalChain([this, 'access', _8 => _8.logger, 'optionalAccess', _9 => _9.info, 'call', _10 => _10(
|
|
273
|
+
{ dependency: dependency.name, durationMs: Date.now() - start },
|
|
274
|
+
dependency.successMessage
|
|
275
|
+
)]);
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
name: dependency.name,
|
|
279
|
+
ok: true,
|
|
280
|
+
required: _nullishCoalesce(dependency.required, () => ( true)),
|
|
281
|
+
durationMs: Date.now() - start,
|
|
282
|
+
timedOut: false
|
|
283
|
+
};
|
|
284
|
+
} catch (error) {
|
|
285
|
+
const timedOut = error instanceof DependencyTimeoutError;
|
|
286
|
+
_optionalChain([this, 'access', _11 => _11.logger, 'optionalAccess', _12 => _12.warn, 'call', _13 => _13(
|
|
287
|
+
{
|
|
288
|
+
dependency: dependency.name,
|
|
289
|
+
durationMs: Date.now() - start,
|
|
290
|
+
timedOut,
|
|
291
|
+
...normalizeErrorContext3(error)
|
|
292
|
+
},
|
|
293
|
+
"Dependency check failed"
|
|
294
|
+
)]);
|
|
295
|
+
return {
|
|
296
|
+
name: dependency.name,
|
|
297
|
+
ok: false,
|
|
298
|
+
required: _nullishCoalesce(dependency.required, () => ( true)),
|
|
299
|
+
durationMs: Date.now() - start,
|
|
300
|
+
timedOut,
|
|
301
|
+
error
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async checkAll(dependencies) {
|
|
306
|
+
const results = [];
|
|
307
|
+
for (const dependency of dependencies) {
|
|
308
|
+
results.push(await this.check(dependency));
|
|
309
|
+
}
|
|
310
|
+
const ok = results.every((result) => result.ok || !result.required);
|
|
311
|
+
return { ok, results };
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// src/lifecycle/GracefulShutdown.ts
|
|
316
|
+
var ShutdownTimeoutError = class extends Error {
|
|
317
|
+
constructor(timeoutMs) {
|
|
318
|
+
super(`Shutdown task timed out after ${timeoutMs}ms`);
|
|
319
|
+
this.name = "ShutdownTimeoutError";
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
var normalizeContext3 = (data) => {
|
|
323
|
+
if (data === void 0 || data === null) return {};
|
|
324
|
+
if (typeof data === "object" && !Array.isArray(data)) {
|
|
325
|
+
return { ...data };
|
|
326
|
+
}
|
|
327
|
+
return { data };
|
|
328
|
+
};
|
|
329
|
+
var normalizeErrorContext4 = (error) => {
|
|
330
|
+
if (error === void 0 || error === null) return {};
|
|
331
|
+
if (error instanceof Error) return { err: error };
|
|
332
|
+
if (typeof error === "object" && !Array.isArray(error)) {
|
|
333
|
+
return { ...error };
|
|
334
|
+
}
|
|
335
|
+
return { err: error };
|
|
336
|
+
};
|
|
337
|
+
var withTimeout2 = async (promise, timeoutMs) => {
|
|
338
|
+
if (!timeoutMs || timeoutMs <= 0) return promise;
|
|
339
|
+
let timeoutId;
|
|
340
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
341
|
+
timeoutId = setTimeout(() => {
|
|
342
|
+
reject(new ShutdownTimeoutError(timeoutMs));
|
|
343
|
+
}, timeoutMs);
|
|
344
|
+
});
|
|
345
|
+
try {
|
|
346
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
347
|
+
} finally {
|
|
348
|
+
if (timeoutId) {
|
|
349
|
+
clearTimeout(timeoutId);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
var GracefulShutdown = (_class = class {
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
__init() {this.tasks = []}
|
|
359
|
+
__init2() {this.shuttingDown = false}
|
|
360
|
+
|
|
361
|
+
constructor(options) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);
|
|
362
|
+
this.logger = _optionalChain([options, 'optionalAccess', _14 => _14.logger]);
|
|
363
|
+
this.timeoutMs = _optionalChain([options, 'optionalAccess', _15 => _15.timeoutMs]);
|
|
364
|
+
this.exitOnComplete = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _16 => _16.exitOnComplete]), () => ( false));
|
|
365
|
+
this.exitFn = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _17 => _17.exitFn]), () => ( process.exit));
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Register a shutdown task.
|
|
369
|
+
*/
|
|
370
|
+
register(task) {
|
|
371
|
+
this.tasks.push(task);
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Register a shutdown task that runs before the ones already registered.
|
|
375
|
+
*/
|
|
376
|
+
registerFirst(task) {
|
|
377
|
+
this.tasks.unshift(task);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Execute all shutdown tasks and return the aggregated result.
|
|
381
|
+
*/
|
|
382
|
+
async shutdown(reason, exitCode = 1) {
|
|
383
|
+
if (this.shuttingDown && this.lastResult) {
|
|
384
|
+
return this.lastResult;
|
|
385
|
+
}
|
|
386
|
+
this.shuttingDown = true;
|
|
387
|
+
_optionalChain([this, 'access', _18 => _18.logger, 'optionalAccess', _19 => _19.info, 'call', _20 => _20({ reason }, "Shutting down gracefully")]);
|
|
388
|
+
const results = [];
|
|
389
|
+
for (const task of this.tasks) {
|
|
390
|
+
const start = Date.now();
|
|
391
|
+
const timeoutMs = _nullishCoalesce(task.timeoutMs, () => ( this.timeoutMs));
|
|
392
|
+
try {
|
|
393
|
+
await withTimeout2(task.close(), timeoutMs);
|
|
394
|
+
results.push({
|
|
395
|
+
name: task.name,
|
|
396
|
+
ok: true,
|
|
397
|
+
timedOut: false,
|
|
398
|
+
durationMs: Date.now() - start
|
|
399
|
+
});
|
|
400
|
+
_optionalChain([this, 'access', _21 => _21.logger, 'optionalAccess', _22 => _22.info, 'call', _23 => _23(
|
|
401
|
+
{ task: task.name, durationMs: Date.now() - start },
|
|
402
|
+
"Shutdown task completed"
|
|
403
|
+
)]);
|
|
404
|
+
} catch (error) {
|
|
405
|
+
const timedOut = error instanceof ShutdownTimeoutError;
|
|
406
|
+
results.push({
|
|
407
|
+
name: task.name,
|
|
408
|
+
ok: false,
|
|
409
|
+
timedOut,
|
|
410
|
+
durationMs: Date.now() - start,
|
|
411
|
+
error
|
|
412
|
+
});
|
|
413
|
+
_optionalChain([this, 'access', _24 => _24.logger, 'optionalAccess', _25 => _25.warn, 'call', _26 => _26(
|
|
414
|
+
{
|
|
415
|
+
task: task.name,
|
|
416
|
+
timedOut,
|
|
417
|
+
durationMs: Date.now() - start,
|
|
418
|
+
...normalizeErrorContext4(error)
|
|
419
|
+
},
|
|
420
|
+
"Shutdown task failed"
|
|
421
|
+
)]);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const failed = results.some((result2) => !result2.ok || result2.timedOut);
|
|
425
|
+
const finalExitCode = failed ? exitCode : 0;
|
|
426
|
+
if (failed) {
|
|
427
|
+
const failedTasks = results.filter((result2) => !result2.ok || result2.timedOut).map((result2) => result2.name);
|
|
428
|
+
_optionalChain([this, 'access', _27 => _27.logger, 'optionalAccess', _28 => _28.error, 'call', _29 => _29(
|
|
429
|
+
normalizeContext3({ failedTasks }),
|
|
430
|
+
"Shutdown completed with failures"
|
|
431
|
+
)]);
|
|
432
|
+
} else {
|
|
433
|
+
_optionalChain([this, 'access', _30 => _30.logger, 'optionalAccess', _31 => _31.info, 'call', _32 => _32({}, "Shutdown completed")]);
|
|
434
|
+
}
|
|
435
|
+
const result = { reason, exitCode: finalExitCode, tasks: results };
|
|
436
|
+
this.lastResult = result;
|
|
437
|
+
if (this.exitOnComplete) {
|
|
438
|
+
this.exitFn(finalExitCode);
|
|
439
|
+
}
|
|
440
|
+
return result;
|
|
441
|
+
}
|
|
442
|
+
}, _class);
|
|
443
|
+
|
|
444
|
+
// src/lifecycle/SignalHandlers.ts
|
|
445
|
+
var registerSignalHandlers = (options) => {
|
|
446
|
+
const signals = _nullishCoalesce(options.signals, () => ( ["SIGINT", "SIGTERM"]));
|
|
447
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
448
|
+
for (const signal of signals) {
|
|
449
|
+
const handler = () => {
|
|
450
|
+
_optionalChain([options, 'access', _33 => _33.logger, 'optionalAccess', _34 => _34.info, 'call', _35 => _35({ signal }, "Shutdown signal received")]);
|
|
451
|
+
void options.shutdown(`Signal ${signal} received`, _nullishCoalesce(options.exitCode, () => ( 0)));
|
|
452
|
+
};
|
|
453
|
+
handlers.set(signal, handler);
|
|
454
|
+
process.on(signal, handler);
|
|
455
|
+
}
|
|
456
|
+
return () => {
|
|
457
|
+
for (const [signal, handler] of handlers.entries()) {
|
|
458
|
+
process.off(signal, handler);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
// src/bootstrap/ServiceBootstrap.ts
|
|
464
|
+
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
465
|
+
var _emmettobservability = require('@emmett-community/emmett-observability');
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
var _emmettexpressjswithopenapi = require('@emmett-community/emmett-expressjs-with-openapi');
|
|
471
|
+
var _emmettgooglepubsub = require('@emmett-community/emmett-google-pubsub');
|
|
472
|
+
var DependencyUnavailableError = class extends Error {
|
|
473
|
+
|
|
474
|
+
constructor(summary) {
|
|
475
|
+
const failed = summary.results.filter((result) => result.required && !result.ok).map((result) => result.name).join(", ");
|
|
476
|
+
super(`Dependency check failed: ${failed}`);
|
|
477
|
+
this.name = "DependencyUnavailableError";
|
|
478
|
+
this.summary = summary;
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
var READINESS_CHECK_TIMEOUT_MS = 3e3;
|
|
482
|
+
var resolvePath = (inputPath) => {
|
|
483
|
+
return _path2.default.isAbsolute(inputPath) ? inputPath : _path2.default.resolve(process.cwd(), inputPath);
|
|
484
|
+
};
|
|
485
|
+
var ServiceBootstrap = class {
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
constructor(config) {
|
|
493
|
+
this.config = config;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Resolve configuration with defaults and environment variables.
|
|
497
|
+
*/
|
|
498
|
+
getConfig() {
|
|
499
|
+
return this.resolveConfig();
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Return the initialized context if available.
|
|
503
|
+
*/
|
|
504
|
+
getContext() {
|
|
505
|
+
return this.context;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Register process signal handlers that trigger shutdown.
|
|
509
|
+
*/
|
|
510
|
+
registerSignalHandlers(options) {
|
|
511
|
+
return registerSignalHandlers({
|
|
512
|
+
shutdown: async (reason, exitCode) => {
|
|
513
|
+
await this.shutdown(reason, exitCode);
|
|
514
|
+
},
|
|
515
|
+
signals: _optionalChain([options, 'optionalAccess', _36 => _36.signals]),
|
|
516
|
+
logger: this.getLogger(),
|
|
517
|
+
exitCode: _optionalChain([options, 'optionalAccess', _37 => _37.exitCode])
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Create an Express app using emmett-expressjs-with-openapi defaults.
|
|
522
|
+
*/
|
|
523
|
+
async createApp(options) {
|
|
524
|
+
const logger = this.getLogger();
|
|
525
|
+
const openApiValidator = _emmettexpressjswithopenapi.createOpenApiValidatorOptions.call(void 0,
|
|
526
|
+
resolvePath(options.openApiPath),
|
|
527
|
+
{
|
|
528
|
+
...options.openApiOptions,
|
|
529
|
+
operationHandlers: resolvePath(options.handlersPath),
|
|
530
|
+
initializeHandlers: options.initializeHandlers
|
|
531
|
+
}
|
|
532
|
+
);
|
|
533
|
+
return _emmettexpressjswithopenapi.getApplication.call(void 0, {
|
|
534
|
+
..._nullishCoalesce(options.appOptions, () => ( {})),
|
|
535
|
+
openApiValidator,
|
|
536
|
+
observability: logger ? { logger } : void 0
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Initialize, run dependency checks, create the app, and start the HTTP server.
|
|
541
|
+
*/
|
|
542
|
+
async startApi(options) {
|
|
543
|
+
const {
|
|
544
|
+
port,
|
|
545
|
+
registerSignalHandlers: registerSignalHandlers2,
|
|
546
|
+
beforeStart,
|
|
547
|
+
initializeHandlers,
|
|
548
|
+
...createAppOptions
|
|
549
|
+
} = options;
|
|
550
|
+
const logger = this.getLogger();
|
|
551
|
+
try {
|
|
552
|
+
const ctx = await this.initialize();
|
|
553
|
+
if (beforeStart) {
|
|
554
|
+
await beforeStart(ctx);
|
|
555
|
+
}
|
|
556
|
+
if (registerSignalHandlers2 !== false) {
|
|
557
|
+
this.registerSignalHandlers();
|
|
558
|
+
}
|
|
559
|
+
await this.start();
|
|
560
|
+
const app = await this.createApp({
|
|
561
|
+
...createAppOptions,
|
|
562
|
+
initializeHandlers: (handlers) => initializeHandlers(handlers, ctx)
|
|
563
|
+
});
|
|
564
|
+
const server = _emmettexpressjswithopenapi.startAPI.call(void 0, app, { port, logger });
|
|
565
|
+
this.getShutdownManager().registerFirst({
|
|
566
|
+
name: "httpServer",
|
|
567
|
+
close: () => new Promise((resolve, reject) => {
|
|
568
|
+
server.close((error) => {
|
|
569
|
+
if (error) {
|
|
570
|
+
reject(error);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
resolve();
|
|
574
|
+
});
|
|
575
|
+
})
|
|
576
|
+
});
|
|
577
|
+
_optionalChain([logger, 'optionalAccess', _38 => _38.info, 'call', _39 => _39({ port: _nullishCoalesce(port, () => ( 3e3)) }, "API server started")]);
|
|
578
|
+
return { app, server, context: ctx };
|
|
579
|
+
} catch (error) {
|
|
580
|
+
_optionalChain([logger, 'optionalAccess', _40 => _40.error, 'call', _41 => _41({ err: error }, "Startup failed")]);
|
|
581
|
+
await this.shutdown("Startup failed");
|
|
582
|
+
throw error;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Initialize Firebase, PubSub, and event store wiring.
|
|
587
|
+
* Does not execute dependency checks.
|
|
588
|
+
*/
|
|
589
|
+
async initialize() {
|
|
590
|
+
if (this.context) {
|
|
591
|
+
return this.context;
|
|
592
|
+
}
|
|
593
|
+
const resolvedConfig = this.resolveConfig();
|
|
594
|
+
const logger = this.getLogger();
|
|
595
|
+
const firebaseInitializer = new FirebaseInitializer({
|
|
596
|
+
projectId: resolvedConfig.firebase.projectId,
|
|
597
|
+
databaseURL: resolvedConfig.firebase.databaseURL,
|
|
598
|
+
firestoreEmulatorHost: resolvedConfig.firebase.firestoreEmulatorHost,
|
|
599
|
+
databaseEmulatorHost: resolvedConfig.firebase.databaseEmulatorHost,
|
|
600
|
+
logger,
|
|
601
|
+
_firebaseAdmin: _optionalChain([resolvedConfig, 'access', _42 => _42._testOverrides, 'optionalAccess', _43 => _43.firebaseAdmin])
|
|
602
|
+
});
|
|
603
|
+
const firebase = await firebaseInitializer.initialize();
|
|
604
|
+
const firestore = _nullishCoalesce(_optionalChain([resolvedConfig, 'access', _44 => _44._testOverrides, 'optionalAccess', _45 => _45.firestore]), () => ( firebase.firestore));
|
|
605
|
+
const database = _nullishCoalesce(_optionalChain([resolvedConfig, 'access', _46 => _46._testOverrides, 'optionalAccess', _47 => _47.database]), () => ( firebase.database));
|
|
606
|
+
const auth = _nullishCoalesce(_optionalChain([resolvedConfig, 'access', _48 => _48._testOverrides, 'optionalAccess', _49 => _49.auth]), () => ( firebase.auth));
|
|
607
|
+
const pubsubInitializer = new PubSubInitializer({
|
|
608
|
+
projectId: resolvedConfig.pubsub.projectId,
|
|
609
|
+
emulatorHost: resolvedConfig.pubsub.emulatorHost,
|
|
610
|
+
logger,
|
|
611
|
+
_pubsub: _optionalChain([resolvedConfig, 'access', _50 => _50._testOverrides, 'optionalAccess', _51 => _51.pubsub])
|
|
612
|
+
});
|
|
613
|
+
const pubsub = await pubsubInitializer.initialize();
|
|
614
|
+
const eventStore = _nullishCoalesce(_optionalChain([resolvedConfig, 'access', _52 => _52._testOverrides, 'optionalAccess', _53 => _53.eventStore]), () => ( createEventStore({
|
|
615
|
+
firestore,
|
|
616
|
+
database,
|
|
617
|
+
projections: resolvedConfig.projections,
|
|
618
|
+
collections: resolvedConfig.eventStore.collections,
|
|
619
|
+
logger
|
|
620
|
+
})));
|
|
621
|
+
const messageBus = _nullishCoalesce(_optionalChain([resolvedConfig, 'access', _54 => _54._testOverrides, 'optionalAccess', _55 => _55.messageBus]), () => ( _emmettgooglepubsub.getPubSubMessageBus.call(void 0, {
|
|
622
|
+
pubsub,
|
|
623
|
+
useEmulator: pubsubInitializer.isEmulatorMode(),
|
|
624
|
+
topicPrefix: resolvedConfig.pubsub.topicPrefix,
|
|
625
|
+
// Event subscriptions are shared by the replicas of the service and survive restarts
|
|
626
|
+
subscriptionId: resolvedConfig.pubsub.subscriptionId,
|
|
627
|
+
// Command subscriptions are shared too, so a command has one handler
|
|
628
|
+
instanceId: resolvedConfig.pubsub.subscriptionId,
|
|
629
|
+
subscriptionOptions: resolvedConfig.pubsub.subscriptionOptions,
|
|
630
|
+
autoCreateResources: resolvedConfig.pubsub.autoCreateResources,
|
|
631
|
+
cleanupOnClose: resolvedConfig.pubsub.cleanupOnClose,
|
|
632
|
+
closePubSubClient: resolvedConfig.pubsub.closePubSubClient,
|
|
633
|
+
observability: { logger }
|
|
634
|
+
})));
|
|
635
|
+
this.shutdownManager = new GracefulShutdown({
|
|
636
|
+
...resolvedConfig.shutdown,
|
|
637
|
+
logger
|
|
638
|
+
});
|
|
639
|
+
this.registerDefaultShutdownTasks({
|
|
640
|
+
messageBus,
|
|
641
|
+
firestore,
|
|
642
|
+
firebaseApp: firebase.app
|
|
643
|
+
});
|
|
644
|
+
this.dependencyChecker = new DependencyChecker({ logger });
|
|
645
|
+
this.context = {
|
|
646
|
+
serviceName: resolvedConfig.serviceName,
|
|
647
|
+
firebase: {
|
|
648
|
+
app: firebase.app,
|
|
649
|
+
firestore,
|
|
650
|
+
database,
|
|
651
|
+
auth
|
|
652
|
+
},
|
|
653
|
+
firestore,
|
|
654
|
+
database,
|
|
655
|
+
auth,
|
|
656
|
+
pubsub,
|
|
657
|
+
eventStore,
|
|
658
|
+
messageBus,
|
|
659
|
+
logger,
|
|
660
|
+
getCurrentTime: resolvedConfig.getCurrentTime,
|
|
661
|
+
config: resolvedConfig,
|
|
662
|
+
checkReadiness: () => this.checkReadiness()
|
|
663
|
+
};
|
|
664
|
+
return this.context;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Initialize and run dependency checks before returning context.
|
|
668
|
+
*/
|
|
669
|
+
async start() {
|
|
670
|
+
const ctx = await this.initialize();
|
|
671
|
+
const resolvedConfig = this.resolveConfig();
|
|
672
|
+
const checks = [];
|
|
673
|
+
if (resolvedConfig.includeDefaultDependencyChecks) {
|
|
674
|
+
checks.push(...this.buildDefaultDependencyChecks(ctx));
|
|
675
|
+
}
|
|
676
|
+
if (resolvedConfig.dependencies.length > 0) {
|
|
677
|
+
checks.push(...resolvedConfig.dependencies);
|
|
678
|
+
}
|
|
679
|
+
if (checks.length > 0) {
|
|
680
|
+
const summary = await this.getDependencyChecker().checkAll(checks);
|
|
681
|
+
if (!summary.ok) {
|
|
682
|
+
const error = new DependencyUnavailableError(summary);
|
|
683
|
+
_optionalChain([this, 'access', _56 => _56.logger, 'optionalAccess', _57 => _57.error, 'call', _58 => _58(
|
|
684
|
+
{
|
|
685
|
+
err: error,
|
|
686
|
+
results: summary.results
|
|
687
|
+
},
|
|
688
|
+
"Dependency checks failed"
|
|
689
|
+
)]);
|
|
690
|
+
if (resolvedConfig.shutdownOnDependencyFailure) {
|
|
691
|
+
await this.shutdown("Dependency check failed");
|
|
692
|
+
}
|
|
693
|
+
throw error;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
_optionalChain([this, 'access', _59 => _59.logger, 'optionalAccess', _60 => _60.info, 'call', _61 => _61(
|
|
697
|
+
{ serviceName: resolvedConfig.serviceName },
|
|
698
|
+
"Service is running"
|
|
699
|
+
)]);
|
|
700
|
+
return ctx;
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Run the readiness checks: the stores and the message bus must answer, plus the configured
|
|
704
|
+
* dependencies. A hung dependency fails the check instead of hanging the probe.
|
|
705
|
+
*/
|
|
706
|
+
async checkReadiness() {
|
|
707
|
+
const ctx = await this.initialize();
|
|
708
|
+
const resolvedConfig = this.resolveConfig();
|
|
709
|
+
const checks = [];
|
|
710
|
+
if (resolvedConfig.includeDefaultDependencyChecks) {
|
|
711
|
+
checks.push(...this.buildStoreDependencyChecks(ctx));
|
|
712
|
+
if (resolvedConfig.autoStartMessageBus) {
|
|
713
|
+
checks.push({
|
|
714
|
+
name: "PubSub",
|
|
715
|
+
check: async () => {
|
|
716
|
+
if (!_optionalChain([ctx, 'access', _62 => _62.messageBus, 'access', _63 => _63.isStarted, 'optionalCall', _64 => _64()])) {
|
|
717
|
+
throw new Error("Message bus is not started");
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
checks.push(...resolvedConfig.dependencies);
|
|
724
|
+
return this.getDependencyChecker().checkAll(
|
|
725
|
+
checks.map((check) => ({
|
|
726
|
+
...check,
|
|
727
|
+
timeoutMs: _nullishCoalesce(check.timeoutMs, () => ( READINESS_CHECK_TIMEOUT_MS))
|
|
728
|
+
}))
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Execute graceful shutdown tasks and return the shutdown result.
|
|
733
|
+
*/
|
|
734
|
+
async shutdown(reason, exitCode = 1) {
|
|
735
|
+
return this.getShutdownManager().shutdown(reason, exitCode);
|
|
736
|
+
}
|
|
737
|
+
getLogger() {
|
|
738
|
+
if (this.logger !== void 0) {
|
|
739
|
+
return this.logger;
|
|
740
|
+
}
|
|
741
|
+
const resolvedConfig = this.resolveConfig();
|
|
742
|
+
const observability = resolvedConfig.observability;
|
|
743
|
+
if (observability.logger) {
|
|
744
|
+
this.logger = observability.logger;
|
|
745
|
+
return this.logger;
|
|
746
|
+
}
|
|
747
|
+
if (observability.createLogger === false) {
|
|
748
|
+
this.logger = void 0;
|
|
749
|
+
return this.logger;
|
|
750
|
+
}
|
|
751
|
+
this.logger = _emmettobservability.createLogger.call(void 0, {
|
|
752
|
+
serviceName: resolvedConfig.serviceName,
|
|
753
|
+
environment: observability.environment,
|
|
754
|
+
logLevel: observability.logLevel
|
|
755
|
+
});
|
|
756
|
+
return this.logger;
|
|
757
|
+
}
|
|
758
|
+
resolveConfig() {
|
|
759
|
+
if (this.resolvedConfig) {
|
|
760
|
+
return this.resolvedConfig;
|
|
761
|
+
}
|
|
762
|
+
const missing = [];
|
|
763
|
+
const serviceName = _optionalChain([this, 'access', _65 => _65.config, 'access', _66 => _66.serviceName, 'optionalAccess', _67 => _67.trim, 'call', _68 => _68()]);
|
|
764
|
+
if (!serviceName) {
|
|
765
|
+
missing.push("serviceName");
|
|
766
|
+
}
|
|
767
|
+
const firestoreEmulatorHost = _nullishCoalesce(_optionalChain([this, 'access', _69 => _69.config, 'access', _70 => _70.firebase, 'optionalAccess', _71 => _71.firestoreEmulatorHost]), () => ( process.env.FIRESTORE_EMULATOR_HOST));
|
|
768
|
+
const databaseEmulatorHost = _nullishCoalesce(_optionalChain([this, 'access', _72 => _72.config, 'access', _73 => _73.firebase, 'optionalAccess', _74 => _74.databaseEmulatorHost]), () => ( process.env.FIREBASE_DATABASE_EMULATOR_HOST));
|
|
769
|
+
const pubsubEmulatorHost = _nullishCoalesce(_optionalChain([this, 'access', _75 => _75.config, 'access', _76 => _76.pubsub, 'optionalAccess', _77 => _77.emulatorHost]), () => ( process.env.PUBSUB_EMULATOR_HOST));
|
|
770
|
+
const hasEmulator = Boolean(firestoreEmulatorHost) || Boolean(databaseEmulatorHost) || Boolean(pubsubEmulatorHost);
|
|
771
|
+
const firebaseProjectId = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([this, 'access', _78 => _78.config, 'access', _79 => _79.firebase, 'optionalAccess', _80 => _80.projectId]), () => ( process.env.FIRESTORE_PROJECT_ID)), () => ( process.env.GCLOUD_PROJECT)), () => ( process.env.GOOGLE_CLOUD_PROJECT)), () => ( (hasEmulator ? "demo-project" : void 0)));
|
|
772
|
+
if (!firebaseProjectId) {
|
|
773
|
+
missing.push("firebase.projectId (or FIRESTORE_PROJECT_ID)");
|
|
774
|
+
}
|
|
775
|
+
const pubsubProjectId = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([this, 'access', _81 => _81.config, 'access', _82 => _82.pubsub, 'optionalAccess', _83 => _83.projectId]), () => ( process.env.PUBSUB_PROJECT_ID)), () => ( firebaseProjectId)), () => ( (hasEmulator ? "demo-project" : void 0)));
|
|
776
|
+
if (!pubsubProjectId) {
|
|
777
|
+
missing.push("pubsub.projectId (or PUBSUB_PROJECT_ID)");
|
|
778
|
+
}
|
|
779
|
+
if (missing.length > 0) {
|
|
780
|
+
throw new Error(`Missing required configuration: ${missing.join(", ")}`);
|
|
781
|
+
}
|
|
782
|
+
const resolvedServiceName = serviceName;
|
|
783
|
+
const resolvedFirebaseProjectId = firebaseProjectId;
|
|
784
|
+
const resolvedPubsubProjectId = pubsubProjectId;
|
|
785
|
+
const databaseURL = _nullishCoalesce(_optionalChain([this, 'access', _84 => _84.config, 'access', _85 => _85.firebase, 'optionalAccess', _86 => _86.databaseURL]), () => ( this.getDatabaseURL(resolvedFirebaseProjectId, databaseEmulatorHost)));
|
|
786
|
+
const observability = {
|
|
787
|
+
...this.config.observability,
|
|
788
|
+
environment: _nullishCoalesce(_optionalChain([this, 'access', _87 => _87.config, 'access', _88 => _88.observability, 'optionalAccess', _89 => _89.environment]), () => ( process.env.NODE_ENV)),
|
|
789
|
+
logLevel: _nullishCoalesce(_optionalChain([this, 'access', _90 => _90.config, 'access', _91 => _91.observability, 'optionalAccess', _92 => _92.logLevel]), () => ( process.env.LOG_LEVEL)),
|
|
790
|
+
createLogger: _nullishCoalesce(_optionalChain([this, 'access', _93 => _93.config, 'access', _94 => _94.observability, 'optionalAccess', _95 => _95.createLogger]), () => ( true))
|
|
791
|
+
};
|
|
792
|
+
const getCurrentTime = _nullishCoalesce(this.config.getCurrentTime, () => ( (() => /* @__PURE__ */ new Date())));
|
|
793
|
+
this.resolvedConfig = {
|
|
794
|
+
serviceName: resolvedServiceName,
|
|
795
|
+
firebase: {
|
|
796
|
+
projectId: resolvedFirebaseProjectId,
|
|
797
|
+
databaseURL,
|
|
798
|
+
firestoreEmulatorHost,
|
|
799
|
+
databaseEmulatorHost
|
|
800
|
+
},
|
|
801
|
+
pubsub: {
|
|
802
|
+
projectId: resolvedPubsubProjectId,
|
|
803
|
+
emulatorHost: pubsubEmulatorHost,
|
|
804
|
+
topicPrefix: _nullishCoalesce(_nullishCoalesce(_optionalChain([this, 'access', _96 => _96.config, 'access', _97 => _97.pubsub, 'optionalAccess', _98 => _98.topicPrefix]), () => ( process.env.PUBSUB_TOPIC_PREFIX)), () => ( resolvedServiceName)),
|
|
805
|
+
subscriptionId: _nullishCoalesce(_optionalChain([this, 'access', _99 => _99.config, 'access', _100 => _100.pubsub, 'optionalAccess', _101 => _101.subscriptionId]), () => ( resolvedServiceName)),
|
|
806
|
+
subscriptionOptions: _optionalChain([this, 'access', _102 => _102.config, 'access', _103 => _103.pubsub, 'optionalAccess', _104 => _104.subscriptionOptions]),
|
|
807
|
+
autoCreateResources: _nullishCoalesce(_optionalChain([this, 'access', _105 => _105.config, 'access', _106 => _106.pubsub, 'optionalAccess', _107 => _107.autoCreateResources]), () => ( true)),
|
|
808
|
+
cleanupOnClose: _nullishCoalesce(_optionalChain([this, 'access', _108 => _108.config, 'access', _109 => _109.pubsub, 'optionalAccess', _110 => _110.cleanupOnClose]), () => ( false)),
|
|
809
|
+
closePubSubClient: _nullishCoalesce(_optionalChain([this, 'access', _111 => _111.config, 'access', _112 => _112.pubsub, 'optionalAccess', _113 => _113.closePubSubClient]), () => ( true))
|
|
810
|
+
},
|
|
811
|
+
observability,
|
|
812
|
+
projections: _nullishCoalesce(this.config.projections, () => ( [])),
|
|
813
|
+
getCurrentTime,
|
|
814
|
+
dependencies: _nullishCoalesce(this.config.dependencies, () => ( [])),
|
|
815
|
+
includeDefaultDependencyChecks: _nullishCoalesce(this.config.includeDefaultDependencyChecks, () => ( true)),
|
|
816
|
+
includeRealtimeDatabaseCheck: _nullishCoalesce(this.config.includeRealtimeDatabaseCheck, () => ( true)),
|
|
817
|
+
autoStartMessageBus: _nullishCoalesce(this.config.autoStartMessageBus, () => ( true)),
|
|
818
|
+
shutdownOnDependencyFailure: _nullishCoalesce(this.config.shutdownOnDependencyFailure, () => ( true)),
|
|
819
|
+
shutdown: {
|
|
820
|
+
timeoutMs: _nullishCoalesce(_optionalChain([this, 'access', _114 => _114.config, 'access', _115 => _115.shutdown, 'optionalAccess', _116 => _116.timeoutMs]), () => ( 3e4)),
|
|
821
|
+
exitOnComplete: _nullishCoalesce(_optionalChain([this, 'access', _117 => _117.config, 'access', _118 => _118.shutdown, 'optionalAccess', _119 => _119.exitOnComplete]), () => ( false)),
|
|
822
|
+
exitFn: _optionalChain([this, 'access', _120 => _120.config, 'access', _121 => _121.shutdown, 'optionalAccess', _122 => _122.exitFn])
|
|
823
|
+
},
|
|
824
|
+
eventStore: {
|
|
825
|
+
collections: _optionalChain([this, 'access', _123 => _123.config, 'access', _124 => _124.eventStore, 'optionalAccess', _125 => _125.collections])
|
|
826
|
+
},
|
|
827
|
+
_testOverrides: this.config._testOverrides
|
|
828
|
+
};
|
|
829
|
+
return this.resolvedConfig;
|
|
830
|
+
}
|
|
831
|
+
getDatabaseURL(projectId, emulatorHost) {
|
|
832
|
+
if (emulatorHost) {
|
|
833
|
+
return `http://${emulatorHost}?ns=${projectId}`;
|
|
834
|
+
}
|
|
835
|
+
return `https://${projectId}-default-rtdb.firebaseio.com`;
|
|
836
|
+
}
|
|
837
|
+
getDependencyChecker() {
|
|
838
|
+
if (!this.dependencyChecker) {
|
|
839
|
+
this.dependencyChecker = new DependencyChecker({ logger: this.getLogger() });
|
|
840
|
+
}
|
|
841
|
+
return this.dependencyChecker;
|
|
842
|
+
}
|
|
843
|
+
getShutdownManager() {
|
|
844
|
+
if (!this.shutdownManager) {
|
|
845
|
+
this.shutdownManager = new GracefulShutdown({
|
|
846
|
+
...this.resolveConfig().shutdown,
|
|
847
|
+
logger: this.getLogger()
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
return this.shutdownManager;
|
|
851
|
+
}
|
|
852
|
+
registerDefaultShutdownTasks(options) {
|
|
853
|
+
const shutdownManager = this.getShutdownManager();
|
|
854
|
+
if (_optionalChain([options, 'access', _126 => _126.messageBus, 'optionalAccess', _127 => _127.close])) {
|
|
855
|
+
shutdownManager.register({
|
|
856
|
+
name: "messageBus",
|
|
857
|
+
close: () => options.messageBus.close()
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
if (typeof options.firestore.terminate === "function") {
|
|
861
|
+
shutdownManager.register({
|
|
862
|
+
name: "firestore",
|
|
863
|
+
close: () => options.firestore.terminate()
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
if (typeof options.firebaseApp.delete === "function") {
|
|
867
|
+
shutdownManager.register({
|
|
868
|
+
name: "firebaseApp",
|
|
869
|
+
close: () => options.firebaseApp.delete()
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
buildDefaultDependencyChecks(ctx) {
|
|
874
|
+
const checks = [];
|
|
875
|
+
if (this.resolveConfig().autoStartMessageBus) {
|
|
876
|
+
checks.push({
|
|
877
|
+
name: "PubSub",
|
|
878
|
+
check: () => ctx.messageBus.start(),
|
|
879
|
+
successMessage: "Message bus started"
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
checks.push(...this.buildStoreDependencyChecks(ctx));
|
|
883
|
+
return checks;
|
|
884
|
+
}
|
|
885
|
+
buildStoreDependencyChecks(ctx) {
|
|
886
|
+
const checks = [
|
|
887
|
+
{
|
|
888
|
+
name: "Firestore",
|
|
889
|
+
check: async () => {
|
|
890
|
+
await ctx.firestore.collection("_healthcheck").limit(1).get();
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
];
|
|
894
|
+
if (this.resolveConfig().includeRealtimeDatabaseCheck) {
|
|
895
|
+
checks.push({
|
|
896
|
+
name: "Realtime Database",
|
|
897
|
+
check: async () => {
|
|
898
|
+
await ctx.database.ref("_healthcheck/connection").once("value");
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
return checks;
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
exports.FirebaseInitializer = FirebaseInitializer; exports.PubSubInitializer = PubSubInitializer; exports.createEventStore = createEventStore; exports.DependencyTimeoutError = DependencyTimeoutError; exports.startDependency = startDependency; exports.DependencyChecker = DependencyChecker; exports.ShutdownTimeoutError = ShutdownTimeoutError; exports.GracefulShutdown = GracefulShutdown; exports.registerSignalHandlers = registerSignalHandlers; exports.DependencyUnavailableError = DependencyUnavailableError; exports.ServiceBootstrap = ServiceBootstrap;
|
|
919
|
+
//# sourceMappingURL=chunk-G3V6OOZL.cjs.map
|