@emmett-community/emmett-google-services-bootstrap 0.2.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 +85 -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 -942
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -294
- package/dist/index.d.ts +19 -294
- package/dist/index.js +32 -883
- 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
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bootstrap/ServiceBootstrap.ts","../src/firebase/FirebaseInitializer.ts","../src/pubsub/PubSubInitializer.ts","../src/eventstore/EventStoreFactory.ts","../src/lifecycle/DependencyChecker.ts","../src/lifecycle/GracefulShutdown.ts","../src/lifecycle/SignalHandlers.ts","../src/index.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Application } from 'express';\nimport { createLogger } from '@emmett-community/emmett-observability';\nimport {\n createOpenApiValidatorOptions,\n getApplication,\n startAPI,\n type ApplicationOptions,\n type ImportedHandlerModules,\n type OpenApiValidatorOptions,\n} from '@emmett-community/emmett-expressjs-with-openapi';\nimport {\n getPubSubMessageBus,\n type Logger as PubSubLogger,\n} from '@emmett-community/emmett-google-pubsub';\nimport type { EventStore } from '@event-driven-io/emmett';\nimport type { Firestore } from '@google-cloud/firestore';\nimport type { PubSub } from '@google-cloud/pubsub';\nimport type { Auth } from 'firebase-admin/auth';\nimport type { app as FirebaseAppNamespace } from 'firebase-admin';\nimport type { Database } from 'firebase-admin/database';\nimport { FirebaseInitializer } from '../firebase/FirebaseInitializer';\nimport { PubSubInitializer } from '../pubsub/PubSubInitializer';\nimport { createEventStore } from '../eventstore/EventStoreFactory';\nimport {\n DependencyChecker,\n type DependencyCheckSummary,\n} from '../lifecycle/DependencyChecker';\nimport {\n GracefulShutdown,\n type GracefulShutdownResult,\n} from '../lifecycle/GracefulShutdown';\nimport { registerSignalHandlers } from '../lifecycle/SignalHandlers';\nimport type {\n DependencyCheck,\n Logger as BootstrapLogger,\n MessageBusWithLifecycle,\n ResolvedServiceBootstrapConfig,\n ServiceBootstrapConfig,\n} from '../types/config';\n\n/**\n * Runtime context produced by ServiceBootstrap.\n */\nexport type ServiceBootstrapContext = {\n serviceName: string;\n firebase: {\n app: FirebaseAppNamespace.App;\n firestore: Firestore;\n database: Database;\n auth: Auth;\n };\n firestore: Firestore;\n database: Database;\n auth: Auth;\n pubsub: PubSub;\n eventStore: EventStore;\n messageBus: MessageBusWithLifecycle;\n logger?: BootstrapLogger;\n getCurrentTime: () => Date;\n config: ResolvedServiceBootstrapConfig;\n};\n\n/**\n * Error thrown when required dependencies fail health checks.\n */\nexport class DependencyUnavailableError extends Error {\n readonly summary: DependencyCheckSummary;\n\n constructor(summary: DependencyCheckSummary) {\n const failed = summary.results\n .filter((result) => result.required && !result.ok)\n .map((result) => result.name)\n .join(', ');\n\n super(`Dependency check failed: ${failed}`);\n this.name = 'DependencyUnavailableError';\n this.summary = summary;\n }\n}\n\nexport type CreateAppOptions = {\n openApiPath: string;\n handlersPath: string;\n initializeHandlers: (handlers?: ImportedHandlerModules) => void | Promise<void>;\n openApiOptions?: Partial<\n Omit<\n OpenApiValidatorOptions,\n 'apiSpec' | 'operationHandlers' | 'initializeHandlers'\n >\n >;\n appOptions?: Omit<ApplicationOptions, 'openApiValidator' | 'observability'>;\n};\n\nexport type StartApiOptions = Omit<CreateAppOptions, 'initializeHandlers'> & {\n initializeHandlers: (\n handlers: ImportedHandlerModules | undefined,\n ctx: ServiceBootstrapContext,\n ) => void | Promise<void>;\n port?: number;\n registerSignalHandlers?: boolean;\n beforeStart?: (ctx: ServiceBootstrapContext) => void | Promise<void>;\n};\n\nconst normalizeContext = (data: unknown): Record<string, unknown> => {\n if (data === undefined || data === null) return {};\n if (typeof data === 'object' && !Array.isArray(data)) {\n return { ...(data as Record<string, unknown>) };\n }\n return { data };\n};\n\nconst normalizeErrorContext = (error: unknown): Record<string, unknown> => {\n if (error === undefined || error === null) return {};\n if (error instanceof Error) return { err: error };\n if (typeof error === 'object' && !Array.isArray(error)) {\n return { ...(error as Record<string, unknown>) };\n }\n return { err: error };\n};\n\nconst createPubSubLogger = (\n logger?: BootstrapLogger,\n): PubSubLogger | undefined => {\n if (!logger) return undefined;\n // Adapter: PubSub logger expects (msg, data), canonical logger uses (context, msg).\n const canonicalLogger = logger as any;\n return {\n debug: (msg, data) => canonicalLogger.debug(normalizeContext(data), msg),\n info: (msg, data) => canonicalLogger.info(normalizeContext(data), msg),\n warn: (msg, data) => canonicalLogger.warn(normalizeContext(data), msg),\n error: (msg, error) =>\n canonicalLogger.error(normalizeErrorContext(error), msg),\n };\n};\n\nconst resolvePath = (inputPath: string): string => {\n return path.isAbsolute(inputPath)\n ? inputPath\n : path.resolve(process.cwd(), inputPath);\n};\n\n/**\n * Orchestrates Firebase, PubSub, event store, and lifecycle utilities.\n */\nexport class ServiceBootstrap {\n private readonly config: ServiceBootstrapConfig;\n private resolvedConfig?: ResolvedServiceBootstrapConfig;\n private logger?: BootstrapLogger;\n private context?: ServiceBootstrapContext;\n private dependencyChecker?: DependencyChecker;\n private shutdownManager?: GracefulShutdown;\n\n constructor(config: ServiceBootstrapConfig) {\n this.config = config;\n }\n\n /**\n * Resolve configuration with defaults and environment variables.\n */\n getConfig(): ResolvedServiceBootstrapConfig {\n return this.resolveConfig();\n }\n\n /**\n * Return the initialized context if available.\n */\n getContext(): ServiceBootstrapContext | undefined {\n return this.context;\n }\n\n /**\n * Register process signal handlers that trigger shutdown.\n */\n registerSignalHandlers(options?: {\n signals?: NodeJS.Signals[];\n exitCode?: number;\n }): () => void {\n return registerSignalHandlers({\n shutdown: async (reason, exitCode) => {\n await this.shutdown(reason, exitCode);\n },\n signals: options?.signals,\n logger: this.getLogger(),\n exitCode: options?.exitCode,\n });\n }\n\n /**\n * Create an Express app using emmett-expressjs-with-openapi defaults.\n */\n async createApp(options: CreateAppOptions) {\n const logger = this.getLogger();\n const openApiValidator = createOpenApiValidatorOptions(\n resolvePath(options.openApiPath),\n {\n ...options.openApiOptions,\n operationHandlers: resolvePath(options.handlersPath),\n initializeHandlers: options.initializeHandlers,\n },\n );\n\n return getApplication({\n ...(options.appOptions ?? {}),\n openApiValidator,\n observability: logger ? { logger } : undefined,\n });\n }\n\n /**\n * Initialize, run dependency checks, create the app, and start the HTTP server.\n */\n async startApi(options: StartApiOptions): Promise<{\n app: Application;\n server: ReturnType<typeof startAPI>;\n context: ServiceBootstrapContext;\n }> {\n const {\n port,\n registerSignalHandlers,\n beforeStart,\n initializeHandlers,\n ...createAppOptions\n } = options;\n\n const logger = this.getLogger();\n\n try {\n const ctx = await this.initialize();\n\n if (beforeStart) {\n await beforeStart(ctx);\n }\n\n if (registerSignalHandlers !== false) {\n this.registerSignalHandlers();\n }\n\n await this.start();\n\n const app = await this.createApp({\n ...createAppOptions,\n initializeHandlers: (handlers) => initializeHandlers(handlers, ctx),\n });\n\n const server = startAPI(app, { port, logger });\n\n this.getShutdownManager().register({\n name: 'httpServer',\n close: () =>\n new Promise<void>((resolve, reject) => {\n server.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n }),\n });\n\n logger?.info({ port: port ?? 3000 }, 'API server started');\n\n return { app, server, context: ctx };\n } catch (error) {\n logger?.error({ err: error }, 'Startup failed');\n await this.shutdown('Startup failed');\n throw error;\n }\n }\n\n /**\n * Initialize Firebase, PubSub, and event store wiring.\n * Does not execute dependency checks.\n */\n async initialize(): Promise<ServiceBootstrapContext> {\n if (this.context) {\n return this.context;\n }\n\n const resolvedConfig = this.resolveConfig();\n const logger = this.getLogger();\n\n const firebaseInitializer = new FirebaseInitializer({\n projectId: resolvedConfig.firebase.projectId,\n databaseURL: resolvedConfig.firebase.databaseURL,\n firestoreEmulatorHost: resolvedConfig.firebase.firestoreEmulatorHost,\n databaseEmulatorHost: resolvedConfig.firebase.databaseEmulatorHost,\n logger,\n _firebaseAdmin: resolvedConfig._testOverrides?.firebaseAdmin,\n });\n\n const firebase = await firebaseInitializer.initialize();\n\n const firestore = resolvedConfig._testOverrides?.firestore ?? firebase.firestore;\n const database = resolvedConfig._testOverrides?.database ?? firebase.database;\n const auth = resolvedConfig._testOverrides?.auth ?? firebase.auth;\n\n const pubsubInitializer = new PubSubInitializer({\n projectId: resolvedConfig.pubsub.projectId,\n emulatorHost: resolvedConfig.pubsub.emulatorHost,\n logger,\n _pubsub: resolvedConfig._testOverrides?.pubsub,\n });\n\n const pubsub = await pubsubInitializer.initialize();\n\n const eventStore =\n resolvedConfig._testOverrides?.eventStore ??\n createEventStore({\n firestore,\n database,\n projections: resolvedConfig.projections,\n logger,\n });\n\n const messageBus =\n resolvedConfig._testOverrides?.messageBus ??\n getPubSubMessageBus({\n pubsub,\n useEmulator: pubsubInitializer.isEmulatorMode(),\n topicPrefix: resolvedConfig.pubsub.topicPrefix,\n subscriptionOptions: resolvedConfig.pubsub.subscriptionOptions,\n autoCreateResources: resolvedConfig.pubsub.autoCreateResources,\n cleanupOnClose: resolvedConfig.pubsub.cleanupOnClose,\n closePubSubClient: resolvedConfig.pubsub.closePubSubClient,\n observability: { logger: createPubSubLogger(logger) },\n });\n\n this.shutdownManager = new GracefulShutdown({\n ...resolvedConfig.shutdown,\n logger,\n });\n\n this.registerDefaultShutdownTasks({\n messageBus,\n firestore,\n firebaseApp: firebase.app,\n });\n\n this.dependencyChecker = new DependencyChecker({ logger });\n\n this.context = {\n serviceName: resolvedConfig.serviceName,\n firebase: {\n app: firebase.app,\n firestore,\n database,\n auth,\n },\n firestore,\n database,\n auth,\n pubsub,\n eventStore,\n messageBus,\n logger,\n getCurrentTime: resolvedConfig.getCurrentTime,\n config: resolvedConfig,\n };\n\n return this.context;\n }\n\n /**\n * Initialize and run dependency checks before returning context.\n */\n async start(): Promise<ServiceBootstrapContext> {\n const ctx = await this.initialize();\n const resolvedConfig = this.resolveConfig();\n\n const checks: DependencyCheck[] = [];\n\n if (resolvedConfig.includeDefaultDependencyChecks) {\n checks.push(...this.buildDefaultDependencyChecks(ctx));\n }\n\n if (resolvedConfig.dependencies.length > 0) {\n checks.push(...resolvedConfig.dependencies);\n }\n\n if (checks.length > 0) {\n const summary = await this.getDependencyChecker().checkAll(checks);\n\n if (!summary.ok) {\n const error = new DependencyUnavailableError(summary);\n\n this.logger?.error(\n {\n err: error,\n results: summary.results,\n },\n 'Dependency checks failed',\n );\n\n if (resolvedConfig.shutdownOnDependencyFailure) {\n await this.shutdown('Dependency check failed');\n }\n\n throw error;\n }\n }\n\n this.logger?.info(\n { serviceName: resolvedConfig.serviceName },\n 'Service is running',\n );\n\n return ctx;\n }\n\n /**\n * Execute graceful shutdown tasks and return the shutdown result.\n */\n async shutdown(reason: string, exitCode = 1): Promise<GracefulShutdownResult> {\n return this.getShutdownManager().shutdown(reason, exitCode);\n }\n\n private getLogger(): BootstrapLogger | undefined {\n if (this.logger !== undefined) {\n return this.logger;\n }\n\n const resolvedConfig = this.resolveConfig();\n const observability = resolvedConfig.observability;\n\n if (observability.logger) {\n this.logger = observability.logger;\n return this.logger;\n }\n\n if (observability.createLogger === false) {\n this.logger = undefined;\n return this.logger;\n }\n\n this.logger = createLogger({\n serviceName: resolvedConfig.serviceName,\n environment: observability.environment,\n logLevel: observability.logLevel,\n });\n\n return this.logger;\n }\n\n private resolveConfig(): ResolvedServiceBootstrapConfig {\n if (this.resolvedConfig) {\n return this.resolvedConfig;\n }\n\n const missing: string[] = [];\n\n const serviceName = this.config.serviceName?.trim();\n if (!serviceName) {\n missing.push('serviceName');\n }\n\n const firestoreEmulatorHost =\n this.config.firebase?.firestoreEmulatorHost ??\n process.env.FIRESTORE_EMULATOR_HOST;\n\n const databaseEmulatorHost =\n this.config.firebase?.databaseEmulatorHost ??\n process.env.FIREBASE_DATABASE_EMULATOR_HOST;\n\n const pubsubEmulatorHost =\n this.config.pubsub?.emulatorHost ?? process.env.PUBSUB_EMULATOR_HOST;\n\n const hasEmulator =\n Boolean(firestoreEmulatorHost) ||\n Boolean(databaseEmulatorHost) ||\n Boolean(pubsubEmulatorHost);\n\n const firebaseProjectId =\n this.config.firebase?.projectId ??\n process.env.FIRESTORE_PROJECT_ID ??\n process.env.GCLOUD_PROJECT ??\n process.env.GOOGLE_CLOUD_PROJECT ??\n (hasEmulator ? 'demo-project' : undefined);\n\n if (!firebaseProjectId) {\n missing.push('firebase.projectId (or FIRESTORE_PROJECT_ID)');\n }\n\n const pubsubProjectId =\n this.config.pubsub?.projectId ??\n process.env.PUBSUB_PROJECT_ID ??\n firebaseProjectId ??\n (hasEmulator ? 'demo-project' : undefined);\n\n if (!pubsubProjectId) {\n missing.push('pubsub.projectId (or PUBSUB_PROJECT_ID)');\n }\n\n if (missing.length > 0) {\n throw new Error(`Missing required configuration: ${missing.join(', ')}`);\n }\n\n const resolvedServiceName = serviceName as string;\n const resolvedFirebaseProjectId = firebaseProjectId as string;\n const resolvedPubsubProjectId = pubsubProjectId as string;\n\n const databaseURL =\n this.config.firebase?.databaseURL ??\n this.getDatabaseURL(resolvedFirebaseProjectId, databaseEmulatorHost);\n\n const observability = {\n ...this.config.observability,\n environment:\n this.config.observability?.environment ?? process.env.NODE_ENV,\n logLevel:\n this.config.observability?.logLevel ??\n (process.env.LOG_LEVEL as\n | 'fatal'\n | 'error'\n | 'warn'\n | 'info'\n | 'debug'\n | 'trace'\n | 'silent'\n | undefined),\n createLogger: this.config.observability?.createLogger ?? true,\n };\n\n const getCurrentTime = this.config.getCurrentTime ?? (() => new Date());\n\n this.resolvedConfig = {\n serviceName: resolvedServiceName,\n firebase: {\n projectId: resolvedFirebaseProjectId,\n databaseURL,\n firestoreEmulatorHost,\n databaseEmulatorHost,\n },\n pubsub: {\n projectId: resolvedPubsubProjectId,\n emulatorHost: pubsubEmulatorHost,\n topicPrefix: this.config.pubsub?.topicPrefix ?? resolvedServiceName,\n subscriptionOptions: this.config.pubsub?.subscriptionOptions,\n autoCreateResources: this.config.pubsub?.autoCreateResources ?? true,\n cleanupOnClose: this.config.pubsub?.cleanupOnClose ?? false,\n closePubSubClient: this.config.pubsub?.closePubSubClient ?? true,\n },\n observability,\n projections: this.config.projections ?? [],\n getCurrentTime,\n dependencies: this.config.dependencies ?? [],\n includeDefaultDependencyChecks:\n this.config.includeDefaultDependencyChecks ?? true,\n autoStartMessageBus: this.config.autoStartMessageBus ?? true,\n shutdownOnDependencyFailure:\n this.config.shutdownOnDependencyFailure ?? true,\n shutdown: {\n timeoutMs: this.config.shutdown?.timeoutMs ?? 30000,\n exitOnComplete: this.config.shutdown?.exitOnComplete ?? false,\n exitFn: this.config.shutdown?.exitFn,\n },\n _testOverrides: this.config._testOverrides,\n };\n\n return this.resolvedConfig;\n }\n\n private getDatabaseURL(projectId: string, emulatorHost?: string): string {\n if (emulatorHost) {\n return `http://${emulatorHost}?ns=${projectId}`;\n }\n return `https://${projectId}-default-rtdb.firebaseio.com`;\n }\n\n private getDependencyChecker(): DependencyChecker {\n if (!this.dependencyChecker) {\n this.dependencyChecker = new DependencyChecker({ logger: this.getLogger() });\n }\n return this.dependencyChecker;\n }\n\n private getShutdownManager(): GracefulShutdown {\n if (!this.shutdownManager) {\n this.shutdownManager = new GracefulShutdown({\n ...this.resolveConfig().shutdown,\n logger: this.getLogger(),\n });\n }\n return this.shutdownManager;\n }\n\n private registerDefaultShutdownTasks(options: {\n messageBus: MessageBusWithLifecycle;\n firestore: Firestore;\n firebaseApp: FirebaseAppNamespace.App;\n }): void {\n const shutdownManager = this.getShutdownManager();\n\n if (options.messageBus?.close) {\n shutdownManager.register({\n name: 'messageBus',\n close: () => options.messageBus.close(),\n });\n }\n\n if (typeof options.firestore.terminate === 'function') {\n shutdownManager.register({\n name: 'firestore',\n close: () => options.firestore.terminate(),\n });\n }\n\n if (typeof options.firebaseApp.delete === 'function') {\n shutdownManager.register({\n name: 'firebaseApp',\n close: () => options.firebaseApp.delete(),\n });\n }\n }\n\n private buildDefaultDependencyChecks(\n ctx: ServiceBootstrapContext,\n ): DependencyCheck[] {\n const checks: DependencyCheck[] = [];\n\n if (this.resolveConfig().autoStartMessageBus) {\n checks.push({\n name: 'PubSub',\n check: () => ctx.messageBus.start(),\n successMessage: 'Message bus started',\n });\n }\n\n checks.push({\n name: 'Firestore',\n check: async () => {\n await ctx.firestore.doc('_healthcheck/connection').get();\n },\n });\n\n checks.push({\n name: 'Realtime Database',\n check: async () => {\n await ctx.database.ref('_healthcheck/connection').once('value');\n },\n });\n\n return checks;\n }\n}\n","import admin from 'firebase-admin';\nimport type { Firestore } from '@google-cloud/firestore';\nimport type { Auth } from 'firebase-admin/auth';\nimport type { app as FirebaseAppNamespace } from 'firebase-admin';\nimport type { Database } from 'firebase-admin/database';\nimport type { FirebaseAdminLike, Logger } from '../types/config';\n\nexport type FirebaseInitializationOptions = {\n projectId: string;\n databaseURL?: string;\n firestoreEmulatorHost?: string;\n databaseEmulatorHost?: string;\n logger?: Logger;\n _firebaseAdmin?: FirebaseAdminLike;\n};\n\nexport type FirebaseInitResult = {\n app: FirebaseAppNamespace.App;\n firestore: Firestore;\n database: Database;\n auth: Auth;\n};\n\nconst normalizeContext = (data: unknown): Record<string, unknown> => {\n if (data === undefined || data === null) return {};\n if (typeof data === 'object' && !Array.isArray(data)) {\n return { ...(data as Record<string, unknown>) };\n }\n return { data };\n};\n\nconst normalizeErrorContext = (error: unknown): Record<string, unknown> => {\n if (error === undefined || error === null) return {};\n if (error instanceof Error) return { err: error };\n if (typeof error === 'object' && !Array.isArray(error)) {\n return { ...(error as Record<string, unknown>) };\n }\n return { err: error };\n};\n\nconst safeLog = {\n debug: (logger: Logger | undefined, msg: string, data?: unknown): void => {\n if (!logger) return;\n logger.debug(normalizeContext(data), msg);\n },\n info: (logger: Logger | undefined, msg: string, data?: unknown): void => {\n if (!logger) return;\n logger.info(normalizeContext(data), msg);\n },\n warn: (logger: Logger | undefined, msg: string, data?: unknown): void => {\n if (!logger) return;\n logger.warn(normalizeContext(data), msg);\n },\n error: (logger: Logger | undefined, msg: string, error?: unknown): void => {\n if (!logger) return;\n logger.error(normalizeErrorContext(error), msg);\n },\n};\n\n/**\n * Initializes Firebase Admin SDK with emulator support.\n */\nexport class FirebaseInitializer {\n private readonly options: FirebaseInitializationOptions;\n\n constructor(options: FirebaseInitializationOptions) {\n this.options = options;\n }\n\n /**\n * Returns true if emulator hosts are configured.\n */\n isEmulatorMode(): boolean {\n return Boolean(\n this.options.firestoreEmulatorHost ||\n this.options.databaseEmulatorHost ||\n process.env.FIRESTORE_EMULATOR_HOST ||\n process.env.FIREBASE_DATABASE_EMULATOR_HOST,\n );\n }\n\n /**\n * Initialize Firebase Admin and return service handles.\n */\n async initialize(): Promise<FirebaseInitResult> {\n const {\n projectId,\n databaseURL,\n firestoreEmulatorHost,\n databaseEmulatorHost,\n logger,\n } = this.options;\n\n if (firestoreEmulatorHost && !process.env.FIRESTORE_EMULATOR_HOST) {\n process.env.FIRESTORE_EMULATOR_HOST = firestoreEmulatorHost;\n }\n\n if (databaseEmulatorHost && !process.env.FIREBASE_DATABASE_EMULATOR_HOST) {\n process.env.FIREBASE_DATABASE_EMULATOR_HOST = databaseEmulatorHost;\n }\n\n const resolvedDatabaseURL =\n databaseURL ?? this.getDatabaseURL(projectId, databaseEmulatorHost);\n\n const firebaseAdmin = this.options._firebaseAdmin ?? admin;\n const app = this.getOrCreateApp(firebaseAdmin, projectId, resolvedDatabaseURL);\n\n safeLog.info(logger, 'Firebase initialized', {\n projectId,\n databaseURL: resolvedDatabaseURL,\n emulatorMode: this.isEmulatorMode(),\n });\n\n const firestore = app.firestore();\n const database = app.database();\n const auth = app.auth();\n\n if (firestoreEmulatorHost) {\n firestore.settings({\n host: firestoreEmulatorHost,\n ssl: false,\n });\n safeLog.info(logger, 'Firestore emulator configured', {\n host: firestoreEmulatorHost,\n });\n }\n\n return { app, firestore, database, auth };\n }\n\n private getOrCreateApp(\n firebaseAdmin: FirebaseAdminLike,\n projectId: string,\n databaseURL: string,\n ): FirebaseAppNamespace.App {\n const apps = (firebaseAdmin.apps ?? []).filter(\n (app): app is FirebaseAppNamespace.App => app !== null,\n );\n\n if (apps.length > 0 && firebaseAdmin.app) {\n safeLog.debug(this.options.logger, 'Reusing existing Firebase app');\n return firebaseAdmin.app();\n }\n\n safeLog.debug(this.options.logger, 'Creating Firebase app');\n return firebaseAdmin.initializeApp(\n {\n projectId,\n databaseURL,\n },\n );\n }\n\n private getDatabaseURL(projectId: string, emulatorHost?: string): string {\n if (emulatorHost) {\n return `http://${emulatorHost}?ns=${projectId}`;\n }\n return `https://${projectId}-default-rtdb.firebaseio.com`;\n }\n}\n","import { PubSub } from '@google-cloud/pubsub';\nimport type { Logger } from '../types/config';\n\nexport type PubSubInitializationOptions = {\n projectId: string;\n emulatorHost?: string;\n logger?: Logger;\n _pubsub?: PubSub;\n};\n\nconst normalizeContext = (data: unknown): Record<string, unknown> => {\n if (data === undefined || data === null) return {};\n if (typeof data === 'object' && !Array.isArray(data)) {\n return { ...(data as Record<string, unknown>) };\n }\n return { data };\n};\n\nconst normalizeErrorContext = (error: unknown): Record<string, unknown> => {\n if (error === undefined || error === null) return {};\n if (error instanceof Error) return { err: error };\n if (typeof error === 'object' && !Array.isArray(error)) {\n return { ...(error as Record<string, unknown>) };\n }\n return { err: error };\n};\n\nconst safeLog = {\n debug: (logger: Logger | undefined, msg: string, data?: unknown): void => {\n if (!logger) return;\n logger.debug(normalizeContext(data), msg);\n },\n info: (logger: Logger | undefined, msg: string, data?: unknown): void => {\n if (!logger) return;\n logger.info(normalizeContext(data), msg);\n },\n warn: (logger: Logger | undefined, msg: string, data?: unknown): void => {\n if (!logger) return;\n logger.warn(normalizeContext(data), msg);\n },\n error: (logger: Logger | undefined, msg: string, error?: unknown): void => {\n if (!logger) return;\n logger.error(normalizeErrorContext(error), msg);\n },\n};\n\n/**\n * Initializes PubSub client with optional emulator support.\n */\nexport class PubSubInitializer {\n private readonly options: PubSubInitializationOptions;\n\n constructor(options: PubSubInitializationOptions) {\n this.options = options;\n }\n\n /**\n * Returns true if emulator host is configured.\n */\n isEmulatorMode(): boolean {\n return Boolean(\n this.options.emulatorHost || process.env.PUBSUB_EMULATOR_HOST,\n );\n }\n\n /**\n * Initialize PubSub client or return injected instance.\n */\n async initialize(): Promise<PubSub> {\n const { projectId, emulatorHost, logger } = this.options;\n\n if (emulatorHost && !process.env.PUBSUB_EMULATOR_HOST) {\n process.env.PUBSUB_EMULATOR_HOST = emulatorHost;\n }\n\n if (this.options._pubsub) {\n safeLog.info(logger, 'Using provided PubSub client', {\n projectId,\n emulatorHost: emulatorHost ?? process.env.PUBSUB_EMULATOR_HOST,\n });\n return this.options._pubsub;\n }\n\n const pubsub = new PubSub({ projectId });\n\n safeLog.info(logger, 'PubSub initialized', {\n projectId,\n emulatorHost: emulatorHost ?? process.env.PUBSUB_EMULATOR_HOST,\n emulatorMode: this.isEmulatorMode(),\n });\n\n return pubsub;\n }\n}\n","import { getFirestoreEventStore } from '@emmett-community/emmett-google-firestore';\nimport {\n wireRealtimeDBProjections,\n type RealtimeDBInlineProjectionDefinition,\n} from '@emmett-community/emmett-google-realtime-db';\nimport type { EventStore } from '@event-driven-io/emmett';\nimport type { Firestore } from '@google-cloud/firestore';\nimport type { Database } from 'firebase-admin/database';\nimport type { Logger } from '../types/config';\n\nexport type EventStoreFactoryOptions = {\n firestore: Firestore;\n database: Database;\n projections?: RealtimeDBInlineProjectionDefinition[];\n logger?: Logger;\n};\n\n/**\n * Create a Firestore-backed event store and wire RealtimeDB projections if provided.\n */\nexport const createEventStore = <T extends EventStore = EventStore>(\n options: EventStoreFactoryOptions,\n): T => {\n const { firestore, database, projections, logger } = options;\n const observability = logger ? { logger } : undefined;\n\n const baseEventStore = getFirestoreEventStore(firestore, { observability });\n\n if (projections && projections.length > 0) {\n return wireRealtimeDBProjections({\n eventStore: baseEventStore as unknown as T,\n database,\n projections,\n observability,\n }) as T;\n }\n\n return baseEventStore as unknown as T;\n};\n","import type { DependencyCheck, Logger } from '../types/config';\n\nexport class DependencyTimeoutError extends Error {\n constructor(timeoutMs: number) {\n super(`Dependency check timed out after ${timeoutMs}ms`);\n this.name = 'DependencyTimeoutError';\n }\n}\n\nexport type DependencyCheckResult = {\n name: string;\n ok: boolean;\n required: boolean;\n durationMs: number;\n timedOut: boolean;\n error?: unknown;\n};\n\nexport type DependencyCheckSummary = {\n ok: boolean;\n results: DependencyCheckResult[];\n};\n\nconst normalizeErrorContext = (error: unknown): Record<string, unknown> => {\n if (error === undefined || error === null) return {};\n if (error instanceof Error) return { err: error };\n if (typeof error === 'object' && !Array.isArray(error)) {\n return { ...(error as Record<string, unknown>) };\n }\n return { err: error };\n};\n\nconst withTimeout = async <T>(promise: Promise<T>, timeoutMs?: number): Promise<T> => {\n if (!timeoutMs || timeoutMs <= 0) return promise;\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new DependencyTimeoutError(timeoutMs));\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([promise, timeoutPromise]);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n};\n\n/**\n * Helper for one-off dependency checks with optional logging.\n */\nexport const startDependency = async (\n dependency: string,\n startFn: () => Promise<void>,\n options?: {\n logger?: Logger;\n successMessage?: string;\n timeoutMs?: number;\n },\n): Promise<boolean> => {\n const { logger, successMessage, timeoutMs } = options ?? {};\n const startTime = Date.now();\n\n try {\n await withTimeout(startFn(), timeoutMs);\n logger?.info({ dependency, durationMs: Date.now() - startTime }, successMessage);\n return true;\n } catch (error) {\n logger?.warn(\n { dependency, durationMs: Date.now() - startTime, ...normalizeErrorContext(error) },\n 'Unable to reach dependency',\n );\n return false;\n }\n};\n\n/**\n * Executes dependency checks with optional timeouts.\n */\nexport class DependencyChecker {\n private readonly logger?: Logger;\n private readonly defaultTimeoutMs?: number;\n\n constructor(options?: { logger?: Logger; defaultTimeoutMs?: number }) {\n this.logger = options?.logger;\n this.defaultTimeoutMs = options?.defaultTimeoutMs;\n }\n\n async check(dependency: DependencyCheck): Promise<DependencyCheckResult> {\n const start = Date.now();\n const timeoutMs = dependency.timeoutMs ?? this.defaultTimeoutMs;\n\n try {\n await withTimeout(dependency.check(), timeoutMs);\n\n if (dependency.successMessage) {\n this.logger?.info(\n { dependency: dependency.name, durationMs: Date.now() - start },\n dependency.successMessage,\n );\n }\n\n return {\n name: dependency.name,\n ok: true,\n required: dependency.required ?? true,\n durationMs: Date.now() - start,\n timedOut: false,\n };\n } catch (error) {\n const timedOut = error instanceof DependencyTimeoutError;\n\n this.logger?.warn(\n {\n dependency: dependency.name,\n durationMs: Date.now() - start,\n timedOut,\n ...normalizeErrorContext(error),\n },\n 'Dependency check failed',\n );\n\n return {\n name: dependency.name,\n ok: false,\n required: dependency.required ?? true,\n durationMs: Date.now() - start,\n timedOut,\n error,\n };\n }\n }\n\n async checkAll(dependencies: DependencyCheck[]): Promise<DependencyCheckSummary> {\n const results: DependencyCheckResult[] = [];\n\n for (const dependency of dependencies) {\n results.push(await this.check(dependency));\n }\n\n const ok = results.every((result) => result.ok || !result.required);\n\n return { ok, results };\n }\n}\n","import type { GracefulShutdownConfig, Logger } from '../types/config';\n\nexport class ShutdownTimeoutError extends Error {\n constructor(timeoutMs: number) {\n super(`Shutdown task timed out after ${timeoutMs}ms`);\n this.name = 'ShutdownTimeoutError';\n }\n}\n\nexport type ShutdownTask = {\n name: string;\n close: () => Promise<void>;\n timeoutMs?: number;\n};\n\nexport type ShutdownTaskResult = {\n name: string;\n ok: boolean;\n timedOut: boolean;\n durationMs: number;\n error?: unknown;\n};\n\nexport type GracefulShutdownResult = {\n reason: string;\n exitCode: number;\n tasks: ShutdownTaskResult[];\n};\n\nconst normalizeContext = (data: unknown): Record<string, unknown> => {\n if (data === undefined || data === null) return {};\n if (typeof data === 'object' && !Array.isArray(data)) {\n return { ...(data as Record<string, unknown>) };\n }\n return { data };\n};\n\nconst normalizeErrorContext = (error: unknown): Record<string, unknown> => {\n if (error === undefined || error === null) return {};\n if (error instanceof Error) return { err: error };\n if (typeof error === 'object' && !Array.isArray(error)) {\n return { ...(error as Record<string, unknown>) };\n }\n return { err: error };\n};\n\nconst withTimeout = async <T>(promise: Promise<T>, timeoutMs?: number): Promise<T> => {\n if (!timeoutMs || timeoutMs <= 0) return promise;\n\n let timeoutId: NodeJS.Timeout | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new ShutdownTimeoutError(timeoutMs));\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([promise, timeoutPromise]);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n};\n\n/**\n * Runs shutdown tasks with timeout handling and optional exit.\n */\nexport class GracefulShutdown {\n private readonly logger?: Logger;\n private readonly timeoutMs?: number;\n private readonly exitOnComplete: boolean;\n private readonly exitFn: (code: number) => void;\n private readonly tasks: ShutdownTask[] = [];\n private shuttingDown = false;\n private lastResult?: GracefulShutdownResult;\n\n constructor(options?: GracefulShutdownConfig & { logger?: Logger }) {\n this.logger = options?.logger;\n this.timeoutMs = options?.timeoutMs;\n this.exitOnComplete = options?.exitOnComplete ?? false;\n this.exitFn = options?.exitFn ?? process.exit;\n }\n\n /**\n * Register a shutdown task.\n */\n register(task: ShutdownTask): void {\n this.tasks.push(task);\n }\n\n /**\n * Execute all shutdown tasks and return the aggregated result.\n */\n async shutdown(reason: string, exitCode = 1): Promise<GracefulShutdownResult> {\n if (this.shuttingDown && this.lastResult) {\n return this.lastResult;\n }\n\n this.shuttingDown = true;\n this.logger?.info({ reason }, 'Shutting down gracefully');\n\n const results: ShutdownTaskResult[] = [];\n\n for (const task of this.tasks) {\n const start = Date.now();\n const timeoutMs = task.timeoutMs ?? this.timeoutMs;\n\n try {\n await withTimeout(task.close(), timeoutMs);\n results.push({\n name: task.name,\n ok: true,\n timedOut: false,\n durationMs: Date.now() - start,\n });\n this.logger?.info(\n { task: task.name, durationMs: Date.now() - start },\n 'Shutdown task completed',\n );\n } catch (error) {\n const timedOut = error instanceof ShutdownTimeoutError;\n results.push({\n name: task.name,\n ok: false,\n timedOut,\n durationMs: Date.now() - start,\n error,\n });\n this.logger?.warn(\n {\n task: task.name,\n timedOut,\n durationMs: Date.now() - start,\n ...normalizeErrorContext(error),\n },\n 'Shutdown task failed',\n );\n }\n }\n\n const failed = results.some((result) => !result.ok || result.timedOut);\n const finalExitCode = failed ? exitCode : 0;\n\n if (failed) {\n const failedTasks = results\n .filter((result) => !result.ok || result.timedOut)\n .map((result) => result.name);\n this.logger?.error(\n normalizeContext({ failedTasks }),\n 'Shutdown completed with failures',\n );\n } else {\n this.logger?.info({}, 'Shutdown completed');\n }\n\n const result = { reason, exitCode: finalExitCode, tasks: results };\n this.lastResult = result;\n\n if (this.exitOnComplete) {\n this.exitFn(finalExitCode);\n }\n\n return result;\n }\n}\n","import type { Logger } from '../types/config';\n\nexport type SignalHandlerOptions = {\n shutdown: (reason: string, exitCode?: number) => void | Promise<void>;\n signals?: NodeJS.Signals[];\n logger?: Logger;\n exitCode?: number;\n};\n\n/**\n * Register process signal handlers and return an unregister function.\n */\nexport const registerSignalHandlers = (\n options: SignalHandlerOptions,\n): (() => void) => {\n const signals = options.signals ?? ['SIGINT', 'SIGTERM'];\n const handlers = new Map<NodeJS.Signals, () => void>();\n\n for (const signal of signals) {\n const handler = () => {\n options.logger?.info({ signal }, 'Shutdown signal received');\n void options.shutdown(`Signal ${signal} received`, options.exitCode ?? 0);\n };\n\n handlers.set(signal, handler);\n process.on(signal, handler);\n }\n\n return () => {\n for (const [signal, handler] of handlers.entries()) {\n process.off(signal, handler);\n }\n };\n};\n","export * from './bootstrap';\nexport * from './types';\nexport * from './firebase';\nexport * from './pubsub';\nexport * from './lifecycle';\nexport * from './eventstore';\nexport { realtimeDBInlineProjection } from '@emmett-community/emmett-google-realtime-db';\nexport {\n ApiE2ESpecification,\n ApiSpecification,\n createFirebaseAuthSecurityHandlers,\n existingStream,\n expectNewEvents,\n expectResponse,\n NoContent,\n OK,\n on,\n type ImportedHandlerModules,\n type TestRequest,\n} from '@emmett-community/emmett-expressjs-with-openapi';\n"],"mappings":"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","names":["normalizeContext","normalizeErrorContext","safeLog","normalizeErrorContext","normalizeContext","normalizeErrorContext","withTimeout","result","normalizeContext","normalizeErrorContext","registerSignalHandlers"]}
|
|
1
|
+
{"version":3,"sources":["../src/bootstrap/probes.ts","../src/index.ts"],"sourcesContent":["import { HttpResponse, OK, on } from '@emmett-community/emmett-expressjs-with-openapi';\nimport type { ServiceBootstrapContext } from './ServiceBootstrap';\n\n/**\n * Handler for `GET /health` (liveness): the process is up and serving requests.\n * Wire it to the `getHealth` operation of the service contract.\n */\nexport const getHealth = on(async () => OK({ body: { status: 'ok' } }));\n\n/**\n * Handler for `GET /ready` (readiness): answers 200 when the stores, the message bus and the\n * configured dependencies answer, and 503 with the failed checks otherwise.\n * Wire it to the `getReady` operation of the service contract.\n */\nexport const getReady = (ctx: Pick<ServiceBootstrapContext, 'checkReadiness'>) =>\n on(async () => {\n const summary = await ctx.checkReadiness();\n const checks = summary.results.map((result) => ({\n name: result.name,\n ok: result.ok,\n }));\n\n return summary.ok\n ? OK({ body: { status: 'ready', checks } })\n : HttpResponse(503, { body: { status: 'unavailable', checks } });\n });\n","export * from './bootstrap';\nexport * from './types';\nexport * from './firebase';\nexport * from './pubsub';\nexport * from './lifecycle';\nexport * from './eventstore';\nexport { realtimeDBInlineProjection } from '@emmett-community/emmett-google-realtime-db';\nexport {\n createFirebaseAuthSecurityHandlers,\n lazyApplication,\n NoContent,\n OK,\n on,\n type ImportedHandlerModules,\n} from '@emmett-community/emmett-expressjs-with-openapi';\n"],"mappings":";;;;;;;;;;;;;;;AAAA,SAAS,cAAc,IAAI,UAAU;AAO9B,IAAM,YAAY,GAAG,YAAY,GAAG,EAAE,MAAM,EAAE,QAAQ,KAAK,EAAE,CAAC,CAAC;AAO/D,IAAM,WAAW,CAAC,QACvB,GAAG,YAAY;AACb,QAAM,UAAU,MAAM,IAAI,eAAe;AACzC,QAAM,SAAS,QAAQ,QAAQ,IAAI,CAAC,YAAY;AAAA,IAC9C,MAAM,OAAO;AAAA,IACb,IAAI,OAAO;AAAA,EACb,EAAE;AAEF,SAAO,QAAQ,KACX,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAS,OAAO,EAAE,CAAC,IACxC,aAAa,KAAK,EAAE,MAAM,EAAE,QAAQ,eAAe,OAAO,EAAE,CAAC;AACnE,CAAC;;;ACnBH,SAAS,kCAAkC;AAC3C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAAA;AAAA,EACA,MAAAC;AAAA,OAEK;","names":["OK","on"]}
|
package/dist/testing.cjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); 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; }
|
|
2
|
+
|
|
3
|
+
var _chunkG3V6OOZLcjs = require('./chunk-G3V6OOZL.cjs');
|
|
4
|
+
|
|
5
|
+
// src/testing/index.ts
|
|
6
|
+
var _testing = require('@emmett-community/emmett-google-firestore/testing');
|
|
7
|
+
var _testing3 = require('@emmett-community/emmett-google-pubsub/testing');
|
|
8
|
+
var _testing5 = require('@emmett-community/emmett-google-realtime-db/testing');
|
|
9
|
+
var createTestServiceBootstrap = (options) => {
|
|
10
|
+
const { overrides, ...config } = options;
|
|
11
|
+
const firestore = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _ => _.firestore]), () => ( new (0, _testing.InMemoryFirestore)()));
|
|
12
|
+
const database = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _2 => _2.database]), () => ( _testing5.createInMemoryDatabase.call(void 0, )));
|
|
13
|
+
const auth = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _3 => _3.auth]), () => ( {}));
|
|
14
|
+
const app = {
|
|
15
|
+
firestore: () => firestore,
|
|
16
|
+
database: () => database,
|
|
17
|
+
auth: () => auth,
|
|
18
|
+
delete: async () => {
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const firebaseAdmin = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _4 => _4.firebaseAdmin]), () => ( {
|
|
22
|
+
apps: [],
|
|
23
|
+
initializeApp: () => app,
|
|
24
|
+
app: () => app
|
|
25
|
+
}));
|
|
26
|
+
const messageBus = _nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _5 => _5.messageBus]), () => ( _testing3.createInMemoryMessageBus.call(void 0, {
|
|
27
|
+
subscriptionId: _nullishCoalesce(_optionalChain([config, 'access', _6 => _6.pubsub, 'optionalAccess', _7 => _7.subscriptionId]), () => ( config.serviceName)),
|
|
28
|
+
instanceId: _nullishCoalesce(_optionalChain([config, 'access', _8 => _8.pubsub, 'optionalAccess', _9 => _9.subscriptionId]), () => ( config.serviceName))
|
|
29
|
+
})));
|
|
30
|
+
const bootstrap = new (0, _chunkG3V6OOZLcjs.ServiceBootstrap)({
|
|
31
|
+
...config,
|
|
32
|
+
firebase: { projectId: "test-project", ...config.firebase },
|
|
33
|
+
pubsub: { projectId: "test-project", ...config.pubsub },
|
|
34
|
+
observability: { createLogger: false, ...config.observability },
|
|
35
|
+
_testOverrides: {
|
|
36
|
+
...overrides,
|
|
37
|
+
firebaseAdmin,
|
|
38
|
+
firestore,
|
|
39
|
+
database,
|
|
40
|
+
auth,
|
|
41
|
+
messageBus
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return { bootstrap, firestore, database, messageBus };
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
exports.createTestServiceBootstrap = createTestServiceBootstrap;
|
|
49
|
+
//# sourceMappingURL=testing.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/emmett-google-services-bootstrap/emmett-google-services-bootstrap/dist/testing.cjs","../src/testing/index.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACHA,4EAAkC;AAClC,0EAAyC;AACzC,+EAAuC;AA+BhC,IAAM,2BAAA,EAA6B,CACxC,OAAA,EAAA,GACyB;AACzB,EAAA,MAAM,EAAE,SAAA,EAAW,GAAG,OAAO,EAAA,EAAI,OAAA;AAEjC,EAAA,MAAM,UAAA,mCACJ,SAAA,2BAAW,WAAA,UAAc,IAAI,+BAAA,CAAkB,GAAA;AACjD,EAAA,MAAM,SAAA,mCAAW,SAAA,6BAAW,UAAA,UAAY,8CAAA,GAAuB;AAC/D,EAAA,MAAM,KAAA,mCAAO,SAAA,6BAAW,MAAA,UAAS,CAAC,GAAA;AAElC,EAAA,MAAM,IAAA,EAAM;AAAA,IACV,SAAA,EAAW,CAAA,EAAA,GAAM,SAAA;AAAA,IACjB,QAAA,EAAU,CAAA,EAAA,GAAM,QAAA;AAAA,IAChB,IAAA,EAAM,CAAA,EAAA,GAAM,IAAA;AAAA,IACZ,MAAA,EAAQ,MAAA,CAAA,EAAA,GAAY;AAAA,IAAC;AAAA,EACvB,CAAA;AAEA,EAAA,MAAM,cAAA,mCAAgB,SAAA,6BAAW,eAAA,UAAiB;AAAA,IAChD,IAAA,EAAM,CAAC,CAAA;AAAA,IACP,aAAA,EAAe,CAAA,EAAA,GAAM,GAAA;AAAA,IACrB,GAAA,EAAK,CAAA,EAAA,GAAM;AAAA,EACb,GAAA;AAEA,EAAA,MAAM,WAAA,mCAAc,SAAA,6BAAW,YAAA,UAC7B,gDAAA;AAAyB,IACvB,cAAA,mCAAgB,MAAA,qBAAO,MAAA,6BAAQ,gBAAA,UAAkB,MAAA,CAAO,aAAA;AAAA,IACxD,UAAA,mCAAY,MAAA,qBAAO,MAAA,6BAAQ,gBAAA,UAAkB,MAAA,CAAO;AAAA,EACtD,CAAC,GAAA;AAEH,EAAA,MAAM,UAAA,EAAY,IAAI,uCAAA,CAAiB;AAAA,IACrC,GAAG,MAAA;AAAA,IACH,QAAA,EAAU,EAAE,SAAA,EAAW,cAAA,EAAgB,GAAG,MAAA,CAAO,SAAS,CAAA;AAAA,IAC1D,MAAA,EAAQ,EAAE,SAAA,EAAW,cAAA,EAAgB,GAAG,MAAA,CAAO,OAAO,CAAA;AAAA,IACtD,aAAA,EAAe,EAAE,YAAA,EAAc,KAAA,EAAO,GAAG,MAAA,CAAO,cAAc,CAAA;AAAA,IAC9D,cAAA,EAAgB;AAAA,MACd,GAAG,SAAA;AAAA,MACH,aAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AAED,EAAA,OAAO,EAAE,SAAA,EAAW,SAAA,EAAW,QAAA,EAAU,WAAW,CAAA;AACtD,CAAA;ADlCA;AACE;AACF,gEAAC","file":"/home/runner/work/emmett-google-services-bootstrap/emmett-google-services-bootstrap/dist/testing.cjs","sourcesContent":[null,"// Test support: import from '@emmett-community/emmett-google-services-bootstrap/testing'\nimport { InMemoryFirestore } from '@emmett-community/emmett-google-firestore/testing';\nimport { createInMemoryMessageBus } from '@emmett-community/emmett-google-pubsub/testing';\nimport { createInMemoryDatabase } from '@emmett-community/emmett-google-realtime-db/testing';\nimport type { Firestore } from '@google-cloud/firestore';\nimport type { Auth } from 'firebase-admin/auth';\nimport type { Database } from 'firebase-admin/database';\nimport { ServiceBootstrap } from '../bootstrap/ServiceBootstrap';\nimport type {\n MessageBusWithLifecycle,\n ServiceBootstrapConfig,\n ServiceBootstrapOverrides,\n} from '../types/config';\n\nexport type TestServiceBootstrapOptions = Omit<\n ServiceBootstrapConfig,\n '_testOverrides'\n> & {\n /** Replace any of the in-memory pieces, for example to share one `pubsub` between two services. */\n overrides?: ServiceBootstrapOverrides;\n};\n\nexport type TestServiceBootstrap = {\n bootstrap: ServiceBootstrap;\n firestore: Firestore;\n database: Database;\n messageBus: MessageBusWithLifecycle;\n};\n\n/**\n * Service bootstrap on in-memory Firestore, Realtime Database and Pub/Sub, with no Firebase\n * app, no emulator and no network. The event store, the projections and the message bus are\n * the real ones, wired to the fakes, so a service test exercises the same code as production.\n */\nexport const createTestServiceBootstrap = (\n options: TestServiceBootstrapOptions,\n): TestServiceBootstrap => {\n const { overrides, ...config } = options;\n\n const firestore =\n overrides?.firestore ?? (new InMemoryFirestore() as unknown as Firestore);\n const database = overrides?.database ?? createInMemoryDatabase();\n const auth = overrides?.auth ?? ({} as Auth);\n\n const app = {\n firestore: () => firestore,\n database: () => database,\n auth: () => auth,\n delete: async () => {},\n };\n\n const firebaseAdmin = overrides?.firebaseAdmin ?? {\n apps: [],\n initializeApp: () => app,\n app: () => app,\n };\n\n const messageBus = (overrides?.messageBus ??\n createInMemoryMessageBus({\n subscriptionId: config.pubsub?.subscriptionId ?? config.serviceName,\n instanceId: config.pubsub?.subscriptionId ?? config.serviceName,\n })) as MessageBusWithLifecycle;\n\n const bootstrap = new ServiceBootstrap({\n ...config,\n firebase: { projectId: 'test-project', ...config.firebase },\n pubsub: { projectId: 'test-project', ...config.pubsub },\n observability: { createLogger: false, ...config.observability },\n _testOverrides: {\n ...overrides,\n firebaseAdmin: firebaseAdmin as ServiceBootstrapOverrides['firebaseAdmin'],\n firestore,\n database,\n auth,\n messageBus,\n },\n });\n\n return { bootstrap, firestore, database, messageBus };\n};\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Firestore } from '@google-cloud/firestore';
|
|
2
|
+
import { Database } from 'firebase-admin/database';
|
|
3
|
+
import { g as ServiceBootstrapConfig, f as ServiceBootstrapOverrides, b as ServiceBootstrap, M as MessageBusWithLifecycle } from './ServiceBootstrap-Cn1LJQgC.cjs';
|
|
4
|
+
import 'express';
|
|
5
|
+
import '@emmett-community/emmett-expressjs-with-openapi';
|
|
6
|
+
import '@event-driven-io/emmett';
|
|
7
|
+
import '@google-cloud/pubsub';
|
|
8
|
+
import 'firebase-admin/auth';
|
|
9
|
+
import 'firebase-admin';
|
|
10
|
+
import '@emmett-community/emmett-google-pubsub';
|
|
11
|
+
import '@emmett-community/emmett-google-realtime-db';
|
|
12
|
+
import '@emmett-community/emmett-google-firestore';
|
|
13
|
+
|
|
14
|
+
type TestServiceBootstrapOptions = Omit<ServiceBootstrapConfig, '_testOverrides'> & {
|
|
15
|
+
/** Replace any of the in-memory pieces, for example to share one `pubsub` between two services. */
|
|
16
|
+
overrides?: ServiceBootstrapOverrides;
|
|
17
|
+
};
|
|
18
|
+
type TestServiceBootstrap = {
|
|
19
|
+
bootstrap: ServiceBootstrap;
|
|
20
|
+
firestore: Firestore;
|
|
21
|
+
database: Database;
|
|
22
|
+
messageBus: MessageBusWithLifecycle;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Service bootstrap on in-memory Firestore, Realtime Database and Pub/Sub, with no Firebase
|
|
26
|
+
* app, no emulator and no network. The event store, the projections and the message bus are
|
|
27
|
+
* the real ones, wired to the fakes, so a service test exercises the same code as production.
|
|
28
|
+
*/
|
|
29
|
+
declare const createTestServiceBootstrap: (options: TestServiceBootstrapOptions) => TestServiceBootstrap;
|
|
30
|
+
|
|
31
|
+
export { type TestServiceBootstrap, type TestServiceBootstrapOptions, createTestServiceBootstrap };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Firestore } from '@google-cloud/firestore';
|
|
2
|
+
import { Database } from 'firebase-admin/database';
|
|
3
|
+
import { g as ServiceBootstrapConfig, f as ServiceBootstrapOverrides, b as ServiceBootstrap, M as MessageBusWithLifecycle } from './ServiceBootstrap-Cn1LJQgC.js';
|
|
4
|
+
import 'express';
|
|
5
|
+
import '@emmett-community/emmett-expressjs-with-openapi';
|
|
6
|
+
import '@event-driven-io/emmett';
|
|
7
|
+
import '@google-cloud/pubsub';
|
|
8
|
+
import 'firebase-admin/auth';
|
|
9
|
+
import 'firebase-admin';
|
|
10
|
+
import '@emmett-community/emmett-google-pubsub';
|
|
11
|
+
import '@emmett-community/emmett-google-realtime-db';
|
|
12
|
+
import '@emmett-community/emmett-google-firestore';
|
|
13
|
+
|
|
14
|
+
type TestServiceBootstrapOptions = Omit<ServiceBootstrapConfig, '_testOverrides'> & {
|
|
15
|
+
/** Replace any of the in-memory pieces, for example to share one `pubsub` between two services. */
|
|
16
|
+
overrides?: ServiceBootstrapOverrides;
|
|
17
|
+
};
|
|
18
|
+
type TestServiceBootstrap = {
|
|
19
|
+
bootstrap: ServiceBootstrap;
|
|
20
|
+
firestore: Firestore;
|
|
21
|
+
database: Database;
|
|
22
|
+
messageBus: MessageBusWithLifecycle;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Service bootstrap on in-memory Firestore, Realtime Database and Pub/Sub, with no Firebase
|
|
26
|
+
* app, no emulator and no network. The event store, the projections and the message bus are
|
|
27
|
+
* the real ones, wired to the fakes, so a service test exercises the same code as production.
|
|
28
|
+
*/
|
|
29
|
+
declare const createTestServiceBootstrap: (options: TestServiceBootstrapOptions) => TestServiceBootstrap;
|
|
30
|
+
|
|
31
|
+
export { type TestServiceBootstrap, type TestServiceBootstrapOptions, createTestServiceBootstrap };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ServiceBootstrap
|
|
3
|
+
} from "./chunk-XUV26TX4.js";
|
|
4
|
+
|
|
5
|
+
// src/testing/index.ts
|
|
6
|
+
import { InMemoryFirestore } from "@emmett-community/emmett-google-firestore/testing";
|
|
7
|
+
import { createInMemoryMessageBus } from "@emmett-community/emmett-google-pubsub/testing";
|
|
8
|
+
import { createInMemoryDatabase } from "@emmett-community/emmett-google-realtime-db/testing";
|
|
9
|
+
var createTestServiceBootstrap = (options) => {
|
|
10
|
+
const { overrides, ...config } = options;
|
|
11
|
+
const firestore = overrides?.firestore ?? new InMemoryFirestore();
|
|
12
|
+
const database = overrides?.database ?? createInMemoryDatabase();
|
|
13
|
+
const auth = overrides?.auth ?? {};
|
|
14
|
+
const app = {
|
|
15
|
+
firestore: () => firestore,
|
|
16
|
+
database: () => database,
|
|
17
|
+
auth: () => auth,
|
|
18
|
+
delete: async () => {
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const firebaseAdmin = overrides?.firebaseAdmin ?? {
|
|
22
|
+
apps: [],
|
|
23
|
+
initializeApp: () => app,
|
|
24
|
+
app: () => app
|
|
25
|
+
};
|
|
26
|
+
const messageBus = overrides?.messageBus ?? createInMemoryMessageBus({
|
|
27
|
+
subscriptionId: config.pubsub?.subscriptionId ?? config.serviceName,
|
|
28
|
+
instanceId: config.pubsub?.subscriptionId ?? config.serviceName
|
|
29
|
+
});
|
|
30
|
+
const bootstrap = new ServiceBootstrap({
|
|
31
|
+
...config,
|
|
32
|
+
firebase: { projectId: "test-project", ...config.firebase },
|
|
33
|
+
pubsub: { projectId: "test-project", ...config.pubsub },
|
|
34
|
+
observability: { createLogger: false, ...config.observability },
|
|
35
|
+
_testOverrides: {
|
|
36
|
+
...overrides,
|
|
37
|
+
firebaseAdmin,
|
|
38
|
+
firestore,
|
|
39
|
+
database,
|
|
40
|
+
auth,
|
|
41
|
+
messageBus
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return { bootstrap, firestore, database, messageBus };
|
|
45
|
+
};
|
|
46
|
+
export {
|
|
47
|
+
createTestServiceBootstrap
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing/index.ts"],"sourcesContent":["// Test support: import from '@emmett-community/emmett-google-services-bootstrap/testing'\nimport { InMemoryFirestore } from '@emmett-community/emmett-google-firestore/testing';\nimport { createInMemoryMessageBus } from '@emmett-community/emmett-google-pubsub/testing';\nimport { createInMemoryDatabase } from '@emmett-community/emmett-google-realtime-db/testing';\nimport type { Firestore } from '@google-cloud/firestore';\nimport type { Auth } from 'firebase-admin/auth';\nimport type { Database } from 'firebase-admin/database';\nimport { ServiceBootstrap } from '../bootstrap/ServiceBootstrap';\nimport type {\n MessageBusWithLifecycle,\n ServiceBootstrapConfig,\n ServiceBootstrapOverrides,\n} from '../types/config';\n\nexport type TestServiceBootstrapOptions = Omit<\n ServiceBootstrapConfig,\n '_testOverrides'\n> & {\n /** Replace any of the in-memory pieces, for example to share one `pubsub` between two services. */\n overrides?: ServiceBootstrapOverrides;\n};\n\nexport type TestServiceBootstrap = {\n bootstrap: ServiceBootstrap;\n firestore: Firestore;\n database: Database;\n messageBus: MessageBusWithLifecycle;\n};\n\n/**\n * Service bootstrap on in-memory Firestore, Realtime Database and Pub/Sub, with no Firebase\n * app, no emulator and no network. The event store, the projections and the message bus are\n * the real ones, wired to the fakes, so a service test exercises the same code as production.\n */\nexport const createTestServiceBootstrap = (\n options: TestServiceBootstrapOptions,\n): TestServiceBootstrap => {\n const { overrides, ...config } = options;\n\n const firestore =\n overrides?.firestore ?? (new InMemoryFirestore() as unknown as Firestore);\n const database = overrides?.database ?? createInMemoryDatabase();\n const auth = overrides?.auth ?? ({} as Auth);\n\n const app = {\n firestore: () => firestore,\n database: () => database,\n auth: () => auth,\n delete: async () => {},\n };\n\n const firebaseAdmin = overrides?.firebaseAdmin ?? {\n apps: [],\n initializeApp: () => app,\n app: () => app,\n };\n\n const messageBus = (overrides?.messageBus ??\n createInMemoryMessageBus({\n subscriptionId: config.pubsub?.subscriptionId ?? config.serviceName,\n instanceId: config.pubsub?.subscriptionId ?? config.serviceName,\n })) as MessageBusWithLifecycle;\n\n const bootstrap = new ServiceBootstrap({\n ...config,\n firebase: { projectId: 'test-project', ...config.firebase },\n pubsub: { projectId: 'test-project', ...config.pubsub },\n observability: { createLogger: false, ...config.observability },\n _testOverrides: {\n ...overrides,\n firebaseAdmin: firebaseAdmin as ServiceBootstrapOverrides['firebaseAdmin'],\n firestore,\n database,\n auth,\n messageBus,\n },\n });\n\n return { bootstrap, firestore, database, messageBus };\n};\n"],"mappings":";;;;;AACA,SAAS,yBAAyB;AAClC,SAAS,gCAAgC;AACzC,SAAS,8BAA8B;AA+BhC,IAAM,6BAA6B,CACxC,YACyB;AACzB,QAAM,EAAE,WAAW,GAAG,OAAO,IAAI;AAEjC,QAAM,YACJ,WAAW,aAAc,IAAI,kBAAkB;AACjD,QAAM,WAAW,WAAW,YAAY,uBAAuB;AAC/D,QAAM,OAAO,WAAW,QAAS,CAAC;AAElC,QAAM,MAAM;AAAA,IACV,WAAW,MAAM;AAAA,IACjB,UAAU,MAAM;AAAA,IAChB,MAAM,MAAM;AAAA,IACZ,QAAQ,YAAY;AAAA,IAAC;AAAA,EACvB;AAEA,QAAM,gBAAgB,WAAW,iBAAiB;AAAA,IAChD,MAAM,CAAC;AAAA,IACP,eAAe,MAAM;AAAA,IACrB,KAAK,MAAM;AAAA,EACb;AAEA,QAAM,aAAc,WAAW,cAC7B,yBAAyB;AAAA,IACvB,gBAAgB,OAAO,QAAQ,kBAAkB,OAAO;AAAA,IACxD,YAAY,OAAO,QAAQ,kBAAkB,OAAO;AAAA,EACtD,CAAC;AAEH,QAAM,YAAY,IAAI,iBAAiB;AAAA,IACrC,GAAG;AAAA,IACH,UAAU,EAAE,WAAW,gBAAgB,GAAG,OAAO,SAAS;AAAA,IAC1D,QAAQ,EAAE,WAAW,gBAAgB,GAAG,OAAO,OAAO;AAAA,IACtD,eAAe,EAAE,cAAc,OAAO,GAAG,OAAO,cAAc;AAAA,IAC9D,gBAAgB;AAAA,MACd,GAAG;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,EAAE,WAAW,WAAW,UAAU,WAAW;AACtD;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emmett-community/emmett-google-services-bootstrap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -48,6 +48,16 @@
|
|
|
48
48
|
"types": "./dist/index.d.cts",
|
|
49
49
|
"default": "./dist/index.cjs"
|
|
50
50
|
}
|
|
51
|
+
},
|
|
52
|
+
"./testing": {
|
|
53
|
+
"import": {
|
|
54
|
+
"types": "./dist/testing.d.ts",
|
|
55
|
+
"default": "./dist/testing.js"
|
|
56
|
+
},
|
|
57
|
+
"require": {
|
|
58
|
+
"types": "./dist/testing.d.cts",
|
|
59
|
+
"default": "./dist/testing.cjs"
|
|
60
|
+
}
|
|
51
61
|
}
|
|
52
62
|
},
|
|
53
63
|
"main": "./dist/index.cjs",
|
|
@@ -57,10 +67,10 @@
|
|
|
57
67
|
"dist"
|
|
58
68
|
],
|
|
59
69
|
"dependencies": {
|
|
60
|
-
"@emmett-community/emmett-expressjs-with-openapi": "^0.
|
|
61
|
-
"@emmett-community/emmett-google-firestore": "^0.
|
|
62
|
-
"@emmett-community/emmett-google-pubsub": "^0.
|
|
63
|
-
"@emmett-community/emmett-google-realtime-db": "^0.
|
|
70
|
+
"@emmett-community/emmett-expressjs-with-openapi": "^0.9.0",
|
|
71
|
+
"@emmett-community/emmett-google-firestore": "^0.6.0",
|
|
72
|
+
"@emmett-community/emmett-google-pubsub": "^0.6.0",
|
|
73
|
+
"@emmett-community/emmett-google-realtime-db": "^0.8.0",
|
|
64
74
|
"@emmett-community/emmett-observability": "^0.3.0",
|
|
65
75
|
"@event-driven-io/emmett": "^0.39.0",
|
|
66
76
|
"@google-cloud/pubsub": "^4.9.0",
|