fhir-engine 0.1.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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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;",
6
+ "names": []
7
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "type": "module",
3
+ "main": "index.mjs",
4
+ "types": "index.d.ts"
5
+ }
@@ -0,0 +1,193 @@
1
+ import { DefinitionProvider } from 'fhir-definition';
2
+ import { DefinitionRegistry } from 'fhir-definition';
3
+ import { FhirPersistence } from 'fhir-persistence';
4
+ import { FhirRuntimeInstance } from 'fhir-runtime';
5
+ import type { FhirSystemReady } from 'fhir-persistence';
6
+ import type { SearchParameterRegistry } from 'fhir-persistence';
7
+ import { StorageAdapter } from 'fhir-persistence';
8
+ import type { StructureDefinitionRegistry } from 'fhir-persistence';
9
+
10
+ /**
11
+ * Create a StorageAdapter from the database configuration.
12
+ *
13
+ * Supported adapters:
14
+ * - `sqlite` → BetterSqlite3Adapter (native, Node.js / Electron)
15
+ * - `sqlite-wasm` → SQLiteAdapter (sql.js WASM, browser / cross-platform)
16
+ * - `postgres` → not yet available (PostgresAdapter not exported from fhir-persistence)
17
+ */
18
+ export declare function createAdapter(config: DatabaseConfig, logger: Logger): StorageAdapter;
19
+
20
+ /**
21
+ * Default console-based logger.
22
+ */
23
+ export declare function createConsoleLogger(): Logger;
24
+
25
+ /**
26
+ * Create and bootstrap a fully initialized FHIR engine.
27
+ *
28
+ * This is the single entry point for all FHIR applications.
29
+ * It assembles fhir-definition, fhir-runtime, and fhir-persistence
30
+ * into a running system from a single configuration object.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const engine = await createFhirEngine({
35
+ * database: { type: 'sqlite', path: ':memory:' },
36
+ * packages: { path: './fhir-packages' },
37
+ * });
38
+ *
39
+ * const patient = await engine.persistence.createResource('Patient', {
40
+ * resourceType: 'Patient',
41
+ * name: [{ family: 'Smith', given: ['John'] }],
42
+ * });
43
+ *
44
+ * await engine.stop();
45
+ * ```
46
+ */
47
+ export declare function createFhirEngine(config?: FhirEngineConfig): Promise<FhirEngine>;
48
+
49
+ export declare type DatabaseConfig = SqliteDatabaseConfig | SqliteWasmDatabaseConfig | PostgresDatabaseConfig;
50
+
51
+ /**
52
+ * Type-safe config helper for `fhir.config.ts` / `fhir.config.js`.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * // fhir.config.ts
57
+ * import { defineConfig } from 'fhir-engine';
58
+ *
59
+ * export default defineConfig({
60
+ * database: { type: 'sqlite', path: './fhir.db' },
61
+ * packages: { path: './fhir-packages' },
62
+ * });
63
+ * ```
64
+ */
65
+ export declare function defineConfig(config: FhirEngineConfig): FhirEngineConfig;
66
+
67
+ export { DefinitionProvider }
68
+
69
+ export { DefinitionRegistry }
70
+
71
+ export declare interface EngineContext {
72
+ /** The resolved engine configuration. */
73
+ readonly config: FhirEngineConfig;
74
+ /** DefinitionRegistry from fhir-definition (always available). */
75
+ readonly definitions: DefinitionRegistry;
76
+ /** FhirRuntimeInstance from fhir-runtime (always available). */
77
+ readonly runtime: FhirRuntimeInstance;
78
+ /** StorageAdapter from fhir-persistence (always available). */
79
+ readonly adapter: StorageAdapter;
80
+ /** FhirPersistence facade — undefined during init(), available from start() onward. */
81
+ readonly persistence: FhirPersistence | undefined;
82
+ /** Logger instance. */
83
+ readonly logger: Logger;
84
+ }
85
+
86
+ export declare interface FhirEngine {
87
+ /** The loaded DefinitionRegistry from fhir-definition. */
88
+ readonly definitions: DefinitionRegistry;
89
+ /** The FhirRuntimeInstance from fhir-runtime. */
90
+ readonly runtime: FhirRuntimeInstance;
91
+ /** The StorageAdapter from fhir-persistence. */
92
+ readonly adapter: StorageAdapter;
93
+ /** The FhirPersistence facade (CRUD + Search + Indexing). */
94
+ readonly persistence: FhirPersistence;
95
+ /** StructureDefinition registry (populated from IG). */
96
+ readonly sdRegistry: StructureDefinitionRegistry;
97
+ /** SearchParameter registry (populated from IG). */
98
+ readonly spRegistry: SearchParameterRegistry;
99
+ /** IG initialization result. */
100
+ readonly igResult: FhirSystemReady['igResult'];
101
+ /** Resource types with database tables. */
102
+ readonly resourceTypes: string[];
103
+ /** Logger in use. */
104
+ readonly logger: Logger;
105
+ /** Shared context (same object plugins receive). */
106
+ readonly context: EngineContext;
107
+ /** Gracefully shut down the engine (closes adapter). */
108
+ stop(): Promise<void>;
109
+ }
110
+
111
+ export declare interface FhirEngineConfig {
112
+ /** Database configuration. */
113
+ database: DatabaseConfig;
114
+ /** FHIR package loading configuration. */
115
+ packages: PackagesConfig;
116
+ /** IG migration label (default: package.json name or 'fhir-engine.default'). */
117
+ packageName?: string;
118
+ /** IG migration version (default: package.json version or '1.0.0'). */
119
+ packageVersion?: string;
120
+ /** Logger instance (default: console-based logger). */
121
+ logger?: Logger;
122
+ /** Plugins to register (executed in registration order). */
123
+ plugins?: FhirEnginePlugin[];
124
+ }
125
+
126
+ export declare interface FhirEnginePlugin {
127
+ /** Unique plugin name (used in logs and error messages). */
128
+ name: string;
129
+ /** Called before FhirSystem.initialize(). ctx.persistence is undefined. */
130
+ init?(ctx: EngineContext): Promise<void>;
131
+ /** Called after FhirSystem.initialize(). ctx.persistence is available. */
132
+ start?(ctx: EngineContext): Promise<void>;
133
+ /** Called after all plugins have started. System is fully operational. */
134
+ ready?(ctx: EngineContext): Promise<void>;
135
+ /** Called on shutdown, in reverse registration order. */
136
+ stop?(ctx: EngineContext): Promise<void>;
137
+ }
138
+
139
+ export { FhirPersistence }
140
+
141
+ export { FhirRuntimeInstance }
142
+
143
+ /**
144
+ * Load engine configuration from a file.
145
+ *
146
+ * If `configPath` is provided, loads that exact file.
147
+ * Otherwise, searches the current working directory for config files
148
+ * in this order: `fhir.config.ts` → `fhir.config.js` → `fhir.config.mjs` → `fhir.config.json`.
149
+ *
150
+ * Environment variable overrides are applied on top of the loaded config.
151
+ *
152
+ * @param configPath - Explicit path to a config file (optional).
153
+ * @returns The resolved `FhirEngineConfig`.
154
+ */
155
+ export declare function loadFhirConfig(configPath?: string): Promise<FhirEngineConfig>;
156
+
157
+ export declare interface Logger {
158
+ debug(message: string, ...args: unknown[]): void;
159
+ info(message: string, ...args: unknown[]): void;
160
+ warn(message: string, ...args: unknown[]): void;
161
+ error(message: string, ...args: unknown[]): void;
162
+ }
163
+
164
+ export declare interface PackagesConfig {
165
+ /** Local directory containing FHIR packages (NPM tarballs or extracted). */
166
+ path: string;
167
+ }
168
+
169
+ export declare interface PostgresDatabaseConfig {
170
+ type: 'postgres';
171
+ /** PostgreSQL connection string. */
172
+ url: string;
173
+ }
174
+
175
+ export declare interface SqliteDatabaseConfig {
176
+ type: 'sqlite';
177
+ /** File path or ':memory:' for in-memory database. */
178
+ path: string;
179
+ /** Enable WAL journal mode (default: true). */
180
+ wal?: boolean;
181
+ /** Busy timeout in milliseconds (default: 5000). */
182
+ busyTimeout?: number;
183
+ }
184
+
185
+ export declare interface SqliteWasmDatabaseConfig {
186
+ type: 'sqlite-wasm';
187
+ /** File path or ':memory:'. */
188
+ path: string;
189
+ }
190
+
191
+ export { StorageAdapter }
192
+
193
+ export { }
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "fhir-engine",
3
+ "version": "0.1.0",
4
+ "description": "FHIR Engine for healthcare data management",
5
+ "homepage": "https://github.com/medxaidev/fhir-engine#readme",
6
+ "license": "MIT",
7
+ "author": "Fangjun <fangjun20208@gmail.com>",
8
+ "keywords": [
9
+ "fhir",
10
+ "fhir-r4",
11
+ "fhir-persistence",
12
+ "fhir-server",
13
+ "fhir-search",
14
+ "hl7",
15
+ "healthcare",
16
+ "sqlite",
17
+ "postgresql",
18
+ "embedded-database",
19
+ "schema-migration",
20
+ "search-indexing",
21
+ "typescript",
22
+ "interoperability",
23
+ "clinical",
24
+ "ehr"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/medxaidev/fhir-engine.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/medxaidev/fhir-engine/issues"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public",
35
+ "registry": "https://registry.npmjs.org/"
36
+ },
37
+ "sideEffects": false,
38
+ "type": "module",
39
+ "main": "./dist/cjs/index.cjs",
40
+ "module": "./dist/esm/index.mjs",
41
+ "types": "./dist/index.d.ts",
42
+ "exports": {
43
+ ".": {
44
+ "import": {
45
+ "types": "./dist/esm/index.d.ts",
46
+ "default": "./dist/esm/index.mjs"
47
+ },
48
+ "require": {
49
+ "types": "./dist/cjs/index.d.ts",
50
+ "default": "./dist/cjs/index.cjs"
51
+ }
52
+ },
53
+ "./package.json": "./package.json"
54
+ },
55
+ "files": [
56
+ "dist/esm",
57
+ "dist/cjs",
58
+ "dist/index.d.ts",
59
+ "README.md",
60
+ "LICENSE",
61
+ "CHANGELOG.md"
62
+ ],
63
+ "scripts": {
64
+ "clean": "rimraf dist",
65
+ "build": "npm run clean && tsc && node ./scripts/esbuild.mjs",
66
+ "test": "vitest run",
67
+ "prepublishOnly": "npm run test && npm run build"
68
+ },
69
+ "dependencies": {
70
+ "fhir-definition": "^0.5.0",
71
+ "fhir-runtime": "^0.8.1",
72
+ "fhir-persistence": "^0.1.0"
73
+ },
74
+ "devDependencies": {
75
+ "@microsoft/api-extractor": "^7.57.7",
76
+ "@types/better-sqlite3": "^7.6.13",
77
+ "@types/node": "25.2.0",
78
+ "esbuild": "0.27.2",
79
+ "rimraf": "6.1.2",
80
+ "tsx": "4.21.0",
81
+ "typescript": "5.9.3",
82
+ "vitest": "4.0.18"
83
+ },
84
+ "engines": {
85
+ "node": ">=18.0.0",
86
+ "npm": ">=9.0.0"
87
+ }
88
+ }