gdc-common-utils-ts 2.1.0 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/claims/claims-helpers-allergy-intolerance.d.ts +8 -0
- package/dist/claims/claims-helpers-allergy-intolerance.js +30 -12
- package/dist/claims/claims-helpers-condition.d.ts +8 -0
- package/dist/claims/claims-helpers-condition.js +30 -12
- package/dist/claims/claims-helpers-consent.d.ts +4 -0
- package/dist/claims/claims-helpers-consent.js +15 -1
- package/dist/claims/claims-helpers-diagnostic-report.d.ts +4 -0
- package/dist/claims/claims-helpers-diagnostic-report.js +17 -2
- package/dist/claims/claims-helpers-medication-statement.d.ts +10 -2
- package/dist/claims/claims-helpers-medication-statement.js +32 -14
- package/dist/constants/didcomm.d.ts +22 -0
- package/dist/constants/didcomm.js +22 -0
- package/dist/constants/fhir-resource-types.d.ts +2 -0
- package/dist/constants/fhir-resource-types.js +1 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.js +1 -0
- package/dist/constants/lifecycle.d.ts +2 -0
- package/dist/constants/lifecycle.js +2 -0
- package/dist/constants/verifiable-credentials.d.ts +9 -0
- package/dist/constants/verifiable-credentials.js +9 -0
- package/dist/examples/bundle-didcomm-payload.js +4 -2
- package/dist/examples/communication-attached-bundle-session.js +27 -27
- package/dist/examples/communication-didcomm-payload.d.ts +8 -4
- package/dist/examples/communication-didcomm-payload.js +17 -11
- package/dist/examples/consent-access.js +2 -1
- package/dist/examples/employee.d.ts +21 -0
- package/dist/examples/employee.js +21 -0
- package/dist/examples/individual-controller.d.ts +27 -0
- package/dist/examples/individual-controller.js +28 -1
- package/dist/examples/inter-tenant-access-contract.js +3 -2
- package/dist/examples/ips-bundle.js +17 -16
- package/dist/examples/lifecycle.js +13 -11
- package/dist/examples/profile-manager-mem.js +2 -1
- package/dist/examples/related-person.d.ts +14 -0
- package/dist/examples/related-person.js +18 -4
- package/dist/examples/shared.d.ts +12 -3
- package/dist/examples/shared.js +13 -4
- package/dist/examples/vital-signs.js +1 -1
- package/dist/examples/wallet-mem.js +4 -2
- package/dist/models/authority-resolution.d.ts +56 -0
- package/dist/models/authority-resolution.js +2 -0
- package/dist/models/bundle-editor-types.d.ts +89 -0
- package/dist/models/bundle-editor-types.js +45 -0
- package/dist/models/bundle.d.ts +5 -0
- package/dist/models/communication-attached-bundle-session.d.ts +6 -4
- package/dist/models/communication-attached-bundle-session.js +6 -4
- package/dist/models/confidential-storage.d.ts +2 -1
- package/dist/models/consent-rule.d.ts +11 -1
- package/dist/models/consent-rule.js +9 -1
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.js +2 -0
- package/dist/models/individual-onboarding.d.ts +18 -0
- package/dist/models/interoperable-claims/allergy-intolerance-claims.d.ts +12 -1
- package/dist/models/interoperable-claims/allergy-intolerance-claims.js +12 -1
- package/dist/models/interoperable-claims/condition-claims.d.ts +12 -1
- package/dist/models/interoperable-claims/condition-claims.js +12 -1
- package/dist/models/interoperable-claims/diagnostic-report-claims.d.ts +13 -2
- package/dist/models/interoperable-claims/diagnostic-report-claims.js +12 -1
- package/dist/models/interoperable-claims/document-reference-claims.d.ts +1 -0
- package/dist/models/interoperable-claims/document-reference-claims.js +3 -0
- package/dist/models/interoperable-claims/medication-statement-claims.d.ts +17 -0
- package/dist/models/interoperable-claims/medication-statement-claims.js +17 -0
- package/dist/utils/allergy-intolerance-entry-editor.d.ts +51 -0
- package/dist/utils/allergy-intolerance-entry-editor.js +55 -0
- package/dist/utils/authority-resolution.d.ts +37 -0
- package/dist/utils/authority-resolution.js +112 -0
- package/dist/utils/backend-message-manager-mem.d.ts +11 -0
- package/dist/utils/backend-message-manager-mem.js +11 -0
- package/dist/utils/bundle-didcomm-payload.js +3 -2
- package/dist/utils/bundle-document-builder.d.ts +1 -0
- package/dist/utils/bundle-document-builder.js +134 -14
- package/dist/utils/bundle-editor-core.d.ts +181 -0
- package/dist/utils/bundle-editor-core.js +467 -0
- package/dist/utils/bundle-editor-helpers.d.ts +53 -0
- package/dist/utils/bundle-editor-helpers.js +146 -0
- package/dist/utils/bundle-editor-registry.d.ts +6 -0
- package/dist/utils/bundle-editor-registry.js +14 -0
- package/dist/utils/bundle-editor.d.ts +21 -722
- package/dist/utils/bundle-editor.js +21 -1601
- package/dist/utils/bundle-entry-editor.d.ts +96 -0
- package/dist/utils/bundle-entry-editor.js +305 -0
- package/dist/utils/bundle-query.d.ts +1 -0
- package/dist/utils/bundle-query.js +12 -3
- package/dist/utils/bundle-reader.d.ts +48 -0
- package/dist/utils/bundle-reader.js +175 -1
- package/dist/utils/care-plan-entry-editor.d.ts +43 -0
- package/dist/utils/care-plan-entry-editor.js +47 -0
- package/dist/utils/client-assertion.d.ts +38 -0
- package/dist/utils/client-assertion.js +113 -0
- package/dist/utils/clinical-impression-entry-editor.d.ts +43 -0
- package/dist/utils/clinical-impression-entry-editor.js +47 -0
- package/dist/utils/clinical-resource-entry-editor.d.ts +123 -0
- package/dist/utils/clinical-resource-entry-editor.js +296 -0
- package/dist/utils/communication-attached-bundle-session-helpers.js +8 -6
- package/dist/utils/communication-attached-bundle-session.d.ts +29 -3
- package/dist/utils/communication-attached-bundle-session.js +62 -3
- package/dist/utils/communication-didcomm-payload.d.ts +5 -0
- package/dist/utils/communication-didcomm-payload.js +28 -18
- package/dist/utils/communication-document-reference.d.ts +23 -0
- package/dist/utils/communication-document-reference.js +89 -0
- package/dist/utils/communication-editor.d.ts +53 -0
- package/dist/utils/communication-editor.js +97 -0
- package/dist/utils/condition-entry-editor.d.ts +30 -0
- package/dist/utils/condition-entry-editor.js +34 -0
- package/dist/utils/consent-claim-helpers.d.ts +4 -0
- package/dist/utils/consent-claim-helpers.js +15 -1
- package/dist/utils/coverage-entry-editor.d.ts +51 -0
- package/dist/utils/coverage-entry-editor.js +55 -0
- package/dist/utils/device-entry-editor.d.ts +59 -0
- package/dist/utils/device-entry-editor.js +63 -0
- package/dist/utils/device-use-statement-entry-editor.d.ts +43 -0
- package/dist/utils/device-use-statement-entry-editor.js +47 -0
- package/dist/utils/diagnostic-report-entry-editor.d.ts +74 -0
- package/dist/utils/diagnostic-report-entry-editor.js +136 -0
- package/dist/utils/did.d.ts +8 -0
- package/dist/utils/did.js +22 -0
- package/dist/utils/document-reference-entry-editor.d.ts +62 -0
- package/dist/utils/document-reference-entry-editor.js +66 -0
- package/dist/utils/employee-entry-editor.d.ts +36 -0
- package/dist/utils/employee-entry-editor.js +88 -0
- package/dist/utils/employee.d.ts +1 -0
- package/dist/utils/employee.js +2 -1
- package/dist/utils/encounter-entry-editor.d.ts +51 -0
- package/dist/utils/encounter-entry-editor.js +55 -0
- package/dist/utils/flag-entry-editor.d.ts +47 -0
- package/dist/utils/flag-entry-editor.js +51 -0
- package/dist/utils/immunization-entry-editor.d.ts +87 -0
- package/dist/utils/immunization-entry-editor.js +169 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/individual-onboarding-acceptance-credential.d.ts +35 -0
- package/dist/utils/individual-onboarding-acceptance-credential.js +55 -0
- package/dist/utils/individual-smart.d.ts +59 -0
- package/dist/utils/individual-smart.js +135 -0
- package/dist/utils/medication-statement-entry-editor.d.ts +90 -0
- package/dist/utils/medication-statement-entry-editor.js +94 -0
- package/dist/utils/observation-component-entry-editor.d.ts +57 -0
- package/dist/utils/observation-component-entry-editor.js +105 -0
- package/dist/utils/observation-entry-editor.d.ts +29 -0
- package/dist/utils/observation-entry-editor.js +68 -0
- package/dist/utils/procedure-entry-editor.d.ts +71 -0
- package/dist/utils/procedure-entry-editor.js +75 -0
- package/dist/utils/professional-smart.d.ts +87 -0
- package/dist/utils/professional-smart.js +118 -4
- package/dist/utils/same-as.d.ts +40 -0
- package/dist/utils/same-as.js +72 -0
- package/dist/utils/unified-health-id.d.ts +51 -0
- package/dist/utils/unified-health-id.js +92 -0
- package/dist/utils/vital-sign-day-batch.d.ts +25 -0
- package/dist/utils/vital-sign-day-batch.js +115 -0
- package/dist/utils/vital-sign-entry-editor.d.ts +61 -0
- package/dist/utils/vital-sign-entry-editor.js +152 -0
- package/package.json +2 -1
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File discipline note:
|
|
3
|
+
* - Read `ARCHITECTURE.md` and `CONTRIBUTING.md` before changing this module.
|
|
4
|
+
* - This file owns only bundle-level orchestration for `BundleEditor`.
|
|
5
|
+
* - Shared helper logic belongs in `bundle-editor-helpers.ts`.
|
|
6
|
+
* - Entry/resource-specific behavior belongs in dedicated `*-entry-editor.ts`
|
|
7
|
+
* modules, one exported class per file.
|
|
8
|
+
*/
|
|
9
|
+
import { ClaimsPersonSchemaorg } from '../constants/schemaorg.js';
|
|
10
|
+
import { ResourceTypesFhirR4 } from '../constants/fhir-resource-types.js';
|
|
11
|
+
import { BundleEditorValidationIssues, BundleTypes, } from '../models/bundle-editor-types.js';
|
|
12
|
+
import { CompositionClaim } from '../models/interoperable-claims/composition-claims.js';
|
|
13
|
+
import { getClaimValues, setClaimValues } from '../claims/claim-list-helpers.js';
|
|
14
|
+
import { buildBundleDocumentFromClaims } from './bundle-document-builder.js';
|
|
15
|
+
import { buildEmployeeBatchEntry, buildEmployeePurgeBundle, buildEmployeeSearchBundle, EmployeeBundleOperations, EmployeeResourceTypes, } from './employee.js';
|
|
16
|
+
import { BundleEntryEditor } from './bundle-entry-editor.js';
|
|
17
|
+
import { cloneEntry, createCanonicalIdentifierUrn, inferGenericEntryType, normalizeOptionalIdentifier, resolveEntryTypeForOperation, resolveRequestMethodForOperation, } from './bundle-editor-helpers.js';
|
|
18
|
+
export class BundleEditor {
|
|
19
|
+
bundleOperation = null;
|
|
20
|
+
allowedResourceType = null;
|
|
21
|
+
bundleType = BundleTypes.batch;
|
|
22
|
+
compositionClaims = {};
|
|
23
|
+
entries = [];
|
|
24
|
+
/**
|
|
25
|
+
* Declares which business action this in-memory bundle is staging.
|
|
26
|
+
*
|
|
27
|
+
* Important distinction:
|
|
28
|
+
* - this is **not** the same concept as FHIR `Bundle.entry.request.method`
|
|
29
|
+
* - this is the higher-level action the editor is helping to assemble, for
|
|
30
|
+
* example `create`, `search`, `disable`, or `purge`
|
|
31
|
+
* - the lower-level transport/request method may later be derived from that
|
|
32
|
+
* action, and may differ by backend contract
|
|
33
|
+
*
|
|
34
|
+
* Example:
|
|
35
|
+
* - bundle operation `disable`
|
|
36
|
+
* - current employee GW contract -> inner `entry.request.method = DELETE`
|
|
37
|
+
* - current individual organization GW contract -> explicit `/_disable`
|
|
38
|
+
* route with inner `entry.request.method = POST`
|
|
39
|
+
*/
|
|
40
|
+
setBundleOperation(operation) {
|
|
41
|
+
this.bundleOperation = operation;
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Returns the current high-level business action assigned to the bundle.
|
|
46
|
+
*
|
|
47
|
+
* Read this as:
|
|
48
|
+
* - "what am I trying to do?"
|
|
49
|
+
*
|
|
50
|
+
* not as:
|
|
51
|
+
* - "which HTTP/FHIR request method will the final entry use?"
|
|
52
|
+
*/
|
|
53
|
+
getBundleOperation() {
|
|
54
|
+
return this.bundleOperation;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Restricts the bundle to one resource type.
|
|
58
|
+
*
|
|
59
|
+
* For employee batch bundles this should be fixed to
|
|
60
|
+
* `EmployeeResourceTypes.employee` so the editor cannot mix unrelated
|
|
61
|
+
* resource kinds.
|
|
62
|
+
*/
|
|
63
|
+
setAllowedResourceType(resourceType) {
|
|
64
|
+
this.allowedResourceType = resourceType;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
/** Returns the bundle resource type restriction when already declared. */
|
|
68
|
+
getAllowedResourceType() {
|
|
69
|
+
return this.allowedResourceType;
|
|
70
|
+
}
|
|
71
|
+
/** Declares which FHIR bundle shape the editor should materialize. */
|
|
72
|
+
setBundleType(type) {
|
|
73
|
+
this.bundleType = type;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
/** Returns the declared target bundle shape. */
|
|
77
|
+
getBundleType() {
|
|
78
|
+
return this.bundleType;
|
|
79
|
+
}
|
|
80
|
+
setCompositionIdentifier(identifier) {
|
|
81
|
+
return this.setCompositionScalarClaim(CompositionClaim.Identifier, identifier);
|
|
82
|
+
}
|
|
83
|
+
/** Returns the normalized `Composition.identifier` staged for the future document root. */
|
|
84
|
+
getCompositionIdentifier() {
|
|
85
|
+
return this.getCompositionScalarClaim(CompositionClaim.Identifier);
|
|
86
|
+
}
|
|
87
|
+
setCompositionSubject(subject) {
|
|
88
|
+
return this.setCompositionScalarClaim(CompositionClaim.Subject, subject);
|
|
89
|
+
}
|
|
90
|
+
/** Returns the normalized `Composition.subject` staged for the future document root. */
|
|
91
|
+
getCompositionSubject() {
|
|
92
|
+
return this.getCompositionScalarClaim(CompositionClaim.Subject);
|
|
93
|
+
}
|
|
94
|
+
setCompositionType(type) {
|
|
95
|
+
return this.setCompositionScalarClaim(CompositionClaim.Type, type);
|
|
96
|
+
}
|
|
97
|
+
/** Returns the normalized `Composition.type` staged for the future document root. */
|
|
98
|
+
getCompositionType() {
|
|
99
|
+
return this.getCompositionScalarClaim(CompositionClaim.Type);
|
|
100
|
+
}
|
|
101
|
+
setCompositionTitle(title) {
|
|
102
|
+
return this.setCompositionScalarClaim(CompositionClaim.Title, title);
|
|
103
|
+
}
|
|
104
|
+
/** Returns the normalized `Composition.title` staged for the future document root. */
|
|
105
|
+
getCompositionTitle() {
|
|
106
|
+
return this.getCompositionScalarClaim(CompositionClaim.Title);
|
|
107
|
+
}
|
|
108
|
+
setCompositionDate(date) {
|
|
109
|
+
return this.setCompositionScalarClaim(CompositionClaim.Date, date);
|
|
110
|
+
}
|
|
111
|
+
/** Returns the normalized `Composition.date` staged for the future document root. */
|
|
112
|
+
getCompositionDate() {
|
|
113
|
+
return this.getCompositionScalarClaim(CompositionClaim.Date);
|
|
114
|
+
}
|
|
115
|
+
setCompositionAuthorList(references) {
|
|
116
|
+
return this.setCompositionCsvClaimList(CompositionClaim.Author, references);
|
|
117
|
+
}
|
|
118
|
+
/** Returns the normalized `Composition.author` reference list staged for the future document root. */
|
|
119
|
+
getCompositionAuthorList() {
|
|
120
|
+
return this.getCompositionCsvClaimList(CompositionClaim.Author);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Opens one new entry and returns a generic entry editor for that slot.
|
|
124
|
+
*
|
|
125
|
+
* The entry editor can later expose resource-specific semantics through
|
|
126
|
+
* methods such as `asEmployee()`.
|
|
127
|
+
*/
|
|
128
|
+
newEntry(resourceId, resourceTypeOverride) {
|
|
129
|
+
const operation = this.requireBundleOperation();
|
|
130
|
+
const resourceType = this.resolveEntryResourceType(resourceTypeOverride);
|
|
131
|
+
const entry = this.createEntryDraft(operation, resourceType, resourceId);
|
|
132
|
+
this.entries.push(entry);
|
|
133
|
+
return new BundleEntryEditor(this, this.entries.length - 1);
|
|
134
|
+
}
|
|
135
|
+
/** Opens one new entry with an explicit resource type, mainly for document bundles. */
|
|
136
|
+
newEntryAs(resourceType, resourceId) {
|
|
137
|
+
return this.newEntry(resourceId, resourceType).asResourceType(resourceType);
|
|
138
|
+
}
|
|
139
|
+
/** Reopens one existing entry by `resource.id` or `fullUrl`. */
|
|
140
|
+
openEntry(resourceIdOrFullUrl) {
|
|
141
|
+
const normalizedIdentifier = normalizeOptionalIdentifier(resourceIdOrFullUrl);
|
|
142
|
+
if (!normalizedIdentifier) {
|
|
143
|
+
throw new Error('openEntry requires a non-empty resource identifier or fullUrl.');
|
|
144
|
+
}
|
|
145
|
+
const entryIndex = this.entries.findIndex((entry) => {
|
|
146
|
+
return normalizeOptionalIdentifier(entry.resource?.id) === normalizedIdentifier
|
|
147
|
+
|| normalizeOptionalIdentifier(entry.fullUrl) === normalizedIdentifier;
|
|
148
|
+
});
|
|
149
|
+
if (entryIndex < 0) {
|
|
150
|
+
throw new Error(`openEntry could not find resource identifier or fullUrl: ${normalizedIdentifier}`);
|
|
151
|
+
}
|
|
152
|
+
return new BundleEntryEditor(this, entryIndex);
|
|
153
|
+
}
|
|
154
|
+
/** Returns cloned staged entries for inspection or debugging. */
|
|
155
|
+
getEntries() {
|
|
156
|
+
return this.entries.map((entry) => cloneEntry(entry));
|
|
157
|
+
}
|
|
158
|
+
/** Returns the current Composition-level claims used when building a document bundle. */
|
|
159
|
+
getCompositionClaims() {
|
|
160
|
+
return { ...this.compositionClaims };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Validates whether the current editor state is sufficient to build one
|
|
164
|
+
* clinical `Bundle.type=document`.
|
|
165
|
+
*/
|
|
166
|
+
validateDocumentAuthoring() {
|
|
167
|
+
const issues = [];
|
|
168
|
+
if (this.bundleType !== BundleTypes.document) {
|
|
169
|
+
issues.push(BundleEditorValidationIssues.DocumentModeRequired);
|
|
170
|
+
}
|
|
171
|
+
if (!this.getCompositionSubject()) {
|
|
172
|
+
issues.push(BundleEditorValidationIssues.CompositionSubjectRequired);
|
|
173
|
+
}
|
|
174
|
+
if (!this.getCompositionType()) {
|
|
175
|
+
issues.push(BundleEditorValidationIssues.CompositionTypeRequired);
|
|
176
|
+
}
|
|
177
|
+
if (!this.getCompositionTitle()) {
|
|
178
|
+
issues.push(BundleEditorValidationIssues.CompositionTitleRequired);
|
|
179
|
+
}
|
|
180
|
+
if (!this.getCompositionDate()) {
|
|
181
|
+
issues.push(BundleEditorValidationIssues.CompositionDateRequired);
|
|
182
|
+
}
|
|
183
|
+
if (this.getCompositionAuthorList().length === 0) {
|
|
184
|
+
issues.push(BundleEditorValidationIssues.CompositionAuthorRequired);
|
|
185
|
+
}
|
|
186
|
+
if (this.entries.length === 0) {
|
|
187
|
+
issues.push(BundleEditorValidationIssues.DocumentEntryRequired);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
ok: issues.length === 0,
|
|
191
|
+
issues,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/** Builds one Composition-first document bundle from the staged resource entries. */
|
|
195
|
+
buildDocument() {
|
|
196
|
+
const validation = this.validateDocumentAuthoring();
|
|
197
|
+
if (!validation.ok) {
|
|
198
|
+
throw new Error(`BundleEditor cannot build document: ${validation.issues.join(' ')}`);
|
|
199
|
+
}
|
|
200
|
+
return buildBundleDocumentFromClaims({
|
|
201
|
+
claimsList: this.entries.map((entry) => ({ ...(entry.resource?.meta?.claims || {}) })),
|
|
202
|
+
subjectDid: this.getCompositionSubject(),
|
|
203
|
+
compositionType: this.getCompositionType(),
|
|
204
|
+
compositionClaims: this.getCompositionClaims(),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Materializes the final bundle payload from the editor state.
|
|
209
|
+
*
|
|
210
|
+
* `build()` does not send, sign, or wrap the payload. It only returns the
|
|
211
|
+
* final bundle object for the declared operation and staged entries.
|
|
212
|
+
*/
|
|
213
|
+
build() {
|
|
214
|
+
if (this.bundleType === BundleTypes.document) {
|
|
215
|
+
return this.buildDocument();
|
|
216
|
+
}
|
|
217
|
+
const operation = this.requireBundleOperation();
|
|
218
|
+
const resourceType = this.requireAllowedResourceType();
|
|
219
|
+
if (operation === EmployeeBundleOperations.search) {
|
|
220
|
+
if (resourceType !== EmployeeResourceTypes.employee) {
|
|
221
|
+
throw new Error(`BundleEditor search currently supports only resource type: ${EmployeeResourceTypes.employee}`);
|
|
222
|
+
}
|
|
223
|
+
return buildEmployeeSearchBundle({
|
|
224
|
+
claims: this.getSingleSearchClaims(),
|
|
225
|
+
resourceType,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (operation === EmployeeBundleOperations.purge) {
|
|
229
|
+
if (resourceType !== EmployeeResourceTypes.employee) {
|
|
230
|
+
throw new Error(`BundleEditor purge currently supports only resource type: ${EmployeeResourceTypes.employee}`);
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
resourceType: EmployeeResourceTypes.bundle,
|
|
234
|
+
type: EmployeeResourceTypes.batch,
|
|
235
|
+
entry: this.entries.map((entry) => {
|
|
236
|
+
const identifier = normalizeOptionalIdentifier(entry.resource?.meta?.claims?.[ClaimsPersonSchemaorg.identifier]
|
|
237
|
+
|| entry.resource?.id
|
|
238
|
+
|| entry.fullUrl);
|
|
239
|
+
if (!identifier) {
|
|
240
|
+
throw new Error('Every purge entry requires one canonical employee identifier.');
|
|
241
|
+
}
|
|
242
|
+
return buildEmployeePurgeBundle({
|
|
243
|
+
identifier,
|
|
244
|
+
resourceType,
|
|
245
|
+
}).entry[0];
|
|
246
|
+
}),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
resourceType: ResourceTypesFhirR4.Bundle,
|
|
251
|
+
type: this.bundleType,
|
|
252
|
+
entry: this.entries.map((entry) => cloneEntry(entry)),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Materializes the staged entries as the `BundleJsonApi` shape used by
|
|
257
|
+
* `CommunicationAttachedBundleSession` and `ConsentAccessEditor`.
|
|
258
|
+
*
|
|
259
|
+
* Use this when the next layer edits bundle entries in-memory before another
|
|
260
|
+
* runtime wraps or sends the bundle.
|
|
261
|
+
*/
|
|
262
|
+
buildJsonApi() {
|
|
263
|
+
return {
|
|
264
|
+
resourceType: ResourceTypesFhirR4.Bundle,
|
|
265
|
+
type: this.bundleType,
|
|
266
|
+
data: this.entries.map((entry) => {
|
|
267
|
+
const clonedEntry = cloneEntry(entry);
|
|
268
|
+
const { request: _requestIgnored, ...clonedEntryWithoutRequest } = clonedEntry;
|
|
269
|
+
const normalizedRequest = clonedEntry.request
|
|
270
|
+
? {
|
|
271
|
+
method: clonedEntry.request.method,
|
|
272
|
+
url: clonedEntry.request.url || '',
|
|
273
|
+
}
|
|
274
|
+
: undefined;
|
|
275
|
+
return normalizedRequest
|
|
276
|
+
? {
|
|
277
|
+
...clonedEntryWithoutRequest,
|
|
278
|
+
request: normalizedRequest,
|
|
279
|
+
}
|
|
280
|
+
: clonedEntryWithoutRequest;
|
|
281
|
+
}),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
/** @internal */
|
|
285
|
+
getMutableEntry(entryIndex) {
|
|
286
|
+
if (!Number.isInteger(entryIndex) || entryIndex < 0 || entryIndex >= this.entries.length) {
|
|
287
|
+
throw new Error(`BundleEditor could not open entry index: ${entryIndex}`);
|
|
288
|
+
}
|
|
289
|
+
return this.entries[entryIndex];
|
|
290
|
+
}
|
|
291
|
+
/** @internal */
|
|
292
|
+
removeMutableEntry(entryIndex) {
|
|
293
|
+
if (!Number.isInteger(entryIndex) || entryIndex < 0 || entryIndex >= this.entries.length) {
|
|
294
|
+
throw new Error(`BundleEditor could not remove entry index: ${entryIndex}`);
|
|
295
|
+
}
|
|
296
|
+
this.entries.splice(entryIndex, 1);
|
|
297
|
+
}
|
|
298
|
+
/** @internal */
|
|
299
|
+
findEntryIndexByIdentifierOrFullUrl(resourceIdOrFullUrl) {
|
|
300
|
+
const normalizedIdentifier = normalizeOptionalIdentifier(resourceIdOrFullUrl);
|
|
301
|
+
if (!normalizedIdentifier) {
|
|
302
|
+
return -1;
|
|
303
|
+
}
|
|
304
|
+
return this.entries.findIndex((entry) => {
|
|
305
|
+
return normalizeOptionalIdentifier(entry.resource?.id) === normalizedIdentifier
|
|
306
|
+
|| normalizeOptionalIdentifier(entry.fullUrl) === normalizedIdentifier;
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Creates the mutable entry state that will later be wrapped by one entry editor.
|
|
311
|
+
*
|
|
312
|
+
* The draft seeds bundle-owned defaults such as:
|
|
313
|
+
* - canonical internal identifier (`resource.id` / `fullUrl`)
|
|
314
|
+
* - request method derived from the high-level bundle operation
|
|
315
|
+
* - generic entry type string
|
|
316
|
+
*
|
|
317
|
+
* It does not yet imply that the public `<ResourceType>.identifier` claim was
|
|
318
|
+
* written; the entry editor decides when that claim must be synchronized.
|
|
319
|
+
*/
|
|
320
|
+
createEntryDraft(operation, resourceType, resourceId) {
|
|
321
|
+
const normalizedIdentifier = operation === EmployeeBundleOperations.search
|
|
322
|
+
? normalizeOptionalIdentifier(resourceId)
|
|
323
|
+
: (normalizeOptionalIdentifier(resourceId) || createCanonicalIdentifierUrn());
|
|
324
|
+
const claims = {
|
|
325
|
+
'@context': 'org.schema',
|
|
326
|
+
...(normalizedIdentifier ? { [ClaimsPersonSchemaorg.identifier]: normalizedIdentifier } : {}),
|
|
327
|
+
};
|
|
328
|
+
if (resourceType !== EmployeeResourceTypes.employee) {
|
|
329
|
+
return {
|
|
330
|
+
type: inferGenericEntryType(resourceType, operation),
|
|
331
|
+
request: { method: resolveRequestMethodForOperation(operation) },
|
|
332
|
+
fullUrl: normalizedIdentifier,
|
|
333
|
+
resource: {
|
|
334
|
+
resourceType,
|
|
335
|
+
...(normalizedIdentifier ? { id: normalizedIdentifier } : {}),
|
|
336
|
+
meta: { claims: {} },
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
const entry = buildEmployeeBatchEntry({
|
|
341
|
+
type: resolveEntryTypeForOperation(operation),
|
|
342
|
+
method: resolveRequestMethodForOperation(operation),
|
|
343
|
+
resourceId: normalizedIdentifier,
|
|
344
|
+
resourceType,
|
|
345
|
+
claims,
|
|
346
|
+
});
|
|
347
|
+
if (normalizedIdentifier) {
|
|
348
|
+
entry.fullUrl = normalizedIdentifier;
|
|
349
|
+
}
|
|
350
|
+
return entry;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Resolves which resource type one new entry should use.
|
|
354
|
+
*
|
|
355
|
+
* Non-document bundles stay homogeneous once an allowed resource type was
|
|
356
|
+
* declared. Document bundles may stage multiple resource types and therefore
|
|
357
|
+
* can use the override path repeatedly.
|
|
358
|
+
*/
|
|
359
|
+
resolveEntryResourceType(resourceTypeOverride) {
|
|
360
|
+
const explicitResourceType = normalizeOptionalIdentifier(resourceTypeOverride);
|
|
361
|
+
if (explicitResourceType) {
|
|
362
|
+
if (this.bundleType !== BundleTypes.document
|
|
363
|
+
&& this.allowedResourceType
|
|
364
|
+
&& explicitResourceType !== this.allowedResourceType) {
|
|
365
|
+
throw new Error(`BundleEditor cannot mix resource types in ${this.bundleType} mode: ${this.allowedResourceType} vs ${explicitResourceType}`);
|
|
366
|
+
}
|
|
367
|
+
return explicitResourceType;
|
|
368
|
+
}
|
|
369
|
+
if (!this.allowedResourceType) {
|
|
370
|
+
throw new Error('BundleEditor requires setAllowedResourceType(...) before newEntry() or build().');
|
|
371
|
+
}
|
|
372
|
+
return this.allowedResourceType;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Normalizes one single-entry search bundle into a plain claims object.
|
|
376
|
+
*
|
|
377
|
+
* Keep this internal because it is specific to the current search serialization
|
|
378
|
+
* contract and should not become the public reader/editor story.
|
|
379
|
+
*/
|
|
380
|
+
getSingleSearchClaims() {
|
|
381
|
+
if (this.entries.length === 0) {
|
|
382
|
+
return {};
|
|
383
|
+
}
|
|
384
|
+
if (this.entries.length > 1) {
|
|
385
|
+
throw new Error('Search bundles currently support one entry per bundle.');
|
|
386
|
+
}
|
|
387
|
+
const claims = this.entries[0].resource?.meta?.claims || {};
|
|
388
|
+
const searchClaims = {};
|
|
389
|
+
for (const [key, value] of Object.entries(claims)) {
|
|
390
|
+
if (value === undefined
|
|
391
|
+
|| value === null
|
|
392
|
+
|| typeof value === 'string'
|
|
393
|
+
|| typeof value === 'number'
|
|
394
|
+
|| typeof value === 'boolean') {
|
|
395
|
+
searchClaims[key] = value;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return searchClaims;
|
|
399
|
+
}
|
|
400
|
+
/** Returns the declared bundle operation or throws one onboarding-grade error. */
|
|
401
|
+
requireBundleOperation() {
|
|
402
|
+
if (!this.bundleOperation) {
|
|
403
|
+
throw new Error('BundleEditor requires setBundleOperation(...) before newEntry() or build().');
|
|
404
|
+
}
|
|
405
|
+
return this.bundleOperation;
|
|
406
|
+
}
|
|
407
|
+
/** Returns the allowed resource type or throws when the editor was not initialized correctly. */
|
|
408
|
+
requireAllowedResourceType() {
|
|
409
|
+
if (!this.allowedResourceType) {
|
|
410
|
+
throw new Error('BundleEditor requires setAllowedResourceType(...) before newEntry() or build().');
|
|
411
|
+
}
|
|
412
|
+
return this.allowedResourceType;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Stores one Composition scalar claim in the staged document metadata.
|
|
416
|
+
*
|
|
417
|
+
* Callers should use semantic wrappers such as `setCompositionTitle(...)`
|
|
418
|
+
* instead of touching claim keys directly.
|
|
419
|
+
*/
|
|
420
|
+
setCompositionScalarClaim(claimKey, value) {
|
|
421
|
+
const normalized = normalizeOptionalIdentifier(value);
|
|
422
|
+
if (!normalized) {
|
|
423
|
+
delete this.compositionClaims[claimKey];
|
|
424
|
+
return this;
|
|
425
|
+
}
|
|
426
|
+
this.compositionClaims[claimKey] = normalized;
|
|
427
|
+
return this;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Reads one normalized Composition scalar claim.
|
|
431
|
+
*
|
|
432
|
+
* This internal helper explains the otherwise opaque `getComposition...()`
|
|
433
|
+
* implementations: they all delegate to the same normalized metadata store.
|
|
434
|
+
*/
|
|
435
|
+
getCompositionScalarClaim(claimKey) {
|
|
436
|
+
return normalizeOptionalIdentifier(this.compositionClaims[claimKey]);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Stores one CSV-backed Composition claim list such as `Composition.author`.
|
|
440
|
+
*
|
|
441
|
+
* The editor keeps the flattened storage representation internal while the
|
|
442
|
+
* public API exposes simple `string[]` setters/getters.
|
|
443
|
+
*/
|
|
444
|
+
setCompositionCsvClaimList(claimKey, values) {
|
|
445
|
+
const next = setClaimValues({}, claimKey, values);
|
|
446
|
+
const normalized = normalizeOptionalIdentifier(next[claimKey]);
|
|
447
|
+
if (!normalized) {
|
|
448
|
+
delete this.compositionClaims[claimKey];
|
|
449
|
+
return this;
|
|
450
|
+
}
|
|
451
|
+
this.compositionClaims[claimKey] = normalized;
|
|
452
|
+
return this;
|
|
453
|
+
}
|
|
454
|
+
/** Reads one CSV-backed Composition claim list as a normalized string array. */
|
|
455
|
+
getCompositionCsvClaimList(claimKey) {
|
|
456
|
+
return getClaimValues(this.compositionClaims, claimKey);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Generic editor for one staged bundle entry.
|
|
461
|
+
*
|
|
462
|
+
* This class only knows generic entry concerns such as:
|
|
463
|
+
* - resource id
|
|
464
|
+
* - fullUrl
|
|
465
|
+
* - meta claims
|
|
466
|
+
* - conversion to one resource-specific entry editor
|
|
467
|
+
*/
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type BundleRequest } from '../models/bundle';
|
|
2
|
+
import { type BundleOperation } from '../models/bundle-editor-types';
|
|
3
|
+
export declare function cloneEntry<T>(value: T): T;
|
|
4
|
+
/** Clones one claim value while preserving simple arrays used by flat claims. */
|
|
5
|
+
export declare function cloneClaimValue<T>(value: T): T;
|
|
6
|
+
/**
|
|
7
|
+
* Trims one candidate identifier-like value and returns `undefined` when it is
|
|
8
|
+
* empty or absent.
|
|
9
|
+
*
|
|
10
|
+
* Use this before writing ids, references, `fullUrl`, or flat-claim scalars so
|
|
11
|
+
* every editor follows the same blank-value rule.
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeOptionalIdentifier(value: unknown): string | undefined;
|
|
14
|
+
/** Returns one canonical `urn:uuid:*` identifier for new entry/resource drafts. */
|
|
15
|
+
export declare function createCanonicalIdentifierUrn(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Maps the editor's high-level business operation to the low-level request
|
|
18
|
+
* method stored in staged bundle entries.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveRequestMethodForOperation(operation: BundleOperation): BundleRequest['method'];
|
|
21
|
+
/**
|
|
22
|
+
* Resolves the legacy entry `type` string used by employee-oriented batch flows.
|
|
23
|
+
*
|
|
24
|
+
* This is not the same concept as `resource.resourceType`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveEntryTypeForOperation(operation: BundleOperation): string;
|
|
27
|
+
/**
|
|
28
|
+
* Builds the generic entry `type` string for non-employee resource drafts.
|
|
29
|
+
*
|
|
30
|
+
* This is one bundle-internal classification token, not a FHIR field.
|
|
31
|
+
*/
|
|
32
|
+
export declare function inferGenericEntryType(resourceType: string, operation: BundleOperation): string;
|
|
33
|
+
/**
|
|
34
|
+
* Resolves which flat-claims key stores the CSV of linked/contained resource
|
|
35
|
+
* references for one parent resource type.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveContainedReferenceListClaimKey(resourceType: string): string;
|
|
38
|
+
/** Returns the internal visibility-control claim that marks one resource as contained. */
|
|
39
|
+
export declare function resolveContainedFlagClaimKey(resourceType: string): string;
|
|
40
|
+
/** Returns the internal parent-reference claim for one contained child resource. */
|
|
41
|
+
export declare function resolveContainedParentReferenceClaimKey(resourceType: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Normalizes one linked-resource reference into both:
|
|
44
|
+
* - `identifier`: the entry-local id part
|
|
45
|
+
* - `reference`: the canonical `ResourceType/<id>` string
|
|
46
|
+
*
|
|
47
|
+
* This lets the editors accept either a bare id or a full FHIR-style reference
|
|
48
|
+
* while keeping one normalized shape internally.
|
|
49
|
+
*/
|
|
50
|
+
export declare function normalizeContainedReference(resourceType: string, referenceOrIdentifier: string): {
|
|
51
|
+
identifier: string;
|
|
52
|
+
reference: string;
|
|
53
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File discipline note:
|
|
3
|
+
* - Read `ARCHITECTURE.md` and `CONTRIBUTING.md` before changing this module.
|
|
4
|
+
* - Keep only reusable pure helper logic here.
|
|
5
|
+
* - Do not move entry-specific orchestration back into this file; that belongs
|
|
6
|
+
* in the editor classes.
|
|
7
|
+
*/
|
|
8
|
+
import { ResourceTypesFhirR4 } from '../constants/fhir-resource-types.js';
|
|
9
|
+
import { AllergyIntoleranceClaim, } from '../models/interoperable-claims/allergy-intolerance-claims.js';
|
|
10
|
+
import { ConditionClaim } from '../models/interoperable-claims/condition-claims.js';
|
|
11
|
+
import { DiagnosticReportClaim } from '../models/interoperable-claims/diagnostic-report-claims.js';
|
|
12
|
+
import { MedicationStatementClaim } from '../models/interoperable-claims/medication-statement-claims.js';
|
|
13
|
+
import { ClaimConsent } from '../models/consent-rule.js';
|
|
14
|
+
import { BundleEditableResourceTypes, } from '../models/bundle-editor-types.js';
|
|
15
|
+
import { EmployeeBatchEntryTypes, EmployeeBundleMethods, EmployeeBundleOperations, } from './employee.js';
|
|
16
|
+
export function cloneEntry(value) {
|
|
17
|
+
return JSON.parse(JSON.stringify(value));
|
|
18
|
+
}
|
|
19
|
+
/** Clones one claim value while preserving simple arrays used by flat claims. */
|
|
20
|
+
export function cloneClaimValue(value) {
|
|
21
|
+
if (Array.isArray(value)) {
|
|
22
|
+
return [...value];
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Trims one candidate identifier-like value and returns `undefined` when it is
|
|
28
|
+
* empty or absent.
|
|
29
|
+
*
|
|
30
|
+
* Use this before writing ids, references, `fullUrl`, or flat-claim scalars so
|
|
31
|
+
* every editor follows the same blank-value rule.
|
|
32
|
+
*/
|
|
33
|
+
export function normalizeOptionalIdentifier(value) {
|
|
34
|
+
if (value === undefined || value === null) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const normalized = String(value).trim();
|
|
38
|
+
return normalized ? normalized : undefined;
|
|
39
|
+
}
|
|
40
|
+
/** Returns one canonical `urn:uuid:*` identifier for new entry/resource drafts. */
|
|
41
|
+
export function createCanonicalIdentifierUrn() {
|
|
42
|
+
const cryptoLike = globalThis;
|
|
43
|
+
const uuid = typeof cryptoLike.crypto?.randomUUID === 'function'
|
|
44
|
+
? cryptoLike.crypto.randomUUID()
|
|
45
|
+
: `resource-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
46
|
+
return `urn:uuid:${uuid}`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Maps the editor's high-level business operation to the low-level request
|
|
50
|
+
* method stored in staged bundle entries.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveRequestMethodForOperation(operation) {
|
|
53
|
+
switch (operation) {
|
|
54
|
+
case EmployeeBundleOperations.disable:
|
|
55
|
+
return EmployeeBundleMethods.disable;
|
|
56
|
+
case EmployeeBundleOperations.search:
|
|
57
|
+
case EmployeeBundleOperations.create:
|
|
58
|
+
case EmployeeBundleOperations.purge:
|
|
59
|
+
default:
|
|
60
|
+
return EmployeeBundleMethods.create;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resolves the legacy entry `type` string used by employee-oriented batch flows.
|
|
65
|
+
*
|
|
66
|
+
* This is not the same concept as `resource.resourceType`.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveEntryTypeForOperation(operation) {
|
|
69
|
+
switch (operation) {
|
|
70
|
+
case EmployeeBundleOperations.search:
|
|
71
|
+
return EmployeeBatchEntryTypes.search;
|
|
72
|
+
case EmployeeBundleOperations.disable:
|
|
73
|
+
return EmployeeBatchEntryTypes.disable;
|
|
74
|
+
case EmployeeBundleOperations.purge:
|
|
75
|
+
return EmployeeBatchEntryTypes.purge;
|
|
76
|
+
case EmployeeBundleOperations.create:
|
|
77
|
+
default:
|
|
78
|
+
return EmployeeBatchEntryTypes.create;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Builds the generic entry `type` string for non-employee resource drafts.
|
|
83
|
+
*
|
|
84
|
+
* This is one bundle-internal classification token, not a FHIR field.
|
|
85
|
+
*/
|
|
86
|
+
export function inferGenericEntryType(resourceType, operation) {
|
|
87
|
+
const normalizedResourceType = String(resourceType || '').trim();
|
|
88
|
+
if (normalizedResourceType === BundleEditableResourceTypes.employee) {
|
|
89
|
+
return resolveEntryTypeForOperation(operation);
|
|
90
|
+
}
|
|
91
|
+
return `${normalizedResourceType}-${operation}-request-v1.0`;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolves which flat-claims key stores the CSV of linked/contained resource
|
|
95
|
+
* references for one parent resource type.
|
|
96
|
+
*/
|
|
97
|
+
export function resolveContainedReferenceListClaimKey(resourceType) {
|
|
98
|
+
switch (resourceType) {
|
|
99
|
+
case ResourceTypesFhirR4.AllergyIntolerance:
|
|
100
|
+
return AllergyIntoleranceClaim.ContainedReferenceList;
|
|
101
|
+
case ResourceTypesFhirR4.Condition:
|
|
102
|
+
return ConditionClaim.ContainedReferenceList;
|
|
103
|
+
case ResourceTypesFhirR4.MedicationStatement:
|
|
104
|
+
return MedicationStatementClaim.ContainedReferenceList;
|
|
105
|
+
case ResourceTypesFhirR4.DiagnosticReport:
|
|
106
|
+
return DiagnosticReportClaim.ContainedReferenceList;
|
|
107
|
+
case ResourceTypesFhirR4.Consent:
|
|
108
|
+
return ClaimConsent.containedReferenceList;
|
|
109
|
+
default:
|
|
110
|
+
return '';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** Returns the internal visibility-control claim that marks one resource as contained. */
|
|
114
|
+
export function resolveContainedFlagClaimKey(resourceType) {
|
|
115
|
+
return `${resourceType}.is-contained`;
|
|
116
|
+
}
|
|
117
|
+
/** Returns the internal parent-reference claim for one contained child resource. */
|
|
118
|
+
export function resolveContainedParentReferenceClaimKey(resourceType) {
|
|
119
|
+
return `${resourceType}.contained-parent-reference`;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Normalizes one linked-resource reference into both:
|
|
123
|
+
* - `identifier`: the entry-local id part
|
|
124
|
+
* - `reference`: the canonical `ResourceType/<id>` string
|
|
125
|
+
*
|
|
126
|
+
* This lets the editors accept either a bare id or a full FHIR-style reference
|
|
127
|
+
* while keeping one normalized shape internally.
|
|
128
|
+
*/
|
|
129
|
+
export function normalizeContainedReference(resourceType, referenceOrIdentifier) {
|
|
130
|
+
const normalizedValue = normalizeOptionalIdentifier(referenceOrIdentifier);
|
|
131
|
+
if (!normalizedValue) {
|
|
132
|
+
return { identifier: '', reference: '' };
|
|
133
|
+
}
|
|
134
|
+
const slashIndex = normalizedValue.indexOf('/');
|
|
135
|
+
if (slashIndex > 0) {
|
|
136
|
+
const identifier = normalizedValue.slice(slashIndex + 1).trim();
|
|
137
|
+
return {
|
|
138
|
+
identifier,
|
|
139
|
+
reference: `${String(resourceType || '').trim() || normalizedValue.slice(0, slashIndex).trim()}/${identifier}`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
identifier: normalizedValue,
|
|
144
|
+
reference: `${String(resourceType || '').trim()}/${normalizedValue}`,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BundleEditor } from './bundle-editor-core';
|
|
2
|
+
import type { BundleEntryEditor } from './bundle-entry-editor';
|
|
3
|
+
export type BundleEntryEditorConstructor<T extends BundleEntryEditor = BundleEntryEditor> = new (bundleEditor: BundleEditor, entryIndex: number) => T;
|
|
4
|
+
export declare function registerBundleEntryEditor(resourceType: string, ctor: BundleEntryEditorConstructor): void;
|
|
5
|
+
export declare function getBundleEntryEditorConstructor(resourceType: string): BundleEntryEditorConstructor | undefined;
|
|
6
|
+
export declare function createRegisteredBundleEntryEditor<T extends BundleEntryEditor>(resourceType: string, bundleEditor: BundleEditor, entryIndex: number): T;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const bundleEntryEditorConstructors = new Map();
|
|
2
|
+
export function registerBundleEntryEditor(resourceType, ctor) {
|
|
3
|
+
bundleEntryEditorConstructors.set(String(resourceType || '').trim(), ctor);
|
|
4
|
+
}
|
|
5
|
+
export function getBundleEntryEditorConstructor(resourceType) {
|
|
6
|
+
return bundleEntryEditorConstructors.get(String(resourceType || '').trim());
|
|
7
|
+
}
|
|
8
|
+
export function createRegisteredBundleEntryEditor(resourceType, bundleEditor, entryIndex) {
|
|
9
|
+
const ctor = getBundleEntryEditorConstructor(resourceType);
|
|
10
|
+
if (!ctor) {
|
|
11
|
+
throw new Error('BundleEntryEditor has not registered a constructor for resource type: ' + String(resourceType || '').trim());
|
|
12
|
+
}
|
|
13
|
+
return new ctor(bundleEditor, entryIndex);
|
|
14
|
+
}
|