fhir-persistence 0.6.0 → 0.7.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.
@@ -8786,16 +8786,726 @@ var FhirRuntimeProvider = class {
8786
8786
  function createFhirRuntimeProvider(options) {
8787
8787
  return new FhirRuntimeProvider(options);
8788
8788
  }
8789
+
8790
+ // src/conformance/ig-resource-map-repo.ts
8791
+ var TABLE = "ig_resource_map";
8792
+ var CREATE_TABLE_DDL = `
8793
+ CREATE TABLE IF NOT EXISTS "${TABLE}" (
8794
+ "ig_id" TEXT NOT NULL,
8795
+ "resource_type" TEXT NOT NULL,
8796
+ "resource_id" TEXT NOT NULL,
8797
+ "resource_url" TEXT,
8798
+ "resource_name" TEXT,
8799
+ "base_type" TEXT,
8800
+ PRIMARY KEY ("ig_id", "resource_type", "resource_id")
8801
+ );
8802
+ `;
8803
+ var CREATE_INDEX_IG = `CREATE INDEX IF NOT EXISTS idx_ig_resource_map_ig ON "${TABLE}"("ig_id")`;
8804
+ var CREATE_INDEX_TYPE = `CREATE INDEX IF NOT EXISTS idx_ig_resource_map_type ON "${TABLE}"("ig_id", "resource_type")`;
8805
+ var IGResourceMapRepo = class {
8806
+ constructor(adapter, dialect = "sqlite") {
8807
+ this.adapter = adapter;
8808
+ this.dialect = dialect;
8809
+ }
8810
+ async ensureTable() {
8811
+ await this.adapter.execute(CREATE_TABLE_DDL);
8812
+ await this.adapter.execute(CREATE_INDEX_IG);
8813
+ await this.adapter.execute(CREATE_INDEX_TYPE);
8814
+ }
8815
+ /** Batch insert resource map entries for an IG. */
8816
+ async batchInsert(igId, entries) {
8817
+ await this.ensureTable();
8818
+ let count = 0;
8819
+ const sql = this.dialect === "postgres" ? `INSERT INTO "${TABLE}" ("ig_id", "resource_type", "resource_id", "resource_url", "resource_name", "base_type") VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT ("ig_id", "resource_type", "resource_id") DO UPDATE SET "resource_url" = EXCLUDED."resource_url", "resource_name" = EXCLUDED."resource_name", "base_type" = EXCLUDED."base_type"` : `INSERT OR REPLACE INTO "${TABLE}" ("ig_id", "resource_type", "resource_id", "resource_url", "resource_name", "base_type") VALUES (?, ?, ?, ?, ?, ?)`;
8820
+ for (const e of entries) {
8821
+ await this.adapter.execute(sql, [
8822
+ igId,
8823
+ e.resourceType,
8824
+ e.resourceId,
8825
+ e.resourceUrl ?? null,
8826
+ e.resourceName ?? null,
8827
+ e.baseType ?? null
8828
+ ]);
8829
+ count++;
8830
+ }
8831
+ return count;
8832
+ }
8833
+ /** Get grouped IG index. */
8834
+ async getIGIndex(igId) {
8835
+ await this.ensureTable();
8836
+ const rows = await this.adapter.query(
8837
+ `SELECT "ig_id", "resource_type", "resource_id", "resource_url", "resource_name", "base_type" FROM "${TABLE}" WHERE "ig_id" = ? ORDER BY "resource_type", "resource_id"`,
8838
+ [igId]
8839
+ );
8840
+ const index = {
8841
+ profiles: [],
8842
+ extensions: [],
8843
+ valueSets: [],
8844
+ codeSystems: [],
8845
+ searchParameters: []
8846
+ };
8847
+ for (const r of rows) {
8848
+ const entry = {
8849
+ igId: r.ig_id,
8850
+ resourceType: r.resource_type,
8851
+ resourceId: r.resource_id,
8852
+ resourceUrl: r.resource_url ?? void 0,
8853
+ resourceName: r.resource_name ?? void 0,
8854
+ baseType: r.base_type ?? void 0
8855
+ };
8856
+ switch (r.resource_type) {
8857
+ case "StructureDefinition":
8858
+ if (r.base_type === "Extension") {
8859
+ index.extensions.push(entry);
8860
+ } else {
8861
+ index.profiles.push(entry);
8862
+ }
8863
+ break;
8864
+ case "ValueSet":
8865
+ index.valueSets.push(entry);
8866
+ break;
8867
+ case "CodeSystem":
8868
+ index.codeSystems.push(entry);
8869
+ break;
8870
+ case "SearchParameter":
8871
+ index.searchParameters.push(entry);
8872
+ break;
8873
+ default:
8874
+ break;
8875
+ }
8876
+ }
8877
+ return index;
8878
+ }
8879
+ /** Get resources of a specific type within an IG. */
8880
+ async getByType(igId, resourceType) {
8881
+ await this.ensureTable();
8882
+ const rows = await this.adapter.query(
8883
+ `SELECT "ig_id", "resource_type", "resource_id", "resource_url", "resource_name", "base_type" FROM "${TABLE}" WHERE "ig_id" = ? AND "resource_type" = ? ORDER BY "resource_id"`,
8884
+ [igId, resourceType]
8885
+ );
8886
+ return rows.map((r) => ({
8887
+ igId: r.ig_id,
8888
+ resourceType: r.resource_type,
8889
+ resourceId: r.resource_id,
8890
+ resourceUrl: r.resource_url ?? void 0,
8891
+ resourceName: r.resource_name ?? void 0,
8892
+ baseType: r.base_type ?? void 0
8893
+ }));
8894
+ }
8895
+ /** Remove all resource mappings for an IG. */
8896
+ async removeIG(igId) {
8897
+ await this.ensureTable();
8898
+ await this.adapter.execute(`DELETE FROM "${TABLE}" WHERE "ig_id" = ?`, [igId]);
8899
+ }
8900
+ };
8901
+
8902
+ // src/conformance/sd-index-repo.ts
8903
+ var TABLE2 = "structure_definition_index";
8904
+ var CREATE_TABLE_DDL2 = `
8905
+ CREATE TABLE IF NOT EXISTS "${TABLE2}" (
8906
+ "id" TEXT PRIMARY KEY,
8907
+ "url" TEXT,
8908
+ "version" TEXT,
8909
+ "type" TEXT,
8910
+ "kind" TEXT,
8911
+ "base_definition" TEXT,
8912
+ "derivation" TEXT,
8913
+ "snapshot_hash" TEXT
8914
+ );
8915
+ `;
8916
+ var CREATE_INDEX_TYPE2 = `CREATE INDEX IF NOT EXISTS idx_sdi_type ON "${TABLE2}"("type")`;
8917
+ var CREATE_INDEX_KIND = `CREATE INDEX IF NOT EXISTS idx_sdi_kind ON "${TABLE2}"("kind")`;
8918
+ var CREATE_INDEX_BASE = `CREATE INDEX IF NOT EXISTS idx_sdi_base ON "${TABLE2}"("base_definition")`;
8919
+ var SDIndexRepo = class {
8920
+ constructor(adapter, dialect = "sqlite") {
8921
+ this.adapter = adapter;
8922
+ this.dialect = dialect;
8923
+ }
8924
+ async ensureTable() {
8925
+ await this.adapter.execute(CREATE_TABLE_DDL2);
8926
+ await this.adapter.execute(CREATE_INDEX_TYPE2);
8927
+ await this.adapter.execute(CREATE_INDEX_KIND);
8928
+ await this.adapter.execute(CREATE_INDEX_BASE);
8929
+ }
8930
+ async upsert(entry) {
8931
+ await this.ensureTable();
8932
+ const sql = this.dialect === "postgres" ? `INSERT INTO "${TABLE2}" ("id", "url", "version", "type", "kind", "base_definition", "derivation", "snapshot_hash") VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT ("id") DO UPDATE SET "url" = EXCLUDED."url", "version" = EXCLUDED."version", "type" = EXCLUDED."type", "kind" = EXCLUDED."kind", "base_definition" = EXCLUDED."base_definition", "derivation" = EXCLUDED."derivation", "snapshot_hash" = EXCLUDED."snapshot_hash"` : `INSERT OR REPLACE INTO "${TABLE2}" ("id", "url", "version", "type", "kind", "base_definition", "derivation", "snapshot_hash") VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
8933
+ await this.adapter.execute(sql, [
8934
+ entry.id,
8935
+ entry.url ?? null,
8936
+ entry.version ?? null,
8937
+ entry.type ?? null,
8938
+ entry.kind ?? null,
8939
+ entry.baseDefinition ?? null,
8940
+ entry.derivation ?? null,
8941
+ entry.snapshotHash ?? null
8942
+ ]);
8943
+ }
8944
+ async batchUpsert(entries) {
8945
+ let count = 0;
8946
+ for (const entry of entries) {
8947
+ await this.upsert(entry);
8948
+ count++;
8949
+ }
8950
+ return count;
8951
+ }
8952
+ async getById(id) {
8953
+ await this.ensureTable();
8954
+ const row = await this.adapter.queryOne(
8955
+ `SELECT "id", "url", "version", "type", "kind", "base_definition", "derivation", "snapshot_hash" FROM "${TABLE2}" WHERE "id" = ?`,
8956
+ [id]
8957
+ );
8958
+ return row ? this.mapRow(row) : void 0;
8959
+ }
8960
+ async getByUrl(url) {
8961
+ await this.ensureTable();
8962
+ const rows = await this.adapter.query(
8963
+ `SELECT "id", "url", "version", "type", "kind", "base_definition", "derivation", "snapshot_hash" FROM "${TABLE2}" WHERE "url" = ? ORDER BY "version"`,
8964
+ [url]
8965
+ );
8966
+ return rows.map((r) => this.mapRow(r));
8967
+ }
8968
+ async getByType(type) {
8969
+ await this.ensureTable();
8970
+ const rows = await this.adapter.query(
8971
+ `SELECT "id", "url", "version", "type", "kind", "base_definition", "derivation", "snapshot_hash" FROM "${TABLE2}" WHERE "type" = ? ORDER BY "id"`,
8972
+ [type]
8973
+ );
8974
+ return rows.map((r) => this.mapRow(r));
8975
+ }
8976
+ async getByBaseDefinition(baseUrl) {
8977
+ await this.ensureTable();
8978
+ const rows = await this.adapter.query(
8979
+ `SELECT "id", "url", "version", "type", "kind", "base_definition", "derivation", "snapshot_hash" FROM "${TABLE2}" WHERE "base_definition" = ? ORDER BY "id"`,
8980
+ [baseUrl]
8981
+ );
8982
+ return rows.map((r) => this.mapRow(r));
8983
+ }
8984
+ async remove(id) {
8985
+ await this.ensureTable();
8986
+ await this.adapter.execute(`DELETE FROM "${TABLE2}" WHERE "id" = ?`, [id]);
8987
+ }
8988
+ mapRow(r) {
8989
+ return {
8990
+ id: r.id,
8991
+ url: r.url ?? void 0,
8992
+ version: r.version ?? void 0,
8993
+ type: r.type ?? void 0,
8994
+ kind: r.kind ?? void 0,
8995
+ baseDefinition: r.base_definition ?? void 0,
8996
+ derivation: r.derivation ?? void 0,
8997
+ snapshotHash: r.snapshot_hash ?? void 0
8998
+ };
8999
+ }
9000
+ };
9001
+
9002
+ // src/conformance/element-index-repo.ts
9003
+ var TABLE3 = "structure_element_index";
9004
+ function createTableDDL(dialect) {
9005
+ if (dialect === "postgres") {
9006
+ return `
9007
+ CREATE TABLE IF NOT EXISTS "${TABLE3}" (
9008
+ "id" TEXT PRIMARY KEY,
9009
+ "structure_id" TEXT NOT NULL,
9010
+ "path" TEXT NOT NULL,
9011
+ "min" INTEGER,
9012
+ "max" TEXT,
9013
+ "type_codes" JSONB,
9014
+ "is_slice" BOOLEAN DEFAULT FALSE,
9015
+ "slice_name" TEXT,
9016
+ "is_extension" BOOLEAN DEFAULT FALSE,
9017
+ "binding_value_set" TEXT,
9018
+ "must_support" BOOLEAN DEFAULT FALSE
9019
+ );
9020
+ `;
9021
+ }
9022
+ return `
9023
+ CREATE TABLE IF NOT EXISTS "${TABLE3}" (
9024
+ "id" TEXT PRIMARY KEY,
9025
+ "structure_id" TEXT NOT NULL,
9026
+ "path" TEXT NOT NULL,
9027
+ "min" INTEGER,
9028
+ "max" TEXT,
9029
+ "type_codes" TEXT,
9030
+ "is_slice" INTEGER DEFAULT 0,
9031
+ "slice_name" TEXT,
9032
+ "is_extension" INTEGER DEFAULT 0,
9033
+ "binding_value_set" TEXT,
9034
+ "must_support" INTEGER DEFAULT 0
9035
+ );
9036
+ `;
9037
+ }
9038
+ var CREATE_INDEX_STRUCTURE = `CREATE INDEX IF NOT EXISTS idx_sei_structure ON "${TABLE3}"("structure_id")`;
9039
+ var CREATE_INDEX_PATH = `CREATE INDEX IF NOT EXISTS idx_sei_path ON "${TABLE3}"("path")`;
9040
+ var CREATE_INDEX_SLICE = `CREATE INDEX IF NOT EXISTS idx_sei_slice ON "${TABLE3}"("structure_id", "is_slice")`;
9041
+ var ElementIndexRepo = class {
9042
+ constructor(adapter, dialect = "sqlite") {
9043
+ this.adapter = adapter;
9044
+ this.dialect = dialect;
9045
+ }
9046
+ async ensureTable() {
9047
+ await this.adapter.execute(createTableDDL(this.dialect));
9048
+ await this.adapter.execute(CREATE_INDEX_STRUCTURE);
9049
+ await this.adapter.execute(CREATE_INDEX_PATH);
9050
+ await this.adapter.execute(CREATE_INDEX_SLICE);
9051
+ }
9052
+ /** Batch insert element index entries for a StructureDefinition. */
9053
+ async batchInsert(structureId, elements) {
9054
+ await this.ensureTable();
9055
+ let count = 0;
9056
+ const sql = this.dialect === "postgres" ? `INSERT INTO "${TABLE3}" ("id", "structure_id", "path", "min", "max", "type_codes", "is_slice", "slice_name", "is_extension", "binding_value_set", "must_support") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT ("id") DO UPDATE SET "path" = EXCLUDED."path", "min" = EXCLUDED."min", "max" = EXCLUDED."max", "type_codes" = EXCLUDED."type_codes", "is_slice" = EXCLUDED."is_slice", "slice_name" = EXCLUDED."slice_name", "is_extension" = EXCLUDED."is_extension", "binding_value_set" = EXCLUDED."binding_value_set", "must_support" = EXCLUDED."must_support"` : `INSERT OR REPLACE INTO "${TABLE3}" ("id", "structure_id", "path", "min", "max", "type_codes", "is_slice", "slice_name", "is_extension", "binding_value_set", "must_support") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
9057
+ for (const e of elements) {
9058
+ const typeCodes = e.typeCodes ? this.dialect === "postgres" ? JSON.stringify(e.typeCodes) : JSON.stringify(e.typeCodes) : null;
9059
+ const isSlice = this.dialect === "postgres" ? e.isSlice ?? false : e.isSlice ? 1 : 0;
9060
+ const isExtension = this.dialect === "postgres" ? e.isExtension ?? false : e.isExtension ? 1 : 0;
9061
+ const mustSupport = this.dialect === "postgres" ? e.mustSupport ?? false : e.mustSupport ? 1 : 0;
9062
+ await this.adapter.execute(sql, [
9063
+ e.id,
9064
+ structureId,
9065
+ e.path,
9066
+ e.min ?? null,
9067
+ e.max ?? null,
9068
+ typeCodes,
9069
+ isSlice,
9070
+ e.sliceName ?? null,
9071
+ isExtension,
9072
+ e.bindingValueSet ?? null,
9073
+ mustSupport
9074
+ ]);
9075
+ count++;
9076
+ }
9077
+ return count;
9078
+ }
9079
+ /** Get all elements for a StructureDefinition. */
9080
+ async getByStructureId(structureId) {
9081
+ await this.ensureTable();
9082
+ const rows = await this.adapter.query(
9083
+ `SELECT "id", "structure_id", "path", "min", "max", "type_codes", "is_slice", "slice_name", "is_extension", "binding_value_set", "must_support" FROM "${TABLE3}" WHERE "structure_id" = ? ORDER BY "id"`,
9084
+ [structureId]
9085
+ );
9086
+ return rows.map((r) => this.mapRow(r));
9087
+ }
9088
+ /** Search elements by path pattern (LIKE). */
9089
+ async searchByPath(pathPattern) {
9090
+ await this.ensureTable();
9091
+ const rows = await this.adapter.query(
9092
+ `SELECT "id", "structure_id", "path", "min", "max", "type_codes", "is_slice", "slice_name", "is_extension", "binding_value_set", "must_support" FROM "${TABLE3}" WHERE "path" LIKE ? ORDER BY "structure_id", "path"`,
9093
+ [pathPattern]
9094
+ );
9095
+ return rows.map((r) => this.mapRow(r));
9096
+ }
9097
+ /** Remove all element indexes for a StructureDefinition. */
9098
+ async removeByStructureId(structureId) {
9099
+ await this.ensureTable();
9100
+ await this.adapter.execute(`DELETE FROM "${TABLE3}" WHERE "structure_id" = ?`, [structureId]);
9101
+ }
9102
+ mapRow(r) {
9103
+ const typeCodes = r.type_codes ? typeof r.type_codes === "string" ? JSON.parse(r.type_codes) : r.type_codes : void 0;
9104
+ return {
9105
+ id: r.id,
9106
+ structureId: r.structure_id,
9107
+ path: r.path,
9108
+ min: r.min != null ? Number(r.min) : void 0,
9109
+ max: r.max ?? void 0,
9110
+ typeCodes,
9111
+ isSlice: Boolean(r.is_slice),
9112
+ sliceName: r.slice_name ?? void 0,
9113
+ isExtension: Boolean(r.is_extension),
9114
+ bindingValueSet: r.binding_value_set ?? void 0,
9115
+ mustSupport: Boolean(r.must_support)
9116
+ };
9117
+ }
9118
+ };
9119
+
9120
+ // src/conformance/expansion-cache-repo.ts
9121
+ var TABLE4 = "value_set_expansion";
9122
+ function createTableDDL2(dialect) {
9123
+ const ts = dialect === "postgres" ? "TIMESTAMPTZ DEFAULT NOW()" : "TEXT DEFAULT (datetime('now'))";
9124
+ const jsonType = dialect === "postgres" ? "JSONB NOT NULL" : "TEXT NOT NULL";
9125
+ return `
9126
+ CREATE TABLE IF NOT EXISTS "${TABLE4}" (
9127
+ "valueset_url" TEXT NOT NULL,
9128
+ "version" TEXT NOT NULL DEFAULT '',
9129
+ "expanded_at" ${ts},
9130
+ "code_count" INTEGER,
9131
+ "expansion_json" ${jsonType},
9132
+ PRIMARY KEY ("valueset_url", "version")
9133
+ );
9134
+ `;
9135
+ }
9136
+ var ExpansionCacheRepo = class {
9137
+ constructor(adapter, dialect = "sqlite") {
9138
+ this.adapter = adapter;
9139
+ this.dialect = dialect;
9140
+ }
9141
+ async ensureTable() {
9142
+ await this.adapter.execute(createTableDDL2(this.dialect));
9143
+ }
9144
+ /** Write or update an expansion cache entry. */
9145
+ async upsert(url, version, expansionJson, codeCount) {
9146
+ await this.ensureTable();
9147
+ const sql = this.dialect === "postgres" ? `INSERT INTO "${TABLE4}" ("valueset_url", "version", "expansion_json", "code_count") VALUES (?, ?, ?, ?) ON CONFLICT ("valueset_url", "version") DO UPDATE SET "expansion_json" = EXCLUDED."expansion_json", "code_count" = EXCLUDED."code_count", "expanded_at" = NOW()` : `INSERT OR REPLACE INTO "${TABLE4}" ("valueset_url", "version", "expansion_json", "code_count") VALUES (?, ?, ?, ?)`;
9148
+ await this.adapter.execute(sql, [url, version, expansionJson, codeCount]);
9149
+ }
9150
+ /** Get a cached expansion by URL and version. */
9151
+ async get(url, version) {
9152
+ await this.ensureTable();
9153
+ const row = await this.adapter.queryOne(
9154
+ `SELECT "valueset_url", "version", "expanded_at", "code_count", "expansion_json" FROM "${TABLE4}" WHERE "valueset_url" = ? AND "version" = ?`,
9155
+ [url, version]
9156
+ );
9157
+ if (!row) return void 0;
9158
+ return {
9159
+ valuesetUrl: row.valueset_url,
9160
+ version: row.version,
9161
+ expandedAt: row.expanded_at,
9162
+ codeCount: row.code_count,
9163
+ expansionJson: row.expansion_json
9164
+ };
9165
+ }
9166
+ /** Invalidate a specific expansion cache entry. */
9167
+ async invalidate(url, version) {
9168
+ await this.ensureTable();
9169
+ await this.adapter.execute(
9170
+ `DELETE FROM "${TABLE4}" WHERE "valueset_url" = ? AND "version" = ?`,
9171
+ [url, version]
9172
+ );
9173
+ }
9174
+ /** Clear all expansion caches. */
9175
+ async clear() {
9176
+ await this.ensureTable();
9177
+ await this.adapter.execute(`DELETE FROM "${TABLE4}"`);
9178
+ }
9179
+ };
9180
+
9181
+ // src/conformance/concept-hierarchy-repo.ts
9182
+ var TABLE5 = "code_system_concept";
9183
+ var CREATE_TABLE_DDL3 = `
9184
+ CREATE TABLE IF NOT EXISTS "${TABLE5}" (
9185
+ "id" TEXT PRIMARY KEY,
9186
+ "code_system_url" TEXT NOT NULL,
9187
+ "code_system_version" TEXT,
9188
+ "code" TEXT NOT NULL,
9189
+ "display" TEXT,
9190
+ "parent_code" TEXT,
9191
+ "level" INTEGER DEFAULT 0
9192
+ );
9193
+ `;
9194
+ var CREATE_INDEX_URL = `CREATE INDEX IF NOT EXISTS idx_csc_url ON "${TABLE5}"("code_system_url")`;
9195
+ var CREATE_INDEX_CODE = `CREATE INDEX IF NOT EXISTS idx_csc_code ON "${TABLE5}"("code_system_url", "code")`;
9196
+ var CREATE_INDEX_PARENT = `CREATE INDEX IF NOT EXISTS idx_csc_parent ON "${TABLE5}"("code_system_url", "parent_code")`;
9197
+ var ConceptHierarchyRepo = class {
9198
+ constructor(adapter, dialect = "sqlite") {
9199
+ this.adapter = adapter;
9200
+ this.dialect = dialect;
9201
+ }
9202
+ async ensureTable() {
9203
+ await this.adapter.execute(CREATE_TABLE_DDL3);
9204
+ await this.adapter.execute(CREATE_INDEX_URL);
9205
+ await this.adapter.execute(CREATE_INDEX_CODE);
9206
+ await this.adapter.execute(CREATE_INDEX_PARENT);
9207
+ }
9208
+ /** Batch insert hierarchical concept entries. */
9209
+ async batchInsert(entries) {
9210
+ await this.ensureTable();
9211
+ let count = 0;
9212
+ const sql = this.dialect === "postgres" ? `INSERT INTO "${TABLE5}" ("id", "code_system_url", "code_system_version", "code", "display", "parent_code", "level") VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT ("id") DO UPDATE SET "display" = EXCLUDED."display", "parent_code" = EXCLUDED."parent_code", "level" = EXCLUDED."level"` : `INSERT OR REPLACE INTO "${TABLE5}" ("id", "code_system_url", "code_system_version", "code", "display", "parent_code", "level") VALUES (?, ?, ?, ?, ?, ?, ?)`;
9213
+ for (const e of entries) {
9214
+ await this.adapter.execute(sql, [
9215
+ e.id,
9216
+ e.codeSystemUrl,
9217
+ e.codeSystemVersion ?? null,
9218
+ e.code,
9219
+ e.display ?? null,
9220
+ e.parentCode ?? null,
9221
+ e.level
9222
+ ]);
9223
+ count++;
9224
+ }
9225
+ return count;
9226
+ }
9227
+ /** Get all concepts for a CodeSystem (tree order by level). */
9228
+ async getTree(codeSystemUrl) {
9229
+ await this.ensureTable();
9230
+ const rows = await this.adapter.query(
9231
+ `SELECT "id", "code_system_url", "code_system_version", "code", "display", "parent_code", "level" FROM "${TABLE5}" WHERE "code_system_url" = ? ORDER BY "level", "code"`,
9232
+ [codeSystemUrl]
9233
+ );
9234
+ return rows.map((r) => this.mapRow(r));
9235
+ }
9236
+ /** Get direct children of a concept. */
9237
+ async getChildren(codeSystemUrl, parentCode) {
9238
+ await this.ensureTable();
9239
+ const rows = await this.adapter.query(
9240
+ `SELECT "id", "code_system_url", "code_system_version", "code", "display", "parent_code", "level" FROM "${TABLE5}" WHERE "code_system_url" = ? AND "parent_code" = ? ORDER BY "code"`,
9241
+ [codeSystemUrl, parentCode]
9242
+ );
9243
+ return rows.map((r) => this.mapRow(r));
9244
+ }
9245
+ /** Lookup a single concept by code. */
9246
+ async lookup(codeSystemUrl, code) {
9247
+ await this.ensureTable();
9248
+ const row = await this.adapter.queryOne(
9249
+ `SELECT "id", "code_system_url", "code_system_version", "code", "display", "parent_code", "level" FROM "${TABLE5}" WHERE "code_system_url" = ? AND "code" = ?`,
9250
+ [codeSystemUrl, code]
9251
+ );
9252
+ return row ? this.mapRow(row) : void 0;
9253
+ }
9254
+ /** Remove all concepts for a CodeSystem. */
9255
+ async removeByCodeSystem(codeSystemUrl) {
9256
+ await this.ensureTable();
9257
+ await this.adapter.execute(`DELETE FROM "${TABLE5}" WHERE "code_system_url" = ?`, [codeSystemUrl]);
9258
+ }
9259
+ mapRow(r) {
9260
+ return {
9261
+ id: r.id,
9262
+ codeSystemUrl: r.code_system_url,
9263
+ codeSystemVersion: r.code_system_version ?? void 0,
9264
+ code: r.code,
9265
+ display: r.display ?? void 0,
9266
+ parentCode: r.parent_code ?? void 0,
9267
+ level: Number(r.level)
9268
+ };
9269
+ }
9270
+ };
9271
+
9272
+ // src/conformance/search-param-index-repo.ts
9273
+ var TABLE6 = "search_parameter_index";
9274
+ function createTableDDL3(dialect) {
9275
+ const baseType = dialect === "postgres" ? "JSONB" : "TEXT";
9276
+ return `
9277
+ CREATE TABLE IF NOT EXISTS "${TABLE6}" (
9278
+ "id" TEXT PRIMARY KEY,
9279
+ "ig_id" TEXT NOT NULL,
9280
+ "url" TEXT,
9281
+ "code" TEXT NOT NULL,
9282
+ "type" TEXT NOT NULL,
9283
+ "base" ${baseType},
9284
+ "expression" TEXT
9285
+ );
9286
+ `;
9287
+ }
9288
+ var CREATE_INDEX_IG2 = `CREATE INDEX IF NOT EXISTS idx_spi_ig ON "${TABLE6}"("ig_id")`;
9289
+ var CREATE_INDEX_CODE2 = `CREATE INDEX IF NOT EXISTS idx_spi_code ON "${TABLE6}"("code")`;
9290
+ var SearchParamIndexRepo = class {
9291
+ constructor(adapter, dialect = "sqlite") {
9292
+ this.adapter = adapter;
9293
+ this.dialect = dialect;
9294
+ }
9295
+ async ensureTable() {
9296
+ await this.adapter.execute(createTableDDL3(this.dialect));
9297
+ await this.adapter.execute(CREATE_INDEX_IG2);
9298
+ await this.adapter.execute(CREATE_INDEX_CODE2);
9299
+ }
9300
+ async upsert(entry) {
9301
+ await this.ensureTable();
9302
+ const baseJson = JSON.stringify(entry.base);
9303
+ const sql = this.dialect === "postgres" ? `INSERT INTO "${TABLE6}" ("id", "ig_id", "url", "code", "type", "base", "expression") VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT ("id") DO UPDATE SET "url" = EXCLUDED."url", "code" = EXCLUDED."code", "type" = EXCLUDED."type", "base" = EXCLUDED."base", "expression" = EXCLUDED."expression"` : `INSERT OR REPLACE INTO "${TABLE6}" ("id", "ig_id", "url", "code", "type", "base", "expression") VALUES (?, ?, ?, ?, ?, ?, ?)`;
9304
+ await this.adapter.execute(sql, [
9305
+ entry.id,
9306
+ entry.igId,
9307
+ entry.url ?? null,
9308
+ entry.code,
9309
+ entry.type,
9310
+ baseJson,
9311
+ entry.expression ?? null
9312
+ ]);
9313
+ }
9314
+ async batchUpsert(entries) {
9315
+ let count = 0;
9316
+ for (const entry of entries) {
9317
+ await this.upsert(entry);
9318
+ count++;
9319
+ }
9320
+ return count;
9321
+ }
9322
+ async getByIG(igId) {
9323
+ await this.ensureTable();
9324
+ const rows = await this.adapter.query(
9325
+ `SELECT "id", "ig_id", "url", "code", "type", "base", "expression" FROM "${TABLE6}" WHERE "ig_id" = ? ORDER BY "code"`,
9326
+ [igId]
9327
+ );
9328
+ return rows.map((r) => this.mapRow(r));
9329
+ }
9330
+ async getByCode(code) {
9331
+ await this.ensureTable();
9332
+ const rows = await this.adapter.query(
9333
+ `SELECT "id", "ig_id", "url", "code", "type", "base", "expression" FROM "${TABLE6}" WHERE "code" = ? ORDER BY "ig_id"`,
9334
+ [code]
9335
+ );
9336
+ return rows.map((r) => this.mapRow(r));
9337
+ }
9338
+ async remove(id) {
9339
+ await this.ensureTable();
9340
+ await this.adapter.execute(`DELETE FROM "${TABLE6}" WHERE "id" = ?`, [id]);
9341
+ }
9342
+ async removeByIG(igId) {
9343
+ await this.ensureTable();
9344
+ await this.adapter.execute(`DELETE FROM "${TABLE6}" WHERE "ig_id" = ?`, [igId]);
9345
+ }
9346
+ mapRow(r) {
9347
+ const base = r.base ? typeof r.base === "string" ? JSON.parse(r.base) : r.base : [];
9348
+ return {
9349
+ id: r.id,
9350
+ igId: r.ig_id,
9351
+ url: r.url ?? void 0,
9352
+ code: r.code,
9353
+ type: r.type,
9354
+ base,
9355
+ expression: r.expression ?? void 0
9356
+ };
9357
+ }
9358
+ };
9359
+
9360
+ // src/conformance/ig-import-orchestrator.ts
9361
+ var IGImportOrchestrator = class {
9362
+ resourceMapRepo;
9363
+ sdIndexRepo;
9364
+ elementIndexRepo;
9365
+ expansionCacheRepo;
9366
+ conceptRepo;
9367
+ spIndexRepo;
9368
+ opts;
9369
+ constructor(adapter, dialect = "sqlite", options) {
9370
+ this.resourceMapRepo = new IGResourceMapRepo(adapter, dialect);
9371
+ this.sdIndexRepo = new SDIndexRepo(adapter, dialect);
9372
+ this.elementIndexRepo = new ElementIndexRepo(adapter, dialect);
9373
+ this.expansionCacheRepo = new ExpansionCacheRepo(adapter, dialect);
9374
+ this.conceptRepo = new ConceptHierarchyRepo(adapter, dialect);
9375
+ this.spIndexRepo = new SearchParamIndexRepo(adapter, dialect);
9376
+ this.opts = options ?? {};
9377
+ }
9378
+ /** Ensure all conformance tables exist. */
9379
+ async ensureAllTables() {
9380
+ await this.resourceMapRepo.ensureTable();
9381
+ await this.sdIndexRepo.ensureTable();
9382
+ await this.elementIndexRepo.ensureTable();
9383
+ await this.expansionCacheRepo.ensureTable();
9384
+ await this.conceptRepo.ensureTable();
9385
+ await this.spIndexRepo.ensureTable();
9386
+ }
9387
+ /** Execute a complete IG import from a FHIR Bundle. */
9388
+ async importIG(igId, bundle) {
9389
+ await this.ensureAllTables();
9390
+ const result = {
9391
+ igId,
9392
+ resourceCount: 0,
9393
+ sdIndexCount: 0,
9394
+ elementIndexCount: 0,
9395
+ conceptCount: 0,
9396
+ spIndexCount: 0,
9397
+ errors: []
9398
+ };
9399
+ const entries = bundle.entry ?? [];
9400
+ const resourceMapEntries = [];
9401
+ const structureDefs = [];
9402
+ const codeSystems = [];
9403
+ const searchParams = [];
9404
+ for (const entry of entries) {
9405
+ const resource = entry.resource;
9406
+ if (!resource || !resource.resourceType || !resource.id) continue;
9407
+ const resourceType = resource.resourceType;
9408
+ const resourceId = resource.id;
9409
+ const mapEntry = {
9410
+ resourceType,
9411
+ resourceId,
9412
+ resourceUrl: resource.url ?? void 0,
9413
+ resourceName: resource.name ?? void 0
9414
+ };
9415
+ if (resourceType === "StructureDefinition") {
9416
+ mapEntry.baseType = resource.type ?? void 0;
9417
+ structureDefs.push(resource);
9418
+ } else if (resourceType === "CodeSystem") {
9419
+ codeSystems.push(resource);
9420
+ } else if (resourceType === "SearchParameter") {
9421
+ searchParams.push(resource);
9422
+ }
9423
+ resourceMapEntries.push(mapEntry);
9424
+ }
9425
+ try {
9426
+ result.resourceCount = await this.resourceMapRepo.batchInsert(igId, resourceMapEntries);
9427
+ } catch (err) {
9428
+ result.errors.push(`Resource map insert failed: ${String(err)}`);
9429
+ }
9430
+ for (const sd of structureDefs) {
9431
+ try {
9432
+ const sdEntry = {
9433
+ id: sd.id,
9434
+ url: sd.url ?? void 0,
9435
+ version: sd.version ?? void 0,
9436
+ type: sd.type ?? void 0,
9437
+ kind: sd.kind ?? void 0,
9438
+ baseDefinition: sd.baseDefinition ?? void 0,
9439
+ derivation: sd.derivation ?? void 0
9440
+ };
9441
+ await this.sdIndexRepo.upsert(sdEntry);
9442
+ result.sdIndexCount++;
9443
+ if (this.opts.extractElementIndex) {
9444
+ const elements = this.opts.extractElementIndex(sd);
9445
+ const count = await this.elementIndexRepo.batchInsert(sd.id, elements);
9446
+ result.elementIndexCount += count;
9447
+ }
9448
+ } catch (err) {
9449
+ result.errors.push(`SD processing failed for ${sd.id}: ${String(err)}`);
9450
+ }
9451
+ }
9452
+ for (const cs of codeSystems) {
9453
+ try {
9454
+ if (this.opts.flattenConcepts) {
9455
+ const concepts = this.opts.flattenConcepts(cs);
9456
+ const count = await this.conceptRepo.batchInsert(concepts);
9457
+ result.conceptCount += count;
9458
+ }
9459
+ } catch (err) {
9460
+ result.errors.push(`CodeSystem processing failed for ${cs.id}: ${String(err)}`);
9461
+ }
9462
+ }
9463
+ for (const sp of searchParams) {
9464
+ try {
9465
+ const spEntry = {
9466
+ id: sp.id,
9467
+ igId,
9468
+ url: sp.url ?? void 0,
9469
+ code: sp.code ?? "",
9470
+ type: sp.type ?? "",
9471
+ base: Array.isArray(sp.base) ? sp.base : [],
9472
+ expression: sp.expression ?? void 0
9473
+ };
9474
+ await this.spIndexRepo.upsert(spEntry);
9475
+ result.spIndexCount++;
9476
+ } catch (err) {
9477
+ result.errors.push(`SearchParameter processing failed for ${sp.id}: ${String(err)}`);
9478
+ }
9479
+ }
9480
+ return result;
9481
+ }
9482
+ /** Get individual repos for direct access. */
9483
+ get repos() {
9484
+ return {
9485
+ resourceMap: this.resourceMapRepo,
9486
+ sdIndex: this.sdIndexRepo,
9487
+ elementIndex: this.elementIndexRepo,
9488
+ expansionCache: this.expansionCacheRepo,
9489
+ conceptHierarchy: this.conceptRepo,
9490
+ searchParamIndex: this.spIndexRepo
9491
+ };
9492
+ }
9493
+ };
8789
9494
  export {
8790
9495
  BetterSqlite3Adapter,
9496
+ ConceptHierarchyRepo,
8791
9497
  DEFAULT_SEARCH_COUNT,
8792
9498
  DELETED_SCHEMA_VERSION,
9499
+ ElementIndexRepo,
9500
+ ExpansionCacheRepo,
8793
9501
  FhirDefinitionBridge,
8794
9502
  FhirPersistence,
8795
9503
  FhirRuntimeProvider,
8796
9504
  FhirStore,
8797
9505
  FhirSystem,
9506
+ IGImportOrchestrator,
8798
9507
  IGPersistenceManager,
9508
+ IGResourceMapRepo,
8799
9509
  IndexingPipeline,
8800
9510
  LookupTableWriter,
8801
9511
  MAX_SEARCH_COUNT,
@@ -8817,9 +9527,11 @@ export {
8817
9527
  ResourceNotFoundError,
8818
9528
  ResourceVersionConflictError,
8819
9529
  SCHEMA_VERSION,
9530
+ SDIndexRepo,
8820
9531
  SEARCH_PREFIXES,
8821
9532
  SQLiteDialect,
8822
9533
  SearchLogger,
9534
+ SearchParamIndexRepo,
8823
9535
  SearchParameterRegistry,
8824
9536
  StructureDefinitionRegistry,
8825
9537
  TerminologyCodeRepo,