fhir-persistence 0.1.0 → 0.3.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.
@@ -5049,7 +5049,35 @@ function quoteColumn(name) {
5049
5049
  function escapeLikeString(value) {
5050
5050
  return value.replace(/[%_\\]/g, "\\$&");
5051
5051
  }
5052
- function buildWhereFragmentV2(impl, param) {
5052
+ function arrayContainsV2(col, values, dialect) {
5053
+ if (dialect?.name === "postgres") {
5054
+ const placeholders2 = values.map(() => "?").join(", ");
5055
+ return { sql: `${col} && ARRAY[${placeholders2}]::text[]`, values: [...values] };
5056
+ }
5057
+ if (values.length === 1) {
5058
+ return { sql: `EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value = ?)`, values: [values[0]] };
5059
+ }
5060
+ const placeholders = values.map(() => "?").join(", ");
5061
+ return { sql: `EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value IN (${placeholders}))`, values: [...values] };
5062
+ }
5063
+ function arrayNotContainsV2(col, values, dialect) {
5064
+ if (dialect?.name === "postgres") {
5065
+ const placeholders2 = values.map(() => "?").join(", ");
5066
+ return { sql: `NOT (${col} && ARRAY[${placeholders2}]::text[])`, values: [...values] };
5067
+ }
5068
+ if (values.length === 1) {
5069
+ return { sql: `NOT EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value = ?)`, values: [values[0]] };
5070
+ }
5071
+ const placeholders = values.map(() => "?").join(", ");
5072
+ return { sql: `NOT EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value IN (${placeholders}))`, values: [...values] };
5073
+ }
5074
+ function arrayContainsLikeV2(col, value, dialect) {
5075
+ if (dialect?.name === "postgres") {
5076
+ return { sql: `EXISTS (SELECT unnest FROM unnest(${col}) WHERE unnest LIKE ?)`, values: [value] };
5077
+ }
5078
+ return { sql: `EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value LIKE ?)`, values: [value] };
5079
+ }
5080
+ function buildWhereFragmentV2(impl, param, dialect) {
5053
5081
  if (param.modifier === "missing") {
5054
5082
  return buildMissingFragmentV2(impl, param);
5055
5083
  }
@@ -5057,7 +5085,7 @@ function buildWhereFragmentV2(impl, param) {
5057
5085
  return buildLookupTableFragmentV2(impl, param);
5058
5086
  }
5059
5087
  if (impl.strategy === "token-column") {
5060
- return buildTokenColumnFragmentV2(impl, param);
5088
+ return buildTokenColumnFragmentV2(impl, param, dialect);
5061
5089
  }
5062
5090
  switch (impl.type) {
5063
5091
  case "string":
@@ -5068,11 +5096,11 @@ function buildWhereFragmentV2(impl, param) {
5068
5096
  case "quantity":
5069
5097
  return buildNumberFragmentV2(impl, param);
5070
5098
  case "reference":
5071
- return buildReferenceFragmentV2(impl, param);
5099
+ return buildReferenceFragmentV2(impl, param, dialect);
5072
5100
  case "uri":
5073
- return buildUriFragmentV2(impl, param);
5101
+ return buildUriFragmentV2(impl, param, dialect);
5074
5102
  case "token":
5075
- return buildTokenColumnFragmentV2(impl, param);
5103
+ return buildTokenColumnFragmentV2(impl, param, dialect);
5076
5104
  default:
5077
5105
  return buildDefaultFragmentV2(impl, param);
5078
5106
  }
@@ -5163,29 +5191,21 @@ function buildNumberFragmentV2(impl, param) {
5163
5191
  });
5164
5192
  return buildOrFragmentV2Raw(col, op, numVals);
5165
5193
  }
5166
- function buildReferenceFragmentV2(impl, param) {
5194
+ function buildReferenceFragmentV2(impl, param, dialect) {
5167
5195
  const col = quoteColumn(impl.columnName);
5168
5196
  if (impl.array) {
5169
- if (param.values.length === 1) {
5170
- return { sql: `EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value = ?)`, values: [param.values[0]] };
5171
- }
5172
- const placeholders = param.values.map(() => "?").join(", ");
5173
- return { sql: `EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value IN (${placeholders}))`, values: [...param.values] };
5197
+ return arrayContainsV2(col, param.values, dialect);
5174
5198
  }
5175
5199
  return buildOrFragmentV2(col, "=", param.values);
5176
5200
  }
5177
- function buildUriFragmentV2(impl, param) {
5201
+ function buildUriFragmentV2(impl, param, dialect) {
5178
5202
  const col = quoteColumn(impl.columnName);
5179
5203
  if (impl.array) {
5180
- if (param.values.length === 1) {
5181
- return { sql: `EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value = ?)`, values: [param.values[0]] };
5182
- }
5183
- const placeholders = param.values.map(() => "?").join(", ");
5184
- return { sql: `EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value IN (${placeholders}))`, values: [...param.values] };
5204
+ return arrayContainsV2(col, param.values, dialect);
5185
5205
  }
5186
5206
  return buildOrFragmentV2(col, "=", param.values);
5187
5207
  }
5188
- function buildTokenColumnFragmentV2(impl, param) {
5208
+ function buildTokenColumnFragmentV2(impl, param, dialect) {
5189
5209
  const textCol = quoteColumn(`__${impl.columnName}Text`);
5190
5210
  const sortCol = quoteColumn(`__${impl.columnName}Sort`);
5191
5211
  if (param.modifier === "text") {
@@ -5197,11 +5217,14 @@ function buildTokenColumnFragmentV2(impl, param) {
5197
5217
  const vals = [];
5198
5218
  for (const value of param.values) {
5199
5219
  if (value.endsWith("|")) {
5200
- conds.push(`EXISTS (SELECT 1 FROM json_each(${textCol}) WHERE json_each.value LIKE ?)`);
5201
- vals.push(value + "%");
5220
+ const frag = arrayContainsLikeV2(textCol, value + "%", dialect);
5221
+ conds.push(frag.sql);
5222
+ vals.push(...frag.values);
5202
5223
  } else {
5203
- conds.push(`EXISTS (SELECT 1 FROM json_each(${textCol}) WHERE json_each.value = ?)`);
5204
- vals.push(value.startsWith("|") ? value.slice(1) : value);
5224
+ const resolved = value.startsWith("|") ? value.slice(1) : value;
5225
+ const frag = arrayContainsV2(textCol, [resolved], dialect);
5226
+ conds.push(frag.sql);
5227
+ vals.push(...frag.values);
5205
5228
  }
5206
5229
  }
5207
5230
  const inner = conds.length === 1 ? conds[0] : `(${conds.join(" OR ")})`;
@@ -5210,17 +5233,9 @@ function buildTokenColumnFragmentV2(impl, param) {
5210
5233
  }
5211
5234
  const resolvedValues = param.values.map((v) => v.startsWith("|") ? v.slice(1) : v);
5212
5235
  if (param.modifier === "not") {
5213
- if (resolvedValues.length === 1) {
5214
- return { sql: `NOT EXISTS (SELECT 1 FROM json_each(${textCol}) WHERE json_each.value = ?)`, values: [resolvedValues[0]] };
5215
- }
5216
- const placeholders2 = resolvedValues.map(() => "?").join(", ");
5217
- return { sql: `NOT EXISTS (SELECT 1 FROM json_each(${textCol}) WHERE json_each.value IN (${placeholders2}))`, values: [...resolvedValues] };
5236
+ return arrayNotContainsV2(textCol, resolvedValues, dialect);
5218
5237
  }
5219
- if (resolvedValues.length === 1) {
5220
- return { sql: `EXISTS (SELECT 1 FROM json_each(${textCol}) WHERE json_each.value = ?)`, values: [resolvedValues[0]] };
5221
- }
5222
- const placeholders = resolvedValues.map(() => "?").join(", ");
5223
- return { sql: `EXISTS (SELECT 1 FROM json_each(${textCol}) WHERE json_each.value IN (${placeholders}))`, values: [...resolvedValues] };
5238
+ return arrayContainsV2(textCol, resolvedValues, dialect);
5224
5239
  }
5225
5240
  function buildDefaultFragmentV2(impl, param) {
5226
5241
  const col = quoteColumn(impl.columnName);
@@ -5250,7 +5265,7 @@ function buildLikeFragmentV2(col, values, prefix, suffix) {
5250
5265
  }
5251
5266
  return { sql: `(${conds.join(" OR ")})`, values: vals };
5252
5267
  }
5253
- function buildChainedFragmentV2(param, chain, registry, sourceResourceType) {
5268
+ function buildChainedFragmentV2(param, chain, registry, sourceResourceType, dialect) {
5254
5269
  const targetImpl = resolveImplV2(
5255
5270
  { code: chain.targetParam, values: param.values, modifier: param.modifier, prefix: param.prefix },
5256
5271
  registry,
@@ -5263,7 +5278,7 @@ function buildChainedFragmentV2(param, chain, registry, sourceResourceType) {
5263
5278
  modifier: param.modifier,
5264
5279
  prefix: param.prefix
5265
5280
  };
5266
- const innerFragment = buildWhereFragmentV2(targetImpl, innerParam);
5281
+ const innerFragment = buildWhereFragmentV2(targetImpl, innerParam, dialect);
5267
5282
  if (!innerFragment) return null;
5268
5283
  const innerSql = rewriteColumnRefsForAlias(innerFragment.sql, "__target");
5269
5284
  const refTable = `${sourceResourceType}_References`;
@@ -5280,11 +5295,11 @@ function buildChainedFragmentV2(param, chain, registry, sourceResourceType) {
5280
5295
  ].join("\n");
5281
5296
  return { sql, values: [param.code, chain.targetType, ...innerFragment.values] };
5282
5297
  }
5283
- function buildWhereClauseV2(params, registry, resourceType) {
5298
+ function buildWhereClauseV2(params, registry, resourceType, dialect) {
5284
5299
  const fragments = [];
5285
5300
  for (const param of params) {
5286
5301
  if (param.chain) {
5287
- const fragment2 = buildChainedFragmentV2(param, param.chain, registry, resourceType);
5302
+ const fragment2 = buildChainedFragmentV2(param, param.chain, registry, resourceType, dialect);
5288
5303
  if (fragment2) {
5289
5304
  fragments.push(fragment2);
5290
5305
  }
@@ -5292,7 +5307,7 @@ function buildWhereClauseV2(params, registry, resourceType) {
5292
5307
  }
5293
5308
  const impl = resolveImplV2(param, registry, resourceType);
5294
5309
  if (!impl) continue;
5295
- const fragment = buildWhereFragmentV2(impl, param);
5310
+ const fragment = buildWhereFragmentV2(impl, param, dialect);
5296
5311
  if (fragment) {
5297
5312
  fragments.push(fragment);
5298
5313
  }
@@ -5346,7 +5361,7 @@ function quoteTable(name) {
5346
5361
  return `"${name}"`;
5347
5362
  }
5348
5363
  var SEARCH_COLUMNS_V2 = ['"id"', '"versionId"', '"content"', '"lastUpdated"', '"deleted"'].join(", ");
5349
- function buildSearchSQLv2(request, registry) {
5364
+ function buildSearchSQLv2(request, registry, dialect) {
5350
5365
  const tableName = quoteTable(request.resourceType);
5351
5366
  const parts = [];
5352
5367
  const allValues = [];
@@ -5355,11 +5370,15 @@ function buildSearchSQLv2(request, registry) {
5355
5370
  const whereConditions = [];
5356
5371
  whereConditions.push('"deleted" = 0');
5357
5372
  if (request.compartment) {
5358
- whereConditions.push('EXISTS (SELECT 1 FROM json_each("compartments") WHERE json_each.value = ?)');
5373
+ if (dialect?.name === "postgres") {
5374
+ whereConditions.push('"compartments" && ARRAY[?]::text[]');
5375
+ } else {
5376
+ whereConditions.push('EXISTS (SELECT 1 FROM json_each("compartments") WHERE json_each.value = ?)');
5377
+ }
5359
5378
  allValues.push(request.compartment.id);
5360
5379
  }
5361
5380
  if (request.params.length > 0) {
5362
- const whereFragment = buildWhereClauseV2(request.params, registry, request.resourceType);
5381
+ const whereFragment = buildWhereClauseV2(request.params, registry, request.resourceType, dialect);
5363
5382
  if (whereFragment) {
5364
5383
  whereConditions.push(whereFragment.sql);
5365
5384
  allValues.push(...whereFragment.values);
@@ -5377,7 +5396,7 @@ function buildSearchSQLv2(request, registry) {
5377
5396
  }
5378
5397
  return { sql: parts.join("\n"), values: allValues };
5379
5398
  }
5380
- function buildCountSQLv2(request, registry) {
5399
+ function buildCountSQLv2(request, registry, dialect) {
5381
5400
  const tableName = quoteTable(request.resourceType);
5382
5401
  const parts = [];
5383
5402
  const allValues = [];
@@ -5386,11 +5405,15 @@ function buildCountSQLv2(request, registry) {
5386
5405
  const whereConditions = [];
5387
5406
  whereConditions.push('"deleted" = 0');
5388
5407
  if (request.compartment) {
5389
- whereConditions.push('EXISTS (SELECT 1 FROM json_each("compartments") WHERE json_each.value = ?)');
5408
+ if (dialect?.name === "postgres") {
5409
+ whereConditions.push('"compartments" && ARRAY[?]::text[]');
5410
+ } else {
5411
+ whereConditions.push('EXISTS (SELECT 1 FROM json_each("compartments") WHERE json_each.value = ?)');
5412
+ }
5390
5413
  allValues.push(request.compartment.id);
5391
5414
  }
5392
5415
  if (request.params.length > 0) {
5393
- const whereFragment = buildWhereClauseV2(request.params, registry, request.resourceType);
5416
+ const whereFragment = buildWhereClauseV2(request.params, registry, request.resourceType, dialect);
5394
5417
  if (whereFragment) {
5395
5418
  whereConditions.push(whereFragment.sql);
5396
5419
  allValues.push(...whereFragment.values);
@@ -5412,7 +5435,7 @@ function buildOrderByV2(sort, registry, resourceType) {
5412
5435
  }
5413
5436
  return clauses.length > 0 ? clauses.join(", ") : '"lastUpdated" DESC';
5414
5437
  }
5415
- function buildTwoPhaseSearchSQLv2(request, registry) {
5438
+ function buildTwoPhaseSearchSQLv2(request, registry, dialect) {
5416
5439
  const tableName = quoteTable(request.resourceType);
5417
5440
  const p1Parts = [];
5418
5441
  const p1Values = [];
@@ -5421,11 +5444,15 @@ function buildTwoPhaseSearchSQLv2(request, registry) {
5421
5444
  const whereConditions = [];
5422
5445
  whereConditions.push('"deleted" = 0');
5423
5446
  if (request.compartment) {
5424
- whereConditions.push('EXISTS (SELECT 1 FROM json_each("compartments") WHERE json_each.value = ?)');
5447
+ if (dialect?.name === "postgres") {
5448
+ whereConditions.push('"compartments" && ARRAY[?]::text[]');
5449
+ } else {
5450
+ whereConditions.push('EXISTS (SELECT 1 FROM json_each("compartments") WHERE json_each.value = ?)');
5451
+ }
5425
5452
  p1Values.push(request.compartment.id);
5426
5453
  }
5427
5454
  if (request.params.length > 0) {
5428
- const whereFragment = buildWhereClauseV2(request.params, registry, request.resourceType);
5455
+ const whereFragment = buildWhereClauseV2(request.params, registry, request.resourceType, dialect);
5429
5456
  if (whereFragment) {
5430
5457
  whereConditions.push(whereFragment.sql);
5431
5458
  p1Values.push(...whereFragment.values);
@@ -5556,12 +5583,13 @@ function buildPaginationContext(baseUrl, resourceType, queryParams, count, offse
5556
5583
 
5557
5584
  // src/search/search-executor.ts
5558
5585
  async function executeSearchV2(adapter, request, registry, options) {
5559
- const searchSQL = buildSearchSQLv2(request, registry);
5586
+ const dialect = options?.dialect;
5587
+ const searchSQL = buildSearchSQLv2(request, registry, dialect);
5560
5588
  const rows = await adapter.query(searchSQL.sql, searchSQL.values);
5561
5589
  const resources = mapRowsToResourcesV2(rows);
5562
5590
  let total;
5563
5591
  if (options?.total === "accurate") {
5564
- const countSQL = buildCountSQLv2(request, registry);
5592
+ const countSQL = buildCountSQLv2(request, registry, dialect);
5565
5593
  const countRow = await adapter.queryOne(countSQL.sql, countSQL.values);
5566
5594
  total = countRow?.count ?? 0;
5567
5595
  }
@@ -5868,166 +5896,6 @@ function planSearch(request, registry, options) {
5868
5896
  };
5869
5897
  }
5870
5898
 
5871
- // src/db/sqlite-adapter.ts
5872
- import initSqlJs from "sql.js";
5873
- var sqlJsInitPromise = null;
5874
- async function getSqlJs() {
5875
- if (!sqlJsInitPromise) {
5876
- sqlJsInitPromise = initSqlJs();
5877
- }
5878
- return sqlJsInitPromise;
5879
- }
5880
- var SQLiteAdapter = class {
5881
- /**
5882
- * @param path - Database file path, or ':memory:' for in-memory database.
5883
- * @param data - Optional Uint8Array of an existing database file to load.
5884
- */
5885
- constructor(_path = ":memory:", data) {
5886
- this.data = data;
5887
- this.initPromise = this.initialize();
5888
- }
5889
- db = null;
5890
- initPromise;
5891
- async initialize() {
5892
- const SQL = await getSqlJs();
5893
- this.db = new SQL.Database(this.data);
5894
- this.db.run("PRAGMA journal_mode = WAL");
5895
- this.db.run("PRAGMA foreign_keys = ON");
5896
- }
5897
- async getDb() {
5898
- await this.initPromise;
5899
- if (!this.db) {
5900
- throw new Error("SQLiteAdapter: database is closed");
5901
- }
5902
- return this.db;
5903
- }
5904
- async execute(sql, params = []) {
5905
- const db = await this.getDb();
5906
- db.run(sql, params);
5907
- const result = db.exec("SELECT changes() as c");
5908
- const changes = result.length > 0 ? result[0].values[0][0] : 0;
5909
- return { changes };
5910
- }
5911
- async query(sql, params = []) {
5912
- const db = await this.getDb();
5913
- const stmt = db.prepare(sql);
5914
- stmt.bind(params);
5915
- const rows = [];
5916
- while (stmt.step()) {
5917
- rows.push(stmt.getAsObject());
5918
- }
5919
- stmt.free();
5920
- return rows;
5921
- }
5922
- async queryOne(sql, params = []) {
5923
- const db = await this.getDb();
5924
- const stmt = db.prepare(sql);
5925
- stmt.bind(params);
5926
- let result;
5927
- if (stmt.step()) {
5928
- result = stmt.getAsObject();
5929
- }
5930
- stmt.free();
5931
- return result;
5932
- }
5933
- async *queryStream(sql, params = []) {
5934
- const db = await this.getDb();
5935
- const stmt = db.prepare(sql);
5936
- stmt.bind(params);
5937
- try {
5938
- while (stmt.step()) {
5939
- yield stmt.getAsObject();
5940
- }
5941
- } finally {
5942
- stmt.free();
5943
- }
5944
- }
5945
- prepare(sql) {
5946
- if (!this.db) {
5947
- throw new Error("SQLiteAdapter: database not initialized. Await an operation first.");
5948
- }
5949
- const db = this.db;
5950
- const stmt = db.prepare(sql);
5951
- return {
5952
- query(params = []) {
5953
- stmt.bind(params);
5954
- const rows = [];
5955
- while (stmt.step()) {
5956
- rows.push(stmt.getAsObject());
5957
- }
5958
- stmt.reset();
5959
- return rows;
5960
- },
5961
- execute(params = []) {
5962
- stmt.bind(params);
5963
- stmt.step();
5964
- stmt.reset();
5965
- const changesResult = db.exec("SELECT changes() as c, last_insert_rowid() as r");
5966
- const changes = changesResult.length > 0 ? changesResult[0].values[0][0] : 0;
5967
- const lastInsertRowid = changesResult.length > 0 ? changesResult[0].values[0][1] : void 0;
5968
- return { changes, lastInsertRowid };
5969
- },
5970
- finalize() {
5971
- stmt.free();
5972
- }
5973
- };
5974
- }
5975
- async transaction(fn) {
5976
- const db = await this.getDb();
5977
- db.run("BEGIN IMMEDIATE");
5978
- const ctx = {
5979
- execute(sql, params = []) {
5980
- db.run(sql, params);
5981
- const result = db.exec("SELECT changes() as c");
5982
- const changes = result.length > 0 ? result[0].values[0][0] : 0;
5983
- return { changes };
5984
- },
5985
- query(sql, params = []) {
5986
- const stmt = db.prepare(sql);
5987
- stmt.bind(params);
5988
- const rows = [];
5989
- while (stmt.step()) {
5990
- rows.push(stmt.getAsObject());
5991
- }
5992
- stmt.free();
5993
- return rows;
5994
- },
5995
- queryOne(sql, params = []) {
5996
- const stmt = db.prepare(sql);
5997
- stmt.bind(params);
5998
- let result;
5999
- if (stmt.step()) {
6000
- result = stmt.getAsObject();
6001
- }
6002
- stmt.free();
6003
- return result;
6004
- }
6005
- };
6006
- try {
6007
- const result = await fn(ctx);
6008
- db.run("COMMIT");
6009
- return result;
6010
- } catch (err) {
6011
- db.run("ROLLBACK");
6012
- throw err;
6013
- }
6014
- }
6015
- async close() {
6016
- await this.initPromise;
6017
- if (this.db) {
6018
- this.db.close();
6019
- this.db = null;
6020
- }
6021
- }
6022
- /**
6023
- * Export the database as a Uint8Array (for file persistence).
6024
- */
6025
- async export() {
6026
- const db = await this.getDb();
6027
- return db.export();
6028
- }
6029
- };
6030
-
6031
5899
  // src/db/better-sqlite3-adapter.ts
6032
5900
  import Database from "better-sqlite3";
6033
5901
  var BetterSqlite3Adapter = class {
@@ -6105,14 +5973,14 @@ var BetterSqlite3Adapter = class {
6105
5973
  async transaction(fn) {
6106
5974
  const db = this.ensureOpen();
6107
5975
  const ctx = {
6108
- execute(sql, params = []) {
5976
+ async execute(sql, params = []) {
6109
5977
  const result = db.prepare(sql).run(...params);
6110
5978
  return { changes: result.changes };
6111
5979
  },
6112
- query(sql, params = []) {
5980
+ async query(sql, params = []) {
6113
5981
  return db.prepare(sql).all(...params);
6114
5982
  },
6115
- queryOne(sql, params = []) {
5983
+ async queryOne(sql, params = []) {
6116
5984
  return db.prepare(sql).get(...params);
6117
5985
  }
6118
5986
  };
@@ -6157,6 +6025,202 @@ var BetterSqlite3Adapter = class {
6157
6025
  }
6158
6026
  };
6159
6027
 
6028
+ // src/db/postgres-adapter.ts
6029
+ function rewritePlaceholders(sql) {
6030
+ let result = "";
6031
+ let paramIndex = 1;
6032
+ let inString = false;
6033
+ for (let i = 0; i < sql.length; i++) {
6034
+ const ch = sql[i];
6035
+ if (ch === "'" && !inString) {
6036
+ inString = true;
6037
+ result += ch;
6038
+ } else if (ch === "'" && inString) {
6039
+ if (i + 1 < sql.length && sql[i + 1] === "'") {
6040
+ result += "''";
6041
+ i++;
6042
+ } else {
6043
+ inString = false;
6044
+ result += ch;
6045
+ }
6046
+ } else if (ch === "?" && !inString) {
6047
+ result += `$${paramIndex}`;
6048
+ paramIndex++;
6049
+ } else {
6050
+ result += ch;
6051
+ }
6052
+ }
6053
+ return result;
6054
+ }
6055
+ var PostgresAdapter = class {
6056
+ pool;
6057
+ closed = false;
6058
+ constructor(pool) {
6059
+ this.pool = pool;
6060
+ }
6061
+ async execute(sql, params = []) {
6062
+ this.ensureNotClosed();
6063
+ const pgSql = rewritePlaceholders(sql);
6064
+ const result = await this.pool.query(pgSql, params);
6065
+ return { changes: result.rowCount ?? 0 };
6066
+ }
6067
+ async query(sql, params = []) {
6068
+ this.ensureNotClosed();
6069
+ const pgSql = rewritePlaceholders(sql);
6070
+ const result = await this.pool.query(pgSql, params);
6071
+ return result.rows;
6072
+ }
6073
+ async queryOne(sql, params = []) {
6074
+ this.ensureNotClosed();
6075
+ const pgSql = rewritePlaceholders(sql);
6076
+ const result = await this.pool.query(pgSql, params);
6077
+ return result.rows[0] ?? void 0;
6078
+ }
6079
+ async *queryStream(sql, params = []) {
6080
+ this.ensureNotClosed();
6081
+ const pgSql = rewritePlaceholders(sql);
6082
+ const result = await this.pool.query(pgSql, params);
6083
+ for (const row of result.rows) {
6084
+ yield row;
6085
+ }
6086
+ }
6087
+ prepare(_sql) {
6088
+ throw new Error("PostgresAdapter.prepare() is not supported. Use query() or execute() instead.");
6089
+ }
6090
+ async transaction(fn) {
6091
+ this.ensureNotClosed();
6092
+ const client = await this.pool.connect();
6093
+ try {
6094
+ await client.query("BEGIN");
6095
+ const ctx = {
6096
+ execute: async (sql, params = []) => {
6097
+ const pgSql = rewritePlaceholders(sql);
6098
+ const result2 = await client.query(pgSql, params);
6099
+ return { changes: result2.rowCount ?? 0 };
6100
+ },
6101
+ query: async (sql, params = []) => {
6102
+ const pgSql = rewritePlaceholders(sql);
6103
+ const result2 = await client.query(pgSql, params);
6104
+ return result2.rows;
6105
+ },
6106
+ queryOne: async (sql, params = []) => {
6107
+ const pgSql = rewritePlaceholders(sql);
6108
+ const result2 = await client.query(pgSql, params);
6109
+ return result2.rows[0] ?? void 0;
6110
+ }
6111
+ };
6112
+ const result = await fn(ctx);
6113
+ await client.query("COMMIT");
6114
+ return result;
6115
+ } catch (err) {
6116
+ await client.query("ROLLBACK");
6117
+ throw err;
6118
+ } finally {
6119
+ client.release();
6120
+ }
6121
+ }
6122
+ async close() {
6123
+ if (!this.closed) {
6124
+ this.closed = true;
6125
+ await this.pool.end();
6126
+ }
6127
+ }
6128
+ ensureNotClosed() {
6129
+ if (this.closed) {
6130
+ throw new Error("PostgresAdapter: pool is closed");
6131
+ }
6132
+ }
6133
+ };
6134
+
6135
+ // src/db/sqlite-dialect.ts
6136
+ var SQLiteDialect = class {
6137
+ name = "sqlite";
6138
+ placeholder(_index) {
6139
+ return "?";
6140
+ }
6141
+ textArrayContains(column, paramCount, _paramStartIndex) {
6142
+ const placeholders = Array.from({ length: paramCount }, () => "?").join(", ");
6143
+ return {
6144
+ sql: `EXISTS (SELECT 1 FROM json_each("${column}") WHERE value IN (${placeholders}))`,
6145
+ values: []
6146
+ // caller supplies values separately
6147
+ };
6148
+ }
6149
+ like(column, _paramIndex) {
6150
+ return `"${column}" LIKE ? ESCAPE '\\'`;
6151
+ }
6152
+ limitOffset(_paramStartIndex) {
6153
+ return { sql: "LIMIT ? OFFSET ?" };
6154
+ }
6155
+ arrayLiteral(values) {
6156
+ return JSON.stringify(values);
6157
+ }
6158
+ timestampType() {
6159
+ return "TEXT";
6160
+ }
6161
+ booleanType() {
6162
+ return "INTEGER";
6163
+ }
6164
+ textArrayType() {
6165
+ return "TEXT";
6166
+ }
6167
+ upsertSuffix(conflictColumn, updateColumns) {
6168
+ const sets = updateColumns.map((c) => `"${c}" = excluded."${c}"`).join(", ");
6169
+ return `ON CONFLICT("${conflictColumn}") DO UPDATE SET ${sets}`;
6170
+ }
6171
+ autoIncrementPK() {
6172
+ return "INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT";
6173
+ }
6174
+ };
6175
+
6176
+ // src/db/postgres-dialect.ts
6177
+ var PostgresDialect = class {
6178
+ name = "postgres";
6179
+ placeholder(index) {
6180
+ return `$${index}`;
6181
+ }
6182
+ textArrayContains(column, paramCount, paramStartIndex) {
6183
+ const placeholders = Array.from(
6184
+ { length: paramCount },
6185
+ (_, i) => `$${paramStartIndex + i}`
6186
+ ).join(", ");
6187
+ return {
6188
+ sql: `"${column}" && ARRAY[${placeholders}]::text[]`,
6189
+ values: []
6190
+ // caller supplies values separately
6191
+ };
6192
+ }
6193
+ like(column, paramIndex) {
6194
+ return `"${column}" LIKE $${paramIndex}`;
6195
+ }
6196
+ limitOffset(paramStartIndex) {
6197
+ return { sql: `LIMIT $${paramStartIndex} OFFSET $${paramStartIndex + 1}` };
6198
+ }
6199
+ arrayLiteral(values) {
6200
+ if (values.length === 0) {
6201
+ return "ARRAY[]::text[]";
6202
+ }
6203
+ const escaped = values.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
6204
+ return `ARRAY[${escaped}]::text[]`;
6205
+ }
6206
+ timestampType() {
6207
+ return "TIMESTAMPTZ";
6208
+ }
6209
+ booleanType() {
6210
+ return "BOOLEAN";
6211
+ }
6212
+ textArrayType() {
6213
+ return "TEXT[]";
6214
+ }
6215
+ upsertSuffix(conflictColumn, updateColumns) {
6216
+ const sets = updateColumns.map((c) => `"${c}" = excluded."${c}"`).join(", ");
6217
+ return `ON CONFLICT("${conflictColumn}") DO UPDATE SET ${sets}`;
6218
+ }
6219
+ autoIncrementPK() {
6220
+ return "INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY";
6221
+ }
6222
+ };
6223
+
6160
6224
  // src/store/fhir-store.ts
6161
6225
  import { randomUUID as randomUUID3 } from "node:crypto";
6162
6226
  var FhirStore = class {
@@ -6197,11 +6261,11 @@ var FhirStore = class {
6197
6261
  lastUpdated: now,
6198
6262
  deleted: 0
6199
6263
  };
6200
- await this.adapter.transaction((tx) => {
6264
+ await this.adapter.transaction(async (tx) => {
6201
6265
  const mainSQL = buildInsertMainSQLv2(resourceType, mainRow);
6202
- tx.execute(mainSQL.sql, mainSQL.values);
6266
+ await tx.execute(mainSQL.sql, mainSQL.values);
6203
6267
  const histSQL = buildInsertHistorySQLv2(`${resourceType}_History`, historyRow);
6204
- tx.execute(histSQL.sql, histSQL.values);
6268
+ await tx.execute(histSQL.sql, histSQL.values);
6205
6269
  });
6206
6270
  return persisted;
6207
6271
  }
@@ -6275,13 +6339,13 @@ var FhirStore = class {
6275
6339
  lastUpdated: now,
6276
6340
  deleted: 0
6277
6341
  };
6278
- await this.adapter.transaction((tx) => {
6342
+ await this.adapter.transaction(async (tx) => {
6279
6343
  const updateSQL = buildUpdateMainSQLv2(resourceType, mainRow);
6280
- tx.execute(updateSQL.sql, updateSQL.values);
6344
+ await tx.execute(updateSQL.sql, updateSQL.values);
6281
6345
  const histSQL = buildInsertHistorySQLv2(`${resourceType}_History`, historyRow);
6282
- tx.execute(histSQL.sql, histSQL.values);
6346
+ await tx.execute(histSQL.sql, histSQL.values);
6283
6347
  const delRefSQL = buildDeleteReferencesSQLv2(`${resourceType}_References`);
6284
- tx.execute(delRefSQL, [id]);
6348
+ await tx.execute(delRefSQL, [id]);
6285
6349
  });
6286
6350
  return persisted;
6287
6351
  }
@@ -6313,13 +6377,13 @@ var FhirStore = class {
6313
6377
  lastUpdated: now,
6314
6378
  deleted: 1
6315
6379
  };
6316
- await this.adapter.transaction((tx) => {
6380
+ await this.adapter.transaction(async (tx) => {
6317
6381
  const updateSQL = buildUpdateMainSQLv2(resourceType, deleteRow);
6318
- tx.execute(updateSQL.sql, updateSQL.values);
6382
+ await tx.execute(updateSQL.sql, updateSQL.values);
6319
6383
  const histSQL = buildInsertHistorySQLv2(`${resourceType}_History`, historyRow);
6320
- tx.execute(histSQL.sql, histSQL.values);
6384
+ await tx.execute(histSQL.sql, histSQL.values);
6321
6385
  const delRefSQL = buildDeleteReferencesSQLv2(`${resourceType}_References`);
6322
- tx.execute(delRefSQL, [id]);
6386
+ await tx.execute(delRefSQL, [id]);
6323
6387
  });
6324
6388
  }
6325
6389
  // ---------------------------------------------------------------------------
@@ -6762,11 +6826,11 @@ var FhirPersistence = class {
6762
6826
  lastUpdated: now,
6763
6827
  deleted: 0
6764
6828
  };
6765
- await this.adapter.transaction((tx) => {
6829
+ await this.adapter.transaction(async (tx) => {
6766
6830
  const mainSQL = buildInsertMainSQLv2(resourceType, mainRow);
6767
- tx.execute(mainSQL.sql, mainSQL.values);
6831
+ await tx.execute(mainSQL.sql, mainSQL.values);
6768
6832
  const histSQL = buildInsertHistorySQLv2(`${resourceType}_History`, historyRow);
6769
- tx.execute(histSQL.sql, histSQL.values);
6833
+ await tx.execute(histSQL.sql, histSQL.values);
6770
6834
  });
6771
6835
  return persisted;
6772
6836
  }
@@ -6843,11 +6907,11 @@ var FhirPersistence = class {
6843
6907
  lastUpdated: now,
6844
6908
  deleted: 0
6845
6909
  };
6846
- await this.adapter.transaction((tx) => {
6910
+ await this.adapter.transaction(async (tx) => {
6847
6911
  const updateSQL = buildUpdateMainSQLv2(resourceType, mainRow);
6848
- tx.execute(updateSQL.sql, updateSQL.values);
6912
+ await tx.execute(updateSQL.sql, updateSQL.values);
6849
6913
  const histSQL = buildInsertHistorySQLv2(`${resourceType}_History`, historyRow);
6850
- tx.execute(histSQL.sql, histSQL.values);
6914
+ await tx.execute(histSQL.sql, histSQL.values);
6851
6915
  });
6852
6916
  return persisted;
6853
6917
  }
@@ -6880,11 +6944,11 @@ var FhirPersistence = class {
6880
6944
  deleted: 1
6881
6945
  };
6882
6946
  await this.pipeline.deleteIndex(resourceType, id);
6883
- await this.adapter.transaction((tx) => {
6947
+ await this.adapter.transaction(async (tx) => {
6884
6948
  const updateSQL = buildUpdateMainSQLv2(resourceType, deleteRow);
6885
- tx.execute(updateSQL.sql, updateSQL.values);
6949
+ await tx.execute(updateSQL.sql, updateSQL.values);
6886
6950
  const histSQL = buildInsertHistorySQLv2(`${resourceType}_History`, historyRow);
6887
- tx.execute(histSQL.sql, histSQL.values);
6951
+ await tx.execute(histSQL.sql, histSQL.values);
6888
6952
  });
6889
6953
  }
6890
6954
  // ---------------------------------------------------------------------------
@@ -7550,22 +7614,22 @@ var MigrationRunnerV2 = class {
7550
7614
  return new Set(rows.map((r) => r.version));
7551
7615
  }
7552
7616
  async applyMigration(migration) {
7553
- await this.adapter.transaction((tx) => {
7617
+ await this.adapter.transaction(async (tx) => {
7554
7618
  for (const sql of migration.up) {
7555
- tx.execute(sql);
7619
+ await tx.execute(sql);
7556
7620
  }
7557
- tx.execute(
7621
+ await tx.execute(
7558
7622
  `INSERT INTO "${TRACKING_TABLE_V2}" ("version", "description", "type") VALUES (?, ?, ?)`,
7559
7623
  [migration.version, migration.description, migration.type]
7560
7624
  );
7561
7625
  });
7562
7626
  }
7563
7627
  async revertMigration(migration) {
7564
- await this.adapter.transaction((tx) => {
7628
+ await this.adapter.transaction(async (tx) => {
7565
7629
  for (const sql of migration.down) {
7566
- tx.execute(sql);
7630
+ await tx.execute(sql);
7567
7631
  }
7568
- tx.execute(
7632
+ await tx.execute(
7569
7633
  `DELETE FROM "${TRACKING_TABLE_V2}" WHERE "version" = ?`,
7570
7634
  [migration.version]
7571
7635
  );
@@ -7821,10 +7885,10 @@ var TerminologyCodeRepo = class {
7821
7885
  const chunkSize = 100;
7822
7886
  for (let i = 0; i < codes.length; i += chunkSize) {
7823
7887
  const chunk = codes.slice(i, i + chunkSize);
7824
- const result = await this.adapter.transaction((tx) => {
7888
+ const result = await this.adapter.transaction(async (tx) => {
7825
7889
  let count = 0;
7826
7890
  for (const c of chunk) {
7827
- const r = tx.execute(
7891
+ const r = await tx.execute(
7828
7892
  `INSERT OR IGNORE INTO "${CODES_TABLE}" ("system", "code", "display") VALUES (?, ?, ?)`,
7829
7893
  [c.system, c.code, c.display]
7830
7894
  );
@@ -8674,6 +8738,8 @@ export {
8674
8738
  PROJECT_ADMIN_RESOURCE_TYPES,
8675
8739
  PROTECTED_RESOURCE_TYPES,
8676
8740
  PackageRegistryRepo,
8741
+ PostgresAdapter,
8742
+ PostgresDialect,
8677
8743
  ReindexScheduler,
8678
8744
  RepositoryError,
8679
8745
  ResourceCacheV2,
@@ -8682,7 +8748,7 @@ export {
8682
8748
  ResourceVersionConflictError,
8683
8749
  SCHEMA_VERSION,
8684
8750
  SEARCH_PREFIXES,
8685
- SQLiteAdapter,
8751
+ SQLiteDialect,
8686
8752
  SearchLogger,
8687
8753
  SearchParameterRegistry,
8688
8754
  StructureDefinitionRegistry,