phenoml 17.1.0 → 17.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.
Files changed (41) hide show
  1. package/dist/cjs/BaseClient.js +2 -2
  2. package/dist/cjs/api/resources/fhir2Omop/client/Client.d.ts +20 -13
  3. package/dist/cjs/api/resources/fhir2Omop/client/Client.js +22 -13
  4. package/dist/cjs/api/resources/fhir2Omop/errors/ServiceUnavailableError.d.ts +5 -0
  5. package/dist/cjs/api/resources/fhir2Omop/errors/ServiceUnavailableError.js +54 -0
  6. package/dist/cjs/api/resources/fhir2Omop/errors/index.d.ts +1 -0
  7. package/dist/cjs/api/resources/fhir2Omop/errors/index.js +1 -0
  8. package/dist/cjs/api/resources/fhir2Omop/types/CreateOmopResponse.d.ts +11 -5
  9. package/dist/cjs/api/resources/fhir2Omop/types/MappingEntry.d.ts +37 -0
  10. package/dist/cjs/api/resources/fhir2Omop/types/Summary.d.ts +15 -0
  11. package/dist/cjs/api/resources/fhir2Omop/types/index.d.ts +2 -2
  12. package/dist/cjs/api/resources/fhir2Omop/types/index.js +2 -2
  13. package/dist/cjs/core/fetcher/signals.js +9 -1
  14. package/dist/cjs/version.d.ts +1 -1
  15. package/dist/cjs/version.js +1 -1
  16. package/dist/esm/BaseClient.mjs +2 -2
  17. package/dist/esm/api/resources/fhir2Omop/client/Client.d.mts +20 -13
  18. package/dist/esm/api/resources/fhir2Omop/client/Client.mjs +22 -13
  19. package/dist/esm/api/resources/fhir2Omop/errors/ServiceUnavailableError.d.mts +5 -0
  20. package/dist/esm/api/resources/fhir2Omop/errors/ServiceUnavailableError.mjs +17 -0
  21. package/dist/esm/api/resources/fhir2Omop/errors/index.d.mts +1 -0
  22. package/dist/esm/api/resources/fhir2Omop/errors/index.mjs +1 -0
  23. package/dist/esm/api/resources/fhir2Omop/types/CreateOmopResponse.d.mts +11 -5
  24. package/dist/esm/api/resources/fhir2Omop/types/MappingEntry.d.mts +37 -0
  25. package/dist/esm/api/resources/fhir2Omop/types/Summary.d.mts +15 -0
  26. package/dist/esm/api/resources/fhir2Omop/types/index.d.mts +2 -2
  27. package/dist/esm/api/resources/fhir2Omop/types/index.mjs +2 -2
  28. package/dist/esm/core/fetcher/signals.mjs +9 -1
  29. package/dist/esm/version.d.mts +1 -1
  30. package/dist/esm/version.mjs +1 -1
  31. package/openapi/openapi.json +63 -80
  32. package/package.json +1 -1
  33. package/reference.md +19 -13
  34. package/dist/cjs/api/resources/fhir2Omop/types/MappingReportEntry.d.ts +0 -18
  35. package/dist/cjs/api/resources/fhir2Omop/types/ScanSummary.d.ts +0 -15
  36. package/dist/esm/api/resources/fhir2Omop/types/MappingReportEntry.d.mts +0 -18
  37. package/dist/esm/api/resources/fhir2Omop/types/ScanSummary.d.mts +0 -15
  38. /package/dist/cjs/api/resources/fhir2Omop/types/{MappingReportEntry.js → MappingEntry.js} +0 -0
  39. /package/dist/cjs/api/resources/fhir2Omop/types/{ScanSummary.js → Summary.js} +0 -0
  40. /package/dist/esm/api/resources/fhir2Omop/types/{MappingReportEntry.mjs → MappingEntry.mjs} +0 -0
  41. /package/dist/esm/api/resources/fhir2Omop/types/{ScanSummary.mjs → Summary.mjs} +0 -0
@@ -43,8 +43,8 @@ function normalizeClientOptions(options) {
43
43
  const headers = (0, headers_js_1.mergeHeaders)({
44
44
  "X-Fern-Language": "JavaScript",
45
45
  "X-Fern-SDK-Name": "phenoml",
46
- "X-Fern-SDK-Version": "17.1.0",
47
- "User-Agent": "phenoml/17.1.0",
46
+ "X-Fern-SDK-Version": "17.3.0",
47
+ "User-Agent": "phenoml/17.3.0",
48
48
  "X-Fern-Runtime": core.RUNTIME.type,
49
49
  "X-Fern-Runtime-Version": core.RUNTIME.version,
50
50
  }, options === null || options === void 0 ? void 0 : options.headers);
@@ -11,26 +11,32 @@ export declare class Fhir2OmopClient {
11
11
  protected readonly _options: NormalizedClientOptionsWithAuth<Fhir2OmopClient.Options>;
12
12
  constructor(options?: Fhir2OmopClient.Options);
13
13
  /**
14
- * Shapes a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows
14
+ * Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows
15
15
  * (person, visit_occurrence, condition_occurrence, drug_exposure,
16
16
  * procedure_occurrence, measurement, observation).
17
17
  *
18
- * **This is a structural mapping (`mode: "structural"`).** Rows are
19
- * structurally valid OMOP, but every clinical and source `concept_id` is `0`:
20
- * the vocabulary crosswalk that assigns real OMOP `concept_id`s is a planned
21
- * follow-up. Each `*_source_value` carries the verbatim FHIR coding
22
- * (`system#code`), `*_type_concept_id` is set to `32817` (EHR), and the
23
- * response `report` lists a standard-code *suggestion* for each source coding
24
- * (already-standard, an unchecked normalization suggestion, or unmapped). Do
25
- * not treat the output as analytically resolved OMOP until `concept_id`s are
26
- * populated.
18
+ * Each resource's primary clinical coding is resolved to a standard OMOP
19
+ * `concept_id`. Alongside the OMOP rows grouped by table (`tables`), the
20
+ * response carries `mappings` (how each source coding resolved, linked back
21
+ * to the row it produced), `dropped` (resources that could not be shaped
22
+ * into a row), `vocab_version` (the OMOP vocabulary release codes were
23
+ * resolved against), and a small `summary` of the resolution outcomes.
24
+ *
25
+ * A `concept_id` of `0` means "no matching standard concept" (OMOP
26
+ * semantics) and is reported, not omitted — a coding with no match is
27
+ * `UNMAPPED`. Only the primary clinical coding is resolved;
28
+ * `gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are
29
+ * always `0`. Each `*_source_value` carries the verbatim FHIR coding
30
+ * (`system#code`), and `*_type_concept_id` is set to `32817` (EHR).
27
31
  *
28
32
  * Medication codes are resolved whether they appear inline
29
33
  * (`medicationCodeableConcept`) or via a `medicationReference` to a contained,
30
34
  * relative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.
31
- * A medication with no usable code, resolvable reference, or display is
32
- * reported under `scan_summary.dropped_resources` rather than emitted as a
33
- * blank row. The bundle must contain at least one Patient resource.
35
+ * Resources that cannot be shaped into a row a medication with no usable
36
+ * code, resolvable reference, or display, or any clinical resource whose
37
+ * subject/patient reference cannot be tied to a person are reported under
38
+ * `dropped` rather than emitted as blank rows. The
39
+ * bundle must contain at least one Patient resource.
34
40
  *
35
41
  * @param {phenoml.fhir2Omop.CreateOmopRequest} request
36
42
  * @param {Fhir2OmopClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -38,6 +44,7 @@ export declare class Fhir2OmopClient {
38
44
  * @throws {@link phenoml.fhir2Omop.BadRequestError}
39
45
  * @throws {@link phenoml.fhir2Omop.UnauthorizedError}
40
46
  * @throws {@link phenoml.fhir2Omop.InternalServerError}
47
+ * @throws {@link phenoml.fhir2Omop.ServiceUnavailableError}
41
48
  *
42
49
  * @example
43
50
  * await client.fhir2Omop.create({
@@ -56,26 +56,32 @@ class Fhir2OmopClient {
56
56
  this._options = (0, BaseClient_js_1.normalizeClientOptionsWithAuth)(options);
57
57
  }
58
58
  /**
59
- * Shapes a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows
59
+ * Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows
60
60
  * (person, visit_occurrence, condition_occurrence, drug_exposure,
61
61
  * procedure_occurrence, measurement, observation).
62
62
  *
63
- * **This is a structural mapping (`mode: "structural"`).** Rows are
64
- * structurally valid OMOP, but every clinical and source `concept_id` is `0`:
65
- * the vocabulary crosswalk that assigns real OMOP `concept_id`s is a planned
66
- * follow-up. Each `*_source_value` carries the verbatim FHIR coding
67
- * (`system#code`), `*_type_concept_id` is set to `32817` (EHR), and the
68
- * response `report` lists a standard-code *suggestion* for each source coding
69
- * (already-standard, an unchecked normalization suggestion, or unmapped). Do
70
- * not treat the output as analytically resolved OMOP until `concept_id`s are
71
- * populated.
63
+ * Each resource's primary clinical coding is resolved to a standard OMOP
64
+ * `concept_id`. Alongside the OMOP rows grouped by table (`tables`), the
65
+ * response carries `mappings` (how each source coding resolved, linked back
66
+ * to the row it produced), `dropped` (resources that could not be shaped
67
+ * into a row), `vocab_version` (the OMOP vocabulary release codes were
68
+ * resolved against), and a small `summary` of the resolution outcomes.
69
+ *
70
+ * A `concept_id` of `0` means "no matching standard concept" (OMOP
71
+ * semantics) and is reported, not omitted — a coding with no match is
72
+ * `UNMAPPED`. Only the primary clinical coding is resolved;
73
+ * `gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are
74
+ * always `0`. Each `*_source_value` carries the verbatim FHIR coding
75
+ * (`system#code`), and `*_type_concept_id` is set to `32817` (EHR).
72
76
  *
73
77
  * Medication codes are resolved whether they appear inline
74
78
  * (`medicationCodeableConcept`) or via a `medicationReference` to a contained,
75
79
  * relative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.
76
- * A medication with no usable code, resolvable reference, or display is
77
- * reported under `scan_summary.dropped_resources` rather than emitted as a
78
- * blank row. The bundle must contain at least one Patient resource.
80
+ * Resources that cannot be shaped into a row a medication with no usable
81
+ * code, resolvable reference, or display, or any clinical resource whose
82
+ * subject/patient reference cannot be tied to a person are reported under
83
+ * `dropped` rather than emitted as blank rows. The
84
+ * bundle must contain at least one Patient resource.
79
85
  *
80
86
  * @param {phenoml.fhir2Omop.CreateOmopRequest} request
81
87
  * @param {Fhir2OmopClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -83,6 +89,7 @@ class Fhir2OmopClient {
83
89
  * @throws {@link phenoml.fhir2Omop.BadRequestError}
84
90
  * @throws {@link phenoml.fhir2Omop.UnauthorizedError}
85
91
  * @throws {@link phenoml.fhir2Omop.InternalServerError}
92
+ * @throws {@link phenoml.fhir2Omop.ServiceUnavailableError}
86
93
  *
87
94
  * @example
88
95
  * await client.fhir2Omop.create({
@@ -183,6 +190,8 @@ class Fhir2OmopClient {
183
190
  throw new phenoml.fhir2Omop.UnauthorizedError(_response.error.body, _response.rawResponse);
184
191
  case 500:
185
192
  throw new phenoml.fhir2Omop.InternalServerError(_response.error.body, _response.rawResponse);
193
+ case 503:
194
+ throw new phenoml.fhir2Omop.ServiceUnavailableError(_response.error.body, _response.rawResponse);
186
195
  default:
187
196
  throw new errors.phenomlError({
188
197
  statusCode: _response.error.statusCode,
@@ -0,0 +1,5 @@
1
+ import type * as core from "../../../../core/index.js";
2
+ import * as errors from "../../../../errors/index.js";
3
+ export declare class ServiceUnavailableError extends errors.phenomlError {
4
+ constructor(body?: unknown, rawResponse?: core.RawResponse);
5
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ // This file was auto-generated by Fern from our API Definition.
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.ServiceUnavailableError = void 0;
38
+ const errors = __importStar(require("../../../../errors/index.js"));
39
+ class ServiceUnavailableError extends errors.phenomlError {
40
+ constructor(body, rawResponse) {
41
+ super({
42
+ message: "ServiceUnavailableError",
43
+ statusCode: 503,
44
+ body: body,
45
+ rawResponse: rawResponse,
46
+ });
47
+ Object.setPrototypeOf(this, new.target.prototype);
48
+ if (Error.captureStackTrace) {
49
+ Error.captureStackTrace(this, this.constructor);
50
+ }
51
+ this.name = this.constructor.name;
52
+ }
53
+ }
54
+ exports.ServiceUnavailableError = ServiceUnavailableError;
@@ -1,3 +1,4 @@
1
1
  export * from "./BadRequestError.js";
2
2
  export * from "./InternalServerError.js";
3
+ export * from "./ServiceUnavailableError.js";
3
4
  export * from "./UnauthorizedError.js";
@@ -16,4 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./BadRequestError.js"), exports);
18
18
  __exportStar(require("./InternalServerError.js"), exports);
19
+ __exportStar(require("./ServiceUnavailableError.js"), exports);
19
20
  __exportStar(require("./UnauthorizedError.js"), exports);
@@ -2,10 +2,16 @@ import type * as phenoml from "../../../index.js";
2
2
  export interface CreateOmopResponse {
3
3
  success?: boolean | undefined;
4
4
  message?: string | undefined;
5
- /** Resolution mode. `structural` means all clinical concept_ids are 0 (vocabulary crosswalk pending). */
6
- mode?: string | undefined;
7
5
  tables?: phenoml.fhir2Omop.OmopTables | undefined;
8
- /** One Usagi-shaped entry per source coding routed through concept resolution. */
9
- report?: phenoml.fhir2Omop.MappingReportEntry[] | undefined;
10
- scan_summary?: phenoml.fhir2Omop.ScanSummary | undefined;
6
+ /** One entry per source coding (or one entry for a text-only resource with no coding), describing how it resolved and linking back to the row it produced. */
7
+ mappings?: phenoml.fhir2Omop.MappingEntry[] | undefined;
8
+ /** Resources that could not be shaped into an OMOP row (rather than emitted as blank rows). */
9
+ dropped?: phenoml.fhir2Omop.DroppedResource[] | undefined;
10
+ /**
11
+ * The OMOP vocabulary release the clinical codes were resolved against
12
+ * (e.g. "v20240229"), for reproducibility. Present when at least one
13
+ * coded concept was resolved.
14
+ */
15
+ vocab_version?: string | undefined;
16
+ summary?: phenoml.fhir2Omop.Summary | undefined;
11
17
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * How one source coding (or a text-only resource's free text) resolved to an OMOP standard concept.
3
+ */
4
+ export interface MappingEntry {
5
+ resource_type?: string | undefined;
6
+ resource_id?: string | undefined;
7
+ omop_table?: string | undefined;
8
+ /**
9
+ * The id of the OMOP row this coding produced (e.g. `condition_occurrence_id`),
10
+ * within `omop_table`. A resource with multiple codings yields one entry
11
+ * per coding, all sharing this id.
12
+ */
13
+ omop_id?: number | undefined;
14
+ source_system?: string | undefined;
15
+ source_code?: string | undefined;
16
+ source_name?: string | undefined;
17
+ target_vocabulary?: string | undefined;
18
+ /**
19
+ * The standard concept's code, when present. Populated only for an
20
+ * `UNCHECKED` suggestion (where the API normalized a text-only resource
21
+ * to a suggested code); omitted for codings resolved through concept
22
+ * resolution (`ALREADY_STANDARD` / `MAPPED` / `UNMAPPED`), which are
23
+ * identified by `target_vocabulary`, `target_name`, and the row's
24
+ * `*_concept_id` rather than by code.
25
+ */
26
+ target_code?: string | undefined;
27
+ target_name?: string | undefined;
28
+ /**
29
+ * ALREADY_STANDARD (source coding is already a standard OMOP concept),
30
+ * MAPPED (source coding was mapped to a standard concept), UNCHECKED (a
31
+ * standard code was suggested — e.g. for a text-only resource — but not
32
+ * verified against the OMOP vocabulary, so `concept_id` stays `0`), or
33
+ * UNMAPPED (no standard concept found).
34
+ */
35
+ mapping_status?: string | undefined;
36
+ note?: string | undefined;
37
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The request's data-quality headline: how the coded concepts split across
3
+ * resolution outcomes, and the share that was not already in a target
4
+ * standard vocabulary.
5
+ */
6
+ export interface Summary {
7
+ /** Codings already a standard OMOP concept. */
8
+ codes_already_standard?: number | undefined;
9
+ /** Codings mapped or suggested to a standard concept (MAPPED or UNCHECKED). */
10
+ codes_normalized?: number | undefined;
11
+ /** Codings with no standard concept found. */
12
+ codes_unmapped?: number | undefined;
13
+ /** Share of coded concepts not already standard ((normalized + unmapped) / total). */
14
+ off_vocab_rate?: number | undefined;
15
+ }
@@ -2,11 +2,11 @@ export * from "./ConditionOccurrenceRow.js";
2
2
  export * from "./CreateOmopResponse.js";
3
3
  export * from "./DroppedResource.js";
4
4
  export * from "./DrugExposureRow.js";
5
- export * from "./MappingReportEntry.js";
5
+ export * from "./MappingEntry.js";
6
6
  export * from "./MeasurementRow.js";
7
7
  export * from "./ObservationRow.js";
8
8
  export * from "./OmopTables.js";
9
9
  export * from "./PersonRow.js";
10
10
  export * from "./ProcedureOccurrenceRow.js";
11
- export * from "./ScanSummary.js";
11
+ export * from "./Summary.js";
12
12
  export * from "./VisitOccurrenceRow.js";
@@ -18,11 +18,11 @@ __exportStar(require("./ConditionOccurrenceRow.js"), exports);
18
18
  __exportStar(require("./CreateOmopResponse.js"), exports);
19
19
  __exportStar(require("./DroppedResource.js"), exports);
20
20
  __exportStar(require("./DrugExposureRow.js"), exports);
21
- __exportStar(require("./MappingReportEntry.js"), exports);
21
+ __exportStar(require("./MappingEntry.js"), exports);
22
22
  __exportStar(require("./MeasurementRow.js"), exports);
23
23
  __exportStar(require("./ObservationRow.js"), exports);
24
24
  __exportStar(require("./OmopTables.js"), exports);
25
25
  __exportStar(require("./PersonRow.js"), exports);
26
26
  __exportStar(require("./ProcedureOccurrenceRow.js"), exports);
27
- __exportStar(require("./ScanSummary.js"), exports);
27
+ __exportStar(require("./Summary.js"), exports);
28
28
  __exportStar(require("./VisitOccurrenceRow.js"), exports);
@@ -14,11 +14,19 @@ function anySignal(...args) {
14
14
  for (const signal of signals) {
15
15
  if (signal.aborted) {
16
16
  controller.abort(signal === null || signal === void 0 ? void 0 : signal.reason);
17
- break;
17
+ return controller.signal;
18
18
  }
19
19
  signal.addEventListener("abort", () => controller.abort(signal === null || signal === void 0 ? void 0 : signal.reason), {
20
20
  signal: controller.signal,
21
21
  });
22
+ // Re-check after adding listener: the signal may have aborted
23
+ // between the initial `signal.aborted` check and the `addEventListener`
24
+ // call above. If it did, the abort event was already dispatched and
25
+ // the listener will never fire — we must manually abort.
26
+ if (signal.aborted) {
27
+ controller.abort(signal === null || signal === void 0 ? void 0 : signal.reason);
28
+ return controller.signal;
29
+ }
22
30
  }
23
31
  return controller.signal;
24
32
  }
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "17.1.0";
1
+ export declare const SDK_VERSION = "17.3.0";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SDK_VERSION = void 0;
4
- exports.SDK_VERSION = "17.1.0";
4
+ exports.SDK_VERSION = "17.3.0";
@@ -6,8 +6,8 @@ export function normalizeClientOptions(options) {
6
6
  const headers = mergeHeaders({
7
7
  "X-Fern-Language": "JavaScript",
8
8
  "X-Fern-SDK-Name": "phenoml",
9
- "X-Fern-SDK-Version": "17.1.0",
10
- "User-Agent": "phenoml/17.1.0",
9
+ "X-Fern-SDK-Version": "17.3.0",
10
+ "User-Agent": "phenoml/17.3.0",
11
11
  "X-Fern-Runtime": core.RUNTIME.type,
12
12
  "X-Fern-Runtime-Version": core.RUNTIME.version,
13
13
  }, options === null || options === void 0 ? void 0 : options.headers);
@@ -11,26 +11,32 @@ export declare class Fhir2OmopClient {
11
11
  protected readonly _options: NormalizedClientOptionsWithAuth<Fhir2OmopClient.Options>;
12
12
  constructor(options?: Fhir2OmopClient.Options);
13
13
  /**
14
- * Shapes a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows
14
+ * Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows
15
15
  * (person, visit_occurrence, condition_occurrence, drug_exposure,
16
16
  * procedure_occurrence, measurement, observation).
17
17
  *
18
- * **This is a structural mapping (`mode: "structural"`).** Rows are
19
- * structurally valid OMOP, but every clinical and source `concept_id` is `0`:
20
- * the vocabulary crosswalk that assigns real OMOP `concept_id`s is a planned
21
- * follow-up. Each `*_source_value` carries the verbatim FHIR coding
22
- * (`system#code`), `*_type_concept_id` is set to `32817` (EHR), and the
23
- * response `report` lists a standard-code *suggestion* for each source coding
24
- * (already-standard, an unchecked normalization suggestion, or unmapped). Do
25
- * not treat the output as analytically resolved OMOP until `concept_id`s are
26
- * populated.
18
+ * Each resource's primary clinical coding is resolved to a standard OMOP
19
+ * `concept_id`. Alongside the OMOP rows grouped by table (`tables`), the
20
+ * response carries `mappings` (how each source coding resolved, linked back
21
+ * to the row it produced), `dropped` (resources that could not be shaped
22
+ * into a row), `vocab_version` (the OMOP vocabulary release codes were
23
+ * resolved against), and a small `summary` of the resolution outcomes.
24
+ *
25
+ * A `concept_id` of `0` means "no matching standard concept" (OMOP
26
+ * semantics) and is reported, not omitted — a coding with no match is
27
+ * `UNMAPPED`. Only the primary clinical coding is resolved;
28
+ * `gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are
29
+ * always `0`. Each `*_source_value` carries the verbatim FHIR coding
30
+ * (`system#code`), and `*_type_concept_id` is set to `32817` (EHR).
27
31
  *
28
32
  * Medication codes are resolved whether they appear inline
29
33
  * (`medicationCodeableConcept`) or via a `medicationReference` to a contained,
30
34
  * relative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.
31
- * A medication with no usable code, resolvable reference, or display is
32
- * reported under `scan_summary.dropped_resources` rather than emitted as a
33
- * blank row. The bundle must contain at least one Patient resource.
35
+ * Resources that cannot be shaped into a row a medication with no usable
36
+ * code, resolvable reference, or display, or any clinical resource whose
37
+ * subject/patient reference cannot be tied to a person are reported under
38
+ * `dropped` rather than emitted as blank rows. The
39
+ * bundle must contain at least one Patient resource.
34
40
  *
35
41
  * @param {phenoml.fhir2Omop.CreateOmopRequest} request
36
42
  * @param {Fhir2OmopClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -38,6 +44,7 @@ export declare class Fhir2OmopClient {
38
44
  * @throws {@link phenoml.fhir2Omop.BadRequestError}
39
45
  * @throws {@link phenoml.fhir2Omop.UnauthorizedError}
40
46
  * @throws {@link phenoml.fhir2Omop.InternalServerError}
47
+ * @throws {@link phenoml.fhir2Omop.ServiceUnavailableError}
41
48
  *
42
49
  * @example
43
50
  * await client.fhir2Omop.create({
@@ -20,26 +20,32 @@ export class Fhir2OmopClient {
20
20
  this._options = normalizeClientOptionsWithAuth(options);
21
21
  }
22
22
  /**
23
- * Shapes a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows
23
+ * Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows
24
24
  * (person, visit_occurrence, condition_occurrence, drug_exposure,
25
25
  * procedure_occurrence, measurement, observation).
26
26
  *
27
- * **This is a structural mapping (`mode: "structural"`).** Rows are
28
- * structurally valid OMOP, but every clinical and source `concept_id` is `0`:
29
- * the vocabulary crosswalk that assigns real OMOP `concept_id`s is a planned
30
- * follow-up. Each `*_source_value` carries the verbatim FHIR coding
31
- * (`system#code`), `*_type_concept_id` is set to `32817` (EHR), and the
32
- * response `report` lists a standard-code *suggestion* for each source coding
33
- * (already-standard, an unchecked normalization suggestion, or unmapped). Do
34
- * not treat the output as analytically resolved OMOP until `concept_id`s are
35
- * populated.
27
+ * Each resource's primary clinical coding is resolved to a standard OMOP
28
+ * `concept_id`. Alongside the OMOP rows grouped by table (`tables`), the
29
+ * response carries `mappings` (how each source coding resolved, linked back
30
+ * to the row it produced), `dropped` (resources that could not be shaped
31
+ * into a row), `vocab_version` (the OMOP vocabulary release codes were
32
+ * resolved against), and a small `summary` of the resolution outcomes.
33
+ *
34
+ * A `concept_id` of `0` means "no matching standard concept" (OMOP
35
+ * semantics) and is reported, not omitted — a coding with no match is
36
+ * `UNMAPPED`. Only the primary clinical coding is resolved;
37
+ * `gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are
38
+ * always `0`. Each `*_source_value` carries the verbatim FHIR coding
39
+ * (`system#code`), and `*_type_concept_id` is set to `32817` (EHR).
36
40
  *
37
41
  * Medication codes are resolved whether they appear inline
38
42
  * (`medicationCodeableConcept`) or via a `medicationReference` to a contained,
39
43
  * relative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.
40
- * A medication with no usable code, resolvable reference, or display is
41
- * reported under `scan_summary.dropped_resources` rather than emitted as a
42
- * blank row. The bundle must contain at least one Patient resource.
44
+ * Resources that cannot be shaped into a row a medication with no usable
45
+ * code, resolvable reference, or display, or any clinical resource whose
46
+ * subject/patient reference cannot be tied to a person are reported under
47
+ * `dropped` rather than emitted as blank rows. The
48
+ * bundle must contain at least one Patient resource.
43
49
  *
44
50
  * @param {phenoml.fhir2Omop.CreateOmopRequest} request
45
51
  * @param {Fhir2OmopClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -47,6 +53,7 @@ export class Fhir2OmopClient {
47
53
  * @throws {@link phenoml.fhir2Omop.BadRequestError}
48
54
  * @throws {@link phenoml.fhir2Omop.UnauthorizedError}
49
55
  * @throws {@link phenoml.fhir2Omop.InternalServerError}
56
+ * @throws {@link phenoml.fhir2Omop.ServiceUnavailableError}
50
57
  *
51
58
  * @example
52
59
  * await client.fhir2Omop.create({
@@ -147,6 +154,8 @@ export class Fhir2OmopClient {
147
154
  throw new phenoml.fhir2Omop.UnauthorizedError(_response.error.body, _response.rawResponse);
148
155
  case 500:
149
156
  throw new phenoml.fhir2Omop.InternalServerError(_response.error.body, _response.rawResponse);
157
+ case 503:
158
+ throw new phenoml.fhir2Omop.ServiceUnavailableError(_response.error.body, _response.rawResponse);
150
159
  default:
151
160
  throw new errors.phenomlError({
152
161
  statusCode: _response.error.statusCode,
@@ -0,0 +1,5 @@
1
+ import type * as core from "../../../../core/index.mjs";
2
+ import * as errors from "../../../../errors/index.mjs";
3
+ export declare class ServiceUnavailableError extends errors.phenomlError {
4
+ constructor(body?: unknown, rawResponse?: core.RawResponse);
5
+ }
@@ -0,0 +1,17 @@
1
+ // This file was auto-generated by Fern from our API Definition.
2
+ import * as errors from "../../../../errors/index.mjs";
3
+ export class ServiceUnavailableError extends errors.phenomlError {
4
+ constructor(body, rawResponse) {
5
+ super({
6
+ message: "ServiceUnavailableError",
7
+ statusCode: 503,
8
+ body: body,
9
+ rawResponse: rawResponse,
10
+ });
11
+ Object.setPrototypeOf(this, new.target.prototype);
12
+ if (Error.captureStackTrace) {
13
+ Error.captureStackTrace(this, this.constructor);
14
+ }
15
+ this.name = this.constructor.name;
16
+ }
17
+ }
@@ -1,3 +1,4 @@
1
1
  export * from "./BadRequestError.mjs";
2
2
  export * from "./InternalServerError.mjs";
3
+ export * from "./ServiceUnavailableError.mjs";
3
4
  export * from "./UnauthorizedError.mjs";
@@ -1,3 +1,4 @@
1
1
  export * from "./BadRequestError.mjs";
2
2
  export * from "./InternalServerError.mjs";
3
+ export * from "./ServiceUnavailableError.mjs";
3
4
  export * from "./UnauthorizedError.mjs";
@@ -2,10 +2,16 @@ import type * as phenoml from "../../../index.mjs";
2
2
  export interface CreateOmopResponse {
3
3
  success?: boolean | undefined;
4
4
  message?: string | undefined;
5
- /** Resolution mode. `structural` means all clinical concept_ids are 0 (vocabulary crosswalk pending). */
6
- mode?: string | undefined;
7
5
  tables?: phenoml.fhir2Omop.OmopTables | undefined;
8
- /** One Usagi-shaped entry per source coding routed through concept resolution. */
9
- report?: phenoml.fhir2Omop.MappingReportEntry[] | undefined;
10
- scan_summary?: phenoml.fhir2Omop.ScanSummary | undefined;
6
+ /** One entry per source coding (or one entry for a text-only resource with no coding), describing how it resolved and linking back to the row it produced. */
7
+ mappings?: phenoml.fhir2Omop.MappingEntry[] | undefined;
8
+ /** Resources that could not be shaped into an OMOP row (rather than emitted as blank rows). */
9
+ dropped?: phenoml.fhir2Omop.DroppedResource[] | undefined;
10
+ /**
11
+ * The OMOP vocabulary release the clinical codes were resolved against
12
+ * (e.g. "v20240229"), for reproducibility. Present when at least one
13
+ * coded concept was resolved.
14
+ */
15
+ vocab_version?: string | undefined;
16
+ summary?: phenoml.fhir2Omop.Summary | undefined;
11
17
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * How one source coding (or a text-only resource's free text) resolved to an OMOP standard concept.
3
+ */
4
+ export interface MappingEntry {
5
+ resource_type?: string | undefined;
6
+ resource_id?: string | undefined;
7
+ omop_table?: string | undefined;
8
+ /**
9
+ * The id of the OMOP row this coding produced (e.g. `condition_occurrence_id`),
10
+ * within `omop_table`. A resource with multiple codings yields one entry
11
+ * per coding, all sharing this id.
12
+ */
13
+ omop_id?: number | undefined;
14
+ source_system?: string | undefined;
15
+ source_code?: string | undefined;
16
+ source_name?: string | undefined;
17
+ target_vocabulary?: string | undefined;
18
+ /**
19
+ * The standard concept's code, when present. Populated only for an
20
+ * `UNCHECKED` suggestion (where the API normalized a text-only resource
21
+ * to a suggested code); omitted for codings resolved through concept
22
+ * resolution (`ALREADY_STANDARD` / `MAPPED` / `UNMAPPED`), which are
23
+ * identified by `target_vocabulary`, `target_name`, and the row's
24
+ * `*_concept_id` rather than by code.
25
+ */
26
+ target_code?: string | undefined;
27
+ target_name?: string | undefined;
28
+ /**
29
+ * ALREADY_STANDARD (source coding is already a standard OMOP concept),
30
+ * MAPPED (source coding was mapped to a standard concept), UNCHECKED (a
31
+ * standard code was suggested — e.g. for a text-only resource — but not
32
+ * verified against the OMOP vocabulary, so `concept_id` stays `0`), or
33
+ * UNMAPPED (no standard concept found).
34
+ */
35
+ mapping_status?: string | undefined;
36
+ note?: string | undefined;
37
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The request's data-quality headline: how the coded concepts split across
3
+ * resolution outcomes, and the share that was not already in a target
4
+ * standard vocabulary.
5
+ */
6
+ export interface Summary {
7
+ /** Codings already a standard OMOP concept. */
8
+ codes_already_standard?: number | undefined;
9
+ /** Codings mapped or suggested to a standard concept (MAPPED or UNCHECKED). */
10
+ codes_normalized?: number | undefined;
11
+ /** Codings with no standard concept found. */
12
+ codes_unmapped?: number | undefined;
13
+ /** Share of coded concepts not already standard ((normalized + unmapped) / total). */
14
+ off_vocab_rate?: number | undefined;
15
+ }
@@ -2,11 +2,11 @@ export * from "./ConditionOccurrenceRow.mjs";
2
2
  export * from "./CreateOmopResponse.mjs";
3
3
  export * from "./DroppedResource.mjs";
4
4
  export * from "./DrugExposureRow.mjs";
5
- export * from "./MappingReportEntry.mjs";
5
+ export * from "./MappingEntry.mjs";
6
6
  export * from "./MeasurementRow.mjs";
7
7
  export * from "./ObservationRow.mjs";
8
8
  export * from "./OmopTables.mjs";
9
9
  export * from "./PersonRow.mjs";
10
10
  export * from "./ProcedureOccurrenceRow.mjs";
11
- export * from "./ScanSummary.mjs";
11
+ export * from "./Summary.mjs";
12
12
  export * from "./VisitOccurrenceRow.mjs";
@@ -2,11 +2,11 @@ export * from "./ConditionOccurrenceRow.mjs";
2
2
  export * from "./CreateOmopResponse.mjs";
3
3
  export * from "./DroppedResource.mjs";
4
4
  export * from "./DrugExposureRow.mjs";
5
- export * from "./MappingReportEntry.mjs";
5
+ export * from "./MappingEntry.mjs";
6
6
  export * from "./MeasurementRow.mjs";
7
7
  export * from "./ObservationRow.mjs";
8
8
  export * from "./OmopTables.mjs";
9
9
  export * from "./PersonRow.mjs";
10
10
  export * from "./ProcedureOccurrenceRow.mjs";
11
- export * from "./ScanSummary.mjs";
11
+ export * from "./Summary.mjs";
12
12
  export * from "./VisitOccurrenceRow.mjs";
@@ -10,11 +10,19 @@ export function anySignal(...args) {
10
10
  for (const signal of signals) {
11
11
  if (signal.aborted) {
12
12
  controller.abort(signal === null || signal === void 0 ? void 0 : signal.reason);
13
- break;
13
+ return controller.signal;
14
14
  }
15
15
  signal.addEventListener("abort", () => controller.abort(signal === null || signal === void 0 ? void 0 : signal.reason), {
16
16
  signal: controller.signal,
17
17
  });
18
+ // Re-check after adding listener: the signal may have aborted
19
+ // between the initial `signal.aborted` check and the `addEventListener`
20
+ // call above. If it did, the abort event was already dispatched and
21
+ // the listener will never fire — we must manually abort.
22
+ if (signal.aborted) {
23
+ controller.abort(signal === null || signal === void 0 ? void 0 : signal.reason);
24
+ return controller.signal;
25
+ }
18
26
  }
19
27
  return controller.signal;
20
28
  }
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "17.1.0";
1
+ export declare const SDK_VERSION = "17.3.0";
@@ -1 +1 @@
1
- export const SDK_VERSION = "17.1.0";
1
+ export const SDK_VERSION = "17.3.0";
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.0.3",
3
3
  "info": {
4
4
  "title": "Phenoml API",
5
- "version": "f063af0e8ddd9e9322b47bb8bc90c378af75764a"
5
+ "version": "5084e3096e5bd89bf2e86ea44d96e3407f6f5c6b"
6
6
  },
7
7
  "x-services": [
8
8
  {
@@ -3021,7 +3021,7 @@
3021
3021
  "post": {
3022
3022
  "operationId": "fhir2omop_create",
3023
3023
  "summary": "Map FHIR resources to OMOP CDM v5.4",
3024
- "description": "Shapes a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows\n(person, visit_occurrence, condition_occurrence, drug_exposure,\nprocedure_occurrence, measurement, observation).\n\n**This is a structural mapping (`mode: \"structural\"`).** Rows are\nstructurally valid OMOP, but every clinical and source `concept_id` is `0`:\nthe vocabulary crosswalk that assigns real OMOP `concept_id`s is a planned\nfollow-up. Each `*_source_value` carries the verbatim FHIR coding\n(`system#code`), `*_type_concept_id` is set to `32817` (EHR), and the\nresponse `report` lists a standard-code *suggestion* for each source coding\n(already-standard, an unchecked normalization suggestion, or unmapped). Do\nnot treat the output as analytically resolved OMOP until `concept_id`s are\npopulated.\n\nMedication codes are resolved whether they appear inline\n(`medicationCodeableConcept`) or via a `medicationReference` to a contained,\nrelative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.\nA medication with no usable code, resolvable reference, or display is\nreported under `scan_summary.dropped_resources` rather than emitted as a\nblank row. The bundle must contain at least one Patient resource.\n",
3024
+ "description": "Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows\n(person, visit_occurrence, condition_occurrence, drug_exposure,\nprocedure_occurrence, measurement, observation).\n\nEach resource's primary clinical coding is resolved to a standard OMOP\n`concept_id`. Alongside the OMOP rows grouped by table (`tables`), the\nresponse carries `mappings` (how each source coding resolved, linked back\nto the row it produced), `dropped` (resources that could not be shaped\ninto a row), `vocab_version` (the OMOP vocabulary release codes were\nresolved against), and a small `summary` of the resolution outcomes.\n\nA `concept_id` of `0` means \"no matching standard concept\" (OMOP\nsemantics) and is reported, not omitted \u2014 a coding with no match is\n`UNMAPPED`. Only the primary clinical coding is resolved;\n`gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are\nalways `0`. Each `*_source_value` carries the verbatim FHIR coding\n(`system#code`), and `*_type_concept_id` is set to `32817` (EHR).\n\nMedication codes are resolved whether they appear inline\n(`medicationCodeableConcept`) or via a `medicationReference` to a contained,\nrelative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.\nResources that cannot be shaped into a row \u2014 a medication with no usable\ncode, resolvable reference, or display, or any clinical resource whose\nsubject/patient reference cannot be tied to a person \u2014 are reported under\n`dropped` rather than emitted as blank rows. The\nbundle must contain at least one Patient resource.\n",
3025
3025
  "requestBody": {
3026
3026
  "required": true,
3027
3027
  "content": {
@@ -3104,19 +3104,19 @@
3104
3104
  },
3105
3105
  "responses": {
3106
3106
  "200": {
3107
- "description": "FHIR resources mapped to OMOP CDM v5.4 (structural)",
3107
+ "description": "FHIR resources mapped to OMOP CDM v5.4",
3108
3108
  "content": {
3109
3109
  "application/json": {
3110
3110
  "schema": {
3111
3111
  "$ref": "#/components/schemas/fhir2omop_CreateOmopResponse"
3112
3112
  },
3113
3113
  "examples": {
3114
- "structural_mapping": {
3115
- "summary": "Structural mapping result",
3114
+ "mapping_result": {
3115
+ "summary": "Mapping result",
3116
+ "description": "The example bundle mapped to OMOP. Both source codes are already\nstandard, so each clinical row carries its own OMOP `concept_id`\nwith `ALREADY_STANDARD` status. `target_code` is omitted for a\nresolved concept \u2014 it is identified by `target_vocabulary` and\n`target_name` (and the row's `*_concept_id`). Illustrative\n`concept_id` values.\n",
3116
3117
  "value": {
3117
3118
  "success": true,
3118
- "message": "FHIR resources mapped to OMOP CDM v5.4 (structural; concept_ids pending vocabulary crosswalk)",
3119
- "mode": "structural",
3119
+ "message": "FHIR resources mapped to OMOP CDM v5.4",
3120
3120
  "tables": {
3121
3121
  "person": [
3122
3122
  {
@@ -3136,37 +3136,37 @@
3136
3136
  {
3137
3137
  "condition_occurrence_id": 1,
3138
3138
  "person_id": 1,
3139
- "condition_concept_id": 0,
3139
+ "condition_concept_id": 201826,
3140
3140
  "condition_start_date": "2024-01-15",
3141
3141
  "condition_start_datetime": "2024-01-15",
3142
3142
  "condition_type_concept_id": 32817,
3143
3143
  "condition_source_value": "http://snomed.info/sct#44054006",
3144
- "condition_source_concept_id": 0
3144
+ "condition_source_concept_id": 201826
3145
3145
  }
3146
3146
  ],
3147
3147
  "drug_exposure": [
3148
3148
  {
3149
3149
  "drug_exposure_id": 1,
3150
3150
  "person_id": 1,
3151
- "drug_concept_id": 0,
3151
+ "drug_concept_id": 40163924,
3152
3152
  "drug_exposure_start_date": "2024-01-16",
3153
3153
  "drug_exposure_start_datetime": "2024-01-16",
3154
3154
  "drug_type_concept_id": 32817,
3155
3155
  "drug_source_value": "http://www.nlm.nih.gov/research/umls/rxnorm#860975",
3156
- "drug_source_concept_id": 0
3156
+ "drug_source_concept_id": 40163924
3157
3157
  }
3158
3158
  ]
3159
3159
  },
3160
- "report": [
3160
+ "mappings": [
3161
3161
  {
3162
3162
  "resource_type": "Condition",
3163
3163
  "resource_id": "condition-1",
3164
3164
  "omop_table": "condition_occurrence",
3165
+ "omop_id": 1,
3165
3166
  "source_system": "http://snomed.info/sct",
3166
3167
  "source_code": "44054006",
3167
3168
  "source_name": "Type 2 diabetes mellitus",
3168
3169
  "target_vocabulary": "SNOMED",
3169
- "target_code": "44054006",
3170
3170
  "target_name": "Type 2 diabetes mellitus",
3171
3171
  "mapping_status": "ALREADY_STANDARD"
3172
3172
  },
@@ -3174,31 +3174,17 @@
3174
3174
  "resource_type": "MedicationRequest",
3175
3175
  "resource_id": "medreq-1",
3176
3176
  "omop_table": "drug_exposure",
3177
+ "omop_id": 1,
3177
3178
  "source_system": "http://www.nlm.nih.gov/research/umls/rxnorm",
3178
3179
  "source_code": "860975",
3179
3180
  "source_name": "metformin hydrochloride 500 MG",
3180
3181
  "target_vocabulary": "RXNORM",
3181
- "target_code": "860975",
3182
3182
  "target_name": "metformin hydrochloride 500 MG",
3183
3183
  "mapping_status": "ALREADY_STANDARD"
3184
3184
  }
3185
3185
  ],
3186
- "scan_summary": {
3187
- "total_resources": 3,
3188
- "resource_counts": {
3189
- "Patient": 1,
3190
- "Condition": 1,
3191
- "MedicationRequest": 1
3192
- },
3193
- "tables_populated": {
3194
- "person": 1,
3195
- "condition_occurrence": 1,
3196
- "drug_exposure": 1
3197
- },
3198
- "coding_systems": {
3199
- "http://snomed.info/sct": 1,
3200
- "http://www.nlm.nih.gov/research/umls/rxnorm": 1
3201
- },
3186
+ "vocab_version": "v20240229",
3187
+ "summary": {
3202
3188
  "codes_already_standard": 2,
3203
3189
  "codes_normalized": 0,
3204
3190
  "codes_unmapped": 0,
@@ -3211,7 +3197,7 @@
3211
3197
  }
3212
3198
  },
3213
3199
  "400": {
3214
- "description": "Bad request - invalid input. Possible reasons:\n- Invalid FHIR input (missing or non-Bundle/non-resource payload)\n- No Patient resource found (OMOP requires at least one person)\n",
3200
+ "description": "Bad request - invalid input. Possible reasons:\n- Missing or malformed request body (no `fhir_resources`)\n- Invalid FHIR input (non-Bundle/non-resource payload)\n- No Patient resource found (OMOP requires at least one person)\n",
3215
3201
  "content": {
3216
3202
  "application/json": {
3217
3203
  "schema": {
@@ -3225,6 +3211,16 @@
3225
3211
  },
3226
3212
  "500": {
3227
3213
  "description": "Internal server error"
3214
+ },
3215
+ "503": {
3216
+ "description": "Service unavailable due to a server configuration error. No rows are\nreturned.\n",
3217
+ "content": {
3218
+ "application/json": {
3219
+ "schema": {
3220
+ "$ref": "#/components/schemas/fhir2omop_CreateOmopResponse"
3221
+ }
3222
+ }
3223
+ }
3228
3224
  }
3229
3225
  },
3230
3226
  "security": [
@@ -8559,22 +8555,29 @@
8559
8555
  "message": {
8560
8556
  "type": "string"
8561
8557
  },
8562
- "mode": {
8563
- "type": "string",
8564
- "description": "Resolution mode. `structural` means all clinical concept_ids are 0 (vocabulary crosswalk pending)."
8565
- },
8566
8558
  "tables": {
8567
8559
  "$ref": "#/components/schemas/fhir2omop_OmopTables"
8568
8560
  },
8569
- "report": {
8561
+ "mappings": {
8570
8562
  "type": "array",
8571
- "description": "One Usagi-shaped entry per source coding routed through concept resolution.",
8563
+ "description": "One entry per source coding (or one entry for a text-only resource with no coding), describing how it resolved and linking back to the row it produced.",
8572
8564
  "items": {
8573
- "$ref": "#/components/schemas/fhir2omop_MappingReportEntry"
8565
+ "$ref": "#/components/schemas/fhir2omop_MappingEntry"
8574
8566
  }
8575
8567
  },
8576
- "scan_summary": {
8577
- "$ref": "#/components/schemas/fhir2omop_ScanSummary"
8568
+ "dropped": {
8569
+ "type": "array",
8570
+ "description": "Resources that could not be shaped into an OMOP row (rather than emitted as blank rows).",
8571
+ "items": {
8572
+ "$ref": "#/components/schemas/fhir2omop_DroppedResource"
8573
+ }
8574
+ },
8575
+ "vocab_version": {
8576
+ "type": "string",
8577
+ "description": "The OMOP vocabulary release the clinical codes were resolved against\n(e.g. \"v20240229\"), for reproducibility. Present when at least one\ncoded concept was resolved.\n"
8578
+ },
8579
+ "summary": {
8580
+ "$ref": "#/components/schemas/fhir2omop_Summary"
8578
8581
  }
8579
8582
  }
8580
8583
  },
@@ -8939,9 +8942,9 @@
8939
8942
  }
8940
8943
  }
8941
8944
  },
8942
- "fhir2omop_MappingReportEntry": {
8945
+ "fhir2omop_MappingEntry": {
8943
8946
  "type": "object",
8944
- "description": "One source_to_concept_map-style mapping suggestion (Usagi-shaped).",
8947
+ "description": "How one source coding (or a text-only resource's free text) resolved to an OMOP standard concept.",
8945
8948
  "properties": {
8946
8949
  "resource_type": {
8947
8950
  "type": "string"
@@ -8952,6 +8955,11 @@
8952
8955
  "omop_table": {
8953
8956
  "type": "string"
8954
8957
  },
8958
+ "omop_id": {
8959
+ "type": "integer",
8960
+ "format": "int64",
8961
+ "description": "The id of the OMOP row this coding produced (e.g. `condition_occurrence_id`),\nwithin `omop_table`. A resource with multiple codings yields one entry\nper coding, all sharing this id.\n"
8962
+ },
8955
8963
  "source_system": {
8956
8964
  "type": "string"
8957
8965
  },
@@ -8965,66 +8973,41 @@
8965
8973
  "type": "string"
8966
8974
  },
8967
8975
  "target_code": {
8968
- "type": "string"
8976
+ "type": "string",
8977
+ "description": "The standard concept's code, when present. Populated only for an\n`UNCHECKED` suggestion (where the API normalized a text-only resource\nto a suggested code); omitted for codings resolved through concept\nresolution (`ALREADY_STANDARD` / `MAPPED` / `UNMAPPED`), which are\nidentified by `target_vocabulary`, `target_name`, and the row's\n`*_concept_id` rather than by code.\n"
8969
8978
  },
8970
8979
  "target_name": {
8971
8980
  "type": "string"
8972
8981
  },
8973
8982
  "mapping_status": {
8974
8983
  "type": "string",
8975
- "description": "ALREADY_STANDARD (source already in target vocabulary), UNCHECKED (unreviewed suggestion), or UNMAPPED (no candidate found)."
8976
- },
8977
- "equivalence": {
8978
- "type": "string"
8984
+ "description": "ALREADY_STANDARD (source coding is already a standard OMOP concept),\nMAPPED (source coding was mapped to a standard concept), UNCHECKED (a\nstandard code was suggested \u2014 e.g. for a text-only resource \u2014 but not\nverified against the OMOP vocabulary, so `concept_id` stays `0`), or\nUNMAPPED (no standard concept found).\n"
8979
8985
  },
8980
8986
  "note": {
8981
8987
  "type": "string"
8982
8988
  }
8983
8989
  }
8984
8990
  },
8985
- "fhir2omop_ScanSummary": {
8991
+ "fhir2omop_Summary": {
8986
8992
  "type": "object",
8987
- "description": "White Rabbit-style aggregate profile of the mapping request.",
8993
+ "description": "The request's data-quality headline: how the coded concepts split across\nresolution outcomes, and the share that was not already in a target\nstandard vocabulary.\n",
8988
8994
  "properties": {
8989
- "total_resources": {
8990
- "type": "integer"
8991
- },
8992
- "resource_counts": {
8993
- "type": "object",
8994
- "additionalProperties": {
8995
- "type": "integer"
8996
- }
8997
- },
8998
- "tables_populated": {
8999
- "type": "object",
9000
- "additionalProperties": {
9001
- "type": "integer"
9002
- }
9003
- },
9004
- "coding_systems": {
9005
- "type": "object",
9006
- "additionalProperties": {
9007
- "type": "integer"
9008
- }
9009
- },
9010
8995
  "codes_already_standard": {
9011
- "type": "integer"
8996
+ "type": "integer",
8997
+ "description": "Codings already a standard OMOP concept."
9012
8998
  },
9013
8999
  "codes_normalized": {
9014
- "type": "integer"
9000
+ "type": "integer",
9001
+ "description": "Codings mapped or suggested to a standard concept (MAPPED or UNCHECKED)."
9015
9002
  },
9016
9003
  "codes_unmapped": {
9017
- "type": "integer"
9004
+ "type": "integer",
9005
+ "description": "Codings with no standard concept found."
9018
9006
  },
9019
9007
  "off_vocab_rate": {
9020
9008
  "type": "number",
9021
- "format": "double"
9022
- },
9023
- "dropped_resources": {
9024
- "type": "array",
9025
- "items": {
9026
- "$ref": "#/components/schemas/fhir2omop_DroppedResource"
9027
- }
9009
+ "format": "double",
9010
+ "description": "Share of coded concepts not already standard ((normalized + unmapped) / total)."
9028
9011
  }
9029
9012
  }
9030
9013
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phenoml",
3
- "version": "17.1.0",
3
+ "version": "17.3.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
package/reference.md CHANGED
@@ -2673,26 +2673,32 @@ The ID of the FHIR provider to use. Can be either:
2673
2673
  <dl>
2674
2674
  <dd>
2675
2675
 
2676
- Shapes a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows
2676
+ Maps a FHIR R4 resource or Bundle into OMOP Common Data Model v5.4 rows
2677
2677
  (person, visit_occurrence, condition_occurrence, drug_exposure,
2678
2678
  procedure_occurrence, measurement, observation).
2679
2679
 
2680
- **This is a structural mapping (`mode: "structural"`).** Rows are
2681
- structurally valid OMOP, but every clinical and source `concept_id` is `0`:
2682
- the vocabulary crosswalk that assigns real OMOP `concept_id`s is a planned
2683
- follow-up. Each `*_source_value` carries the verbatim FHIR coding
2684
- (`system#code`), `*_type_concept_id` is set to `32817` (EHR), and the
2685
- response `report` lists a standard-code *suggestion* for each source coding
2686
- (already-standard, an unchecked normalization suggestion, or unmapped). Do
2687
- not treat the output as analytically resolved OMOP until `concept_id`s are
2688
- populated.
2680
+ Each resource's primary clinical coding is resolved to a standard OMOP
2681
+ `concept_id`. Alongside the OMOP rows grouped by table (`tables`), the
2682
+ response carries `mappings` (how each source coding resolved, linked back
2683
+ to the row it produced), `dropped` (resources that could not be shaped
2684
+ into a row), `vocab_version` (the OMOP vocabulary release codes were
2685
+ resolved against), and a small `summary` of the resolution outcomes.
2686
+
2687
+ A `concept_id` of `0` means "no matching standard concept" (OMOP
2688
+ semantics) and is reported, not omitted — a coding with no match is
2689
+ `UNMAPPED`. Only the primary clinical coding is resolved;
2690
+ `gender`/`race`/`ethnicity`/`visit`/`value`/`unit` `concept_id`s are
2691
+ always `0`. Each `*_source_value` carries the verbatim FHIR coding
2692
+ (`system#code`), and `*_type_concept_id` is set to `32817` (EHR).
2689
2693
 
2690
2694
  Medication codes are resolved whether they appear inline
2691
2695
  (`medicationCodeableConcept`) or via a `medicationReference` to a contained,
2692
2696
  relative (`Type/id`), or bundle-entry (`urn:uuid`) `Medication` resource.
2693
- A medication with no usable code, resolvable reference, or display is
2694
- reported under `scan_summary.dropped_resources` rather than emitted as a
2695
- blank row. The bundle must contain at least one Patient resource.
2697
+ Resources that cannot be shaped into a row a medication with no usable
2698
+ code, resolvable reference, or display, or any clinical resource whose
2699
+ subject/patient reference cannot be tied to a person are reported under
2700
+ `dropped` rather than emitted as blank rows. The
2701
+ bundle must contain at least one Patient resource.
2696
2702
  </dd>
2697
2703
  </dl>
2698
2704
  </dd>
@@ -1,18 +0,0 @@
1
- /**
2
- * One source_to_concept_map-style mapping suggestion (Usagi-shaped).
3
- */
4
- export interface MappingReportEntry {
5
- resource_type?: string | undefined;
6
- resource_id?: string | undefined;
7
- omop_table?: string | undefined;
8
- source_system?: string | undefined;
9
- source_code?: string | undefined;
10
- source_name?: string | undefined;
11
- target_vocabulary?: string | undefined;
12
- target_code?: string | undefined;
13
- target_name?: string | undefined;
14
- /** ALREADY_STANDARD (source already in target vocabulary), UNCHECKED (unreviewed suggestion), or UNMAPPED (no candidate found). */
15
- mapping_status?: string | undefined;
16
- equivalence?: string | undefined;
17
- note?: string | undefined;
18
- }
@@ -1,15 +0,0 @@
1
- import type * as phenoml from "../../../index.js";
2
- /**
3
- * White Rabbit-style aggregate profile of the mapping request.
4
- */
5
- export interface ScanSummary {
6
- total_resources?: number | undefined;
7
- resource_counts?: Record<string, number> | undefined;
8
- tables_populated?: Record<string, number> | undefined;
9
- coding_systems?: Record<string, number> | undefined;
10
- codes_already_standard?: number | undefined;
11
- codes_normalized?: number | undefined;
12
- codes_unmapped?: number | undefined;
13
- off_vocab_rate?: number | undefined;
14
- dropped_resources?: phenoml.fhir2Omop.DroppedResource[] | undefined;
15
- }
@@ -1,18 +0,0 @@
1
- /**
2
- * One source_to_concept_map-style mapping suggestion (Usagi-shaped).
3
- */
4
- export interface MappingReportEntry {
5
- resource_type?: string | undefined;
6
- resource_id?: string | undefined;
7
- omop_table?: string | undefined;
8
- source_system?: string | undefined;
9
- source_code?: string | undefined;
10
- source_name?: string | undefined;
11
- target_vocabulary?: string | undefined;
12
- target_code?: string | undefined;
13
- target_name?: string | undefined;
14
- /** ALREADY_STANDARD (source already in target vocabulary), UNCHECKED (unreviewed suggestion), or UNMAPPED (no candidate found). */
15
- mapping_status?: string | undefined;
16
- equivalence?: string | undefined;
17
- note?: string | undefined;
18
- }
@@ -1,15 +0,0 @@
1
- import type * as phenoml from "../../../index.mjs";
2
- /**
3
- * White Rabbit-style aggregate profile of the mapping request.
4
- */
5
- export interface ScanSummary {
6
- total_resources?: number | undefined;
7
- resource_counts?: Record<string, number> | undefined;
8
- tables_populated?: Record<string, number> | undefined;
9
- coding_systems?: Record<string, number> | undefined;
10
- codes_already_standard?: number | undefined;
11
- codes_normalized?: number | undefined;
12
- codes_unmapped?: number | undefined;
13
- off_vocab_rate?: number | undefined;
14
- dropped_resources?: phenoml.fhir2Omop.DroppedResource[] | undefined;
15
- }