gdc-common-utils-ts 1.14.15 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/utils/bundle-editor.d.ts +88 -0
- package/dist/utils/bundle-editor.js +316 -0
- package/dist/utils/communication-attached-bundle-session.d.ts +14 -0
- package/dist/utils/communication-attached-bundle-session.js +86 -0
- package/dist/utils/employee.d.ts +40 -0
- package/dist/utils/employee.js +49 -6
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,6 +35,7 @@ entry types, FHIR-like resources, and `resource.meta.claims` fit together,
|
|
|
35
35
|
read first:
|
|
36
36
|
|
|
37
37
|
- [`docs/101-COMMUNICATION_LAYERING.md`](docs/101-COMMUNICATION_LAYERING.md)
|
|
38
|
+
- [`docs/101-BUNDLE_EDITOR_READER.md`](docs/101-BUNDLE_EDITOR_READER.md)
|
|
38
39
|
|
|
39
40
|
## Install
|
|
40
41
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { buildEmployeeBatchBundle, buildEmployeeBatchEntry, buildEmployeeSearchBundle, EmployeeBundleOperations } from './employee';
|
|
2
|
+
export type BundleOperation = (typeof EmployeeBundleOperations)[keyof typeof EmployeeBundleOperations];
|
|
3
|
+
export type BuiltEmployeeBatchEntry = ReturnType<typeof buildEmployeeBatchEntry> & {
|
|
4
|
+
fullUrl?: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Generic bundle editor with an employee adapter surface.
|
|
8
|
+
*
|
|
9
|
+
* Current scope:
|
|
10
|
+
* - one declared business operation per bundle
|
|
11
|
+
* - active-entry editing through generic claim helpers
|
|
12
|
+
* - employee convenience setters layered on the same active entry
|
|
13
|
+
*
|
|
14
|
+
* This editor lives in `common-utils` because it is runtime-neutral and can be
|
|
15
|
+
* consumed by frontend SDKs, backend SDKs, and backend services.
|
|
16
|
+
*/
|
|
17
|
+
export declare class BundleEditor {
|
|
18
|
+
private bundleOperation;
|
|
19
|
+
private readonly entries;
|
|
20
|
+
private activeEntryIndex;
|
|
21
|
+
/** Declares which business operation the bundle is assembling. */
|
|
22
|
+
setBundleOperation(operation: BundleOperation): this;
|
|
23
|
+
/** Returns the operation currently assigned to this bundle. */
|
|
24
|
+
getBundleOperation(): BundleOperation | null;
|
|
25
|
+
/**
|
|
26
|
+
* Opens one new active entry.
|
|
27
|
+
*
|
|
28
|
+
* If the current bundle operation needs an identifier and none is supplied,
|
|
29
|
+
* a canonical `urn:uuid:*` identifier is generated and aligned across
|
|
30
|
+
* `fullUrl`, `resource.id`, and `org.schema.Person.identifier`.
|
|
31
|
+
*/
|
|
32
|
+
newEntry(resourceId?: string): this;
|
|
33
|
+
/** Reopens an existing entry by identifier or `fullUrl`. */
|
|
34
|
+
openEntry(resourceId: string): this;
|
|
35
|
+
/** Closes the current active entry while preserving it inside the bundle draft. */
|
|
36
|
+
doneEntry(): this;
|
|
37
|
+
/** Returns a cloned snapshot of the currently staged entries. */
|
|
38
|
+
getEntries(): readonly BuiltEmployeeBatchEntry[];
|
|
39
|
+
/**
|
|
40
|
+
* Builds the final bundle payload for the declared operation.
|
|
41
|
+
*
|
|
42
|
+
* `search` returns one canonical search bundle.
|
|
43
|
+
* `purge` returns a batch bundle whose entries are routed later to
|
|
44
|
+
* `Employee/_purge`.
|
|
45
|
+
* `create` and `disable` return canonical batch bundles.
|
|
46
|
+
*/
|
|
47
|
+
build(): ReturnType<typeof buildEmployeeBatchBundle> | ReturnType<typeof buildEmployeeSearchBundle>;
|
|
48
|
+
/** Reads one claim from the active entry. */
|
|
49
|
+
getClaim(key: string): unknown;
|
|
50
|
+
/** Checks whether the active entry contains one claim key. */
|
|
51
|
+
hasClaim(key: string): boolean;
|
|
52
|
+
/** Writes one claim on the active entry. */
|
|
53
|
+
setClaim(key: string, value: unknown): this;
|
|
54
|
+
/** Appends one claim value on the active entry. */
|
|
55
|
+
addClaim(key: string, value: unknown): this;
|
|
56
|
+
/** Removes one claim from the active entry. */
|
|
57
|
+
removeClaim(key: string): this;
|
|
58
|
+
/**
|
|
59
|
+
* Sets the active entry identifier.
|
|
60
|
+
*
|
|
61
|
+
* When informed, the value is synchronized into `fullUrl`, `resource.id`,
|
|
62
|
+
* and the canonical identifier claim.
|
|
63
|
+
* Empty values remove the identifier from all three places.
|
|
64
|
+
*/
|
|
65
|
+
setIdentifier(identifier?: string | null): this;
|
|
66
|
+
/** Reads the active entry identifier from claims, resource id, or fullUrl. */
|
|
67
|
+
getIdentifier(): string | undefined;
|
|
68
|
+
/** Ensures the active entry carries one canonical identifier. */
|
|
69
|
+
ensureIdentifier(): string;
|
|
70
|
+
/** Sets the active entry `fullUrl` explicitly. */
|
|
71
|
+
setFullUrl(fullUrl?: string | null): this;
|
|
72
|
+
/** Returns the active entry `fullUrl` when present. */
|
|
73
|
+
getFullUrl(): string | undefined;
|
|
74
|
+
/** Convenience employee setter for email. */
|
|
75
|
+
setEmail(email: string): this;
|
|
76
|
+
/** Convenience employee setter for occupational role. */
|
|
77
|
+
setRole(role: string): this;
|
|
78
|
+
/** Convenience employee setter for `worksFor`. */
|
|
79
|
+
setWorksFor(worksFor: string): this;
|
|
80
|
+
/** Convenience employee setter for `memberOf`. */
|
|
81
|
+
setMemberOf(memberOf: string): this;
|
|
82
|
+
/** Convenience employee setter for `memberOf.taxID`. */
|
|
83
|
+
setMemberOfOrgTaxId(taxId: string): this;
|
|
84
|
+
private requireBundleOperation;
|
|
85
|
+
private getRequiredActiveEntry;
|
|
86
|
+
private getActiveEntryClaims;
|
|
87
|
+
private getActiveOrSingleSearchClaims;
|
|
88
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { ClaimsPersonSchemaorg } from '../constants/schemaorg.js';
|
|
2
|
+
import { EmployeeBatchEntryTypes, buildEmployeeBatchEntry, buildEmployeeClaims, buildEmployeePurgeBundle, buildEmployeeSearchBundle, EmployeeBundleMethods, EmployeeBundleOperations, } from './employee.js';
|
|
3
|
+
function cloneEntry(entry) {
|
|
4
|
+
return JSON.parse(JSON.stringify(entry));
|
|
5
|
+
}
|
|
6
|
+
function cloneClaimValue(value) {
|
|
7
|
+
if (Array.isArray(value)) {
|
|
8
|
+
return [...value];
|
|
9
|
+
}
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
function normalizeOptionalIdentifier(value) {
|
|
13
|
+
if (value === undefined || value === null) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const normalized = String(value).trim();
|
|
17
|
+
return normalized ? normalized : undefined;
|
|
18
|
+
}
|
|
19
|
+
function createEmployeeIdentifierUrn() {
|
|
20
|
+
const cryptoLike = globalThis;
|
|
21
|
+
const uuid = typeof cryptoLike.crypto?.randomUUID === 'function'
|
|
22
|
+
? cryptoLike.crypto.randomUUID()
|
|
23
|
+
: `employee-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
24
|
+
return `urn:uuid:${uuid}`;
|
|
25
|
+
}
|
|
26
|
+
function resolveRequestMethodForOperation(operation) {
|
|
27
|
+
switch (operation) {
|
|
28
|
+
case EmployeeBundleOperations.disable:
|
|
29
|
+
return EmployeeBundleMethods.disable;
|
|
30
|
+
case EmployeeBundleOperations.create:
|
|
31
|
+
case EmployeeBundleOperations.purge:
|
|
32
|
+
case EmployeeBundleOperations.search:
|
|
33
|
+
default:
|
|
34
|
+
return EmployeeBundleMethods.create;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function resolveEntryTypeForOperation(operation) {
|
|
38
|
+
switch (operation) {
|
|
39
|
+
case EmployeeBundleOperations.disable:
|
|
40
|
+
return EmployeeBatchEntryTypes.disable;
|
|
41
|
+
case EmployeeBundleOperations.purge:
|
|
42
|
+
return EmployeeBatchEntryTypes.purge;
|
|
43
|
+
case EmployeeBundleOperations.search:
|
|
44
|
+
return EmployeeBatchEntryTypes.search;
|
|
45
|
+
case EmployeeBundleOperations.create:
|
|
46
|
+
default:
|
|
47
|
+
return EmployeeBatchEntryTypes.create;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generic bundle editor with an employee adapter surface.
|
|
52
|
+
*
|
|
53
|
+
* Current scope:
|
|
54
|
+
* - one declared business operation per bundle
|
|
55
|
+
* - active-entry editing through generic claim helpers
|
|
56
|
+
* - employee convenience setters layered on the same active entry
|
|
57
|
+
*
|
|
58
|
+
* This editor lives in `common-utils` because it is runtime-neutral and can be
|
|
59
|
+
* consumed by frontend SDKs, backend SDKs, and backend services.
|
|
60
|
+
*/
|
|
61
|
+
export class BundleEditor {
|
|
62
|
+
bundleOperation = null;
|
|
63
|
+
entries = [];
|
|
64
|
+
activeEntryIndex = null;
|
|
65
|
+
/** Declares which business operation the bundle is assembling. */
|
|
66
|
+
setBundleOperation(operation) {
|
|
67
|
+
this.bundleOperation = operation;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
/** Returns the operation currently assigned to this bundle. */
|
|
71
|
+
getBundleOperation() {
|
|
72
|
+
return this.bundleOperation;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Opens one new active entry.
|
|
76
|
+
*
|
|
77
|
+
* If the current bundle operation needs an identifier and none is supplied,
|
|
78
|
+
* a canonical `urn:uuid:*` identifier is generated and aligned across
|
|
79
|
+
* `fullUrl`, `resource.id`, and `org.schema.Person.identifier`.
|
|
80
|
+
*/
|
|
81
|
+
newEntry(resourceId) {
|
|
82
|
+
const operation = this.requireBundleOperation();
|
|
83
|
+
const normalizedIdentifier = operation === EmployeeBundleOperations.search
|
|
84
|
+
? normalizeOptionalIdentifier(resourceId)
|
|
85
|
+
: (normalizeOptionalIdentifier(resourceId) || createEmployeeIdentifierUrn());
|
|
86
|
+
const claims = normalizedIdentifier
|
|
87
|
+
? buildEmployeeClaims({ identifier: normalizedIdentifier })
|
|
88
|
+
: buildEmployeeClaims({});
|
|
89
|
+
const entry = buildEmployeeBatchEntry({
|
|
90
|
+
type: resolveEntryTypeForOperation(operation),
|
|
91
|
+
method: resolveRequestMethodForOperation(operation),
|
|
92
|
+
resourceId: normalizedIdentifier,
|
|
93
|
+
claims,
|
|
94
|
+
});
|
|
95
|
+
if (normalizedIdentifier) {
|
|
96
|
+
entry.fullUrl = normalizedIdentifier;
|
|
97
|
+
}
|
|
98
|
+
this.entries.push(entry);
|
|
99
|
+
this.activeEntryIndex = this.entries.length - 1;
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
/** Reopens an existing entry by identifier or `fullUrl`. */
|
|
103
|
+
openEntry(resourceId) {
|
|
104
|
+
const normalizedIdentifier = normalizeOptionalIdentifier(resourceId);
|
|
105
|
+
if (!normalizedIdentifier) {
|
|
106
|
+
throw new Error('openEntry requires a non-empty resource identifier.');
|
|
107
|
+
}
|
|
108
|
+
const nextIndex = this.entries.findIndex((entry) => {
|
|
109
|
+
const claims = entry.resource?.meta?.claims || {};
|
|
110
|
+
return normalizeOptionalIdentifier(entry.resource?.id) === normalizedIdentifier
|
|
111
|
+
|| normalizeOptionalIdentifier(entry.fullUrl) === normalizedIdentifier
|
|
112
|
+
|| normalizeOptionalIdentifier(claims[ClaimsPersonSchemaorg.identifier]) === normalizedIdentifier;
|
|
113
|
+
});
|
|
114
|
+
if (nextIndex < 0) {
|
|
115
|
+
throw new Error(`openEntry could not find resource identifier: ${normalizedIdentifier}`);
|
|
116
|
+
}
|
|
117
|
+
this.activeEntryIndex = nextIndex;
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
/** Closes the current active entry while preserving it inside the bundle draft. */
|
|
121
|
+
doneEntry() {
|
|
122
|
+
this.activeEntryIndex = null;
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
/** Returns a cloned snapshot of the currently staged entries. */
|
|
126
|
+
getEntries() {
|
|
127
|
+
return this.entries.map((entry) => cloneEntry(entry));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Builds the final bundle payload for the declared operation.
|
|
131
|
+
*
|
|
132
|
+
* `search` returns one canonical search bundle.
|
|
133
|
+
* `purge` returns a batch bundle whose entries are routed later to
|
|
134
|
+
* `Employee/_purge`.
|
|
135
|
+
* `create` and `disable` return canonical batch bundles.
|
|
136
|
+
*/
|
|
137
|
+
build() {
|
|
138
|
+
const operation = this.requireBundleOperation();
|
|
139
|
+
if (operation === EmployeeBundleOperations.search) {
|
|
140
|
+
const claims = this.getActiveOrSingleSearchClaims();
|
|
141
|
+
return buildEmployeeSearchBundle({ claims });
|
|
142
|
+
}
|
|
143
|
+
if (operation === EmployeeBundleOperations.purge) {
|
|
144
|
+
return {
|
|
145
|
+
resourceType: 'Bundle',
|
|
146
|
+
type: 'batch',
|
|
147
|
+
entry: this.entries.map((entry) => {
|
|
148
|
+
const identifier = normalizeOptionalIdentifier(entry.resource?.meta?.claims?.[ClaimsPersonSchemaorg.identifier]);
|
|
149
|
+
if (!identifier) {
|
|
150
|
+
throw new Error('Every purge entry requires org.schema.Person.identifier.');
|
|
151
|
+
}
|
|
152
|
+
return buildEmployeePurgeBundle({ identifier }).entry[0];
|
|
153
|
+
}),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
resourceType: 'Bundle',
|
|
158
|
+
type: 'batch',
|
|
159
|
+
entry: this.entries.map((entry) => cloneEntry(entry)),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/** Reads one claim from the active entry. */
|
|
163
|
+
getClaim(key) {
|
|
164
|
+
return cloneClaimValue(this.getActiveEntryClaims()[String(key).trim()]);
|
|
165
|
+
}
|
|
166
|
+
/** Checks whether the active entry contains one claim key. */
|
|
167
|
+
hasClaim(key) {
|
|
168
|
+
return Object.prototype.hasOwnProperty.call(this.getActiveEntryClaims(), String(key).trim());
|
|
169
|
+
}
|
|
170
|
+
/** Writes one claim on the active entry. */
|
|
171
|
+
setClaim(key, value) {
|
|
172
|
+
const entry = this.getRequiredActiveEntry();
|
|
173
|
+
entry.resource = entry.resource || { resourceType: 'Employee', meta: { claims: {} } };
|
|
174
|
+
entry.resource.meta = entry.resource.meta || {};
|
|
175
|
+
entry.resource.meta.claims = {
|
|
176
|
+
...(entry.resource.meta.claims || {}),
|
|
177
|
+
[String(key).trim()]: cloneClaimValue(value),
|
|
178
|
+
};
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
/** Appends one claim value on the active entry. */
|
|
182
|
+
addClaim(key, value) {
|
|
183
|
+
const normalizedKey = String(key).trim();
|
|
184
|
+
const current = this.getClaim(normalizedKey);
|
|
185
|
+
if (current === undefined) {
|
|
186
|
+
return this.setClaim(normalizedKey, value);
|
|
187
|
+
}
|
|
188
|
+
if (Array.isArray(current)) {
|
|
189
|
+
return this.setClaim(normalizedKey, [...current, cloneClaimValue(value)]);
|
|
190
|
+
}
|
|
191
|
+
return this.setClaim(normalizedKey, [current, cloneClaimValue(value)]);
|
|
192
|
+
}
|
|
193
|
+
/** Removes one claim from the active entry. */
|
|
194
|
+
removeClaim(key) {
|
|
195
|
+
const entry = this.getRequiredActiveEntry();
|
|
196
|
+
const claims = {
|
|
197
|
+
...(entry.resource?.meta?.claims || {}),
|
|
198
|
+
};
|
|
199
|
+
delete claims[String(key).trim()];
|
|
200
|
+
entry.resource = entry.resource || { resourceType: 'Employee', meta: { claims: {} } };
|
|
201
|
+
entry.resource.meta = entry.resource.meta || {};
|
|
202
|
+
entry.resource.meta.claims = claims;
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Sets the active entry identifier.
|
|
207
|
+
*
|
|
208
|
+
* When informed, the value is synchronized into `fullUrl`, `resource.id`,
|
|
209
|
+
* and the canonical identifier claim.
|
|
210
|
+
* Empty values remove the identifier from all three places.
|
|
211
|
+
*/
|
|
212
|
+
setIdentifier(identifier) {
|
|
213
|
+
const normalized = normalizeOptionalIdentifier(identifier);
|
|
214
|
+
const entry = this.getRequiredActiveEntry();
|
|
215
|
+
if (!normalized) {
|
|
216
|
+
this.removeClaim(ClaimsPersonSchemaorg.identifier);
|
|
217
|
+
delete entry.resource?.id;
|
|
218
|
+
delete entry.fullUrl;
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
this.setClaim(ClaimsPersonSchemaorg.identifier, normalized);
|
|
222
|
+
entry.resource = entry.resource || { resourceType: 'Employee', meta: { claims: {} } };
|
|
223
|
+
entry.resource.id = normalized;
|
|
224
|
+
entry.fullUrl = normalized;
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
/** Reads the active entry identifier from claims, resource id, or fullUrl. */
|
|
228
|
+
getIdentifier() {
|
|
229
|
+
const entry = this.getRequiredActiveEntry();
|
|
230
|
+
return normalizeOptionalIdentifier(entry.resource?.meta?.claims?.[ClaimsPersonSchemaorg.identifier]
|
|
231
|
+
|| entry.resource?.id
|
|
232
|
+
|| entry.fullUrl);
|
|
233
|
+
}
|
|
234
|
+
/** Ensures the active entry carries one canonical identifier. */
|
|
235
|
+
ensureIdentifier() {
|
|
236
|
+
const existing = this.getIdentifier();
|
|
237
|
+
if (existing) {
|
|
238
|
+
return existing;
|
|
239
|
+
}
|
|
240
|
+
const generated = createEmployeeIdentifierUrn();
|
|
241
|
+
this.setIdentifier(generated);
|
|
242
|
+
return generated;
|
|
243
|
+
}
|
|
244
|
+
/** Sets the active entry `fullUrl` explicitly. */
|
|
245
|
+
setFullUrl(fullUrl) {
|
|
246
|
+
const entry = this.getRequiredActiveEntry();
|
|
247
|
+
const normalized = normalizeOptionalIdentifier(fullUrl);
|
|
248
|
+
if (!normalized) {
|
|
249
|
+
delete entry.fullUrl;
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
entry.fullUrl = normalized;
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
/** Returns the active entry `fullUrl` when present. */
|
|
256
|
+
getFullUrl() {
|
|
257
|
+
return normalizeOptionalIdentifier(this.getRequiredActiveEntry().fullUrl);
|
|
258
|
+
}
|
|
259
|
+
/** Convenience employee setter for email. */
|
|
260
|
+
setEmail(email) {
|
|
261
|
+
return this.setClaim(ClaimsPersonSchemaorg.email, String(email).trim());
|
|
262
|
+
}
|
|
263
|
+
/** Convenience employee setter for occupational role. */
|
|
264
|
+
setRole(role) {
|
|
265
|
+
return this.setClaim(ClaimsPersonSchemaorg.hasOccupationalRoleValue, String(role).trim());
|
|
266
|
+
}
|
|
267
|
+
/** Convenience employee setter for `worksFor`. */
|
|
268
|
+
setWorksFor(worksFor) {
|
|
269
|
+
return this.setClaim(ClaimsPersonSchemaorg.worksFor, String(worksFor).trim());
|
|
270
|
+
}
|
|
271
|
+
/** Convenience employee setter for `memberOf`. */
|
|
272
|
+
setMemberOf(memberOf) {
|
|
273
|
+
return this.setClaim(ClaimsPersonSchemaorg.memberOf, String(memberOf).trim());
|
|
274
|
+
}
|
|
275
|
+
/** Convenience employee setter for `memberOf.taxID`. */
|
|
276
|
+
setMemberOfOrgTaxId(taxId) {
|
|
277
|
+
return this.setClaim(ClaimsPersonSchemaorg.memberOfOrgTaxId, String(taxId).trim());
|
|
278
|
+
}
|
|
279
|
+
requireBundleOperation() {
|
|
280
|
+
if (!this.bundleOperation) {
|
|
281
|
+
throw new Error('BundleEditor requires setBundleOperation(...) before newEntry() or build().');
|
|
282
|
+
}
|
|
283
|
+
return this.bundleOperation;
|
|
284
|
+
}
|
|
285
|
+
getRequiredActiveEntry() {
|
|
286
|
+
if (this.activeEntryIndex === null) {
|
|
287
|
+
throw new Error('BundleEditor requires one active entry. Call newEntry(...) or openEntry(...) first.');
|
|
288
|
+
}
|
|
289
|
+
return this.entries[this.activeEntryIndex];
|
|
290
|
+
}
|
|
291
|
+
getActiveEntryClaims() {
|
|
292
|
+
return {
|
|
293
|
+
...(this.getRequiredActiveEntry().resource?.meta?.claims || {}),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
getActiveOrSingleSearchClaims() {
|
|
297
|
+
if (this.entries.length === 0) {
|
|
298
|
+
return {};
|
|
299
|
+
}
|
|
300
|
+
if (this.entries.length > 1) {
|
|
301
|
+
throw new Error('Search bundles currently support one search entry per bundle.');
|
|
302
|
+
}
|
|
303
|
+
const claims = this.entries[0].resource?.meta?.claims || {};
|
|
304
|
+
const searchClaims = {};
|
|
305
|
+
for (const [key, value] of Object.entries(claims)) {
|
|
306
|
+
if (value === undefined
|
|
307
|
+
|| value === null
|
|
308
|
+
|| typeof value === 'string'
|
|
309
|
+
|| typeof value === 'number'
|
|
310
|
+
|| typeof value === 'boolean') {
|
|
311
|
+
searchClaims[key] = value;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return searchClaims;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -38,6 +38,8 @@ export type AddContainedDocumentToActiveEntryInput = Readonly<{
|
|
|
38
38
|
* - `Communication.content-attachment-data` is always derived from the
|
|
39
39
|
* in-memory bundle after each committed update.
|
|
40
40
|
* - saving can release active entry memory via `saveAndReleaseActiveEntry()`.
|
|
41
|
+
* - consent onboarding should prefer semantic helpers first, but this session
|
|
42
|
+
* also exposes direct claim-level editing on the selected active entry.
|
|
41
43
|
*/
|
|
42
44
|
export declare class CommunicationAttachedBundleSession {
|
|
43
45
|
private communicationClaims;
|
|
@@ -53,6 +55,16 @@ export declare class CommunicationAttachedBundleSession {
|
|
|
53
55
|
getActiveEntryIndex(): number | null;
|
|
54
56
|
/** Returns a deep copy of the active entry when selected. */
|
|
55
57
|
getActiveEntry(): BundleEntry | null;
|
|
58
|
+
/** Returns one claim from the currently selected active entry. */
|
|
59
|
+
getActiveEntryClaim(key: string): unknown;
|
|
60
|
+
/** Returns whether the currently selected active entry carries one claim key. */
|
|
61
|
+
hasActiveEntryClaim(key: string): boolean;
|
|
62
|
+
/** Sets one claim on the currently selected active entry and syncs the bundle attachment. */
|
|
63
|
+
setActiveEntryClaim(key: string, value: unknown): this;
|
|
64
|
+
/** Appends one claim value on the currently selected active entry and syncs the bundle attachment. */
|
|
65
|
+
addActiveEntryClaim(key: string, value: unknown): this;
|
|
66
|
+
/** Removes one claim from the currently selected active entry and syncs the bundle attachment. */
|
|
67
|
+
removeActiveEntryClaim(key: string): this;
|
|
56
68
|
/** Selects an active entry by index or fullUrl. */
|
|
57
69
|
selectActiveEntry(selection: ActiveEntrySelection): this;
|
|
58
70
|
/** Clears active entry selection from memory. */
|
|
@@ -164,6 +176,8 @@ export declare class CommunicationAttachedBundleSession {
|
|
|
164
176
|
* Resolves the entry URL (`fullUrl`) for a given entry/resource identifier.
|
|
165
177
|
*/
|
|
166
178
|
getEntryUrl(entryId: string): string | undefined;
|
|
179
|
+
private getRequiredActiveEntry;
|
|
180
|
+
private getRequiredActiveEntryClaims;
|
|
167
181
|
private decodeBundleFromClaims;
|
|
168
182
|
private syncAttachmentFromBundle;
|
|
169
183
|
private resolveCurrentSubject;
|
|
@@ -18,6 +18,8 @@ import { MedicationStatementClaim, } from '../models/interoperable-claims/medica
|
|
|
18
18
|
* - `Communication.content-attachment-data` is always derived from the
|
|
19
19
|
* in-memory bundle after each committed update.
|
|
20
20
|
* - saving can release active entry memory via `saveAndReleaseActiveEntry()`.
|
|
21
|
+
* - consent onboarding should prefer semantic helpers first, but this session
|
|
22
|
+
* also exposes direct claim-level editing on the selected active entry.
|
|
21
23
|
*/
|
|
22
24
|
export class CommunicationAttachedBundleSession {
|
|
23
25
|
communicationClaims;
|
|
@@ -55,6 +57,70 @@ export class CommunicationAttachedBundleSession {
|
|
|
55
57
|
}
|
|
56
58
|
return cloneEntry(this.bundleInMemory.data[this.activeEntryIndex]);
|
|
57
59
|
}
|
|
60
|
+
/** Returns one claim from the currently selected active entry. */
|
|
61
|
+
getActiveEntryClaim(key) {
|
|
62
|
+
const claims = this.getRequiredActiveEntryClaims();
|
|
63
|
+
return cloneUnknownValue(claims[key]);
|
|
64
|
+
}
|
|
65
|
+
/** Returns whether the currently selected active entry carries one claim key. */
|
|
66
|
+
hasActiveEntryClaim(key) {
|
|
67
|
+
const claims = this.getRequiredActiveEntryClaims();
|
|
68
|
+
return Object.prototype.hasOwnProperty.call(claims, key);
|
|
69
|
+
}
|
|
70
|
+
/** Sets one claim on the currently selected active entry and syncs the bundle attachment. */
|
|
71
|
+
setActiveEntryClaim(key, value) {
|
|
72
|
+
const current = cloneEntry(this.getRequiredActiveEntry());
|
|
73
|
+
const resource = ensureEntryResource(current, this.mode);
|
|
74
|
+
resource.meta = resource.meta || {};
|
|
75
|
+
resource.meta.claims = {
|
|
76
|
+
...(resource.meta.claims || {}),
|
|
77
|
+
[String(key).trim()]: cloneUnknownValue(value),
|
|
78
|
+
};
|
|
79
|
+
current.resource = resource;
|
|
80
|
+
this.bundleInMemory.data[this.activeEntryIndex] = current;
|
|
81
|
+
this.syncAttachmentFromBundle();
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
/** Appends one claim value on the currently selected active entry and syncs the bundle attachment. */
|
|
85
|
+
addActiveEntryClaim(key, value) {
|
|
86
|
+
const current = cloneEntry(this.getRequiredActiveEntry());
|
|
87
|
+
const resource = ensureEntryResource(current, this.mode);
|
|
88
|
+
resource.meta = resource.meta || {};
|
|
89
|
+
const claims = {
|
|
90
|
+
...(resource.meta.claims || {}),
|
|
91
|
+
};
|
|
92
|
+
const normalizedKey = String(key).trim();
|
|
93
|
+
const currentValue = claims[normalizedKey];
|
|
94
|
+
if (currentValue === undefined) {
|
|
95
|
+
claims[normalizedKey] = cloneUnknownValue(value);
|
|
96
|
+
}
|
|
97
|
+
else if (Array.isArray(currentValue)) {
|
|
98
|
+
claims[normalizedKey] = [...currentValue, cloneUnknownValue(value)];
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
claims[normalizedKey] = [currentValue, cloneUnknownValue(value)];
|
|
102
|
+
}
|
|
103
|
+
resource.meta.claims = claims;
|
|
104
|
+
current.resource = resource;
|
|
105
|
+
this.bundleInMemory.data[this.activeEntryIndex] = current;
|
|
106
|
+
this.syncAttachmentFromBundle();
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
/** Removes one claim from the currently selected active entry and syncs the bundle attachment. */
|
|
110
|
+
removeActiveEntryClaim(key) {
|
|
111
|
+
const current = cloneEntry(this.getRequiredActiveEntry());
|
|
112
|
+
const resource = ensureEntryResource(current, this.mode);
|
|
113
|
+
resource.meta = resource.meta || {};
|
|
114
|
+
const claims = {
|
|
115
|
+
...(resource.meta.claims || {}),
|
|
116
|
+
};
|
|
117
|
+
delete claims[String(key).trim()];
|
|
118
|
+
resource.meta.claims = claims;
|
|
119
|
+
current.resource = resource;
|
|
120
|
+
this.bundleInMemory.data[this.activeEntryIndex] = current;
|
|
121
|
+
this.syncAttachmentFromBundle();
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
58
124
|
/** Selects an active entry by index or fullUrl. */
|
|
59
125
|
selectActiveEntry(selection) {
|
|
60
126
|
if (typeof selection.index === 'number') {
|
|
@@ -297,6 +363,20 @@ export class CommunicationAttachedBundleSession {
|
|
|
297
363
|
const query = new BundleQuery(this.bundleInMemory);
|
|
298
364
|
return query.getEntryUrl(entryId);
|
|
299
365
|
}
|
|
366
|
+
getRequiredActiveEntry() {
|
|
367
|
+
if (this.activeEntryIndex === null) {
|
|
368
|
+
throw new Error('No active entry selected.');
|
|
369
|
+
}
|
|
370
|
+
return this.bundleInMemory.data[this.activeEntryIndex];
|
|
371
|
+
}
|
|
372
|
+
getRequiredActiveEntryClaims() {
|
|
373
|
+
const current = cloneEntry(this.getRequiredActiveEntry());
|
|
374
|
+
const resource = ensureEntryResource(current, this.mode);
|
|
375
|
+
resource.meta = resource.meta || {};
|
|
376
|
+
return {
|
|
377
|
+
...(resource.meta.claims || {}),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
300
380
|
decodeBundleFromClaims(claims) {
|
|
301
381
|
const encoded = asTrimmedString(claims[CommunicationClaim.ContentAttachmentData]);
|
|
302
382
|
if (!encoded) {
|
|
@@ -495,6 +575,12 @@ function cloneBundle(bundle) {
|
|
|
495
575
|
function cloneEntry(entry) {
|
|
496
576
|
return JSON.parse(JSON.stringify(entry));
|
|
497
577
|
}
|
|
578
|
+
function cloneUnknownValue(value) {
|
|
579
|
+
if (value === undefined) {
|
|
580
|
+
return value;
|
|
581
|
+
}
|
|
582
|
+
return JSON.parse(JSON.stringify(value));
|
|
583
|
+
}
|
|
498
584
|
function asTrimmedString(value) {
|
|
499
585
|
if (value === undefined || value === null) {
|
|
500
586
|
return '';
|
package/dist/utils/employee.d.ts
CHANGED
|
@@ -21,12 +21,39 @@ export type EmployeeBatchEntryInput = Readonly<{
|
|
|
21
21
|
export type EmployeeBatchBundleInput = Readonly<{
|
|
22
22
|
entries: readonly EmployeeBatchEntryInput[];
|
|
23
23
|
}>;
|
|
24
|
+
export type EmployeePurgeBundleInput = Readonly<{
|
|
25
|
+
identifier: string;
|
|
26
|
+
resourceId?: string;
|
|
27
|
+
resourceType?: 'Employee';
|
|
28
|
+
requestType?: (typeof EmployeeBatchEntryTypes)['purge'];
|
|
29
|
+
}>;
|
|
24
30
|
export type EmployeeSearchBundleInput = Readonly<{
|
|
25
31
|
claims?: Record<string, EmployeeSearchValue | undefined>;
|
|
26
32
|
method?: 'GET' | 'POST';
|
|
27
33
|
encoding?: SearchRequestEncoding;
|
|
28
34
|
resourceType?: 'Employee';
|
|
29
35
|
}>;
|
|
36
|
+
export declare const EmployeeBundleOperations: Readonly<{
|
|
37
|
+
readonly create: "create";
|
|
38
|
+
readonly search: "search";
|
|
39
|
+
readonly disable: "disable";
|
|
40
|
+
readonly purge: "purge";
|
|
41
|
+
}>;
|
|
42
|
+
export declare const EmployeeBundleMethods: Readonly<{
|
|
43
|
+
readonly create: "POST";
|
|
44
|
+
readonly search: "POST";
|
|
45
|
+
readonly disable: "DELETE";
|
|
46
|
+
readonly purge: "POST";
|
|
47
|
+
}>;
|
|
48
|
+
export declare const EmployeeBundleRoutes: Readonly<{
|
|
49
|
+
readonly search: "Employee/_search";
|
|
50
|
+
}>;
|
|
51
|
+
export declare const EmployeeBatchEntryTypes: Readonly<{
|
|
52
|
+
readonly create: "Employee-create-request-v1.0";
|
|
53
|
+
readonly disable: "Employee-disable-request-v1.0";
|
|
54
|
+
readonly search: "Employee-search-request-v1.0";
|
|
55
|
+
readonly purge: "Employee-purge-request-v1.0";
|
|
56
|
+
}>;
|
|
30
57
|
/**
|
|
31
58
|
* Builds canonical `org.schema.Person.*` employee claims from semantic input.
|
|
32
59
|
*/
|
|
@@ -60,6 +87,19 @@ export declare function buildEmployeeBatchBundle(input: EmployeeBatchBundleInput
|
|
|
60
87
|
type: 'batch';
|
|
61
88
|
entry: Array<ReturnType<typeof buildEmployeeBatchEntry>>;
|
|
62
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Builds the canonical employee purge batch bundle.
|
|
92
|
+
*
|
|
93
|
+
* Purge is a terminal lifecycle operation routed to the explicit
|
|
94
|
+
* `Employee/_purge` endpoint by runtime layers. The bundle selector should be
|
|
95
|
+
* one concrete employee identity, therefore this helper keeps the payload
|
|
96
|
+
* focused on the canonical employee identifier.
|
|
97
|
+
*/
|
|
98
|
+
export declare function buildEmployeePurgeBundle(input: EmployeePurgeBundleInput): {
|
|
99
|
+
resourceType: 'Bundle';
|
|
100
|
+
type: 'batch';
|
|
101
|
+
entry: Array<ReturnType<typeof buildEmployeeBatchEntry>>;
|
|
102
|
+
};
|
|
63
103
|
/**
|
|
64
104
|
* Builds the legacy query-string employee search target kept for compatibility
|
|
65
105
|
* with older `_search` wrappers.
|
package/dist/utils/employee.js
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
import { ClaimsPersonSchemaorg } from '../constants/schemaorg.js';
|
|
2
2
|
import { buildFhirParametersResourceFromSearchParams, buildSearchQueryString, } from './fhir-search.js';
|
|
3
|
+
export const EmployeeBundleOperations = Object.freeze({
|
|
4
|
+
create: 'create',
|
|
5
|
+
search: 'search',
|
|
6
|
+
disable: 'disable',
|
|
7
|
+
purge: 'purge',
|
|
8
|
+
});
|
|
9
|
+
export const EmployeeBundleMethods = Object.freeze({
|
|
10
|
+
create: 'POST',
|
|
11
|
+
search: 'POST',
|
|
12
|
+
disable: 'DELETE',
|
|
13
|
+
purge: 'POST',
|
|
14
|
+
});
|
|
15
|
+
export const EmployeeBundleRoutes = Object.freeze({
|
|
16
|
+
search: 'Employee/_search',
|
|
17
|
+
});
|
|
18
|
+
export const EmployeeBatchEntryTypes = Object.freeze({
|
|
19
|
+
create: 'Employee-create-request-v1.0',
|
|
20
|
+
disable: 'Employee-disable-request-v1.0',
|
|
21
|
+
search: 'Employee-search-request-v1.0',
|
|
22
|
+
purge: 'Employee-purge-request-v1.0',
|
|
23
|
+
});
|
|
3
24
|
function cloneClaims(claims) {
|
|
4
25
|
return { ...(claims || {}) };
|
|
5
26
|
}
|
|
@@ -15,15 +36,15 @@ function normalizeEmployeeSearchClaims(claims) {
|
|
|
15
36
|
function inferEmployeeEntryType(method) {
|
|
16
37
|
switch (method) {
|
|
17
38
|
case 'DELETE':
|
|
18
|
-
return
|
|
39
|
+
return EmployeeBatchEntryTypes.disable;
|
|
19
40
|
case 'PUT':
|
|
20
41
|
case 'PATCH':
|
|
21
42
|
return 'Employee-update-request-v1.0';
|
|
22
43
|
case 'GET':
|
|
23
|
-
return
|
|
44
|
+
return EmployeeBatchEntryTypes.search;
|
|
24
45
|
case 'POST':
|
|
25
46
|
default:
|
|
26
|
-
return
|
|
47
|
+
return EmployeeBatchEntryTypes.create;
|
|
27
48
|
}
|
|
28
49
|
}
|
|
29
50
|
/**
|
|
@@ -85,6 +106,28 @@ export function buildEmployeeBatchBundle(input) {
|
|
|
85
106
|
entry: [...input.entries].map((entry) => buildEmployeeBatchEntry(entry)),
|
|
86
107
|
};
|
|
87
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Builds the canonical employee purge batch bundle.
|
|
111
|
+
*
|
|
112
|
+
* Purge is a terminal lifecycle operation routed to the explicit
|
|
113
|
+
* `Employee/_purge` endpoint by runtime layers. The bundle selector should be
|
|
114
|
+
* one concrete employee identity, therefore this helper keeps the payload
|
|
115
|
+
* focused on the canonical employee identifier.
|
|
116
|
+
*/
|
|
117
|
+
export function buildEmployeePurgeBundle(input) {
|
|
118
|
+
const identifier = input.identifier.trim();
|
|
119
|
+
return buildEmployeeBatchBundle({
|
|
120
|
+
entries: [
|
|
121
|
+
{
|
|
122
|
+
type: input.requestType || EmployeeBatchEntryTypes.purge,
|
|
123
|
+
method: EmployeeBundleMethods.purge,
|
|
124
|
+
resourceId: input.resourceId || identifier,
|
|
125
|
+
resourceType: input.resourceType,
|
|
126
|
+
claims: buildEmployeeClaims({ identifier }),
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
88
131
|
/**
|
|
89
132
|
* Builds the legacy query-string employee search target kept for compatibility
|
|
90
133
|
* with older `_search` wrappers.
|
|
@@ -103,7 +146,7 @@ export function buildEmployeeSearchQuery(input = {}) {
|
|
|
103
146
|
export function buildEmployeeSearchBundle(input = {}) {
|
|
104
147
|
const resourceType = input.resourceType || 'Employee';
|
|
105
148
|
const claims = normalizeEmployeeSearchClaims(input.claims);
|
|
106
|
-
const method = input.method || (input.encoding === 'get-query' ? 'GET' :
|
|
149
|
+
const method = input.method || (input.encoding === 'get-query' ? 'GET' : EmployeeBundleMethods.search);
|
|
107
150
|
if (method === 'GET') {
|
|
108
151
|
return {
|
|
109
152
|
resourceType: 'Bundle',
|
|
@@ -124,8 +167,8 @@ export function buildEmployeeSearchBundle(input = {}) {
|
|
|
124
167
|
entry: [
|
|
125
168
|
{
|
|
126
169
|
request: {
|
|
127
|
-
method:
|
|
128
|
-
url:
|
|
170
|
+
method: EmployeeBundleMethods.search,
|
|
171
|
+
url: EmployeeBundleRoutes.search,
|
|
129
172
|
},
|
|
130
173
|
resource: buildFhirParametersResourceFromSearchParams(claims),
|
|
131
174
|
},
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from './activation-policy.js';
|
|
|
3
3
|
export * from './base-convert.js';
|
|
4
4
|
export * from './baseN.js';
|
|
5
5
|
export * from './bundle.js';
|
|
6
|
+
export * from './bundle-editor.js';
|
|
6
7
|
export * from './bundle-query.js';
|
|
7
8
|
export * from '../claims/index.js';
|
|
8
9
|
export * from './content.js';
|