gdc-common-utils-ts 1.14.13 → 1.14.14

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # gdc-common-utils-ts
2
2
 
3
+ Employee shared examples live in `src/examples/employee.ts`.
4
+ Employee pure helper functions live in `src/utils/employee.ts`.
5
+
6
+ The canonical employee contract note lives in
7
+ `gdc-sdk-core-ts/docs/EMPLOYEES_101.md`.
8
+
3
9
  Shared TypeScript utilities for GDC client and connector code. This package provides low-level primitives for cryptography, DID/DIDComm-related helpers, and the shared models and interfaces used across SDKs.
4
10
 
5
11
  It is intentionally not a full backend orchestration layer.
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Canonical employee directory fixture shared by docs and tests.
3
+ *
4
+ * Semantics:
5
+ * - `identifier` points to one technical employee profile
6
+ * - `email` can map to multiple active roles
7
+ * - a purged profile remains historically addressable by `identifier`
8
+ */
9
+ export type ExampleEmployeeRecord = Readonly<{
10
+ identifier: string;
11
+ email: string;
12
+ role: string;
13
+ status: 'active' | 'inactive' | 'purged';
14
+ }>;
15
+ export declare const ExampleEmployeeEmails: Readonly<{
16
+ readonly SharedProfessional: "shared.professional@example.org";
17
+ }>;
18
+ export declare const ExampleEmployeeRoles: Readonly<{
19
+ readonly Controller: "ISCO-08|1120";
20
+ readonly Doctor: "ISCO-08|2211";
21
+ }>;
22
+ export declare const EXAMPLE_EMPLOYEE_CONTROLLER_ACTIVE: ExampleEmployeeRecord;
23
+ export declare const EXAMPLE_EMPLOYEE_DOCTOR_ACTIVE: ExampleEmployeeRecord;
24
+ export declare const EXAMPLE_EMPLOYEE_DOCTOR_PURGED_HISTORICAL: ExampleEmployeeRecord;
25
+ export declare const EXAMPLE_EMPLOYEE_DIRECTORY_RECORDS: readonly [Readonly<{
26
+ identifier: string;
27
+ email: string;
28
+ role: string;
29
+ status: "active" | "inactive" | "purged";
30
+ }>, Readonly<{
31
+ identifier: string;
32
+ email: string;
33
+ role: string;
34
+ status: "active" | "inactive" | "purged";
35
+ }>, Readonly<{
36
+ identifier: string;
37
+ email: string;
38
+ role: string;
39
+ status: "active" | "inactive" | "purged";
40
+ }>];
41
+ export declare function buildExampleEmployeeClaims(record: ExampleEmployeeRecord): Readonly<Record<string, string>>;
@@ -0,0 +1,39 @@
1
+ import { ClaimsPersonSchemaorg } from '../constants/schemaorg.js';
2
+ export const ExampleEmployeeEmails = Object.freeze({
3
+ SharedProfessional: 'shared.professional@example.org',
4
+ });
5
+ export const ExampleEmployeeRoles = Object.freeze({
6
+ Controller: 'ISCO-08|1120',
7
+ Doctor: 'ISCO-08|2211',
8
+ });
9
+ export const EXAMPLE_EMPLOYEE_CONTROLLER_ACTIVE = Object.freeze({
10
+ identifier: 'urn:uuid:employee-controller-active-001',
11
+ email: ExampleEmployeeEmails.SharedProfessional,
12
+ role: ExampleEmployeeRoles.Controller,
13
+ status: 'active',
14
+ });
15
+ export const EXAMPLE_EMPLOYEE_DOCTOR_ACTIVE = Object.freeze({
16
+ identifier: 'urn:uuid:employee-doctor-active-001',
17
+ email: ExampleEmployeeEmails.SharedProfessional,
18
+ role: ExampleEmployeeRoles.Doctor,
19
+ status: 'active',
20
+ });
21
+ export const EXAMPLE_EMPLOYEE_DOCTOR_PURGED_HISTORICAL = Object.freeze({
22
+ identifier: 'urn:uuid:employee-doctor-purged-000',
23
+ email: ExampleEmployeeEmails.SharedProfessional,
24
+ role: ExampleEmployeeRoles.Doctor,
25
+ status: 'purged',
26
+ });
27
+ export const EXAMPLE_EMPLOYEE_DIRECTORY_RECORDS = Object.freeze([
28
+ EXAMPLE_EMPLOYEE_CONTROLLER_ACTIVE,
29
+ EXAMPLE_EMPLOYEE_DOCTOR_ACTIVE,
30
+ EXAMPLE_EMPLOYEE_DOCTOR_PURGED_HISTORICAL,
31
+ ]);
32
+ export function buildExampleEmployeeClaims(record) {
33
+ return Object.freeze({
34
+ '@context': 'org.schema',
35
+ [ClaimsPersonSchemaorg.identifier]: record.identifier,
36
+ [ClaimsPersonSchemaorg.email]: record.email,
37
+ [ClaimsPersonSchemaorg.hasOccupationalRoleValue]: record.role,
38
+ });
39
+ }
@@ -4,6 +4,7 @@ export * from './dataspace-discovery';
4
4
  export * from './organization-controller';
5
5
  export * from './individual-controller';
6
6
  export * from './professional';
7
+ export * from './employee';
7
8
  export * from './related-person';
8
9
  export * from './consent-access';
9
10
  export * from './relationship-access';
@@ -4,6 +4,7 @@ export * from './dataspace-discovery.js';
4
4
  export * from './organization-controller.js';
5
5
  export * from './individual-controller.js';
6
6
  export * from './professional.js';
7
+ export * from './employee.js';
7
8
  export * from './related-person.js';
8
9
  export * from './consent-access.js';
9
10
  export * from './relationship-access.js';
@@ -1,5 +1,6 @@
1
1
  import { HealthcareDocumentTypes } from '../constants/healthcare';
2
2
  import type { ParameterData } from '../models/params';
3
+ import { FhirParametersResource } from './fhir-search';
3
4
  export declare const BundleDocumentRequesterKinds: Readonly<{
4
5
  readonly Controller: "controller";
5
6
  readonly Employee: "employee";
@@ -140,6 +141,11 @@ export declare function createSummaryOperationRequestParameters(subjectIdOrInput
140
141
  * path currently stored in `Communication.content-reference`.
141
142
  */
142
143
  export declare function createSummaryOperationRequestReferencePath(parameters: ReadonlyArray<ParameterData>): string;
144
+ /**
145
+ * Builds the preferred FHIR `Parameters` body for the same semantic summary
146
+ * search represented by `createSummaryOperationRequestReferencePath(...)`.
147
+ */
148
+ export declare function createSummaryOperationRequestParametersResource(parameters: ReadonlyArray<ParameterData>): FhirParametersResource;
143
149
  /**
144
150
  * Resolves the full runtime URL to call GW CORE from the provider sector DID
145
151
  * and the generated relative search path.
@@ -3,6 +3,7 @@ import { CommunicationCategoryCodes } from '../constants/communication.js';
3
3
  import { DocumentTypeLoincOntology, HealthcareDocumentTypes } from '../constants/healthcare.js';
4
4
  import { parseActorFromSub } from './actor.js';
5
5
  import { getBaseUrlFromDidWeb } from './did.js';
6
+ import { buildFhirParametersResourceFromParameterData } from './fhir-search.js';
6
7
  import { CommunicationClaim } from '../models/interoperable-claims/communication-claims.js';
7
8
  import { transformCommunicationClaimsToResourceFhirR4 } from './communication-fhir-r4.js';
8
9
  export const BundleDocumentRequesterKinds = Object.freeze({
@@ -260,6 +261,13 @@ export function createSummaryOperationRequestReferencePath(parameters) {
260
261
  }
261
262
  return `individual/org.hl7.fhir.r4/Bundle/_search?${params.filter(Boolean).join('&')}`;
262
263
  }
264
+ /**
265
+ * Builds the preferred FHIR `Parameters` body for the same semantic summary
266
+ * search represented by `createSummaryOperationRequestReferencePath(...)`.
267
+ */
268
+ export function createSummaryOperationRequestParametersResource(parameters) {
269
+ return buildFhirParametersResourceFromParameterData(parameters);
270
+ }
263
271
  /**
264
272
  * Resolves the full runtime URL to call GW CORE from the provider sector DID
265
273
  * and the generated relative search path.
@@ -0,0 +1,75 @@
1
+ import { SearchParameterPrimitive, SearchRequestEncoding } from './fhir-search';
2
+ export type EmployeeClaims = Record<string, unknown>;
3
+ export type EmployeeSearchValue = SearchParameterPrimitive;
4
+ export type EmployeeDraftInput = Readonly<{
5
+ identifier?: string;
6
+ email?: string;
7
+ role?: string;
8
+ worksFor?: string;
9
+ memberOf?: string;
10
+ memberOfOrgTaxId?: string;
11
+ additionalClaims?: EmployeeClaims;
12
+ }>;
13
+ export type EmployeeBatchMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
14
+ export type EmployeeBatchEntryInput = Readonly<{
15
+ method: EmployeeBatchMethod;
16
+ claims: EmployeeClaims;
17
+ resourceId?: string;
18
+ resourceType?: 'Employee';
19
+ type?: string;
20
+ }>;
21
+ export type EmployeeSearchBundleInput = Readonly<{
22
+ claims?: Record<string, EmployeeSearchValue | undefined>;
23
+ method?: 'GET' | 'POST';
24
+ encoding?: SearchRequestEncoding;
25
+ resourceType?: 'Employee';
26
+ }>;
27
+ /**
28
+ * Builds canonical `org.schema.Person.*` employee claims from semantic input.
29
+ */
30
+ export declare function buildEmployeeClaims(input: EmployeeDraftInput): EmployeeClaims;
31
+ /**
32
+ * Builds a claims-first employee batch entry from the minimum semantic input.
33
+ *
34
+ * Callers only provide the operation method, employee claims, and optional
35
+ * resource id. The helper places claims in the canonical `resource.meta.claims`
36
+ * location and infers the business `type` internally.
37
+ */
38
+ export declare function buildEmployeeBatchEntry(input: EmployeeBatchEntryInput): {
39
+ type: string;
40
+ request: {
41
+ method: EmployeeBatchMethod;
42
+ };
43
+ resource: {
44
+ resourceType: 'Employee';
45
+ id?: string;
46
+ meta: {
47
+ claims: EmployeeClaims;
48
+ };
49
+ };
50
+ };
51
+ /**
52
+ * Builds the legacy query-string employee search target kept for compatibility
53
+ * with older `_search` wrappers.
54
+ */
55
+ export declare function buildEmployeeSearchQuery(input?: EmployeeSearchBundleInput): string;
56
+ /**
57
+ * Builds a canonical employee search bundle.
58
+ *
59
+ * Defaults to `POST + Parameters`. Set `method` or `encoding` to legacy GET
60
+ * only when talking to older search consumers.
61
+ */
62
+ export declare function buildEmployeeSearchBundle(input?: EmployeeSearchBundleInput): {
63
+ resourceType: 'Bundle';
64
+ type: 'batch';
65
+ entry: Array<{
66
+ request: {
67
+ method: 'GET' | 'POST';
68
+ url: string;
69
+ };
70
+ resource?: {
71
+ resourceType: 'Parameters';
72
+ parameter: Array<Record<string, unknown>>;
73
+ };
74
+ }>;
75
+ };
@@ -0,0 +1,114 @@
1
+ import { ClaimsPersonSchemaorg } from '../constants/schemaorg.js';
2
+ import { buildFhirParametersResourceFromSearchParams, buildSearchQueryString, } from './fhir-search.js';
3
+ function cloneClaims(claims) {
4
+ return { ...(claims || {}) };
5
+ }
6
+ function inferEmployeeEntryType(method) {
7
+ switch (method) {
8
+ case 'DELETE':
9
+ return 'Employee-delete-request-v1.0';
10
+ case 'PUT':
11
+ case 'PATCH':
12
+ return 'Employee-update-request-v1.0';
13
+ case 'GET':
14
+ return 'Employee-search-request-v1.0';
15
+ case 'POST':
16
+ default:
17
+ return 'Employee-create-request-v1.0';
18
+ }
19
+ }
20
+ /**
21
+ * Builds canonical `org.schema.Person.*` employee claims from semantic input.
22
+ */
23
+ export function buildEmployeeClaims(input) {
24
+ const claims = {
25
+ '@context': 'org.schema',
26
+ ...cloneClaims(input.additionalClaims),
27
+ };
28
+ if (typeof input.identifier === 'string' && input.identifier.trim()) {
29
+ claims[ClaimsPersonSchemaorg.identifier] = input.identifier.trim();
30
+ }
31
+ if (typeof input.email === 'string' && input.email.trim()) {
32
+ claims[ClaimsPersonSchemaorg.email] = input.email.trim();
33
+ }
34
+ if (typeof input.role === 'string' && input.role.trim()) {
35
+ claims[ClaimsPersonSchemaorg.hasOccupationalRoleValue] = input.role.trim();
36
+ }
37
+ if (typeof input.worksFor === 'string' && input.worksFor.trim()) {
38
+ claims[ClaimsPersonSchemaorg.worksFor] = input.worksFor.trim();
39
+ }
40
+ if (typeof input.memberOf === 'string' && input.memberOf.trim()) {
41
+ claims[ClaimsPersonSchemaorg.memberOf] = input.memberOf.trim();
42
+ }
43
+ if (typeof input.memberOfOrgTaxId === 'string' && input.memberOfOrgTaxId.trim()) {
44
+ claims[ClaimsPersonSchemaorg.memberOfOrgTaxId] = input.memberOfOrgTaxId.trim();
45
+ }
46
+ return claims;
47
+ }
48
+ /**
49
+ * Builds a claims-first employee batch entry from the minimum semantic input.
50
+ *
51
+ * Callers only provide the operation method, employee claims, and optional
52
+ * resource id. The helper places claims in the canonical `resource.meta.claims`
53
+ * location and infers the business `type` internally.
54
+ */
55
+ export function buildEmployeeBatchEntry(input) {
56
+ const claims = cloneClaims(input.claims);
57
+ const resourceType = input.resourceType || 'Employee';
58
+ return {
59
+ type: input.type || inferEmployeeEntryType(input.method),
60
+ request: { method: input.method },
61
+ resource: {
62
+ resourceType,
63
+ ...(input.resourceId ? { id: input.resourceId } : {}),
64
+ meta: { claims },
65
+ },
66
+ };
67
+ }
68
+ /**
69
+ * Builds the legacy query-string employee search target kept for compatibility
70
+ * with older `_search` wrappers.
71
+ */
72
+ export function buildEmployeeSearchQuery(input = {}) {
73
+ const resourceType = input.resourceType || 'Employee';
74
+ const query = buildSearchQueryString(input.claims || {});
75
+ return query ? `${resourceType}?${query}` : resourceType;
76
+ }
77
+ /**
78
+ * Builds a canonical employee search bundle.
79
+ *
80
+ * Defaults to `POST + Parameters`. Set `method` or `encoding` to legacy GET
81
+ * only when talking to older search consumers.
82
+ */
83
+ export function buildEmployeeSearchBundle(input = {}) {
84
+ const resourceType = input.resourceType || 'Employee';
85
+ const claims = input.claims || {};
86
+ const method = input.method || (input.encoding === 'get-query' ? 'GET' : 'POST');
87
+ if (method === 'GET') {
88
+ return {
89
+ resourceType: 'Bundle',
90
+ type: 'batch',
91
+ entry: [
92
+ {
93
+ request: {
94
+ method: 'GET',
95
+ url: buildEmployeeSearchQuery({ ...input, resourceType }),
96
+ },
97
+ },
98
+ ],
99
+ };
100
+ }
101
+ return {
102
+ resourceType: 'Bundle',
103
+ type: 'batch',
104
+ entry: [
105
+ {
106
+ request: {
107
+ method: 'POST',
108
+ url: `${resourceType}/_search`,
109
+ },
110
+ resource: buildFhirParametersResourceFromSearchParams(claims),
111
+ },
112
+ ],
113
+ };
114
+ }
@@ -0,0 +1,26 @@
1
+ import { ParameterData } from '../models/params';
2
+ export type SearchParameterPrimitive = string | number | boolean | readonly (string | number | boolean)[];
3
+ export type SearchRequestEncoding = 'get-query' | 'post-parameters';
4
+ export type FhirParametersParameter = {
5
+ name: string;
6
+ valueString?: string;
7
+ valueCode?: string;
8
+ valueBoolean?: boolean;
9
+ valueInteger?: number;
10
+ valueDecimal?: number;
11
+ valueUri?: string;
12
+ valueReference?: {
13
+ reference: string;
14
+ };
15
+ valueCoding?: {
16
+ system?: string;
17
+ code: string;
18
+ };
19
+ };
20
+ export type FhirParametersResource = {
21
+ resourceType: 'Parameters';
22
+ parameter: FhirParametersParameter[];
23
+ };
24
+ export declare function buildSearchQueryString(searchParams: Readonly<Record<string, SearchParameterPrimitive | undefined>>): string;
25
+ export declare function buildFhirParametersResourceFromSearchParams(searchParams: Readonly<Record<string, SearchParameterPrimitive | undefined>>): FhirParametersResource;
26
+ export declare function buildFhirParametersResourceFromParameterData(parameters: ReadonlyArray<ParameterData>): FhirParametersResource;
@@ -0,0 +1,84 @@
1
+ function normalizeSearchPrimitiveValues(value) {
2
+ const values = Array.isArray(value) ? [...value] : [value];
3
+ return values
4
+ .map((item) => typeof item === 'string' ? item.trim() : item)
5
+ .filter((item) => item !== '' && item !== undefined && item !== null);
6
+ }
7
+ function toPrimitiveString(value) {
8
+ return typeof value === 'string' ? value.trim() : String(value);
9
+ }
10
+ function toSearchParameter(name, value) {
11
+ if (typeof value === 'boolean') {
12
+ return { name, valueBoolean: value };
13
+ }
14
+ if (typeof value === 'number') {
15
+ return Number.isInteger(value)
16
+ ? { name, valueInteger: value }
17
+ : { name, valueDecimal: value };
18
+ }
19
+ return { name, valueString: value.trim() };
20
+ }
21
+ function flattenParameterDataValue(parameter) {
22
+ const name = String(parameter?.name || '').trim();
23
+ if (!name) {
24
+ return undefined;
25
+ }
26
+ if (parameter.type === 'reference') {
27
+ const reference = String(parameter.reference || parameter.value || '').trim();
28
+ return reference ? { name, valueReference: { reference } } : undefined;
29
+ }
30
+ if (parameter.type === 'token') {
31
+ const code = String(parameter.value || '').trim();
32
+ if (!code) {
33
+ return undefined;
34
+ }
35
+ return parameter.system
36
+ ? { name, valueCoding: { system: String(parameter.system).trim(), code } }
37
+ : { name, valueCode: code };
38
+ }
39
+ if (parameter.type === 'uri') {
40
+ const value = String(parameter.value || '').trim();
41
+ return value ? { name, valueUri: value } : undefined;
42
+ }
43
+ if (typeof parameter.value === 'number') {
44
+ return Number.isInteger(parameter.value)
45
+ ? { name, valueInteger: parameter.value }
46
+ : { name, valueDecimal: parameter.value };
47
+ }
48
+ const value = String(parameter.value || '').trim();
49
+ return value ? { name, valueString: value } : undefined;
50
+ }
51
+ export function buildSearchQueryString(searchParams) {
52
+ const params = new URLSearchParams();
53
+ for (const [key, value] of Object.entries(searchParams)) {
54
+ if (value === undefined || value === null)
55
+ continue;
56
+ const normalized = normalizeSearchPrimitiveValues(value);
57
+ if (normalized.length === 0)
58
+ continue;
59
+ params.set(key, normalized.map(toPrimitiveString).join(','));
60
+ }
61
+ return params.toString();
62
+ }
63
+ export function buildFhirParametersResourceFromSearchParams(searchParams) {
64
+ const parameter = [];
65
+ for (const [name, value] of Object.entries(searchParams)) {
66
+ if (value === undefined || value === null)
67
+ continue;
68
+ for (const item of normalizeSearchPrimitiveValues(value)) {
69
+ parameter.push(toSearchParameter(name, item));
70
+ }
71
+ }
72
+ return {
73
+ resourceType: 'Parameters',
74
+ parameter,
75
+ };
76
+ }
77
+ export function buildFhirParametersResourceFromParameterData(parameters) {
78
+ return {
79
+ resourceType: 'Parameters',
80
+ parameter: parameters
81
+ .map((parameter) => flattenParameterDataValue(parameter))
82
+ .filter((parameter) => Boolean(parameter)),
83
+ };
84
+ }
@@ -14,12 +14,14 @@ export * from './did-resolution';
14
14
  export * from './dataspace-discovery';
15
15
  export * from './dataspace-discovery-defaults';
16
16
  export * from './dataspace-protocol';
17
+ export * from './employee';
17
18
  export * from './didcomm';
18
19
  export * from './didcomm-submit';
19
20
  export * from './didcomm-submit-policy';
20
21
  export * from './discovery-normalization';
21
22
  export * from './format-converter';
22
23
  export * from './fhir-cid';
24
+ export * from './fhir-search';
23
25
  export * from './communication-fhir-r4';
24
26
  export * from './communication-document-reference';
25
27
  export * from './communication-bundle-document-request';
@@ -14,12 +14,14 @@ export * from './did-resolution.js';
14
14
  export * from './dataspace-discovery.js';
15
15
  export * from './dataspace-discovery-defaults.js';
16
16
  export * from './dataspace-protocol.js';
17
+ export * from './employee.js';
17
18
  export * from './didcomm.js';
18
19
  export * from './didcomm-submit.js';
19
20
  export * from './didcomm-submit-policy.js';
20
21
  export * from './discovery-normalization.js';
21
22
  export * from './format-converter.js';
22
23
  export * from './fhir-cid.js';
24
+ export * from './fhir-search.js';
23
25
  export * from './communication-fhir-r4.js';
24
26
  export * from './communication-document-reference.js';
25
27
  export * from './communication-bundle-document-request.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "1.14.13",
3
+ "version": "1.14.14",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },