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.
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/cjs/index.cjs +319 -0
- package/dist/cjs/index.cjs.map +7 -0
- package/dist/cjs/index.d.ts +193 -0
- package/dist/cjs/package.json +5 -0
- package/dist/esm/index.d.ts +193 -0
- package/dist/esm/index.mjs +288 -0
- package/dist/esm/index.mjs.map +7 -0
- package/dist/esm/package.json +5 -0
- package/dist/index.d.ts +193 -0
- package/package.json +88 -0
|
@@ -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 { }
|
|
@@ -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 { }
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// src/engine.ts
|
|
2
|
+
import { loadDefinitionPackages } from "fhir-definition";
|
|
3
|
+
import { createRuntime, extractSearchValues, extractAllSearchValues, extractReferences } from "fhir-runtime";
|
|
4
|
+
import { FhirDefinitionBridge, FhirRuntimeProvider, FhirSystem } from "fhir-persistence";
|
|
5
|
+
|
|
6
|
+
// src/adapter-factory.ts
|
|
7
|
+
import { BetterSqlite3Adapter, SQLiteAdapter } from "fhir-persistence";
|
|
8
|
+
function createAdapter(config, logger) {
|
|
9
|
+
switch (config.type) {
|
|
10
|
+
case "sqlite": {
|
|
11
|
+
logger.info(`Creating BetterSqlite3Adapter (path: ${config.path})`);
|
|
12
|
+
return new BetterSqlite3Adapter({
|
|
13
|
+
path: config.path,
|
|
14
|
+
wal: config.wal ?? true,
|
|
15
|
+
busyTimeout: config.busyTimeout ?? 5e3
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
case "sqlite-wasm": {
|
|
19
|
+
logger.info(`Creating SQLiteAdapter (WASM, path: ${config.path})`);
|
|
20
|
+
return new SQLiteAdapter(config.path);
|
|
21
|
+
}
|
|
22
|
+
case "postgres": {
|
|
23
|
+
throw new Error(
|
|
24
|
+
'fhir-engine: PostgreSQL adapter is not yet available. PostgresAdapter is not exported from fhir-persistence v0.1.0. Use database.type = "sqlite" for now.'
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
default: {
|
|
28
|
+
const _exhaustive = config;
|
|
29
|
+
throw new Error(`fhir-engine: unknown database type: ${_exhaustive.type}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/config.ts
|
|
35
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
36
|
+
import { resolve, extname } from "node:path";
|
|
37
|
+
import { pathToFileURL } from "node:url";
|
|
38
|
+
function defineConfig(config) {
|
|
39
|
+
return config;
|
|
40
|
+
}
|
|
41
|
+
var CONFIG_FILENAMES = [
|
|
42
|
+
"fhir.config.ts",
|
|
43
|
+
"fhir.config.js",
|
|
44
|
+
"fhir.config.mjs",
|
|
45
|
+
"fhir.config.json"
|
|
46
|
+
];
|
|
47
|
+
async function loadFhirConfig(configPath) {
|
|
48
|
+
let resolvedPath;
|
|
49
|
+
if (configPath) {
|
|
50
|
+
resolvedPath = resolve(configPath);
|
|
51
|
+
if (!existsSync(resolvedPath)) {
|
|
52
|
+
throw new Error(`fhir-engine: config file not found: ${resolvedPath}`);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
const cwd = process.cwd();
|
|
56
|
+
const found = CONFIG_FILENAMES.map((name) => resolve(cwd, name)).find((p) => existsSync(p));
|
|
57
|
+
if (!found) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`fhir-engine: no config file found in ${cwd}. Expected one of: ${CONFIG_FILENAMES.join(", ")}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
resolvedPath = found;
|
|
63
|
+
}
|
|
64
|
+
const config = await loadConfigFile(resolvedPath);
|
|
65
|
+
return applyEnvOverrides(config);
|
|
66
|
+
}
|
|
67
|
+
async function loadConfigFile(filePath) {
|
|
68
|
+
const ext = extname(filePath);
|
|
69
|
+
if (ext === ".json") {
|
|
70
|
+
return loadJsonConfig(filePath);
|
|
71
|
+
}
|
|
72
|
+
if (ext === ".ts" || ext === ".js" || ext === ".mjs") {
|
|
73
|
+
return loadModuleConfig(filePath);
|
|
74
|
+
}
|
|
75
|
+
throw new Error(`fhir-engine: unsupported config file extension: ${ext}`);
|
|
76
|
+
}
|
|
77
|
+
function loadJsonConfig(filePath) {
|
|
78
|
+
try {
|
|
79
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
80
|
+
return JSON.parse(raw);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`fhir-engine: failed to parse config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
84
|
+
{ cause: err }
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function loadModuleConfig(filePath) {
|
|
89
|
+
try {
|
|
90
|
+
const fileUrl = pathToFileURL(filePath).href;
|
|
91
|
+
const mod = await import(fileUrl);
|
|
92
|
+
const config = mod.default ?? mod;
|
|
93
|
+
if (!config || typeof config !== "object") {
|
|
94
|
+
throw new Error("config file must export a FhirEngineConfig object as default export");
|
|
95
|
+
}
|
|
96
|
+
return config;
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (err instanceof Error && err.message.includes("must export")) {
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
throw new Error(
|
|
102
|
+
`fhir-engine: failed to load config file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
103
|
+
{ cause: err }
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
var VALID_DB_TYPES = ["sqlite", "sqlite-wasm", "postgres"];
|
|
108
|
+
function applyEnvOverrides(config) {
|
|
109
|
+
const result = structuredClone(config);
|
|
110
|
+
const dbType = process.env.FHIR_DATABASE_TYPE;
|
|
111
|
+
const dbUrl = process.env.FHIR_DATABASE_URL;
|
|
112
|
+
const pkgPath = process.env.FHIR_PACKAGES_PATH;
|
|
113
|
+
if (dbType) {
|
|
114
|
+
if (!VALID_DB_TYPES.includes(dbType)) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`fhir-engine: FHIR_DATABASE_TYPE must be one of: ${VALID_DB_TYPES.join(", ")}. Got: "${dbType}"`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const validType = dbType;
|
|
120
|
+
if (validType === "postgres") {
|
|
121
|
+
result.database = { type: "postgres", url: result.database.url ?? "" };
|
|
122
|
+
} else if (validType === "sqlite-wasm") {
|
|
123
|
+
result.database = { type: "sqlite-wasm", path: result.database.path ?? "" };
|
|
124
|
+
} else {
|
|
125
|
+
result.database = { type: "sqlite", path: result.database.path ?? "" };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (dbUrl) {
|
|
129
|
+
const type = result.database?.type;
|
|
130
|
+
if (type === "postgres") {
|
|
131
|
+
result.database = { ...result.database, type: "postgres", url: dbUrl };
|
|
132
|
+
} else if (type === "sqlite-wasm") {
|
|
133
|
+
result.database = { ...result.database, type: "sqlite-wasm", path: dbUrl };
|
|
134
|
+
} else {
|
|
135
|
+
result.database = { ...result.database, type: "sqlite", path: dbUrl };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (pkgPath) {
|
|
139
|
+
result.packages = { ...result.packages, path: pkgPath };
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/logger.ts
|
|
145
|
+
function createConsoleLogger() {
|
|
146
|
+
return {
|
|
147
|
+
debug: (message, ...args) => console.debug(`[fhir-engine] ${message}`, ...args),
|
|
148
|
+
info: (message, ...args) => console.info(`[fhir-engine] ${message}`, ...args),
|
|
149
|
+
warn: (message, ...args) => console.warn(`[fhir-engine] ${message}`, ...args),
|
|
150
|
+
error: (message, ...args) => console.error(`[fhir-engine] ${message}`, ...args)
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/engine.ts
|
|
155
|
+
function resolveDialect(type) {
|
|
156
|
+
switch (type) {
|
|
157
|
+
case "sqlite":
|
|
158
|
+
case "sqlite-wasm":
|
|
159
|
+
return "sqlite";
|
|
160
|
+
case "postgres":
|
|
161
|
+
return "postgres";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function validateConfig(config) {
|
|
165
|
+
if (!config.database) {
|
|
166
|
+
throw new Error("fhir-engine: config.database is required");
|
|
167
|
+
}
|
|
168
|
+
if (!config.database.type) {
|
|
169
|
+
throw new Error("fhir-engine: config.database.type is required (sqlite | sqlite-wasm | postgres)");
|
|
170
|
+
}
|
|
171
|
+
if (!config.packages) {
|
|
172
|
+
throw new Error("fhir-engine: config.packages is required");
|
|
173
|
+
}
|
|
174
|
+
if (!config.packages.path) {
|
|
175
|
+
throw new Error("fhir-engine: config.packages.path is required");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function runPluginHook(plugins, hook, ctx) {
|
|
179
|
+
for (const plugin of plugins) {
|
|
180
|
+
const fn = plugin[hook];
|
|
181
|
+
if (fn) {
|
|
182
|
+
try {
|
|
183
|
+
await fn.call(plugin, ctx);
|
|
184
|
+
} catch (err) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`fhir-engine: plugin "${plugin.name}" failed during ${hook}: ${err instanceof Error ? err.message : String(err)}`,
|
|
187
|
+
{ cause: err }
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function createFhirEngine(config) {
|
|
194
|
+
if (!config) {
|
|
195
|
+
config = await loadFhirConfig();
|
|
196
|
+
}
|
|
197
|
+
validateConfig(config);
|
|
198
|
+
const logger = config.logger ?? createConsoleLogger();
|
|
199
|
+
const plugins = config.plugins ?? [];
|
|
200
|
+
logger.info("Initializing fhir-engine...");
|
|
201
|
+
logger.info(`Loading FHIR packages from: ${config.packages.path}`);
|
|
202
|
+
const { registry, result } = loadDefinitionPackages(config.packages.path);
|
|
203
|
+
logger.info(
|
|
204
|
+
`Loaded ${result.packages.length} package(s): ${result.packages.map((p) => `${p.name}@${p.version}`).join(", ")}`
|
|
205
|
+
);
|
|
206
|
+
logger.info("Creating fhir-runtime instance...");
|
|
207
|
+
const runtime = await createRuntime({ definitions: registry, preloadCore: false });
|
|
208
|
+
const definitionBridge = new FhirDefinitionBridge(registry);
|
|
209
|
+
const runtimeProvider = new FhirRuntimeProvider({
|
|
210
|
+
extractSearchValues,
|
|
211
|
+
extractAllSearchValues,
|
|
212
|
+
extractReferences
|
|
213
|
+
});
|
|
214
|
+
const adapter = createAdapter(config.database, logger);
|
|
215
|
+
const ctx = {
|
|
216
|
+
config,
|
|
217
|
+
definitions: registry,
|
|
218
|
+
runtime,
|
|
219
|
+
adapter,
|
|
220
|
+
persistence: void 0,
|
|
221
|
+
logger
|
|
222
|
+
};
|
|
223
|
+
if (plugins.length > 0) {
|
|
224
|
+
logger.info(`Running init for ${plugins.length} plugin(s): ${plugins.map((p) => p.name).join(", ")}`);
|
|
225
|
+
await runPluginHook(plugins, "init", ctx);
|
|
226
|
+
}
|
|
227
|
+
const dialect = resolveDialect(config.database.type);
|
|
228
|
+
const system = new FhirSystem(adapter, {
|
|
229
|
+
dialect,
|
|
230
|
+
runtimeProvider,
|
|
231
|
+
packageName: config.packageName ?? "fhir-engine.default",
|
|
232
|
+
packageVersion: config.packageVersion ?? "1.0.0"
|
|
233
|
+
});
|
|
234
|
+
logger.info("Initializing persistence system (schema + migration)...");
|
|
235
|
+
const { persistence, sdRegistry, spRegistry, igResult, resourceTypes } = await system.initialize(definitionBridge);
|
|
236
|
+
logger.info(`Persistence ready \u2014 IG action: ${igResult.action}, ${resourceTypes.length} resource type(s)`);
|
|
237
|
+
ctx.persistence = persistence;
|
|
238
|
+
if (plugins.length > 0) {
|
|
239
|
+
logger.info(`Running start for ${plugins.length} plugin(s)...`);
|
|
240
|
+
await runPluginHook(plugins, "start", ctx);
|
|
241
|
+
}
|
|
242
|
+
if (plugins.length > 0) {
|
|
243
|
+
logger.info(`Running ready for ${plugins.length} plugin(s)...`);
|
|
244
|
+
await runPluginHook(plugins, "ready", ctx);
|
|
245
|
+
}
|
|
246
|
+
let stopped = false;
|
|
247
|
+
const engine = {
|
|
248
|
+
definitions: registry,
|
|
249
|
+
runtime,
|
|
250
|
+
adapter,
|
|
251
|
+
persistence,
|
|
252
|
+
sdRegistry,
|
|
253
|
+
spRegistry,
|
|
254
|
+
igResult,
|
|
255
|
+
resourceTypes,
|
|
256
|
+
logger,
|
|
257
|
+
context: ctx,
|
|
258
|
+
async stop() {
|
|
259
|
+
if (stopped) return;
|
|
260
|
+
stopped = true;
|
|
261
|
+
logger.info("Stopping fhir-engine...");
|
|
262
|
+
for (let i = plugins.length - 1; i >= 0; i--) {
|
|
263
|
+
const plugin = plugins[i];
|
|
264
|
+
if (plugin.stop) {
|
|
265
|
+
try {
|
|
266
|
+
await plugin.stop.call(plugin, ctx);
|
|
267
|
+
} catch (err) {
|
|
268
|
+
logger.error(
|
|
269
|
+
`Plugin "${plugin.name}" failed during stop: ${err instanceof Error ? err.message : String(err)}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
await adapter.close();
|
|
275
|
+
logger.info("fhir-engine stopped.");
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
logger.info("fhir-engine ready.");
|
|
279
|
+
return engine;
|
|
280
|
+
}
|
|
281
|
+
export {
|
|
282
|
+
createAdapter,
|
|
283
|
+
createConsoleLogger,
|
|
284
|
+
createFhirEngine,
|
|
285
|
+
defineConfig,
|
|
286
|
+
loadFhirConfig
|
|
287
|
+
};
|
|
288
|
+
//# sourceMappingURL=index.mjs.map
|