fhir-persistence 0.3.0 → 0.5.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 +43 -0
- package/README.md +1 -1
- package/dist/cjs/index.cjs +123 -53
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/cjs/index.d.ts +17 -6
- package/dist/esm/index.d.ts +17 -6
- package/dist/esm/index.mjs +123 -53
- package/dist/esm/index.mjs.map +2 -2
- package/dist/index.d.ts +17 -6
- package/dist/lib/migration/migration-generator.d.ts.map +1 -1
- package/dist/lib/migration/reindex-scheduler.d.ts +3 -1
- package/dist/lib/migration/reindex-scheduler.d.ts.map +1 -1
- package/dist/lib/migrations/migration-runner.d.ts +3 -1
- package/dist/lib/migrations/migration-runner.d.ts.map +1 -1
- package/dist/lib/registry/package-registry-repo.d.ts +7 -1
- package/dist/lib/registry/package-registry-repo.d.ts.map +1 -1
- package/dist/lib/repo/indexing-pipeline.d.ts +3 -0
- package/dist/lib/repo/indexing-pipeline.d.ts.map +1 -1
- package/dist/lib/repo/lookup-table-writer.d.ts +3 -1
- package/dist/lib/repo/lookup-table-writer.d.ts.map +1 -1
- package/dist/lib/search/where-builder.d.ts +2 -2
- package/dist/lib/search/where-builder.d.ts.map +1 -1
- package/dist/lib/startup/fhir-system.d.ts.map +1 -1
- package/dist/lib/terminology/valueset-repo.d.ts +3 -1
- package/dist/lib/terminology/valueset-repo.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.d.ts
CHANGED
|
@@ -324,7 +324,7 @@ export declare function buildWhereClauseV2(params: ParsedSearchParam[], registry
|
|
|
324
324
|
* - SQLite: json_each() for array columns
|
|
325
325
|
* - PostgreSQL: native ARRAY operators
|
|
326
326
|
*/
|
|
327
|
-
export declare function buildWhereFragmentV2(impl: SearchParameterImpl, param: ParsedSearchParam, dialect?: SqlDialect): WhereFragment | null;
|
|
327
|
+
export declare function buildWhereFragmentV2(impl: SearchParameterImpl, param: ParsedSearchParam, dialect?: SqlDialect, resourceType?: string): WhereFragment | null;
|
|
328
328
|
|
|
329
329
|
declare interface BundleLink {
|
|
330
330
|
relation: string;
|
|
@@ -1194,6 +1194,8 @@ export declare interface IndexingPipelineOptions {
|
|
|
1194
1194
|
enableReferences?: boolean;
|
|
1195
1195
|
/** Optional RuntimeProvider for FHIRPath-driven extraction (B3). */
|
|
1196
1196
|
runtimeProvider?: RuntimeProvider;
|
|
1197
|
+
/** SQL dialect for lookup table DDL (default: 'sqlite'). */
|
|
1198
|
+
dialect?: DDLDialect;
|
|
1197
1199
|
}
|
|
1198
1200
|
|
|
1199
1201
|
/**
|
|
@@ -1306,7 +1308,8 @@ declare type LookupTableType = 'HumanName' | 'Address' | 'ContactPoint' | 'Ident
|
|
|
1306
1308
|
export declare class LookupTableWriter {
|
|
1307
1309
|
private readonly adapter;
|
|
1308
1310
|
private initialized;
|
|
1309
|
-
|
|
1311
|
+
private readonly ddl;
|
|
1312
|
+
constructor(adapter: StorageAdapter, dialect?: DDLDialect);
|
|
1310
1313
|
/**
|
|
1311
1314
|
* Create all 4 lookup tables + indexes if they don't exist.
|
|
1312
1315
|
*/
|
|
@@ -1390,7 +1393,8 @@ export declare interface MigrationResultV2 {
|
|
|
1390
1393
|
export declare class MigrationRunnerV2 {
|
|
1391
1394
|
private readonly adapter;
|
|
1392
1395
|
private readonly migrations;
|
|
1393
|
-
|
|
1396
|
+
private readonly dialect;
|
|
1397
|
+
constructor(adapter: StorageAdapter, migrations?: MigrationV2[], dialect?: DDLDialect);
|
|
1394
1398
|
/**
|
|
1395
1399
|
* Ensure the tracking table exists.
|
|
1396
1400
|
*/
|
|
@@ -1481,7 +1485,8 @@ export declare interface OperationContext {
|
|
|
1481
1485
|
|
|
1482
1486
|
export declare class PackageRegistryRepo {
|
|
1483
1487
|
private readonly adapter;
|
|
1484
|
-
|
|
1488
|
+
private readonly dialect;
|
|
1489
|
+
constructor(adapter: StorageAdapter, dialect?: DDLDialect);
|
|
1485
1490
|
/**
|
|
1486
1491
|
* Ensure the packages tracking table exists.
|
|
1487
1492
|
*/
|
|
@@ -1513,6 +1518,10 @@ export declare class PackageRegistryRepo {
|
|
|
1513
1518
|
* Uses INSERT OR REPLACE (SQLite UPSERT) to handle both new and upgraded packages.
|
|
1514
1519
|
*/
|
|
1515
1520
|
upsertPackage(pkg: Omit<InstalledPackage, 'installedAt' | 'status'>): Promise<void>;
|
|
1521
|
+
/**
|
|
1522
|
+
* Generate dialect-aware UPSERT SQL.
|
|
1523
|
+
*/
|
|
1524
|
+
private upsertSQL;
|
|
1516
1525
|
/**
|
|
1517
1526
|
* Remove all versions of a package.
|
|
1518
1527
|
*/
|
|
@@ -1900,7 +1909,8 @@ declare interface ReindexResultV2 {
|
|
|
1900
1909
|
|
|
1901
1910
|
export declare class ReindexScheduler {
|
|
1902
1911
|
private readonly adapter;
|
|
1903
|
-
|
|
1912
|
+
private readonly dialect;
|
|
1913
|
+
constructor(adapter: StorageAdapter, dialect?: DDLDialect);
|
|
1904
1914
|
/**
|
|
1905
1915
|
* Ensure the reindex jobs table exists.
|
|
1906
1916
|
*/
|
|
@@ -3199,7 +3209,8 @@ declare interface ValueSetInput {
|
|
|
3199
3209
|
|
|
3200
3210
|
export declare class ValueSetRepo {
|
|
3201
3211
|
private readonly adapter;
|
|
3202
|
-
|
|
3212
|
+
private readonly dialect;
|
|
3213
|
+
constructor(adapter: StorageAdapter, dialect?: DDLDialect);
|
|
3203
3214
|
/**
|
|
3204
3215
|
* Ensure the terminology_valuesets table exists.
|
|
3205
3216
|
*/
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -324,7 +324,7 @@ export declare function buildWhereClauseV2(params: ParsedSearchParam[], registry
|
|
|
324
324
|
* - SQLite: json_each() for array columns
|
|
325
325
|
* - PostgreSQL: native ARRAY operators
|
|
326
326
|
*/
|
|
327
|
-
export declare function buildWhereFragmentV2(impl: SearchParameterImpl, param: ParsedSearchParam, dialect?: SqlDialect): WhereFragment | null;
|
|
327
|
+
export declare function buildWhereFragmentV2(impl: SearchParameterImpl, param: ParsedSearchParam, dialect?: SqlDialect, resourceType?: string): WhereFragment | null;
|
|
328
328
|
|
|
329
329
|
declare interface BundleLink {
|
|
330
330
|
relation: string;
|
|
@@ -1194,6 +1194,8 @@ export declare interface IndexingPipelineOptions {
|
|
|
1194
1194
|
enableReferences?: boolean;
|
|
1195
1195
|
/** Optional RuntimeProvider for FHIRPath-driven extraction (B3). */
|
|
1196
1196
|
runtimeProvider?: RuntimeProvider;
|
|
1197
|
+
/** SQL dialect for lookup table DDL (default: 'sqlite'). */
|
|
1198
|
+
dialect?: DDLDialect;
|
|
1197
1199
|
}
|
|
1198
1200
|
|
|
1199
1201
|
/**
|
|
@@ -1306,7 +1308,8 @@ declare type LookupTableType = 'HumanName' | 'Address' | 'ContactPoint' | 'Ident
|
|
|
1306
1308
|
export declare class LookupTableWriter {
|
|
1307
1309
|
private readonly adapter;
|
|
1308
1310
|
private initialized;
|
|
1309
|
-
|
|
1311
|
+
private readonly ddl;
|
|
1312
|
+
constructor(adapter: StorageAdapter, dialect?: DDLDialect);
|
|
1310
1313
|
/**
|
|
1311
1314
|
* Create all 4 lookup tables + indexes if they don't exist.
|
|
1312
1315
|
*/
|
|
@@ -1390,7 +1393,8 @@ export declare interface MigrationResultV2 {
|
|
|
1390
1393
|
export declare class MigrationRunnerV2 {
|
|
1391
1394
|
private readonly adapter;
|
|
1392
1395
|
private readonly migrations;
|
|
1393
|
-
|
|
1396
|
+
private readonly dialect;
|
|
1397
|
+
constructor(adapter: StorageAdapter, migrations?: MigrationV2[], dialect?: DDLDialect);
|
|
1394
1398
|
/**
|
|
1395
1399
|
* Ensure the tracking table exists.
|
|
1396
1400
|
*/
|
|
@@ -1481,7 +1485,8 @@ export declare interface OperationContext {
|
|
|
1481
1485
|
|
|
1482
1486
|
export declare class PackageRegistryRepo {
|
|
1483
1487
|
private readonly adapter;
|
|
1484
|
-
|
|
1488
|
+
private readonly dialect;
|
|
1489
|
+
constructor(adapter: StorageAdapter, dialect?: DDLDialect);
|
|
1485
1490
|
/**
|
|
1486
1491
|
* Ensure the packages tracking table exists.
|
|
1487
1492
|
*/
|
|
@@ -1513,6 +1518,10 @@ export declare class PackageRegistryRepo {
|
|
|
1513
1518
|
* Uses INSERT OR REPLACE (SQLite UPSERT) to handle both new and upgraded packages.
|
|
1514
1519
|
*/
|
|
1515
1520
|
upsertPackage(pkg: Omit<InstalledPackage, 'installedAt' | 'status'>): Promise<void>;
|
|
1521
|
+
/**
|
|
1522
|
+
* Generate dialect-aware UPSERT SQL.
|
|
1523
|
+
*/
|
|
1524
|
+
private upsertSQL;
|
|
1516
1525
|
/**
|
|
1517
1526
|
* Remove all versions of a package.
|
|
1518
1527
|
*/
|
|
@@ -1900,7 +1909,8 @@ declare interface ReindexResultV2 {
|
|
|
1900
1909
|
|
|
1901
1910
|
export declare class ReindexScheduler {
|
|
1902
1911
|
private readonly adapter;
|
|
1903
|
-
|
|
1912
|
+
private readonly dialect;
|
|
1913
|
+
constructor(adapter: StorageAdapter, dialect?: DDLDialect);
|
|
1904
1914
|
/**
|
|
1905
1915
|
* Ensure the reindex jobs table exists.
|
|
1906
1916
|
*/
|
|
@@ -3199,7 +3209,8 @@ declare interface ValueSetInput {
|
|
|
3199
3209
|
|
|
3200
3210
|
export declare class ValueSetRepo {
|
|
3201
3211
|
private readonly adapter;
|
|
3202
|
-
|
|
3212
|
+
private readonly dialect;
|
|
3213
|
+
constructor(adapter: StorageAdapter, dialect?: DDLDialect);
|
|
3203
3214
|
/**
|
|
3204
3215
|
* Ensure the terminology_valuesets table exists.
|
|
3205
3216
|
*/
|
package/dist/esm/index.mjs
CHANGED
|
@@ -5077,12 +5077,12 @@ function arrayContainsLikeV2(col, value, dialect) {
|
|
|
5077
5077
|
}
|
|
5078
5078
|
return { sql: `EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value LIKE ?)`, values: [value] };
|
|
5079
5079
|
}
|
|
5080
|
-
function buildWhereFragmentV2(impl, param, dialect) {
|
|
5080
|
+
function buildWhereFragmentV2(impl, param, dialect, resourceType) {
|
|
5081
5081
|
if (param.modifier === "missing") {
|
|
5082
5082
|
return buildMissingFragmentV2(impl, param);
|
|
5083
5083
|
}
|
|
5084
5084
|
if (impl.strategy === "lookup-table") {
|
|
5085
|
-
return buildLookupTableFragmentV2(impl, param);
|
|
5085
|
+
return buildLookupTableFragmentV2(impl, param, resourceType ?? "Resource");
|
|
5086
5086
|
}
|
|
5087
5087
|
if (impl.strategy === "token-column") {
|
|
5088
5088
|
return buildTokenColumnFragmentV2(impl, param, dialect);
|
|
@@ -5110,7 +5110,7 @@ function buildMissingFragmentV2(impl, param) {
|
|
|
5110
5110
|
const col = quoteColumn(impl.columnName);
|
|
5111
5111
|
return { sql: isMissing ? `${col} IS NULL` : `${col} IS NOT NULL`, values: [] };
|
|
5112
5112
|
}
|
|
5113
|
-
function buildLookupTableFragmentV2(impl, param) {
|
|
5113
|
+
function buildLookupTableFragmentV2(impl, param, resourceType) {
|
|
5114
5114
|
const mapping = LOOKUP_TABLE_MAP[impl.code];
|
|
5115
5115
|
if (!mapping) {
|
|
5116
5116
|
const sortCol = quoteColumn(`__${impl.columnName}Sort`);
|
|
@@ -5120,25 +5120,26 @@ function buildLookupTableFragmentV2(impl, param) {
|
|
|
5120
5120
|
}
|
|
5121
5121
|
const { table, column } = mapping;
|
|
5122
5122
|
const colRef = `__lookup."${column}"`;
|
|
5123
|
+
const outerIdRef = `"${resourceType}"."id"`;
|
|
5123
5124
|
if (param.modifier === "exact") {
|
|
5124
5125
|
if (param.values.length === 1) {
|
|
5125
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5126
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND ${colRef} = ?)`, values: [param.values[0]] };
|
|
5126
5127
|
}
|
|
5127
5128
|
const conds2 = param.values.map(() => `${colRef} = ?`);
|
|
5128
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5129
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND (${conds2.join(" OR ")}))`, values: [...param.values] };
|
|
5129
5130
|
}
|
|
5130
5131
|
if (param.modifier === "contains") {
|
|
5131
5132
|
if (param.values.length === 1) {
|
|
5132
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5133
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND LOWER(${colRef}) LIKE ?)`, values: [`%${param.values[0].toLowerCase()}%`] };
|
|
5133
5134
|
}
|
|
5134
5135
|
const conds2 = param.values.map(() => `LOWER(${colRef}) LIKE ?`);
|
|
5135
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5136
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND (${conds2.join(" OR ")}))`, values: param.values.map((v) => `%${v.toLowerCase()}%`) };
|
|
5136
5137
|
}
|
|
5137
5138
|
if (param.values.length === 1) {
|
|
5138
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5139
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND LOWER(${colRef}) LIKE ?)`, values: [`${param.values[0].toLowerCase()}%`] };
|
|
5139
5140
|
}
|
|
5140
5141
|
const conds = param.values.map(() => `LOWER(${colRef}) LIKE ?`);
|
|
5141
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5142
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND (${conds.join(" OR ")}))`, values: param.values.map((v) => `${v.toLowerCase()}%`) };
|
|
5142
5143
|
}
|
|
5143
5144
|
function buildStringFragmentV2(impl, param) {
|
|
5144
5145
|
const col = quoteColumn(impl.columnName);
|
|
@@ -5307,7 +5308,7 @@ function buildWhereClauseV2(params, registry, resourceType, dialect) {
|
|
|
5307
5308
|
}
|
|
5308
5309
|
const impl = resolveImplV2(param, registry, resourceType);
|
|
5309
5310
|
if (!impl) continue;
|
|
5310
|
-
const fragment = buildWhereFragmentV2(impl, param, dialect);
|
|
5311
|
+
const fragment = buildWhereFragmentV2(impl, param, dialect, resourceType);
|
|
5311
5312
|
if (fragment) {
|
|
5312
5313
|
fragments.push(fragment);
|
|
5313
5314
|
}
|
|
@@ -6435,16 +6436,18 @@ var FhirStore = class {
|
|
|
6435
6436
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
6436
6437
|
|
|
6437
6438
|
// src/repo/lookup-table-writer.ts
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6439
|
+
function buildLookupTableDDL(dialect) {
|
|
6440
|
+
const pk = dialect === "postgres" ? '"id" SERIAL PRIMARY KEY' : '"id" INTEGER PRIMARY KEY AUTOINCREMENT';
|
|
6441
|
+
return {
|
|
6442
|
+
HumanName: `CREATE TABLE IF NOT EXISTS "HumanName" (
|
|
6443
|
+
${pk},
|
|
6441
6444
|
"resourceId" TEXT NOT NULL,
|
|
6442
6445
|
"name" TEXT,
|
|
6443
6446
|
"given" TEXT,
|
|
6444
6447
|
"family" TEXT
|
|
6445
6448
|
)`,
|
|
6446
|
-
|
|
6447
|
-
|
|
6449
|
+
Address: `CREATE TABLE IF NOT EXISTS "Address" (
|
|
6450
|
+
${pk},
|
|
6448
6451
|
"resourceId" TEXT NOT NULL,
|
|
6449
6452
|
"address" TEXT,
|
|
6450
6453
|
"city" TEXT,
|
|
@@ -6453,20 +6456,21 @@ var LOOKUP_TABLE_DDL = {
|
|
|
6453
6456
|
"state" TEXT,
|
|
6454
6457
|
"use" TEXT
|
|
6455
6458
|
)`,
|
|
6456
|
-
|
|
6457
|
-
|
|
6459
|
+
ContactPoint: `CREATE TABLE IF NOT EXISTS "ContactPoint" (
|
|
6460
|
+
${pk},
|
|
6458
6461
|
"resourceId" TEXT NOT NULL,
|
|
6459
6462
|
"system" TEXT,
|
|
6460
6463
|
"value" TEXT,
|
|
6461
6464
|
"use" TEXT
|
|
6462
6465
|
)`,
|
|
6463
|
-
|
|
6464
|
-
|
|
6466
|
+
Identifier: `CREATE TABLE IF NOT EXISTS "Identifier" (
|
|
6467
|
+
${pk},
|
|
6465
6468
|
"resourceId" TEXT NOT NULL,
|
|
6466
6469
|
"system" TEXT,
|
|
6467
6470
|
"value" TEXT
|
|
6468
6471
|
)`
|
|
6469
|
-
};
|
|
6472
|
+
};
|
|
6473
|
+
}
|
|
6470
6474
|
var LOOKUP_TABLE_INDEXES = {
|
|
6471
6475
|
HumanName: [
|
|
6472
6476
|
'CREATE INDEX IF NOT EXISTS "HumanName_resourceId_idx" ON "HumanName" ("resourceId")',
|
|
@@ -6492,10 +6496,12 @@ var LOOKUP_COLUMNS = {
|
|
|
6492
6496
|
Identifier: ["resourceId", "system", "value"]
|
|
6493
6497
|
};
|
|
6494
6498
|
var LookupTableWriter = class {
|
|
6495
|
-
constructor(adapter) {
|
|
6499
|
+
constructor(adapter, dialect = "sqlite") {
|
|
6496
6500
|
this.adapter = adapter;
|
|
6501
|
+
this.ddl = buildLookupTableDDL(dialect);
|
|
6497
6502
|
}
|
|
6498
6503
|
initialized = false;
|
|
6504
|
+
ddl;
|
|
6499
6505
|
// ---------------------------------------------------------------------------
|
|
6500
6506
|
// DDL
|
|
6501
6507
|
// ---------------------------------------------------------------------------
|
|
@@ -6504,8 +6510,8 @@ var LookupTableWriter = class {
|
|
|
6504
6510
|
*/
|
|
6505
6511
|
async ensureTables() {
|
|
6506
6512
|
if (this.initialized) return;
|
|
6507
|
-
for (const table of Object.keys(
|
|
6508
|
-
await this.adapter.execute(
|
|
6513
|
+
for (const table of Object.keys(this.ddl)) {
|
|
6514
|
+
await this.adapter.execute(this.ddl[table]);
|
|
6509
6515
|
for (const idx of LOOKUP_TABLE_INDEXES[table]) {
|
|
6510
6516
|
await this.adapter.execute(idx);
|
|
6511
6517
|
}
|
|
@@ -6554,7 +6560,7 @@ var LookupTableWriter = class {
|
|
|
6554
6560
|
*/
|
|
6555
6561
|
async deleteRows(resourceId) {
|
|
6556
6562
|
await this.ensureTables();
|
|
6557
|
-
for (const table of Object.keys(
|
|
6563
|
+
for (const table of Object.keys(this.ddl)) {
|
|
6558
6564
|
await this.adapter.execute(
|
|
6559
6565
|
`DELETE FROM "${table}" WHERE "resourceId" = ?`,
|
|
6560
6566
|
[resourceId]
|
|
@@ -6580,7 +6586,7 @@ var LookupTableWriter = class {
|
|
|
6580
6586
|
var IndexingPipeline = class {
|
|
6581
6587
|
constructor(adapter, options) {
|
|
6582
6588
|
this.adapter = adapter;
|
|
6583
|
-
this.lookupWriter = new LookupTableWriter(adapter);
|
|
6589
|
+
this.lookupWriter = new LookupTableWriter(adapter, options?.dialect ?? "sqlite");
|
|
6584
6590
|
this.runtimeProvider = options?.runtimeProvider;
|
|
6585
6591
|
this.options = {
|
|
6586
6592
|
enableLookupTables: options?.enableLookupTables ?? true,
|
|
@@ -7150,6 +7156,14 @@ function generateMigration(deltas, dialect) {
|
|
|
7150
7156
|
const down = [];
|
|
7151
7157
|
const reindexDeltas = [];
|
|
7152
7158
|
const descriptions = [];
|
|
7159
|
+
const needsPgExtensions = deltas.some((d) => d.kind === "ADD_TABLE" || d.kind === "ADD_INDEX");
|
|
7160
|
+
if (dialect === "postgres" && needsPgExtensions) {
|
|
7161
|
+
up.push("CREATE EXTENSION IF NOT EXISTS pg_trgm;");
|
|
7162
|
+
up.push("CREATE EXTENSION IF NOT EXISTS btree_gin;");
|
|
7163
|
+
up.push(
|
|
7164
|
+
`CREATE OR REPLACE FUNCTION token_array_to_text(arr text[]) RETURNS text LANGUAGE sql IMMUTABLE AS $$ SELECT array_to_string(arr, ' ') $$;`
|
|
7165
|
+
);
|
|
7166
|
+
}
|
|
7153
7167
|
for (const delta of deltas) {
|
|
7154
7168
|
switch (delta.kind) {
|
|
7155
7169
|
case "ADD_TABLE": {
|
|
@@ -7278,35 +7292,43 @@ function mapColumnTypeForDialect(type, dialect) {
|
|
|
7278
7292
|
|
|
7279
7293
|
// src/registry/package-registry-repo.ts
|
|
7280
7294
|
var PACKAGES_TABLE = "_packages";
|
|
7281
|
-
var
|
|
7295
|
+
var SCHEMA_VERSION_TABLE = "_schema_version";
|
|
7296
|
+
function createPackagesTableDDL(dialect) {
|
|
7297
|
+
const ts = dialect === "postgres" ? "TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP" : "TEXT NOT NULL DEFAULT (datetime('now'))";
|
|
7298
|
+
return `
|
|
7282
7299
|
CREATE TABLE IF NOT EXISTS "${PACKAGES_TABLE}" (
|
|
7283
7300
|
"name" TEXT NOT NULL,
|
|
7284
7301
|
"version" TEXT NOT NULL,
|
|
7285
7302
|
"checksum" TEXT NOT NULL,
|
|
7286
7303
|
"schemaSnapshot" TEXT,
|
|
7287
|
-
"installedAt"
|
|
7304
|
+
"installedAt" ${ts},
|
|
7288
7305
|
"status" TEXT NOT NULL DEFAULT 'active',
|
|
7289
7306
|
PRIMARY KEY ("name", "version")
|
|
7290
7307
|
);
|
|
7291
7308
|
`;
|
|
7292
|
-
|
|
7293
|
-
|
|
7309
|
+
}
|
|
7310
|
+
function createSchemaVersionTableDDL(dialect) {
|
|
7311
|
+
const ts = dialect === "postgres" ? "TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP" : "TEXT NOT NULL DEFAULT (datetime('now'))";
|
|
7312
|
+
return `
|
|
7294
7313
|
CREATE TABLE IF NOT EXISTS "${SCHEMA_VERSION_TABLE}" (
|
|
7295
7314
|
"version" INTEGER NOT NULL PRIMARY KEY,
|
|
7296
7315
|
"packageList" TEXT NOT NULL,
|
|
7297
7316
|
"description" TEXT NOT NULL DEFAULT '',
|
|
7298
|
-
"appliedAt"
|
|
7317
|
+
"appliedAt" ${ts}
|
|
7299
7318
|
);
|
|
7300
7319
|
`;
|
|
7320
|
+
}
|
|
7301
7321
|
var PackageRegistryRepo = class {
|
|
7302
|
-
constructor(adapter) {
|
|
7322
|
+
constructor(adapter, dialect = "sqlite") {
|
|
7303
7323
|
this.adapter = adapter;
|
|
7324
|
+
this.dialect = dialect;
|
|
7304
7325
|
}
|
|
7326
|
+
dialect;
|
|
7305
7327
|
/**
|
|
7306
7328
|
* Ensure the packages tracking table exists.
|
|
7307
7329
|
*/
|
|
7308
7330
|
async ensureTable() {
|
|
7309
|
-
await this.adapter.execute(
|
|
7331
|
+
await this.adapter.execute(createPackagesTableDDL(this.dialect));
|
|
7310
7332
|
}
|
|
7311
7333
|
/**
|
|
7312
7334
|
* Get the active version of a package by name.
|
|
@@ -7351,7 +7373,7 @@ var PackageRegistryRepo = class {
|
|
|
7351
7373
|
[pkg.name]
|
|
7352
7374
|
);
|
|
7353
7375
|
await this.adapter.execute(
|
|
7354
|
-
|
|
7376
|
+
this.upsertSQL(),
|
|
7355
7377
|
[pkg.name, pkg.version, pkg.checksum, pkg.schemaSnapshot ?? null]
|
|
7356
7378
|
);
|
|
7357
7379
|
await this.recordSchemaVersion(description ?? `Register ${pkg.name}@${pkg.version}`);
|
|
@@ -7368,10 +7390,19 @@ var PackageRegistryRepo = class {
|
|
|
7368
7390
|
[pkg.name]
|
|
7369
7391
|
);
|
|
7370
7392
|
await this.adapter.execute(
|
|
7371
|
-
|
|
7393
|
+
this.upsertSQL(),
|
|
7372
7394
|
[pkg.name, pkg.version, pkg.checksum, pkg.schemaSnapshot ?? null]
|
|
7373
7395
|
);
|
|
7374
7396
|
}
|
|
7397
|
+
/**
|
|
7398
|
+
* Generate dialect-aware UPSERT SQL.
|
|
7399
|
+
*/
|
|
7400
|
+
upsertSQL() {
|
|
7401
|
+
if (this.dialect === "postgres") {
|
|
7402
|
+
return `INSERT INTO "${PACKAGES_TABLE}" ("name", "version", "checksum", "schemaSnapshot", "status") VALUES (?, ?, ?, ?, 'active') ON CONFLICT ("name", "version") DO UPDATE SET "checksum" = EXCLUDED."checksum", "schemaSnapshot" = EXCLUDED."schemaSnapshot", "status" = 'active'`;
|
|
7403
|
+
}
|
|
7404
|
+
return `INSERT OR REPLACE INTO "${PACKAGES_TABLE}" ("name", "version", "checksum", "schemaSnapshot", "status") VALUES (?, ?, ?, ?, 'active')`;
|
|
7405
|
+
}
|
|
7375
7406
|
/**
|
|
7376
7407
|
* Remove all versions of a package.
|
|
7377
7408
|
*/
|
|
@@ -7403,7 +7434,7 @@ var PackageRegistryRepo = class {
|
|
|
7403
7434
|
* Ensure the schema version table exists.
|
|
7404
7435
|
*/
|
|
7405
7436
|
async ensureSchemaVersionTable() {
|
|
7406
|
-
await this.adapter.execute(
|
|
7437
|
+
await this.adapter.execute(createSchemaVersionTableDDL(this.dialect));
|
|
7407
7438
|
}
|
|
7408
7439
|
/**
|
|
7409
7440
|
* Record a schema version with the current active package list.
|
|
@@ -7453,12 +7484,25 @@ CREATE TABLE IF NOT EXISTS "${TRACKING_TABLE_V2}" (
|
|
|
7453
7484
|
"applied_at" TEXT NOT NULL DEFAULT (datetime('now'))
|
|
7454
7485
|
);
|
|
7455
7486
|
`;
|
|
7487
|
+
var CREATE_TRACKING_TABLE_V2_POSTGRES = `
|
|
7488
|
+
CREATE TABLE IF NOT EXISTS "${TRACKING_TABLE_V2}" (
|
|
7489
|
+
"version" INTEGER PRIMARY KEY,
|
|
7490
|
+
"description" TEXT NOT NULL,
|
|
7491
|
+
"type" TEXT NOT NULL DEFAULT 'file',
|
|
7492
|
+
"applied_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
7493
|
+
);
|
|
7494
|
+
`;
|
|
7495
|
+
function createTrackingDDL(dialect) {
|
|
7496
|
+
return dialect === "postgres" ? CREATE_TRACKING_TABLE_V2_POSTGRES : CREATE_TRACKING_TABLE_V2_SQLITE;
|
|
7497
|
+
}
|
|
7456
7498
|
var MigrationRunnerV2 = class {
|
|
7457
7499
|
adapter;
|
|
7458
7500
|
migrations;
|
|
7459
|
-
|
|
7501
|
+
dialect;
|
|
7502
|
+
constructor(adapter, migrations = [], dialect = "sqlite") {
|
|
7460
7503
|
this.adapter = adapter;
|
|
7461
7504
|
this.migrations = [...migrations].sort((a, b) => a.version - b.version);
|
|
7505
|
+
this.dialect = dialect;
|
|
7462
7506
|
}
|
|
7463
7507
|
// ---------------------------------------------------------------------------
|
|
7464
7508
|
// Public API
|
|
@@ -7467,7 +7511,7 @@ var MigrationRunnerV2 = class {
|
|
|
7467
7511
|
* Ensure the tracking table exists.
|
|
7468
7512
|
*/
|
|
7469
7513
|
async ensureTrackingTable() {
|
|
7470
|
-
await this.adapter.execute(
|
|
7514
|
+
await this.adapter.execute(createTrackingDDL(this.dialect));
|
|
7471
7515
|
}
|
|
7472
7516
|
/**
|
|
7473
7517
|
* Apply all pending migrations (or up to a target version).
|
|
@@ -7643,7 +7687,23 @@ var MigrationRunnerV2 = class {
|
|
|
7643
7687
|
|
|
7644
7688
|
// src/migration/reindex-scheduler.ts
|
|
7645
7689
|
var REINDEX_JOBS_TABLE = "_reindex_jobs";
|
|
7646
|
-
|
|
7690
|
+
function createReindexJobsTableDDL(dialect) {
|
|
7691
|
+
if (dialect === "postgres") {
|
|
7692
|
+
return `
|
|
7693
|
+
CREATE TABLE IF NOT EXISTS "${REINDEX_JOBS_TABLE}" (
|
|
7694
|
+
"id" SERIAL PRIMARY KEY,
|
|
7695
|
+
"resourceType" TEXT NOT NULL,
|
|
7696
|
+
"searchParamCode" TEXT NOT NULL,
|
|
7697
|
+
"expression" TEXT NOT NULL,
|
|
7698
|
+
"status" TEXT NOT NULL DEFAULT 'pending',
|
|
7699
|
+
"cursor" TEXT,
|
|
7700
|
+
"processedCount" INTEGER NOT NULL DEFAULT 0,
|
|
7701
|
+
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
7702
|
+
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
7703
|
+
);
|
|
7704
|
+
`;
|
|
7705
|
+
}
|
|
7706
|
+
return `
|
|
7647
7707
|
CREATE TABLE IF NOT EXISTS "${REINDEX_JOBS_TABLE}" (
|
|
7648
7708
|
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7649
7709
|
"resourceType" TEXT NOT NULL,
|
|
@@ -7656,15 +7716,21 @@ CREATE TABLE IF NOT EXISTS "${REINDEX_JOBS_TABLE}" (
|
|
|
7656
7716
|
"updatedAt" TEXT NOT NULL DEFAULT (datetime('now'))
|
|
7657
7717
|
);
|
|
7658
7718
|
`;
|
|
7719
|
+
}
|
|
7720
|
+
function nowExpression(dialect) {
|
|
7721
|
+
return dialect === "postgres" ? "CURRENT_TIMESTAMP" : "datetime('now')";
|
|
7722
|
+
}
|
|
7659
7723
|
var ReindexScheduler = class {
|
|
7660
|
-
constructor(adapter) {
|
|
7724
|
+
constructor(adapter, dialect = "sqlite") {
|
|
7661
7725
|
this.adapter = adapter;
|
|
7726
|
+
this.dialect = dialect;
|
|
7662
7727
|
}
|
|
7728
|
+
dialect;
|
|
7663
7729
|
/**
|
|
7664
7730
|
* Ensure the reindex jobs table exists.
|
|
7665
7731
|
*/
|
|
7666
7732
|
async ensureTable() {
|
|
7667
|
-
await this.adapter.execute(
|
|
7733
|
+
await this.adapter.execute(createReindexJobsTableDDL(this.dialect));
|
|
7668
7734
|
}
|
|
7669
7735
|
/**
|
|
7670
7736
|
* Schedule reindex jobs from REINDEX deltas.
|
|
@@ -7731,7 +7797,7 @@ var ReindexScheduler = class {
|
|
|
7731
7797
|
sets.push('"processedCount" = ?');
|
|
7732
7798
|
values.push(update.processedCount);
|
|
7733
7799
|
}
|
|
7734
|
-
sets.push(`"updatedAt" =
|
|
7800
|
+
sets.push(`"updatedAt" = ${nowExpression(this.dialect)}`);
|
|
7735
7801
|
values.push(id);
|
|
7736
7802
|
await this.adapter.execute(
|
|
7737
7803
|
`UPDATE "${REINDEX_JOBS_TABLE}" SET ${sets.join(", ")} WHERE "id" = ?`,
|
|
@@ -7764,9 +7830,9 @@ var IGPersistenceManager = class {
|
|
|
7764
7830
|
reindexScheduler;
|
|
7765
7831
|
constructor(adapter, dialect = "sqlite") {
|
|
7766
7832
|
this.dialect = dialect;
|
|
7767
|
-
this.packageRepo = new PackageRegistryRepo(adapter);
|
|
7768
|
-
this.migrationRunner = new MigrationRunnerV2(adapter);
|
|
7769
|
-
this.reindexScheduler = new ReindexScheduler(adapter);
|
|
7833
|
+
this.packageRepo = new PackageRegistryRepo(adapter, dialect);
|
|
7834
|
+
this.migrationRunner = new MigrationRunnerV2(adapter, [], dialect);
|
|
7835
|
+
this.reindexScheduler = new ReindexScheduler(adapter, dialect);
|
|
7770
7836
|
}
|
|
7771
7837
|
/**
|
|
7772
7838
|
* Initialize an IG package — the main entry point.
|
|
@@ -7947,25 +8013,30 @@ var TerminologyCodeRepo = class {
|
|
|
7947
8013
|
|
|
7948
8014
|
// src/terminology/valueset-repo.ts
|
|
7949
8015
|
var VALUESETS_TABLE = "terminology_valuesets";
|
|
7950
|
-
|
|
8016
|
+
function createValuesetsTableDDL(dialect) {
|
|
8017
|
+
const ts = dialect === "postgres" ? "TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP" : "TEXT NOT NULL DEFAULT (datetime('now'))";
|
|
8018
|
+
return `
|
|
7951
8019
|
CREATE TABLE IF NOT EXISTS "${VALUESETS_TABLE}" (
|
|
7952
8020
|
"url" TEXT NOT NULL,
|
|
7953
8021
|
"version" TEXT NOT NULL,
|
|
7954
8022
|
"name" TEXT,
|
|
7955
8023
|
"content" TEXT NOT NULL,
|
|
7956
|
-
"storedAt"
|
|
8024
|
+
"storedAt" ${ts},
|
|
7957
8025
|
PRIMARY KEY ("url", "version")
|
|
7958
8026
|
);
|
|
7959
8027
|
`;
|
|
8028
|
+
}
|
|
7960
8029
|
var ValueSetRepo = class {
|
|
7961
|
-
constructor(adapter) {
|
|
8030
|
+
constructor(adapter, dialect = "sqlite") {
|
|
7962
8031
|
this.adapter = adapter;
|
|
8032
|
+
this.dialect = dialect;
|
|
7963
8033
|
}
|
|
8034
|
+
dialect;
|
|
7964
8035
|
/**
|
|
7965
8036
|
* Ensure the terminology_valuesets table exists.
|
|
7966
8037
|
*/
|
|
7967
8038
|
async ensureTable() {
|
|
7968
|
-
await this.adapter.execute(
|
|
8039
|
+
await this.adapter.execute(createValuesetsTableDDL(this.dialect));
|
|
7969
8040
|
}
|
|
7970
8041
|
/**
|
|
7971
8042
|
* Insert or update a ValueSet.
|
|
@@ -7973,10 +8044,8 @@ var ValueSetRepo = class {
|
|
|
7973
8044
|
*/
|
|
7974
8045
|
async upsert(input) {
|
|
7975
8046
|
await this.ensureTable();
|
|
7976
|
-
|
|
7977
|
-
|
|
7978
|
-
[input.url, input.version, input.name ?? null, input.content]
|
|
7979
|
-
);
|
|
8047
|
+
const sql = this.dialect === "postgres" ? `INSERT INTO "${VALUESETS_TABLE}" ("url", "version", "name", "content") VALUES (?, ?, ?, ?) ON CONFLICT ("url", "version") DO UPDATE SET "name" = EXCLUDED."name", "content" = EXCLUDED."content"` : `INSERT OR REPLACE INTO "${VALUESETS_TABLE}" ("url", "version", "name", "content") VALUES (?, ?, ?, ?)`;
|
|
8048
|
+
await this.adapter.execute(sql, [input.url, input.version, input.name ?? null, input.content]);
|
|
7980
8049
|
}
|
|
7981
8050
|
/**
|
|
7982
8051
|
* Get a specific ValueSet by url and version.
|
|
@@ -8435,7 +8504,8 @@ var FhirSystem = class {
|
|
|
8435
8504
|
indexing: {
|
|
8436
8505
|
enableLookupTables: this.options.enableLookupTables,
|
|
8437
8506
|
enableReferences: this.options.enableReferences,
|
|
8438
|
-
runtimeProvider: this.options.runtimeProvider
|
|
8507
|
+
runtimeProvider: this.options.runtimeProvider,
|
|
8508
|
+
dialect: this.dialect
|
|
8439
8509
|
}
|
|
8440
8510
|
});
|
|
8441
8511
|
return {
|