fhir-engine 0.1.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.0] - 2026-03-15
9
+
10
+ ### Added
11
+
12
+ - **`engine.status()`** — Returns `FhirEngineStatus` with `fhirVersions`, `loadedPackages`, `resourceTypes`, `databaseType`, `igAction`, `startedAt`, and `plugins` — enables `fhir doctor` and `/metadata` endpoints
13
+ - **`FhirEngineStatus`** type export
14
+
15
+ ### Changed
16
+
17
+ - **Test suite expanded** — from 38 to 73 tests across 3 test files, meeting the 5+ per critical path requirement:
18
+ - Config validation: 6 tests
19
+ - Bootstrap sequence: 7 tests
20
+ - Plugin init() failure: 5 tests
21
+ - Plugin start() failure: 5 tests
22
+ - Plugin stop() reverse order: 5 tests
23
+ - engine.stop() idempotency: 5 tests
24
+ - E2E CRUD: 7 tests
25
+ - engine.status(): 8 tests
26
+
27
+ ---
28
+
8
29
  ## [0.1.0] - 2026-03-15
9
30
 
10
31
  ### Added
package/README.md CHANGED
@@ -157,6 +157,7 @@ Creates and bootstraps a fully initialized FHIR engine.
157
157
  | `resourceTypes` | `string[]` | Resource types with database tables |
158
158
  | `context` | `EngineContext` | Shared context (same object plugins receive) |
159
159
  | `logger` | `Logger` | Logger instance |
160
+ | `status()` | `() => FhirEngineStatus` | Engine health and status information |
160
161
  | `stop()` | `() => Promise<void>` | Gracefully shut down the engine |
161
162
 
162
163
  ### `FhirEngineConfig`
@@ -172,6 +173,22 @@ interface FhirEngineConfig {
172
173
  }
173
174
  ```
174
175
 
176
+ ### `FhirEngineStatus`
177
+
178
+ Returned by `engine.status()`:
179
+
180
+ ```ts
181
+ interface FhirEngineStatus {
182
+ fhirVersions: string[]; // e.g. ['4.0']
183
+ loadedPackages: string[]; // e.g. ['hl7.fhir.r4.core@4.0.1']
184
+ resourceTypes: string[]; // e.g. ['Patient', 'Observation', ...]
185
+ databaseType: "sqlite" | "sqlite-wasm" | "postgres";
186
+ igAction: "new" | "upgrade" | "consistent";
187
+ startedAt: Date;
188
+ plugins: string[]; // registered plugin names
189
+ }
190
+ ```
191
+
175
192
  ### `defineConfig(config)`
176
193
 
177
194
  Type-safe identity helper for config files. Returns the config unchanged.
@@ -274,6 +274,16 @@ async function createFhirEngine(config) {
274
274
  await runPluginHook(plugins, "ready", ctx);
275
275
  }
276
276
  let stopped = false;
277
+ const startedAt = /* @__PURE__ */ new Date();
278
+ const loadedPackages = result.packages.map((p) => `${p.name}@${p.version}`);
279
+ const fhirVersions = [...new Set(
280
+ result.packages.map((p) => {
281
+ if (p.name.includes(".r4.")) return "4.0";
282
+ if (p.name.includes(".r4b.")) return "4.3";
283
+ if (p.name.includes(".r5.")) return "5.0";
284
+ return void 0;
285
+ }).filter(Boolean)
286
+ )];
277
287
  const engine = {
278
288
  definitions: registry,
279
289
  runtime,
@@ -285,6 +295,17 @@ async function createFhirEngine(config) {
285
295
  resourceTypes,
286
296
  logger,
287
297
  context: ctx,
298
+ status() {
299
+ return {
300
+ fhirVersions,
301
+ loadedPackages,
302
+ resourceTypes,
303
+ databaseType: config.database.type,
304
+ igAction: igResult.action,
305
+ startedAt,
306
+ plugins: plugins.map((p) => p.name)
307
+ };
308
+ },
288
309
  async stop() {
289
310
  if (stopped) return;
290
311
  stopped = true;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts", "../../src/engine.ts", "../../src/adapter-factory.ts", "../../src/config.ts", "../../src/logger.ts"],
4
- "sourcesContent": ["// fhir-engine \u2014 public API\n\nexport { createFhirEngine } from './engine.js';\nexport { defineConfig, loadFhirConfig } from './config.js';\nexport { createConsoleLogger } from './logger.js';\nexport { createAdapter } from './adapter-factory.js';\n\nexport type {\n FhirEngine,\n FhirEngineConfig,\n FhirEnginePlugin,\n EngineContext,\n DatabaseConfig,\n SqliteDatabaseConfig,\n SqliteWasmDatabaseConfig,\n PostgresDatabaseConfig,\n PackagesConfig,\n Logger,\n} from './types.js';\n\n// Re-export key upstream types for convenience\nexport type { DefinitionRegistry, DefinitionProvider } from 'fhir-definition';\nexport type { FhirRuntimeInstance } from 'fhir-runtime';\nexport type { FhirPersistence, StorageAdapter } from 'fhir-persistence';\n", "import { loadDefinitionPackages } from 'fhir-definition';\nimport { createRuntime, extractSearchValues, extractAllSearchValues, extractReferences } from 'fhir-runtime';\nimport { FhirDefinitionBridge, FhirRuntimeProvider, FhirSystem } from 'fhir-persistence';\n\nimport { createAdapter } from './adapter-factory.js';\nimport { loadFhirConfig } from './config.js';\nimport { createConsoleLogger } from './logger.js';\nimport type { EngineContext, FhirEngine, FhirEngineConfig, FhirEnginePlugin } from './types.js';\n\n/**\n * Resolve the SQL dialect from the database config type.\n */\nfunction resolveDialect(type: FhirEngineConfig['database']['type']): 'sqlite' | 'postgres' {\n switch (type) {\n case 'sqlite':\n case 'sqlite-wasm':\n return 'sqlite';\n case 'postgres':\n return 'postgres';\n }\n}\n\n/**\n * Validate the engine configuration, throwing on missing required fields.\n */\nfunction validateConfig(config: FhirEngineConfig): void {\n if (!config.database) {\n throw new Error('fhir-engine: config.database is required');\n }\n if (!config.database.type) {\n throw new Error('fhir-engine: config.database.type is required (sqlite | sqlite-wasm | postgres)');\n }\n if (!config.packages) {\n throw new Error('fhir-engine: config.packages is required');\n }\n if (!config.packages.path) {\n throw new Error('fhir-engine: config.packages.path is required');\n }\n}\n\n/**\n * Run a lifecycle hook on all plugins in order.\n * Wraps errors with the plugin name for clear diagnostics.\n */\nasync function runPluginHook(\n plugins: FhirEnginePlugin[],\n hook: 'init' | 'start' | 'ready',\n ctx: EngineContext,\n): Promise<void> {\n for (const plugin of plugins) {\n const fn = plugin[hook];\n if (fn) {\n try {\n await fn.call(plugin, ctx);\n } catch (err) {\n throw new Error(\n `fhir-engine: plugin \"${plugin.name}\" failed during ${hook}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n }\n }\n}\n\n/**\n * Create and bootstrap a fully initialized FHIR engine.\n *\n * This is the single entry point for all FHIR applications.\n * It assembles fhir-definition, fhir-runtime, and fhir-persistence\n * into a running system from a single configuration object.\n *\n * @example\n * ```ts\n * const engine = await createFhirEngine({\n * database: { type: 'sqlite', path: ':memory:' },\n * packages: { path: './fhir-packages' },\n * });\n *\n * const patient = await engine.persistence.createResource('Patient', {\n * resourceType: 'Patient',\n * name: [{ family: 'Smith', given: ['John'] }],\n * });\n *\n * await engine.stop();\n * ```\n */\nexport async function createFhirEngine(config?: FhirEngineConfig): Promise<FhirEngine> {\n // \u2500\u2500 0. Resolve config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (!config) {\n config = await loadFhirConfig();\n }\n validateConfig(config);\n\n const logger = config.logger ?? createConsoleLogger();\n const plugins = config.plugins ?? [];\n logger.info('Initializing fhir-engine...');\n\n // \u2500\u2500 1. Load FHIR definitions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n logger.info(`Loading FHIR packages from: ${config.packages.path}`);\n const { registry, result } = loadDefinitionPackages(config.packages.path);\n logger.info(\n `Loaded ${result.packages.length} package(s): ${result.packages.map((p) => `${p.name}@${p.version}`).join(', ')}`,\n );\n\n // \u2500\u2500 2. Create fhir-runtime \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n logger.info('Creating fhir-runtime instance...');\n const runtime = await createRuntime({ definitions: registry, preloadCore: false });\n\n // \u2500\u2500 3. Build provider bridges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const definitionBridge = new FhirDefinitionBridge(registry);\n const runtimeProvider = new FhirRuntimeProvider({\n extractSearchValues,\n extractAllSearchValues,\n extractReferences,\n });\n\n // \u2500\u2500 4. Create storage adapter \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const adapter = createAdapter(config.database, logger);\n\n // \u2500\u2500 5. Build EngineContext \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const ctx: { -readonly [K in keyof EngineContext]: EngineContext[K] } = {\n config,\n definitions: registry,\n runtime,\n adapter,\n persistence: undefined,\n logger,\n };\n\n // \u2500\u2500 6. INIT phase \u2014 plugins run before persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running init for ${plugins.length} plugin(s): ${plugins.map((p) => p.name).join(', ')}`);\n await runPluginHook(plugins, 'init', ctx);\n }\n\n // \u2500\u2500 7. Initialize FhirSystem \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const dialect = resolveDialect(config.database.type);\n const system = new FhirSystem(adapter, {\n dialect,\n runtimeProvider,\n packageName: config.packageName ?? 'fhir-engine.default',\n packageVersion: config.packageVersion ?? '1.0.0',\n });\n\n logger.info('Initializing persistence system (schema + migration)...');\n const { persistence, sdRegistry, spRegistry, igResult, resourceTypes } =\n await system.initialize(definitionBridge);\n\n logger.info(`Persistence ready \u2014 IG action: ${igResult.action}, ${resourceTypes.length} resource type(s)`);\n\n // ctx.persistence now available\n ctx.persistence = persistence;\n\n // \u2500\u2500 8. START phase \u2014 plugins can access persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running start for ${plugins.length} plugin(s)...`);\n await runPluginHook(plugins, 'start', ctx);\n }\n\n // \u2500\u2500 9. READY phase \u2014 system fully operational \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running ready for ${plugins.length} plugin(s)...`);\n await runPluginHook(plugins, 'ready', ctx);\n }\n\n // \u2500\u2500 10. Return FhirEngine \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let stopped = false;\n\n const engine: FhirEngine = {\n definitions: registry,\n runtime,\n adapter,\n persistence,\n sdRegistry,\n spRegistry,\n igResult,\n resourceTypes,\n logger,\n context: ctx as EngineContext,\n\n async stop() {\n if (stopped) return;\n stopped = true;\n logger.info('Stopping fhir-engine...');\n\n // Stop plugins in reverse registration order\n for (let i = plugins.length - 1; i >= 0; i--) {\n const plugin = plugins[i];\n if (plugin.stop) {\n try {\n await plugin.stop.call(plugin, ctx as EngineContext);\n } catch (err) {\n logger.error(\n `Plugin \"${plugin.name}\" failed during stop: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n await adapter.close();\n logger.info('fhir-engine stopped.');\n },\n };\n\n logger.info('fhir-engine ready.');\n return engine;\n}\n", "import { BetterSqlite3Adapter, SQLiteAdapter } from 'fhir-persistence';\nimport type { StorageAdapter } from 'fhir-persistence';\nimport type { DatabaseConfig, Logger } from './types.js';\n\n/**\n * Create a StorageAdapter from the database configuration.\n *\n * Supported adapters:\n * - `sqlite` \u2192 BetterSqlite3Adapter (native, Node.js / Electron)\n * - `sqlite-wasm` \u2192 SQLiteAdapter (sql.js WASM, browser / cross-platform)\n * - `postgres` \u2192 not yet available (PostgresAdapter not exported from fhir-persistence)\n */\nexport function createAdapter(config: DatabaseConfig, logger: Logger): StorageAdapter {\n switch (config.type) {\n case 'sqlite': {\n logger.info(`Creating BetterSqlite3Adapter (path: ${config.path})`);\n return new BetterSqlite3Adapter({\n path: config.path,\n wal: config.wal ?? true,\n busyTimeout: config.busyTimeout ?? 5000,\n });\n }\n\n case 'sqlite-wasm': {\n logger.info(`Creating SQLiteAdapter (WASM, path: ${config.path})`);\n return new SQLiteAdapter(config.path);\n }\n\n case 'postgres': {\n throw new Error(\n 'fhir-engine: PostgreSQL adapter is not yet available. ' +\n 'PostgresAdapter is not exported from fhir-persistence v0.1.0. ' +\n 'Use database.type = \"sqlite\" for now.',\n );\n }\n\n default: {\n const _exhaustive: never = config;\n throw new Error(`fhir-engine: unknown database type: ${(_exhaustive as DatabaseConfig).type}`);\n }\n }\n}\n", "import { existsSync, readFileSync } from 'node:fs';\nimport { resolve, extname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { FhirEngineConfig, DatabaseConfig } from './types.js';\n\n// ---------------------------------------------------------------------------\n// defineConfig \u2014 typed identity helper for fhir.config.ts / .js\n// ---------------------------------------------------------------------------\n\n/**\n * Type-safe config helper for `fhir.config.ts` / `fhir.config.js`.\n *\n * @example\n * ```ts\n * // fhir.config.ts\n * import { defineConfig } from 'fhir-engine';\n *\n * export default defineConfig({\n * database: { type: 'sqlite', path: './fhir.db' },\n * packages: { path: './fhir-packages' },\n * });\n * ```\n */\nexport function defineConfig(config: FhirEngineConfig): FhirEngineConfig {\n return config;\n}\n\n// ---------------------------------------------------------------------------\n// Config file discovery order\n// ---------------------------------------------------------------------------\n\nconst CONFIG_FILENAMES = [\n 'fhir.config.ts',\n 'fhir.config.js',\n 'fhir.config.mjs',\n 'fhir.config.json',\n] as const;\n\n// ---------------------------------------------------------------------------\n// loadFhirConfig \u2014 auto-discover and load config\n// ---------------------------------------------------------------------------\n\n/**\n * Load engine configuration from a file.\n *\n * If `configPath` is provided, loads that exact file.\n * Otherwise, searches the current working directory for config files\n * in this order: `fhir.config.ts` \u2192 `fhir.config.js` \u2192 `fhir.config.mjs` \u2192 `fhir.config.json`.\n *\n * Environment variable overrides are applied on top of the loaded config.\n *\n * @param configPath - Explicit path to a config file (optional).\n * @returns The resolved `FhirEngineConfig`.\n */\nexport async function loadFhirConfig(configPath?: string): Promise<FhirEngineConfig> {\n let resolvedPath: string;\n\n if (configPath) {\n resolvedPath = resolve(configPath);\n if (!existsSync(resolvedPath)) {\n throw new Error(`fhir-engine: config file not found: ${resolvedPath}`);\n }\n } else {\n const cwd = process.cwd();\n const found = CONFIG_FILENAMES\n .map((name) => resolve(cwd, name))\n .find((p) => existsSync(p));\n if (!found) {\n throw new Error(\n `fhir-engine: no config file found in ${cwd}. ` +\n `Expected one of: ${CONFIG_FILENAMES.join(', ')}`,\n );\n }\n resolvedPath = found;\n }\n\n const config = await loadConfigFile(resolvedPath);\n return applyEnvOverrides(config);\n}\n\n// ---------------------------------------------------------------------------\n// File loaders\n// ---------------------------------------------------------------------------\n\nasync function loadConfigFile(filePath: string): Promise<FhirEngineConfig> {\n const ext = extname(filePath);\n\n if (ext === '.json') {\n return loadJsonConfig(filePath);\n }\n\n if (ext === '.ts' || ext === '.js' || ext === '.mjs') {\n return loadModuleConfig(filePath);\n }\n\n throw new Error(`fhir-engine: unsupported config file extension: ${ext}`);\n}\n\nfunction loadJsonConfig(filePath: string): FhirEngineConfig {\n try {\n const raw = readFileSync(filePath, 'utf-8');\n return JSON.parse(raw) as FhirEngineConfig;\n } catch (err) {\n throw new Error(\n `fhir-engine: failed to parse config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n}\n\nasync function loadModuleConfig(filePath: string): Promise<FhirEngineConfig> {\n try {\n const fileUrl = pathToFileURL(filePath).href;\n const mod = await import(fileUrl);\n const config = mod.default ?? mod;\n\n if (!config || typeof config !== 'object') {\n throw new Error('config file must export a FhirEngineConfig object as default export');\n }\n\n return config as FhirEngineConfig;\n } catch (err) {\n if (err instanceof Error && err.message.includes('must export')) {\n throw err;\n }\n throw new Error(\n `fhir-engine: failed to load config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Environment variable overrides\n// ---------------------------------------------------------------------------\n\nconst VALID_DB_TYPES = ['sqlite', 'sqlite-wasm', 'postgres'] as const;\n\n/**\n * Apply environment variable overrides on top of a loaded config.\n *\n * | Env Variable | Overrides |\n * |---------------------|----------------------------------|\n * | FHIR_DATABASE_TYPE | config.database.type |\n * | FHIR_DATABASE_URL | config.database.path / .url |\n * | FHIR_PACKAGES_PATH | config.packages.path |\n * | FHIR_LOG_LEVEL | (stored for logger filtering) |\n */\nexport function applyEnvOverrides(config: FhirEngineConfig): FhirEngineConfig {\n const result = structuredClone(config);\n\n const dbType = process.env.FHIR_DATABASE_TYPE;\n const dbUrl = process.env.FHIR_DATABASE_URL;\n const pkgPath = process.env.FHIR_PACKAGES_PATH;\n\n if (dbType) {\n if (!VALID_DB_TYPES.includes(dbType as typeof VALID_DB_TYPES[number])) {\n throw new Error(\n `fhir-engine: FHIR_DATABASE_TYPE must be one of: ${VALID_DB_TYPES.join(', ')}. Got: \"${dbType}\"`,\n );\n }\n // Rebuild database config with overridden type\n const validType = dbType as DatabaseConfig['type'];\n if (validType === 'postgres') {\n result.database = { type: 'postgres', url: (result.database as { url?: string }).url ?? '' };\n } else if (validType === 'sqlite-wasm') {\n result.database = { type: 'sqlite-wasm', path: (result.database as { path?: string }).path ?? '' };\n } else {\n result.database = { type: 'sqlite', path: (result.database as { path?: string }).path ?? '' };\n }\n }\n\n if (dbUrl) {\n const type = result.database?.type;\n if (type === 'postgres') {\n result.database = { ...result.database, type: 'postgres', url: dbUrl };\n } else if (type === 'sqlite-wasm') {\n result.database = { ...result.database, type: 'sqlite-wasm', path: dbUrl };\n } else {\n result.database = { ...result.database, type: 'sqlite', path: dbUrl };\n }\n }\n\n if (pkgPath) {\n result.packages = { ...result.packages, path: pkgPath };\n }\n\n return result;\n}\n", "import type { Logger } from './types.js';\n\n/**\n * Default console-based logger.\n */\nexport function createConsoleLogger(): Logger {\n return {\n debug: (message: string, ...args: unknown[]) => console.debug(`[fhir-engine] ${message}`, ...args),\n info: (message: string, ...args: unknown[]) => console.info(`[fhir-engine] ${message}`, ...args),\n warn: (message: string, ...args: unknown[]) => console.warn(`[fhir-engine] ${message}`, ...args),\n error: (message: string, ...args: unknown[]) => console.error(`[fhir-engine] ${message}`, ...args),\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// fhir-engine \u2014 public API\n\nexport { createFhirEngine } from './engine.js';\nexport { defineConfig, loadFhirConfig } from './config.js';\nexport { createConsoleLogger } from './logger.js';\nexport { createAdapter } from './adapter-factory.js';\n\nexport type {\n FhirEngine,\n FhirEngineConfig,\n FhirEnginePlugin,\n FhirEngineStatus,\n EngineContext,\n DatabaseConfig,\n SqliteDatabaseConfig,\n SqliteWasmDatabaseConfig,\n PostgresDatabaseConfig,\n PackagesConfig,\n Logger,\n} from './types.js';\n\n// Re-export key upstream types for convenience\nexport type { DefinitionRegistry, DefinitionProvider } from 'fhir-definition';\nexport type { FhirRuntimeInstance } from 'fhir-runtime';\nexport type { FhirPersistence, StorageAdapter } from 'fhir-persistence';\n", "import { loadDefinitionPackages } from 'fhir-definition';\nimport { createRuntime, extractSearchValues, extractAllSearchValues, extractReferences } from 'fhir-runtime';\nimport { FhirDefinitionBridge, FhirRuntimeProvider, FhirSystem } from 'fhir-persistence';\n\nimport { createAdapter } from './adapter-factory.js';\nimport { loadFhirConfig } from './config.js';\nimport { createConsoleLogger } from './logger.js';\nimport type { EngineContext, FhirEngine, FhirEngineConfig, FhirEnginePlugin, FhirEngineStatus } from './types.js';\n\n/**\n * Resolve the SQL dialect from the database config type.\n */\nfunction resolveDialect(type: FhirEngineConfig['database']['type']): 'sqlite' | 'postgres' {\n switch (type) {\n case 'sqlite':\n case 'sqlite-wasm':\n return 'sqlite';\n case 'postgres':\n return 'postgres';\n }\n}\n\n/**\n * Validate the engine configuration, throwing on missing required fields.\n */\nfunction validateConfig(config: FhirEngineConfig): void {\n if (!config.database) {\n throw new Error('fhir-engine: config.database is required');\n }\n if (!config.database.type) {\n throw new Error('fhir-engine: config.database.type is required (sqlite | sqlite-wasm | postgres)');\n }\n if (!config.packages) {\n throw new Error('fhir-engine: config.packages is required');\n }\n if (!config.packages.path) {\n throw new Error('fhir-engine: config.packages.path is required');\n }\n}\n\n/**\n * Run a lifecycle hook on all plugins in order.\n * Wraps errors with the plugin name for clear diagnostics.\n */\nasync function runPluginHook(\n plugins: FhirEnginePlugin[],\n hook: 'init' | 'start' | 'ready',\n ctx: EngineContext,\n): Promise<void> {\n for (const plugin of plugins) {\n const fn = plugin[hook];\n if (fn) {\n try {\n await fn.call(plugin, ctx);\n } catch (err) {\n throw new Error(\n `fhir-engine: plugin \"${plugin.name}\" failed during ${hook}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n }\n }\n}\n\n/**\n * Create and bootstrap a fully initialized FHIR engine.\n *\n * This is the single entry point for all FHIR applications.\n * It assembles fhir-definition, fhir-runtime, and fhir-persistence\n * into a running system from a single configuration object.\n *\n * @example\n * ```ts\n * const engine = await createFhirEngine({\n * database: { type: 'sqlite', path: ':memory:' },\n * packages: { path: './fhir-packages' },\n * });\n *\n * const patient = await engine.persistence.createResource('Patient', {\n * resourceType: 'Patient',\n * name: [{ family: 'Smith', given: ['John'] }],\n * });\n *\n * await engine.stop();\n * ```\n */\nexport async function createFhirEngine(config?: FhirEngineConfig): Promise<FhirEngine> {\n // \u2500\u2500 0. Resolve config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (!config) {\n config = await loadFhirConfig();\n }\n validateConfig(config);\n\n const logger = config.logger ?? createConsoleLogger();\n const plugins = config.plugins ?? [];\n logger.info('Initializing fhir-engine...');\n\n // \u2500\u2500 1. Load FHIR definitions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n logger.info(`Loading FHIR packages from: ${config.packages.path}`);\n const { registry, result } = loadDefinitionPackages(config.packages.path);\n logger.info(\n `Loaded ${result.packages.length} package(s): ${result.packages.map((p) => `${p.name}@${p.version}`).join(', ')}`,\n );\n\n // \u2500\u2500 2. Create fhir-runtime \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n logger.info('Creating fhir-runtime instance...');\n const runtime = await createRuntime({ definitions: registry, preloadCore: false });\n\n // \u2500\u2500 3. Build provider bridges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const definitionBridge = new FhirDefinitionBridge(registry);\n const runtimeProvider = new FhirRuntimeProvider({\n extractSearchValues,\n extractAllSearchValues,\n extractReferences,\n });\n\n // \u2500\u2500 4. Create storage adapter \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const adapter = createAdapter(config.database, logger);\n\n // \u2500\u2500 5. Build EngineContext \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const ctx: { -readonly [K in keyof EngineContext]: EngineContext[K] } = {\n config,\n definitions: registry,\n runtime,\n adapter,\n persistence: undefined,\n logger,\n };\n\n // \u2500\u2500 6. INIT phase \u2014 plugins run before persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running init for ${plugins.length} plugin(s): ${plugins.map((p) => p.name).join(', ')}`);\n await runPluginHook(plugins, 'init', ctx);\n }\n\n // \u2500\u2500 7. Initialize FhirSystem \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const dialect = resolveDialect(config.database.type);\n const system = new FhirSystem(adapter, {\n dialect,\n runtimeProvider,\n packageName: config.packageName ?? 'fhir-engine.default',\n packageVersion: config.packageVersion ?? '1.0.0',\n });\n\n logger.info('Initializing persistence system (schema + migration)...');\n const { persistence, sdRegistry, spRegistry, igResult, resourceTypes } =\n await system.initialize(definitionBridge);\n\n logger.info(`Persistence ready \u2014 IG action: ${igResult.action}, ${resourceTypes.length} resource type(s)`);\n\n // ctx.persistence now available\n ctx.persistence = persistence;\n\n // \u2500\u2500 8. START phase \u2014 plugins can access persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running start for ${plugins.length} plugin(s)...`);\n await runPluginHook(plugins, 'start', ctx);\n }\n\n // \u2500\u2500 9. READY phase \u2014 system fully operational \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running ready for ${plugins.length} plugin(s)...`);\n await runPluginHook(plugins, 'ready', ctx);\n }\n\n // \u2500\u2500 10. Return FhirEngine \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let stopped = false;\n const startedAt = new Date();\n const loadedPackages = result.packages.map((p) => `${p.name}@${p.version}`);\n const fhirVersions = [...new Set(\n result.packages\n .map((p) => {\n if (p.name.includes('.r4.')) return '4.0';\n if (p.name.includes('.r4b.')) return '4.3';\n if (p.name.includes('.r5.')) return '5.0';\n return undefined;\n })\n .filter(Boolean),\n )] as string[];\n\n const engine: FhirEngine = {\n definitions: registry,\n runtime,\n adapter,\n persistence,\n sdRegistry,\n spRegistry,\n igResult,\n resourceTypes,\n logger,\n context: ctx as EngineContext,\n\n status(): FhirEngineStatus {\n return {\n fhirVersions,\n loadedPackages,\n resourceTypes,\n databaseType: config.database.type,\n igAction: igResult.action,\n startedAt,\n plugins: plugins.map((p) => p.name),\n };\n },\n\n async stop() {\n if (stopped) return;\n stopped = true;\n logger.info('Stopping fhir-engine...');\n\n // Stop plugins in reverse registration order\n for (let i = plugins.length - 1; i >= 0; i--) {\n const plugin = plugins[i];\n if (plugin.stop) {\n try {\n await plugin.stop.call(plugin, ctx as EngineContext);\n } catch (err) {\n logger.error(\n `Plugin \"${plugin.name}\" failed during stop: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n await adapter.close();\n logger.info('fhir-engine stopped.');\n },\n };\n\n logger.info('fhir-engine ready.');\n return engine;\n}\n", "import { BetterSqlite3Adapter, SQLiteAdapter } from 'fhir-persistence';\nimport type { StorageAdapter } from 'fhir-persistence';\nimport type { DatabaseConfig, Logger } from './types.js';\n\n/**\n * Create a StorageAdapter from the database configuration.\n *\n * Supported adapters:\n * - `sqlite` \u2192 BetterSqlite3Adapter (native, Node.js / Electron)\n * - `sqlite-wasm` \u2192 SQLiteAdapter (sql.js WASM, browser / cross-platform)\n * - `postgres` \u2192 not yet available (PostgresAdapter not exported from fhir-persistence)\n */\nexport function createAdapter(config: DatabaseConfig, logger: Logger): StorageAdapter {\n switch (config.type) {\n case 'sqlite': {\n logger.info(`Creating BetterSqlite3Adapter (path: ${config.path})`);\n return new BetterSqlite3Adapter({\n path: config.path,\n wal: config.wal ?? true,\n busyTimeout: config.busyTimeout ?? 5000,\n });\n }\n\n case 'sqlite-wasm': {\n logger.info(`Creating SQLiteAdapter (WASM, path: ${config.path})`);\n return new SQLiteAdapter(config.path);\n }\n\n case 'postgres': {\n throw new Error(\n 'fhir-engine: PostgreSQL adapter is not yet available. ' +\n 'PostgresAdapter is not exported from fhir-persistence v0.1.0. ' +\n 'Use database.type = \"sqlite\" for now.',\n );\n }\n\n default: {\n const _exhaustive: never = config;\n throw new Error(`fhir-engine: unknown database type: ${(_exhaustive as DatabaseConfig).type}`);\n }\n }\n}\n", "import { existsSync, readFileSync } from 'node:fs';\nimport { resolve, extname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { FhirEngineConfig, DatabaseConfig } from './types.js';\n\n// ---------------------------------------------------------------------------\n// defineConfig \u2014 typed identity helper for fhir.config.ts / .js\n// ---------------------------------------------------------------------------\n\n/**\n * Type-safe config helper for `fhir.config.ts` / `fhir.config.js`.\n *\n * @example\n * ```ts\n * // fhir.config.ts\n * import { defineConfig } from 'fhir-engine';\n *\n * export default defineConfig({\n * database: { type: 'sqlite', path: './fhir.db' },\n * packages: { path: './fhir-packages' },\n * });\n * ```\n */\nexport function defineConfig(config: FhirEngineConfig): FhirEngineConfig {\n return config;\n}\n\n// ---------------------------------------------------------------------------\n// Config file discovery order\n// ---------------------------------------------------------------------------\n\nconst CONFIG_FILENAMES = [\n 'fhir.config.ts',\n 'fhir.config.js',\n 'fhir.config.mjs',\n 'fhir.config.json',\n] as const;\n\n// ---------------------------------------------------------------------------\n// loadFhirConfig \u2014 auto-discover and load config\n// ---------------------------------------------------------------------------\n\n/**\n * Load engine configuration from a file.\n *\n * If `configPath` is provided, loads that exact file.\n * Otherwise, searches the current working directory for config files\n * in this order: `fhir.config.ts` \u2192 `fhir.config.js` \u2192 `fhir.config.mjs` \u2192 `fhir.config.json`.\n *\n * Environment variable overrides are applied on top of the loaded config.\n *\n * @param configPath - Explicit path to a config file (optional).\n * @returns The resolved `FhirEngineConfig`.\n */\nexport async function loadFhirConfig(configPath?: string): Promise<FhirEngineConfig> {\n let resolvedPath: string;\n\n if (configPath) {\n resolvedPath = resolve(configPath);\n if (!existsSync(resolvedPath)) {\n throw new Error(`fhir-engine: config file not found: ${resolvedPath}`);\n }\n } else {\n const cwd = process.cwd();\n const found = CONFIG_FILENAMES\n .map((name) => resolve(cwd, name))\n .find((p) => existsSync(p));\n if (!found) {\n throw new Error(\n `fhir-engine: no config file found in ${cwd}. ` +\n `Expected one of: ${CONFIG_FILENAMES.join(', ')}`,\n );\n }\n resolvedPath = found;\n }\n\n const config = await loadConfigFile(resolvedPath);\n return applyEnvOverrides(config);\n}\n\n// ---------------------------------------------------------------------------\n// File loaders\n// ---------------------------------------------------------------------------\n\nasync function loadConfigFile(filePath: string): Promise<FhirEngineConfig> {\n const ext = extname(filePath);\n\n if (ext === '.json') {\n return loadJsonConfig(filePath);\n }\n\n if (ext === '.ts' || ext === '.js' || ext === '.mjs') {\n return loadModuleConfig(filePath);\n }\n\n throw new Error(`fhir-engine: unsupported config file extension: ${ext}`);\n}\n\nfunction loadJsonConfig(filePath: string): FhirEngineConfig {\n try {\n const raw = readFileSync(filePath, 'utf-8');\n return JSON.parse(raw) as FhirEngineConfig;\n } catch (err) {\n throw new Error(\n `fhir-engine: failed to parse config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n}\n\nasync function loadModuleConfig(filePath: string): Promise<FhirEngineConfig> {\n try {\n const fileUrl = pathToFileURL(filePath).href;\n const mod = await import(fileUrl);\n const config = mod.default ?? mod;\n\n if (!config || typeof config !== 'object') {\n throw new Error('config file must export a FhirEngineConfig object as default export');\n }\n\n return config as FhirEngineConfig;\n } catch (err) {\n if (err instanceof Error && err.message.includes('must export')) {\n throw err;\n }\n throw new Error(\n `fhir-engine: failed to load config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Environment variable overrides\n// ---------------------------------------------------------------------------\n\nconst VALID_DB_TYPES = ['sqlite', 'sqlite-wasm', 'postgres'] as const;\n\n/**\n * Apply environment variable overrides on top of a loaded config.\n *\n * | Env Variable | Overrides |\n * |---------------------|----------------------------------|\n * | FHIR_DATABASE_TYPE | config.database.type |\n * | FHIR_DATABASE_URL | config.database.path / .url |\n * | FHIR_PACKAGES_PATH | config.packages.path |\n * | FHIR_LOG_LEVEL | (stored for logger filtering) |\n */\nexport function applyEnvOverrides(config: FhirEngineConfig): FhirEngineConfig {\n const result = structuredClone(config);\n\n const dbType = process.env.FHIR_DATABASE_TYPE;\n const dbUrl = process.env.FHIR_DATABASE_URL;\n const pkgPath = process.env.FHIR_PACKAGES_PATH;\n\n if (dbType) {\n if (!VALID_DB_TYPES.includes(dbType as typeof VALID_DB_TYPES[number])) {\n throw new Error(\n `fhir-engine: FHIR_DATABASE_TYPE must be one of: ${VALID_DB_TYPES.join(', ')}. Got: \"${dbType}\"`,\n );\n }\n // Rebuild database config with overridden type\n const validType = dbType as DatabaseConfig['type'];\n if (validType === 'postgres') {\n result.database = { type: 'postgres', url: (result.database as { url?: string }).url ?? '' };\n } else if (validType === 'sqlite-wasm') {\n result.database = { type: 'sqlite-wasm', path: (result.database as { path?: string }).path ?? '' };\n } else {\n result.database = { type: 'sqlite', path: (result.database as { path?: string }).path ?? '' };\n }\n }\n\n if (dbUrl) {\n const type = result.database?.type;\n if (type === 'postgres') {\n result.database = { ...result.database, type: 'postgres', url: dbUrl };\n } else if (type === 'sqlite-wasm') {\n result.database = { ...result.database, type: 'sqlite-wasm', path: dbUrl };\n } else {\n result.database = { ...result.database, type: 'sqlite', path: dbUrl };\n }\n }\n\n if (pkgPath) {\n result.packages = { ...result.packages, path: pkgPath };\n }\n\n return result;\n}\n", "import type { Logger } from './types.js';\n\n/**\n * Default console-based logger.\n */\nexport function createConsoleLogger(): Logger {\n return {\n debug: (message: string, ...args: unknown[]) => console.debug(`[fhir-engine] ${message}`, ...args),\n info: (message: string, ...args: unknown[]) => console.info(`[fhir-engine] ${message}`, ...args),\n warn: (message: string, ...args: unknown[]) => console.warn(`[fhir-engine] ${message}`, ...args),\n error: (message: string, ...args: unknown[]) => console.error(`[fhir-engine] ${message}`, ...args),\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["import_fhir_persistence"]
7
7
  }
@@ -104,6 +104,8 @@ export declare interface FhirEngine {
104
104
  readonly logger: Logger;
105
105
  /** Shared context (same object plugins receive). */
106
106
  readonly context: EngineContext;
107
+ /** Return engine health/status information. */
108
+ status(): FhirEngineStatus;
107
109
  /** Gracefully shut down the engine (closes adapter). */
108
110
  stop(): Promise<void>;
109
111
  }
@@ -136,6 +138,23 @@ export declare interface FhirEnginePlugin {
136
138
  stop?(ctx: EngineContext): Promise<void>;
137
139
  }
138
140
 
141
+ export declare interface FhirEngineStatus {
142
+ /** FHIR versions loaded (e.g. ['4.0.1']). */
143
+ fhirVersions: string[];
144
+ /** Loaded package identifiers (e.g. ['hl7.fhir.r4.core@4.0.1']). */
145
+ loadedPackages: string[];
146
+ /** Resource types with database tables. */
147
+ resourceTypes: string[];
148
+ /** Database adapter type in use. */
149
+ databaseType: 'sqlite' | 'sqlite-wasm' | 'postgres';
150
+ /** IG migration action performed at startup. */
151
+ igAction: 'new' | 'upgrade' | 'consistent';
152
+ /** Timestamp when the engine finished bootstrapping. */
153
+ startedAt: Date;
154
+ /** Registered plugin names. */
155
+ plugins: string[];
156
+ }
157
+
139
158
  export { FhirPersistence }
140
159
 
141
160
  export { FhirRuntimeInstance }
@@ -104,6 +104,8 @@ export declare interface FhirEngine {
104
104
  readonly logger: Logger;
105
105
  /** Shared context (same object plugins receive). */
106
106
  readonly context: EngineContext;
107
+ /** Return engine health/status information. */
108
+ status(): FhirEngineStatus;
107
109
  /** Gracefully shut down the engine (closes adapter). */
108
110
  stop(): Promise<void>;
109
111
  }
@@ -136,6 +138,23 @@ export declare interface FhirEnginePlugin {
136
138
  stop?(ctx: EngineContext): Promise<void>;
137
139
  }
138
140
 
141
+ export declare interface FhirEngineStatus {
142
+ /** FHIR versions loaded (e.g. ['4.0.1']). */
143
+ fhirVersions: string[];
144
+ /** Loaded package identifiers (e.g. ['hl7.fhir.r4.core@4.0.1']). */
145
+ loadedPackages: string[];
146
+ /** Resource types with database tables. */
147
+ resourceTypes: string[];
148
+ /** Database adapter type in use. */
149
+ databaseType: 'sqlite' | 'sqlite-wasm' | 'postgres';
150
+ /** IG migration action performed at startup. */
151
+ igAction: 'new' | 'upgrade' | 'consistent';
152
+ /** Timestamp when the engine finished bootstrapping. */
153
+ startedAt: Date;
154
+ /** Registered plugin names. */
155
+ plugins: string[];
156
+ }
157
+
139
158
  export { FhirPersistence }
140
159
 
141
160
  export { FhirRuntimeInstance }
@@ -244,6 +244,16 @@ async function createFhirEngine(config) {
244
244
  await runPluginHook(plugins, "ready", ctx);
245
245
  }
246
246
  let stopped = false;
247
+ const startedAt = /* @__PURE__ */ new Date();
248
+ const loadedPackages = result.packages.map((p) => `${p.name}@${p.version}`);
249
+ const fhirVersions = [...new Set(
250
+ result.packages.map((p) => {
251
+ if (p.name.includes(".r4.")) return "4.0";
252
+ if (p.name.includes(".r4b.")) return "4.3";
253
+ if (p.name.includes(".r5.")) return "5.0";
254
+ return void 0;
255
+ }).filter(Boolean)
256
+ )];
247
257
  const engine = {
248
258
  definitions: registry,
249
259
  runtime,
@@ -255,6 +265,17 @@ async function createFhirEngine(config) {
255
265
  resourceTypes,
256
266
  logger,
257
267
  context: ctx,
268
+ status() {
269
+ return {
270
+ fhirVersions,
271
+ loadedPackages,
272
+ resourceTypes,
273
+ databaseType: config.database.type,
274
+ igAction: igResult.action,
275
+ startedAt,
276
+ plugins: plugins.map((p) => p.name)
277
+ };
278
+ },
258
279
  async stop() {
259
280
  if (stopped) return;
260
281
  stopped = true;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/engine.ts", "../../src/adapter-factory.ts", "../../src/config.ts", "../../src/logger.ts"],
4
- "sourcesContent": ["import { loadDefinitionPackages } from 'fhir-definition';\nimport { createRuntime, extractSearchValues, extractAllSearchValues, extractReferences } from 'fhir-runtime';\nimport { FhirDefinitionBridge, FhirRuntimeProvider, FhirSystem } from 'fhir-persistence';\n\nimport { createAdapter } from './adapter-factory.js';\nimport { loadFhirConfig } from './config.js';\nimport { createConsoleLogger } from './logger.js';\nimport type { EngineContext, FhirEngine, FhirEngineConfig, FhirEnginePlugin } from './types.js';\n\n/**\n * Resolve the SQL dialect from the database config type.\n */\nfunction resolveDialect(type: FhirEngineConfig['database']['type']): 'sqlite' | 'postgres' {\n switch (type) {\n case 'sqlite':\n case 'sqlite-wasm':\n return 'sqlite';\n case 'postgres':\n return 'postgres';\n }\n}\n\n/**\n * Validate the engine configuration, throwing on missing required fields.\n */\nfunction validateConfig(config: FhirEngineConfig): void {\n if (!config.database) {\n throw new Error('fhir-engine: config.database is required');\n }\n if (!config.database.type) {\n throw new Error('fhir-engine: config.database.type is required (sqlite | sqlite-wasm | postgres)');\n }\n if (!config.packages) {\n throw new Error('fhir-engine: config.packages is required');\n }\n if (!config.packages.path) {\n throw new Error('fhir-engine: config.packages.path is required');\n }\n}\n\n/**\n * Run a lifecycle hook on all plugins in order.\n * Wraps errors with the plugin name for clear diagnostics.\n */\nasync function runPluginHook(\n plugins: FhirEnginePlugin[],\n hook: 'init' | 'start' | 'ready',\n ctx: EngineContext,\n): Promise<void> {\n for (const plugin of plugins) {\n const fn = plugin[hook];\n if (fn) {\n try {\n await fn.call(plugin, ctx);\n } catch (err) {\n throw new Error(\n `fhir-engine: plugin \"${plugin.name}\" failed during ${hook}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n }\n }\n}\n\n/**\n * Create and bootstrap a fully initialized FHIR engine.\n *\n * This is the single entry point for all FHIR applications.\n * It assembles fhir-definition, fhir-runtime, and fhir-persistence\n * into a running system from a single configuration object.\n *\n * @example\n * ```ts\n * const engine = await createFhirEngine({\n * database: { type: 'sqlite', path: ':memory:' },\n * packages: { path: './fhir-packages' },\n * });\n *\n * const patient = await engine.persistence.createResource('Patient', {\n * resourceType: 'Patient',\n * name: [{ family: 'Smith', given: ['John'] }],\n * });\n *\n * await engine.stop();\n * ```\n */\nexport async function createFhirEngine(config?: FhirEngineConfig): Promise<FhirEngine> {\n // \u2500\u2500 0. Resolve config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (!config) {\n config = await loadFhirConfig();\n }\n validateConfig(config);\n\n const logger = config.logger ?? createConsoleLogger();\n const plugins = config.plugins ?? [];\n logger.info('Initializing fhir-engine...');\n\n // \u2500\u2500 1. Load FHIR definitions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n logger.info(`Loading FHIR packages from: ${config.packages.path}`);\n const { registry, result } = loadDefinitionPackages(config.packages.path);\n logger.info(\n `Loaded ${result.packages.length} package(s): ${result.packages.map((p) => `${p.name}@${p.version}`).join(', ')}`,\n );\n\n // \u2500\u2500 2. Create fhir-runtime \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n logger.info('Creating fhir-runtime instance...');\n const runtime = await createRuntime({ definitions: registry, preloadCore: false });\n\n // \u2500\u2500 3. Build provider bridges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const definitionBridge = new FhirDefinitionBridge(registry);\n const runtimeProvider = new FhirRuntimeProvider({\n extractSearchValues,\n extractAllSearchValues,\n extractReferences,\n });\n\n // \u2500\u2500 4. Create storage adapter \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const adapter = createAdapter(config.database, logger);\n\n // \u2500\u2500 5. Build EngineContext \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const ctx: { -readonly [K in keyof EngineContext]: EngineContext[K] } = {\n config,\n definitions: registry,\n runtime,\n adapter,\n persistence: undefined,\n logger,\n };\n\n // \u2500\u2500 6. INIT phase \u2014 plugins run before persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running init for ${plugins.length} plugin(s): ${plugins.map((p) => p.name).join(', ')}`);\n await runPluginHook(plugins, 'init', ctx);\n }\n\n // \u2500\u2500 7. Initialize FhirSystem \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const dialect = resolveDialect(config.database.type);\n const system = new FhirSystem(adapter, {\n dialect,\n runtimeProvider,\n packageName: config.packageName ?? 'fhir-engine.default',\n packageVersion: config.packageVersion ?? '1.0.0',\n });\n\n logger.info('Initializing persistence system (schema + migration)...');\n const { persistence, sdRegistry, spRegistry, igResult, resourceTypes } =\n await system.initialize(definitionBridge);\n\n logger.info(`Persistence ready \u2014 IG action: ${igResult.action}, ${resourceTypes.length} resource type(s)`);\n\n // ctx.persistence now available\n ctx.persistence = persistence;\n\n // \u2500\u2500 8. START phase \u2014 plugins can access persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running start for ${plugins.length} plugin(s)...`);\n await runPluginHook(plugins, 'start', ctx);\n }\n\n // \u2500\u2500 9. READY phase \u2014 system fully operational \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running ready for ${plugins.length} plugin(s)...`);\n await runPluginHook(plugins, 'ready', ctx);\n }\n\n // \u2500\u2500 10. Return FhirEngine \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let stopped = false;\n\n const engine: FhirEngine = {\n definitions: registry,\n runtime,\n adapter,\n persistence,\n sdRegistry,\n spRegistry,\n igResult,\n resourceTypes,\n logger,\n context: ctx as EngineContext,\n\n async stop() {\n if (stopped) return;\n stopped = true;\n logger.info('Stopping fhir-engine...');\n\n // Stop plugins in reverse registration order\n for (let i = plugins.length - 1; i >= 0; i--) {\n const plugin = plugins[i];\n if (plugin.stop) {\n try {\n await plugin.stop.call(plugin, ctx as EngineContext);\n } catch (err) {\n logger.error(\n `Plugin \"${plugin.name}\" failed during stop: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n await adapter.close();\n logger.info('fhir-engine stopped.');\n },\n };\n\n logger.info('fhir-engine ready.');\n return engine;\n}\n", "import { BetterSqlite3Adapter, SQLiteAdapter } from 'fhir-persistence';\nimport type { StorageAdapter } from 'fhir-persistence';\nimport type { DatabaseConfig, Logger } from './types.js';\n\n/**\n * Create a StorageAdapter from the database configuration.\n *\n * Supported adapters:\n * - `sqlite` \u2192 BetterSqlite3Adapter (native, Node.js / Electron)\n * - `sqlite-wasm` \u2192 SQLiteAdapter (sql.js WASM, browser / cross-platform)\n * - `postgres` \u2192 not yet available (PostgresAdapter not exported from fhir-persistence)\n */\nexport function createAdapter(config: DatabaseConfig, logger: Logger): StorageAdapter {\n switch (config.type) {\n case 'sqlite': {\n logger.info(`Creating BetterSqlite3Adapter (path: ${config.path})`);\n return new BetterSqlite3Adapter({\n path: config.path,\n wal: config.wal ?? true,\n busyTimeout: config.busyTimeout ?? 5000,\n });\n }\n\n case 'sqlite-wasm': {\n logger.info(`Creating SQLiteAdapter (WASM, path: ${config.path})`);\n return new SQLiteAdapter(config.path);\n }\n\n case 'postgres': {\n throw new Error(\n 'fhir-engine: PostgreSQL adapter is not yet available. ' +\n 'PostgresAdapter is not exported from fhir-persistence v0.1.0. ' +\n 'Use database.type = \"sqlite\" for now.',\n );\n }\n\n default: {\n const _exhaustive: never = config;\n throw new Error(`fhir-engine: unknown database type: ${(_exhaustive as DatabaseConfig).type}`);\n }\n }\n}\n", "import { existsSync, readFileSync } from 'node:fs';\nimport { resolve, extname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { FhirEngineConfig, DatabaseConfig } from './types.js';\n\n// ---------------------------------------------------------------------------\n// defineConfig \u2014 typed identity helper for fhir.config.ts / .js\n// ---------------------------------------------------------------------------\n\n/**\n * Type-safe config helper for `fhir.config.ts` / `fhir.config.js`.\n *\n * @example\n * ```ts\n * // fhir.config.ts\n * import { defineConfig } from 'fhir-engine';\n *\n * export default defineConfig({\n * database: { type: 'sqlite', path: './fhir.db' },\n * packages: { path: './fhir-packages' },\n * });\n * ```\n */\nexport function defineConfig(config: FhirEngineConfig): FhirEngineConfig {\n return config;\n}\n\n// ---------------------------------------------------------------------------\n// Config file discovery order\n// ---------------------------------------------------------------------------\n\nconst CONFIG_FILENAMES = [\n 'fhir.config.ts',\n 'fhir.config.js',\n 'fhir.config.mjs',\n 'fhir.config.json',\n] as const;\n\n// ---------------------------------------------------------------------------\n// loadFhirConfig \u2014 auto-discover and load config\n// ---------------------------------------------------------------------------\n\n/**\n * Load engine configuration from a file.\n *\n * If `configPath` is provided, loads that exact file.\n * Otherwise, searches the current working directory for config files\n * in this order: `fhir.config.ts` \u2192 `fhir.config.js` \u2192 `fhir.config.mjs` \u2192 `fhir.config.json`.\n *\n * Environment variable overrides are applied on top of the loaded config.\n *\n * @param configPath - Explicit path to a config file (optional).\n * @returns The resolved `FhirEngineConfig`.\n */\nexport async function loadFhirConfig(configPath?: string): Promise<FhirEngineConfig> {\n let resolvedPath: string;\n\n if (configPath) {\n resolvedPath = resolve(configPath);\n if (!existsSync(resolvedPath)) {\n throw new Error(`fhir-engine: config file not found: ${resolvedPath}`);\n }\n } else {\n const cwd = process.cwd();\n const found = CONFIG_FILENAMES\n .map((name) => resolve(cwd, name))\n .find((p) => existsSync(p));\n if (!found) {\n throw new Error(\n `fhir-engine: no config file found in ${cwd}. ` +\n `Expected one of: ${CONFIG_FILENAMES.join(', ')}`,\n );\n }\n resolvedPath = found;\n }\n\n const config = await loadConfigFile(resolvedPath);\n return applyEnvOverrides(config);\n}\n\n// ---------------------------------------------------------------------------\n// File loaders\n// ---------------------------------------------------------------------------\n\nasync function loadConfigFile(filePath: string): Promise<FhirEngineConfig> {\n const ext = extname(filePath);\n\n if (ext === '.json') {\n return loadJsonConfig(filePath);\n }\n\n if (ext === '.ts' || ext === '.js' || ext === '.mjs') {\n return loadModuleConfig(filePath);\n }\n\n throw new Error(`fhir-engine: unsupported config file extension: ${ext}`);\n}\n\nfunction loadJsonConfig(filePath: string): FhirEngineConfig {\n try {\n const raw = readFileSync(filePath, 'utf-8');\n return JSON.parse(raw) as FhirEngineConfig;\n } catch (err) {\n throw new Error(\n `fhir-engine: failed to parse config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n}\n\nasync function loadModuleConfig(filePath: string): Promise<FhirEngineConfig> {\n try {\n const fileUrl = pathToFileURL(filePath).href;\n const mod = await import(fileUrl);\n const config = mod.default ?? mod;\n\n if (!config || typeof config !== 'object') {\n throw new Error('config file must export a FhirEngineConfig object as default export');\n }\n\n return config as FhirEngineConfig;\n } catch (err) {\n if (err instanceof Error && err.message.includes('must export')) {\n throw err;\n }\n throw new Error(\n `fhir-engine: failed to load config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Environment variable overrides\n// ---------------------------------------------------------------------------\n\nconst VALID_DB_TYPES = ['sqlite', 'sqlite-wasm', 'postgres'] as const;\n\n/**\n * Apply environment variable overrides on top of a loaded config.\n *\n * | Env Variable | Overrides |\n * |---------------------|----------------------------------|\n * | FHIR_DATABASE_TYPE | config.database.type |\n * | FHIR_DATABASE_URL | config.database.path / .url |\n * | FHIR_PACKAGES_PATH | config.packages.path |\n * | FHIR_LOG_LEVEL | (stored for logger filtering) |\n */\nexport function applyEnvOverrides(config: FhirEngineConfig): FhirEngineConfig {\n const result = structuredClone(config);\n\n const dbType = process.env.FHIR_DATABASE_TYPE;\n const dbUrl = process.env.FHIR_DATABASE_URL;\n const pkgPath = process.env.FHIR_PACKAGES_PATH;\n\n if (dbType) {\n if (!VALID_DB_TYPES.includes(dbType as typeof VALID_DB_TYPES[number])) {\n throw new Error(\n `fhir-engine: FHIR_DATABASE_TYPE must be one of: ${VALID_DB_TYPES.join(', ')}. Got: \"${dbType}\"`,\n );\n }\n // Rebuild database config with overridden type\n const validType = dbType as DatabaseConfig['type'];\n if (validType === 'postgres') {\n result.database = { type: 'postgres', url: (result.database as { url?: string }).url ?? '' };\n } else if (validType === 'sqlite-wasm') {\n result.database = { type: 'sqlite-wasm', path: (result.database as { path?: string }).path ?? '' };\n } else {\n result.database = { type: 'sqlite', path: (result.database as { path?: string }).path ?? '' };\n }\n }\n\n if (dbUrl) {\n const type = result.database?.type;\n if (type === 'postgres') {\n result.database = { ...result.database, type: 'postgres', url: dbUrl };\n } else if (type === 'sqlite-wasm') {\n result.database = { ...result.database, type: 'sqlite-wasm', path: dbUrl };\n } else {\n result.database = { ...result.database, type: 'sqlite', path: dbUrl };\n }\n }\n\n if (pkgPath) {\n result.packages = { ...result.packages, path: pkgPath };\n }\n\n return result;\n}\n", "import type { Logger } from './types.js';\n\n/**\n * Default console-based logger.\n */\nexport function createConsoleLogger(): Logger {\n return {\n debug: (message: string, ...args: unknown[]) => console.debug(`[fhir-engine] ${message}`, ...args),\n info: (message: string, ...args: unknown[]) => console.info(`[fhir-engine] ${message}`, ...args),\n warn: (message: string, ...args: unknown[]) => console.warn(`[fhir-engine] ${message}`, ...args),\n error: (message: string, ...args: unknown[]) => console.error(`[fhir-engine] ${message}`, ...args),\n };\n}\n"],
5
- "mappings": ";AAAA,SAAS,8BAA8B;AACvC,SAAS,eAAe,qBAAqB,wBAAwB,yBAAyB;AAC9F,SAAS,sBAAsB,qBAAqB,kBAAkB;;;ACFtE,SAAS,sBAAsB,qBAAqB;AAY7C,SAAS,cAAc,QAAwB,QAAgC;AACpF,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK,UAAU;AACb,aAAO,KAAK,wCAAwC,OAAO,IAAI,GAAG;AAClE,aAAO,IAAI,qBAAqB;AAAA,QAC9B,MAAM,OAAO;AAAA,QACb,KAAK,OAAO,OAAO;AAAA,QACnB,aAAa,OAAO,eAAe;AAAA,MACrC,CAAC;AAAA,IACH;AAAA,IAEA,KAAK,eAAe;AAClB,aAAO,KAAK,uCAAuC,OAAO,IAAI,GAAG;AACjE,aAAO,IAAI,cAAc,OAAO,IAAI;AAAA,IACtC;AAAA,IAEA,KAAK,YAAY;AACf,YAAM,IAAI;AAAA,QACR;AAAA,MAGF;AAAA,IACF;AAAA,IAEA,SAAS;AACP,YAAM,cAAqB;AAC3B,YAAM,IAAI,MAAM,uCAAwC,YAA+B,IAAI,EAAE;AAAA,IAC/F;AAAA,EACF;AACF;;;ACzCA,SAAS,YAAY,oBAAoB;AACzC,SAAS,SAAS,eAAe;AACjC,SAAS,qBAAqB;AAqBvB,SAAS,aAAa,QAA4C;AACvE,SAAO;AACT;AAMA,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAkBA,eAAsB,eAAe,YAAgD;AACnF,MAAI;AAEJ,MAAI,YAAY;AACd,mBAAe,QAAQ,UAAU;AACjC,QAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,YAAM,IAAI,MAAM,uCAAuC,YAAY,EAAE;AAAA,IACvE;AAAA,EACF,OAAO;AACL,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,QAAQ,iBACX,IAAI,CAAC,SAAS,QAAQ,KAAK,IAAI,CAAC,EAChC,KAAK,CAAC,MAAM,WAAW,CAAC,CAAC;AAC5B,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR,wCAAwC,GAAG,sBACvB,iBAAiB,KAAK,IAAI,CAAC;AAAA,MACjD;AAAA,IACF;AACA,mBAAe;AAAA,EACjB;AAEA,QAAM,SAAS,MAAM,eAAe,YAAY;AAChD,SAAO,kBAAkB,MAAM;AACjC;AAMA,eAAe,eAAe,UAA6C;AACzE,QAAM,MAAM,QAAQ,QAAQ;AAE5B,MAAI,QAAQ,SAAS;AACnB,WAAO,eAAe,QAAQ;AAAA,EAChC;AAEA,MAAI,QAAQ,SAAS,QAAQ,SAAS,QAAQ,QAAQ;AACpD,WAAO,iBAAiB,QAAQ;AAAA,EAClC;AAEA,QAAM,IAAI,MAAM,mDAAmD,GAAG,EAAE;AAC1E;AAEA,SAAS,eAAe,UAAoC;AAC1D,MAAI;AACF,UAAM,MAAM,aAAa,UAAU,OAAO;AAC1C,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,4CAA4C,QAAQ,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACzG,EAAE,OAAO,IAAI;AAAA,IACf;AAAA,EACF;AACF;AAEA,eAAe,iBAAiB,UAA6C;AAC3E,MAAI;AACF,UAAM,UAAU,cAAc,QAAQ,EAAE;AACxC,UAAM,MAAM,MAAM,OAAO;AACzB,UAAM,SAAS,IAAI,WAAW;AAE9B,QAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,YAAM,IAAI,MAAM,qEAAqE;AAAA,IACvF;AAEA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,eAAe,SAAS,IAAI,QAAQ,SAAS,aAAa,GAAG;AAC/D,YAAM;AAAA,IACR;AACA,UAAM,IAAI;AAAA,MACR,2CAA2C,QAAQ,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACxG,EAAE,OAAO,IAAI;AAAA,IACf;AAAA,EACF;AACF;AAMA,IAAM,iBAAiB,CAAC,UAAU,eAAe,UAAU;AAYpD,SAAS,kBAAkB,QAA4C;AAC5E,QAAM,SAAS,gBAAgB,MAAM;AAErC,QAAM,SAAS,QAAQ,IAAI;AAC3B,QAAM,QAAQ,QAAQ,IAAI;AAC1B,QAAM,UAAU,QAAQ,IAAI;AAE5B,MAAI,QAAQ;AACV,QAAI,CAAC,eAAe,SAAS,MAAuC,GAAG;AACrE,YAAM,IAAI;AAAA,QACR,mDAAmD,eAAe,KAAK,IAAI,CAAC,WAAW,MAAM;AAAA,MAC/F;AAAA,IACF;AAEA,UAAM,YAAY;AAClB,QAAI,cAAc,YAAY;AAC5B,aAAO,WAAW,EAAE,MAAM,YAAY,KAAM,OAAO,SAA8B,OAAO,GAAG;AAAA,IAC7F,WAAW,cAAc,eAAe;AACtC,aAAO,WAAW,EAAE,MAAM,eAAe,MAAO,OAAO,SAA+B,QAAQ,GAAG;AAAA,IACnG,OAAO;AACL,aAAO,WAAW,EAAE,MAAM,UAAU,MAAO,OAAO,SAA+B,QAAQ,GAAG;AAAA,IAC9F;AAAA,EACF;AAEA,MAAI,OAAO;AACT,UAAM,OAAO,OAAO,UAAU;AAC9B,QAAI,SAAS,YAAY;AACvB,aAAO,WAAW,EAAE,GAAG,OAAO,UAAU,MAAM,YAAY,KAAK,MAAM;AAAA,IACvE,WAAW,SAAS,eAAe;AACjC,aAAO,WAAW,EAAE,GAAG,OAAO,UAAU,MAAM,eAAe,MAAM,MAAM;AAAA,IAC3E,OAAO;AACL,aAAO,WAAW,EAAE,GAAG,OAAO,UAAU,MAAM,UAAU,MAAM,MAAM;AAAA,IACtE;AAAA,EACF;AAEA,MAAI,SAAS;AACX,WAAO,WAAW,EAAE,GAAG,OAAO,UAAU,MAAM,QAAQ;AAAA,EACxD;AAEA,SAAO;AACT;;;ACvLO,SAAS,sBAA8B;AAC5C,SAAO;AAAA,IACL,OAAO,CAAC,YAAoB,SAAoB,QAAQ,MAAM,iBAAiB,OAAO,IAAI,GAAG,IAAI;AAAA,IACjG,MAAM,CAAC,YAAoB,SAAoB,QAAQ,KAAK,iBAAiB,OAAO,IAAI,GAAG,IAAI;AAAA,IAC/F,MAAM,CAAC,YAAoB,SAAoB,QAAQ,KAAK,iBAAiB,OAAO,IAAI,GAAG,IAAI;AAAA,IAC/F,OAAO,CAAC,YAAoB,SAAoB,QAAQ,MAAM,iBAAiB,OAAO,IAAI,GAAG,IAAI;AAAA,EACnG;AACF;;;AHAA,SAAS,eAAe,MAAmE;AACzF,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAKA,SAAS,eAAe,QAAgC;AACtD,MAAI,CAAC,OAAO,UAAU;AACpB,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,MAAI,CAAC,OAAO,SAAS,MAAM;AACzB,UAAM,IAAI,MAAM,iFAAiF;AAAA,EACnG;AACA,MAAI,CAAC,OAAO,UAAU;AACpB,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,MAAI,CAAC,OAAO,SAAS,MAAM;AACzB,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AACF;AAMA,eAAe,cACb,SACA,MACA,KACe;AACf,aAAW,UAAU,SAAS;AAC5B,UAAM,KAAK,OAAO,IAAI;AACtB,QAAI,IAAI;AACN,UAAI;AACF,cAAM,GAAG,KAAK,QAAQ,GAAG;AAAA,MAC3B,SAAS,KAAK;AACZ,cAAM,IAAI;AAAA,UACR,wBAAwB,OAAO,IAAI,mBAAmB,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,UAC/G,EAAE,OAAO,IAAI;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAwBA,eAAsB,iBAAiB,QAAgD;AAErF,MAAI,CAAC,QAAQ;AACX,aAAS,MAAM,eAAe;AAAA,EAChC;AACA,iBAAe,MAAM;AAErB,QAAM,SAAS,OAAO,UAAU,oBAAoB;AACpD,QAAM,UAAU,OAAO,WAAW,CAAC;AACnC,SAAO,KAAK,6BAA6B;AAGzC,SAAO,KAAK,+BAA+B,OAAO,SAAS,IAAI,EAAE;AACjE,QAAM,EAAE,UAAU,OAAO,IAAI,uBAAuB,OAAO,SAAS,IAAI;AACxE,SAAO;AAAA,IACL,UAAU,OAAO,SAAS,MAAM,gBAAgB,OAAO,SAAS,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,EACjH;AAGA,SAAO,KAAK,mCAAmC;AAC/C,QAAM,UAAU,MAAM,cAAc,EAAE,aAAa,UAAU,aAAa,MAAM,CAAC;AAGjF,QAAM,mBAAmB,IAAI,qBAAqB,QAAQ;AAC1D,QAAM,kBAAkB,IAAI,oBAAoB;AAAA,IAC9C;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAGD,QAAM,UAAU,cAAc,OAAO,UAAU,MAAM;AAGrD,QAAM,MAAkE;AAAA,IACtE;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb;AAAA,EACF;AAGA,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,KAAK,oBAAoB,QAAQ,MAAM,eAAe,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AACpG,UAAM,cAAc,SAAS,QAAQ,GAAG;AAAA,EAC1C;AAGA,QAAM,UAAU,eAAe,OAAO,SAAS,IAAI;AACnD,QAAM,SAAS,IAAI,WAAW,SAAS;AAAA,IACrC;AAAA,IACA;AAAA,IACA,aAAa,OAAO,eAAe;AAAA,IACnC,gBAAgB,OAAO,kBAAkB;AAAA,EAC3C,CAAC;AAED,SAAO,KAAK,yDAAyD;AACrE,QAAM,EAAE,aAAa,YAAY,YAAY,UAAU,cAAc,IACnE,MAAM,OAAO,WAAW,gBAAgB;AAE1C,SAAO,KAAK,uCAAkC,SAAS,MAAM,KAAK,cAAc,MAAM,mBAAmB;AAGzG,MAAI,cAAc;AAGlB,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,KAAK,qBAAqB,QAAQ,MAAM,eAAe;AAC9D,UAAM,cAAc,SAAS,SAAS,GAAG;AAAA,EAC3C;AAGA,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,KAAK,qBAAqB,QAAQ,MAAM,eAAe;AAC9D,UAAM,cAAc,SAAS,SAAS,GAAG;AAAA,EAC3C;AAGA,MAAI,UAAU;AAEd,QAAM,SAAqB;AAAA,IACzB,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IAET,MAAM,OAAO;AACX,UAAI,QAAS;AACb,gBAAU;AACV,aAAO,KAAK,yBAAyB;AAGrC,eAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC5C,cAAM,SAAS,QAAQ,CAAC;AACxB,YAAI,OAAO,MAAM;AACf,cAAI;AACF,kBAAM,OAAO,KAAK,KAAK,QAAQ,GAAoB;AAAA,UACrD,SAAS,KAAK;AACZ,mBAAO;AAAA,cACL,WAAW,OAAO,IAAI,yBAAyB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,YACjG;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,YAAM,QAAQ,MAAM;AACpB,aAAO,KAAK,sBAAsB;AAAA,IACpC;AAAA,EACF;AAEA,SAAO,KAAK,oBAAoB;AAChC,SAAO;AACT;",
4
+ "sourcesContent": ["import { loadDefinitionPackages } from 'fhir-definition';\nimport { createRuntime, extractSearchValues, extractAllSearchValues, extractReferences } from 'fhir-runtime';\nimport { FhirDefinitionBridge, FhirRuntimeProvider, FhirSystem } from 'fhir-persistence';\n\nimport { createAdapter } from './adapter-factory.js';\nimport { loadFhirConfig } from './config.js';\nimport { createConsoleLogger } from './logger.js';\nimport type { EngineContext, FhirEngine, FhirEngineConfig, FhirEnginePlugin, FhirEngineStatus } from './types.js';\n\n/**\n * Resolve the SQL dialect from the database config type.\n */\nfunction resolveDialect(type: FhirEngineConfig['database']['type']): 'sqlite' | 'postgres' {\n switch (type) {\n case 'sqlite':\n case 'sqlite-wasm':\n return 'sqlite';\n case 'postgres':\n return 'postgres';\n }\n}\n\n/**\n * Validate the engine configuration, throwing on missing required fields.\n */\nfunction validateConfig(config: FhirEngineConfig): void {\n if (!config.database) {\n throw new Error('fhir-engine: config.database is required');\n }\n if (!config.database.type) {\n throw new Error('fhir-engine: config.database.type is required (sqlite | sqlite-wasm | postgres)');\n }\n if (!config.packages) {\n throw new Error('fhir-engine: config.packages is required');\n }\n if (!config.packages.path) {\n throw new Error('fhir-engine: config.packages.path is required');\n }\n}\n\n/**\n * Run a lifecycle hook on all plugins in order.\n * Wraps errors with the plugin name for clear diagnostics.\n */\nasync function runPluginHook(\n plugins: FhirEnginePlugin[],\n hook: 'init' | 'start' | 'ready',\n ctx: EngineContext,\n): Promise<void> {\n for (const plugin of plugins) {\n const fn = plugin[hook];\n if (fn) {\n try {\n await fn.call(plugin, ctx);\n } catch (err) {\n throw new Error(\n `fhir-engine: plugin \"${plugin.name}\" failed during ${hook}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n }\n }\n}\n\n/**\n * Create and bootstrap a fully initialized FHIR engine.\n *\n * This is the single entry point for all FHIR applications.\n * It assembles fhir-definition, fhir-runtime, and fhir-persistence\n * into a running system from a single configuration object.\n *\n * @example\n * ```ts\n * const engine = await createFhirEngine({\n * database: { type: 'sqlite', path: ':memory:' },\n * packages: { path: './fhir-packages' },\n * });\n *\n * const patient = await engine.persistence.createResource('Patient', {\n * resourceType: 'Patient',\n * name: [{ family: 'Smith', given: ['John'] }],\n * });\n *\n * await engine.stop();\n * ```\n */\nexport async function createFhirEngine(config?: FhirEngineConfig): Promise<FhirEngine> {\n // \u2500\u2500 0. Resolve config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (!config) {\n config = await loadFhirConfig();\n }\n validateConfig(config);\n\n const logger = config.logger ?? createConsoleLogger();\n const plugins = config.plugins ?? [];\n logger.info('Initializing fhir-engine...');\n\n // \u2500\u2500 1. Load FHIR definitions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n logger.info(`Loading FHIR packages from: ${config.packages.path}`);\n const { registry, result } = loadDefinitionPackages(config.packages.path);\n logger.info(\n `Loaded ${result.packages.length} package(s): ${result.packages.map((p) => `${p.name}@${p.version}`).join(', ')}`,\n );\n\n // \u2500\u2500 2. Create fhir-runtime \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n logger.info('Creating fhir-runtime instance...');\n const runtime = await createRuntime({ definitions: registry, preloadCore: false });\n\n // \u2500\u2500 3. Build provider bridges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const definitionBridge = new FhirDefinitionBridge(registry);\n const runtimeProvider = new FhirRuntimeProvider({\n extractSearchValues,\n extractAllSearchValues,\n extractReferences,\n });\n\n // \u2500\u2500 4. Create storage adapter \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const adapter = createAdapter(config.database, logger);\n\n // \u2500\u2500 5. Build EngineContext \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const ctx: { -readonly [K in keyof EngineContext]: EngineContext[K] } = {\n config,\n definitions: registry,\n runtime,\n adapter,\n persistence: undefined,\n logger,\n };\n\n // \u2500\u2500 6. INIT phase \u2014 plugins run before persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running init for ${plugins.length} plugin(s): ${plugins.map((p) => p.name).join(', ')}`);\n await runPluginHook(plugins, 'init', ctx);\n }\n\n // \u2500\u2500 7. Initialize FhirSystem \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const dialect = resolveDialect(config.database.type);\n const system = new FhirSystem(adapter, {\n dialect,\n runtimeProvider,\n packageName: config.packageName ?? 'fhir-engine.default',\n packageVersion: config.packageVersion ?? '1.0.0',\n });\n\n logger.info('Initializing persistence system (schema + migration)...');\n const { persistence, sdRegistry, spRegistry, igResult, resourceTypes } =\n await system.initialize(definitionBridge);\n\n logger.info(`Persistence ready \u2014 IG action: ${igResult.action}, ${resourceTypes.length} resource type(s)`);\n\n // ctx.persistence now available\n ctx.persistence = persistence;\n\n // \u2500\u2500 8. START phase \u2014 plugins can access persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running start for ${plugins.length} plugin(s)...`);\n await runPluginHook(plugins, 'start', ctx);\n }\n\n // \u2500\u2500 9. READY phase \u2014 system fully operational \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (plugins.length > 0) {\n logger.info(`Running ready for ${plugins.length} plugin(s)...`);\n await runPluginHook(plugins, 'ready', ctx);\n }\n\n // \u2500\u2500 10. Return FhirEngine \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let stopped = false;\n const startedAt = new Date();\n const loadedPackages = result.packages.map((p) => `${p.name}@${p.version}`);\n const fhirVersions = [...new Set(\n result.packages\n .map((p) => {\n if (p.name.includes('.r4.')) return '4.0';\n if (p.name.includes('.r4b.')) return '4.3';\n if (p.name.includes('.r5.')) return '5.0';\n return undefined;\n })\n .filter(Boolean),\n )] as string[];\n\n const engine: FhirEngine = {\n definitions: registry,\n runtime,\n adapter,\n persistence,\n sdRegistry,\n spRegistry,\n igResult,\n resourceTypes,\n logger,\n context: ctx as EngineContext,\n\n status(): FhirEngineStatus {\n return {\n fhirVersions,\n loadedPackages,\n resourceTypes,\n databaseType: config.database.type,\n igAction: igResult.action,\n startedAt,\n plugins: plugins.map((p) => p.name),\n };\n },\n\n async stop() {\n if (stopped) return;\n stopped = true;\n logger.info('Stopping fhir-engine...');\n\n // Stop plugins in reverse registration order\n for (let i = plugins.length - 1; i >= 0; i--) {\n const plugin = plugins[i];\n if (plugin.stop) {\n try {\n await plugin.stop.call(plugin, ctx as EngineContext);\n } catch (err) {\n logger.error(\n `Plugin \"${plugin.name}\" failed during stop: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n await adapter.close();\n logger.info('fhir-engine stopped.');\n },\n };\n\n logger.info('fhir-engine ready.');\n return engine;\n}\n", "import { BetterSqlite3Adapter, SQLiteAdapter } from 'fhir-persistence';\nimport type { StorageAdapter } from 'fhir-persistence';\nimport type { DatabaseConfig, Logger } from './types.js';\n\n/**\n * Create a StorageAdapter from the database configuration.\n *\n * Supported adapters:\n * - `sqlite` \u2192 BetterSqlite3Adapter (native, Node.js / Electron)\n * - `sqlite-wasm` \u2192 SQLiteAdapter (sql.js WASM, browser / cross-platform)\n * - `postgres` \u2192 not yet available (PostgresAdapter not exported from fhir-persistence)\n */\nexport function createAdapter(config: DatabaseConfig, logger: Logger): StorageAdapter {\n switch (config.type) {\n case 'sqlite': {\n logger.info(`Creating BetterSqlite3Adapter (path: ${config.path})`);\n return new BetterSqlite3Adapter({\n path: config.path,\n wal: config.wal ?? true,\n busyTimeout: config.busyTimeout ?? 5000,\n });\n }\n\n case 'sqlite-wasm': {\n logger.info(`Creating SQLiteAdapter (WASM, path: ${config.path})`);\n return new SQLiteAdapter(config.path);\n }\n\n case 'postgres': {\n throw new Error(\n 'fhir-engine: PostgreSQL adapter is not yet available. ' +\n 'PostgresAdapter is not exported from fhir-persistence v0.1.0. ' +\n 'Use database.type = \"sqlite\" for now.',\n );\n }\n\n default: {\n const _exhaustive: never = config;\n throw new Error(`fhir-engine: unknown database type: ${(_exhaustive as DatabaseConfig).type}`);\n }\n }\n}\n", "import { existsSync, readFileSync } from 'node:fs';\nimport { resolve, extname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { FhirEngineConfig, DatabaseConfig } from './types.js';\n\n// ---------------------------------------------------------------------------\n// defineConfig \u2014 typed identity helper for fhir.config.ts / .js\n// ---------------------------------------------------------------------------\n\n/**\n * Type-safe config helper for `fhir.config.ts` / `fhir.config.js`.\n *\n * @example\n * ```ts\n * // fhir.config.ts\n * import { defineConfig } from 'fhir-engine';\n *\n * export default defineConfig({\n * database: { type: 'sqlite', path: './fhir.db' },\n * packages: { path: './fhir-packages' },\n * });\n * ```\n */\nexport function defineConfig(config: FhirEngineConfig): FhirEngineConfig {\n return config;\n}\n\n// ---------------------------------------------------------------------------\n// Config file discovery order\n// ---------------------------------------------------------------------------\n\nconst CONFIG_FILENAMES = [\n 'fhir.config.ts',\n 'fhir.config.js',\n 'fhir.config.mjs',\n 'fhir.config.json',\n] as const;\n\n// ---------------------------------------------------------------------------\n// loadFhirConfig \u2014 auto-discover and load config\n// ---------------------------------------------------------------------------\n\n/**\n * Load engine configuration from a file.\n *\n * If `configPath` is provided, loads that exact file.\n * Otherwise, searches the current working directory for config files\n * in this order: `fhir.config.ts` \u2192 `fhir.config.js` \u2192 `fhir.config.mjs` \u2192 `fhir.config.json`.\n *\n * Environment variable overrides are applied on top of the loaded config.\n *\n * @param configPath - Explicit path to a config file (optional).\n * @returns The resolved `FhirEngineConfig`.\n */\nexport async function loadFhirConfig(configPath?: string): Promise<FhirEngineConfig> {\n let resolvedPath: string;\n\n if (configPath) {\n resolvedPath = resolve(configPath);\n if (!existsSync(resolvedPath)) {\n throw new Error(`fhir-engine: config file not found: ${resolvedPath}`);\n }\n } else {\n const cwd = process.cwd();\n const found = CONFIG_FILENAMES\n .map((name) => resolve(cwd, name))\n .find((p) => existsSync(p));\n if (!found) {\n throw new Error(\n `fhir-engine: no config file found in ${cwd}. ` +\n `Expected one of: ${CONFIG_FILENAMES.join(', ')}`,\n );\n }\n resolvedPath = found;\n }\n\n const config = await loadConfigFile(resolvedPath);\n return applyEnvOverrides(config);\n}\n\n// ---------------------------------------------------------------------------\n// File loaders\n// ---------------------------------------------------------------------------\n\nasync function loadConfigFile(filePath: string): Promise<FhirEngineConfig> {\n const ext = extname(filePath);\n\n if (ext === '.json') {\n return loadJsonConfig(filePath);\n }\n\n if (ext === '.ts' || ext === '.js' || ext === '.mjs') {\n return loadModuleConfig(filePath);\n }\n\n throw new Error(`fhir-engine: unsupported config file extension: ${ext}`);\n}\n\nfunction loadJsonConfig(filePath: string): FhirEngineConfig {\n try {\n const raw = readFileSync(filePath, 'utf-8');\n return JSON.parse(raw) as FhirEngineConfig;\n } catch (err) {\n throw new Error(\n `fhir-engine: failed to parse config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n}\n\nasync function loadModuleConfig(filePath: string): Promise<FhirEngineConfig> {\n try {\n const fileUrl = pathToFileURL(filePath).href;\n const mod = await import(fileUrl);\n const config = mod.default ?? mod;\n\n if (!config || typeof config !== 'object') {\n throw new Error('config file must export a FhirEngineConfig object as default export');\n }\n\n return config as FhirEngineConfig;\n } catch (err) {\n if (err instanceof Error && err.message.includes('must export')) {\n throw err;\n }\n throw new Error(\n `fhir-engine: failed to load config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Environment variable overrides\n// ---------------------------------------------------------------------------\n\nconst VALID_DB_TYPES = ['sqlite', 'sqlite-wasm', 'postgres'] as const;\n\n/**\n * Apply environment variable overrides on top of a loaded config.\n *\n * | Env Variable | Overrides |\n * |---------------------|----------------------------------|\n * | FHIR_DATABASE_TYPE | config.database.type |\n * | FHIR_DATABASE_URL | config.database.path / .url |\n * | FHIR_PACKAGES_PATH | config.packages.path |\n * | FHIR_LOG_LEVEL | (stored for logger filtering) |\n */\nexport function applyEnvOverrides(config: FhirEngineConfig): FhirEngineConfig {\n const result = structuredClone(config);\n\n const dbType = process.env.FHIR_DATABASE_TYPE;\n const dbUrl = process.env.FHIR_DATABASE_URL;\n const pkgPath = process.env.FHIR_PACKAGES_PATH;\n\n if (dbType) {\n if (!VALID_DB_TYPES.includes(dbType as typeof VALID_DB_TYPES[number])) {\n throw new Error(\n `fhir-engine: FHIR_DATABASE_TYPE must be one of: ${VALID_DB_TYPES.join(', ')}. Got: \"${dbType}\"`,\n );\n }\n // Rebuild database config with overridden type\n const validType = dbType as DatabaseConfig['type'];\n if (validType === 'postgres') {\n result.database = { type: 'postgres', url: (result.database as { url?: string }).url ?? '' };\n } else if (validType === 'sqlite-wasm') {\n result.database = { type: 'sqlite-wasm', path: (result.database as { path?: string }).path ?? '' };\n } else {\n result.database = { type: 'sqlite', path: (result.database as { path?: string }).path ?? '' };\n }\n }\n\n if (dbUrl) {\n const type = result.database?.type;\n if (type === 'postgres') {\n result.database = { ...result.database, type: 'postgres', url: dbUrl };\n } else if (type === 'sqlite-wasm') {\n result.database = { ...result.database, type: 'sqlite-wasm', path: dbUrl };\n } else {\n result.database = { ...result.database, type: 'sqlite', path: dbUrl };\n }\n }\n\n if (pkgPath) {\n result.packages = { ...result.packages, path: pkgPath };\n }\n\n return result;\n}\n", "import type { Logger } from './types.js';\n\n/**\n * Default console-based logger.\n */\nexport function createConsoleLogger(): Logger {\n return {\n debug: (message: string, ...args: unknown[]) => console.debug(`[fhir-engine] ${message}`, ...args),\n info: (message: string, ...args: unknown[]) => console.info(`[fhir-engine] ${message}`, ...args),\n warn: (message: string, ...args: unknown[]) => console.warn(`[fhir-engine] ${message}`, ...args),\n error: (message: string, ...args: unknown[]) => console.error(`[fhir-engine] ${message}`, ...args),\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/index.d.ts CHANGED
@@ -104,6 +104,8 @@ export declare interface FhirEngine {
104
104
  readonly logger: Logger;
105
105
  /** Shared context (same object plugins receive). */
106
106
  readonly context: EngineContext;
107
+ /** Return engine health/status information. */
108
+ status(): FhirEngineStatus;
107
109
  /** Gracefully shut down the engine (closes adapter). */
108
110
  stop(): Promise<void>;
109
111
  }
@@ -136,6 +138,23 @@ export declare interface FhirEnginePlugin {
136
138
  stop?(ctx: EngineContext): Promise<void>;
137
139
  }
138
140
 
141
+ export declare interface FhirEngineStatus {
142
+ /** FHIR versions loaded (e.g. ['4.0.1']). */
143
+ fhirVersions: string[];
144
+ /** Loaded package identifiers (e.g. ['hl7.fhir.r4.core@4.0.1']). */
145
+ loadedPackages: string[];
146
+ /** Resource types with database tables. */
147
+ resourceTypes: string[];
148
+ /** Database adapter type in use. */
149
+ databaseType: 'sqlite' | 'sqlite-wasm' | 'postgres';
150
+ /** IG migration action performed at startup. */
151
+ igAction: 'new' | 'upgrade' | 'consistent';
152
+ /** Timestamp when the engine finished bootstrapping. */
153
+ startedAt: Date;
154
+ /** Registered plugin names. */
155
+ plugins: string[];
156
+ }
157
+
139
158
  export { FhirPersistence }
140
159
 
141
160
  export { FhirRuntimeInstance }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fhir-engine",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "FHIR Engine for healthcare data management",
5
5
  "homepage": "https://github.com/medxaidev/fhir-engine#readme",
6
6
  "license": "MIT",