gdc-common-utils-ts 1.20.0 → 1.20.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.
@@ -109,6 +109,20 @@ export declare enum ClaimsOfferSchemaorg {
109
109
  priceCurrency = "org.schema.Offer.priceCurrency",
110
110
  serialNumber = "org.schema.Offer.serialNumber"
111
111
  }
112
+ /**
113
+ * Canonical claim names used by the current GDC profile when a VC or flat
114
+ * request models a `schema.org/IndividualProduct`.
115
+ *
116
+ * Current GW licensing semantics use this family to represent:
117
+ * - the concrete seat/license serial number
118
+ * - the license category (`professional` or `individual`)
119
+ * - the target app/device form factor (`mobile` or `web`)
120
+ */
121
+ export declare enum ClaimsIndividualProductSchemaorg {
122
+ category = "org.schema.IndividualProduct.category",
123
+ additionalType = "org.schema.IndividualProduct.additionalType",
124
+ serialNumber = "org.schema.IndividualProduct.serialNumber"
125
+ }
112
126
  export declare enum ClaimsOrderSchemaorg {
113
127
  orderedItemServiceType = "org.schema.Order.orderedItem.serviceType"
114
128
  }
@@ -114,6 +114,21 @@ export var ClaimsOfferSchemaorg;
114
114
  ClaimsOfferSchemaorg["priceCurrency"] = "org.schema.Offer.priceCurrency";
115
115
  ClaimsOfferSchemaorg["serialNumber"] = "org.schema.Offer.serialNumber";
116
116
  })(ClaimsOfferSchemaorg || (ClaimsOfferSchemaorg = {}));
117
+ /**
118
+ * Canonical claim names used by the current GDC profile when a VC or flat
119
+ * request models a `schema.org/IndividualProduct`.
120
+ *
121
+ * Current GW licensing semantics use this family to represent:
122
+ * - the concrete seat/license serial number
123
+ * - the license category (`professional` or `individual`)
124
+ * - the target app/device form factor (`mobile` or `web`)
125
+ */
126
+ export var ClaimsIndividualProductSchemaorg;
127
+ (function (ClaimsIndividualProductSchemaorg) {
128
+ ClaimsIndividualProductSchemaorg["category"] = "org.schema.IndividualProduct.category";
129
+ ClaimsIndividualProductSchemaorg["additionalType"] = "org.schema.IndividualProduct.additionalType";
130
+ ClaimsIndividualProductSchemaorg["serialNumber"] = "org.schema.IndividualProduct.serialNumber";
131
+ })(ClaimsIndividualProductSchemaorg || (ClaimsIndividualProductSchemaorg = {}));
117
132
  export var ClaimsOrderSchemaorg;
118
133
  (function (ClaimsOrderSchemaorg) {
119
134
  ClaimsOrderSchemaorg["orderedItemServiceType"] = "org.schema.Order.orderedItem.serviceType";
@@ -88,6 +88,10 @@ export type ServiceCapabilityTokenValue = typeof ServiceCapabilityToken[keyof ty
88
88
  * Normalizes a service capability token into its canonical persisted form.
89
89
  */
90
90
  export declare function normalizeServiceCapability(value: string | undefined | null): string | undefined;
91
+ /**
92
+ * Returns whether the token is one of the known persisted service capabilities.
93
+ */
94
+ export declare function isKnownServiceCapability(value: string | undefined | null): boolean;
91
95
  /**
92
96
  * Parses the CSV stored in `org.schema.Service.serviceType`.
93
97
  */
@@ -108,6 +108,15 @@ export function normalizeServiceCapability(value) {
108
108
  return undefined;
109
109
  return CANONICAL_SERVICE_CAPABILITY_BY_VALUE.get(normalized.toLowerCase()) || normalized;
110
110
  }
111
+ /**
112
+ * Returns whether the token is one of the known persisted service capabilities.
113
+ */
114
+ export function isKnownServiceCapability(value) {
115
+ const normalized = String(value || '').trim().toLowerCase();
116
+ if (!normalized)
117
+ return false;
118
+ return CANONICAL_SERVICE_CAPABILITY_BY_VALUE.has(normalized);
119
+ }
111
120
  /**
112
121
  * Parses the CSV stored in `org.schema.Service.serviceType`.
113
122
  */
@@ -5,6 +5,7 @@ export * from './organization-controller';
5
5
  export * from './individual-controller';
6
6
  export * from './professional';
7
7
  export * from './employee';
8
+ export * from './license';
8
9
  export * from './related-person';
9
10
  export * from './consent-access';
10
11
  export * from './relationship-access';
@@ -5,6 +5,7 @@ export * from './organization-controller.js';
5
5
  export * from './individual-controller.js';
6
6
  export * from './professional.js';
7
7
  export * from './employee.js';
8
+ export * from './license.js';
8
9
  export * from './related-person.js';
9
10
  export * from './consent-access.js';
10
11
  export * from './relationship-access.js';
@@ -0,0 +1,32 @@
1
+ export declare const EXAMPLE_LICENSE_SEAT_UUIDS: readonly ["8a8a5e1b-0d8e-4a7c-8c39-3b8034440001", "8a8a5e1b-0d8e-4a7c-8c39-3b8034440002"];
2
+ export declare const EXAMPLE_LICENSE_ISSUE_INPUT: Readonly<{
3
+ readonly email: "controller@acme.org";
4
+ readonly role: "ISCO-08|2211";
5
+ readonly userClass: "employee";
6
+ readonly type: "mobile";
7
+ }>;
8
+ export declare const EXAMPLE_LICENSE_PURCHASE_INPUT: Readonly<{
9
+ readonly quantity: 2;
10
+ readonly userClass: "employee";
11
+ readonly type: "web";
12
+ readonly serialNumbers: readonly ["8a8a5e1b-0d8e-4a7c-8c39-3b8034440001", "8a8a5e1b-0d8e-4a7c-8c39-3b8034440002"];
13
+ }>;
14
+ export declare const EXAMPLE_LICENSE_ACTIVE_RECORD: Readonly<{
15
+ readonly id: "8a8a5e1b-0d8e-4a7c-8c39-3b8034440001";
16
+ readonly status: "active";
17
+ readonly activationCode: "ACT-001";
18
+ readonly subjectId: "urn:uuid:employee-controller-active-001";
19
+ readonly claims: {
20
+ readonly '@context': "org.schema";
21
+ readonly "org.schema.IndividualProduct.serialNumber": "8a8a5e1b-0d8e-4a7c-8c39-3b8034440001";
22
+ readonly "org.schema.IndividualProduct.category": "professional";
23
+ readonly "org.schema.IndividualProduct.additionalType": "mobile";
24
+ readonly "org.schema.Person.email": "controller@acme.org";
25
+ readonly "org.schema.Person.hasOccupation.identifier.value": "ISCO-08|2211";
26
+ };
27
+ }>;
28
+ export declare function buildExampleLicenseIssueClaims(): Readonly<Record<string, unknown>>;
29
+ export declare function buildExampleLicensePurchaseClaims(): Readonly<Record<string, unknown>>;
30
+ export declare const EXAMPLE_LICENSE_PURCHASE_CLAIMS: Readonly<Record<string, unknown>>;
31
+ export declare const EXAMPLE_LICENSE_ISSUE_CLAIMS: Readonly<Record<string, unknown>>;
32
+ export declare const EXAMPLE_LICENSE_PURCHASE_EXPECTED_SERIAL_NUMBER: string;
@@ -0,0 +1,43 @@
1
+ import { DeviceAppTypes, DeviceUserClasses } from '../constants/device.js';
2
+ import { ClaimsIndividualProductSchemaorg, ClaimsOfferSchemaorg, ClaimsPersonSchemaorg, } from '../constants/schemaorg.js';
3
+ import { buildLicenseIssueClaims, buildLicensePurchaseClaims, LicenseCategories, LicenseStatuses, } from '../utils/license.js';
4
+ import { EXAMPLE_EMAIL_CONTROLLER_ORG, EXAMPLE_EMPLOYEE_ACTIVATION_CODE, EXAMPLE_HEALTHCARE_ACTOR_ROLE_GENERALIST_MEDICAL_PRACTITIONER, } from './shared.js';
5
+ export const EXAMPLE_LICENSE_SEAT_UUIDS = Object.freeze([
6
+ '8a8a5e1b-0d8e-4a7c-8c39-3b8034440001',
7
+ '8a8a5e1b-0d8e-4a7c-8c39-3b8034440002',
8
+ ]);
9
+ export const EXAMPLE_LICENSE_ISSUE_INPUT = Object.freeze({
10
+ email: EXAMPLE_EMAIL_CONTROLLER_ORG,
11
+ role: EXAMPLE_HEALTHCARE_ACTOR_ROLE_GENERALIST_MEDICAL_PRACTITIONER,
12
+ userClass: DeviceUserClasses.Employee,
13
+ type: DeviceAppTypes.Mobile,
14
+ });
15
+ export const EXAMPLE_LICENSE_PURCHASE_INPUT = Object.freeze({
16
+ quantity: EXAMPLE_LICENSE_SEAT_UUIDS.length,
17
+ userClass: DeviceUserClasses.Employee,
18
+ type: DeviceAppTypes.Web,
19
+ serialNumbers: EXAMPLE_LICENSE_SEAT_UUIDS,
20
+ });
21
+ export const EXAMPLE_LICENSE_ACTIVE_RECORD = Object.freeze({
22
+ id: EXAMPLE_LICENSE_SEAT_UUIDS[0],
23
+ status: LicenseStatuses.Active,
24
+ activationCode: EXAMPLE_EMPLOYEE_ACTIVATION_CODE,
25
+ subjectId: 'urn:uuid:employee-controller-active-001',
26
+ claims: {
27
+ '@context': 'org.schema',
28
+ [ClaimsIndividualProductSchemaorg.serialNumber]: EXAMPLE_LICENSE_SEAT_UUIDS[0],
29
+ [ClaimsIndividualProductSchemaorg.category]: LicenseCategories.Professional,
30
+ [ClaimsIndividualProductSchemaorg.additionalType]: DeviceAppTypes.Mobile,
31
+ [ClaimsPersonSchemaorg.email]: EXAMPLE_EMAIL_CONTROLLER_ORG,
32
+ [ClaimsPersonSchemaorg.hasOccupationalRoleValue]: EXAMPLE_HEALTHCARE_ACTOR_ROLE_GENERALIST_MEDICAL_PRACTITIONER,
33
+ },
34
+ });
35
+ export function buildExampleLicenseIssueClaims() {
36
+ return Object.freeze(buildLicenseIssueClaims(EXAMPLE_LICENSE_ISSUE_INPUT));
37
+ }
38
+ export function buildExampleLicensePurchaseClaims() {
39
+ return Object.freeze(buildLicensePurchaseClaims(EXAMPLE_LICENSE_PURCHASE_INPUT));
40
+ }
41
+ export const EXAMPLE_LICENSE_PURCHASE_CLAIMS = buildExampleLicensePurchaseClaims();
42
+ export const EXAMPLE_LICENSE_ISSUE_CLAIMS = buildExampleLicenseIssueClaims();
43
+ export const EXAMPLE_LICENSE_PURCHASE_EXPECTED_SERIAL_NUMBER = EXAMPLE_LICENSE_PURCHASE_CLAIMS[ClaimsOfferSchemaorg.serialNumber];
@@ -1,7 +1,7 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
2
  import { ClaimsOrganizationSchemaorg, ClaimsServiceSchemaorg } from '../constants/schemaorg.js';
3
3
  import { isEuCountryCode, normalizeCountryCode } from '../constants/eu-countries.js';
4
- import { isProviderServiceCapability, parseServiceCapabilityTokens, } from '../constants/service-capabilities.js';
4
+ import { isKnownServiceCapability, isProviderServiceCapability, parseServiceCapabilityTokens, } from '../constants/service-capabilities.js';
5
5
  import { DataspaceCoverageScope, } from '../models/dataspace-discovery.js';
6
6
  function asObject(value) {
7
7
  return value && typeof value === 'object' && !Array.isArray(value)
@@ -142,13 +142,20 @@ export function parseServiceTypeCsv(value) {
142
142
  }
143
143
  return parseServiceCapabilityTokens(value);
144
144
  }
145
+ function parseAdditionalTypeCapabilityCsv(value) {
146
+ if (Array.isArray(value)) {
147
+ return Array.from(new Set(value.flatMap((entry) => parseAdditionalTypeCapabilityCsv(entry))));
148
+ }
149
+ return parseServiceCapabilityTokens(value)
150
+ .filter((item) => isKnownServiceCapability(item));
151
+ }
145
152
  /**
146
153
  * Parses service capability tokens from both `serviceType` and `additionalType`.
147
154
  */
148
155
  export function parseServiceTypeClaims(serviceTypeValue, additionalTypeValue) {
149
156
  return Array.from(new Set([
150
157
  ...parseServiceTypeCsv(serviceTypeValue),
151
- ...parseServiceTypeCsv(additionalTypeValue),
158
+ ...parseAdditionalTypeCapabilityCsv(additionalTypeValue),
152
159
  ]));
153
160
  }
154
161
  /**
@@ -43,6 +43,7 @@ export * from './individual-onboarding-document-reference';
43
43
  export * from './individual-organization-kyc';
44
44
  export * from './ips-bundle-claims';
45
45
  export * from './jwt';
46
+ export * from './license';
46
47
  export * from './manager-error';
47
48
  export * from './multibase58';
48
49
  export * from './multibasehash';
@@ -52,5 +53,6 @@ export * from './object-convert';
52
53
  export * from './normalize-uuid';
53
54
  export * from './permission-templates';
54
55
  export * from './smart-scope';
56
+ export * from './service-act-reasons';
55
57
  export * from './activation-request';
56
58
  export * from './vp-token';
@@ -43,6 +43,7 @@ export * from './individual-onboarding-document-reference.js';
43
43
  export * from './individual-organization-kyc.js';
44
44
  export * from './ips-bundle-claims.js';
45
45
  export * from './jwt.js';
46
+ export * from './license.js';
46
47
  export * from './manager-error.js';
47
48
  export * from './multibase58.js';
48
49
  export * from './multibasehash.js';
@@ -52,5 +53,6 @@ export * from './object-convert.js';
52
53
  export * from './normalize-uuid.js';
53
54
  export * from './permission-templates.js';
54
55
  export * from './smart-scope.js';
56
+ export * from './service-act-reasons.js';
55
57
  export * from './activation-request.js';
56
58
  export * from './vp-token.js';
@@ -0,0 +1,149 @@
1
+ import { DeviceAppType, DeviceUserClass } from '../constants/device';
2
+ export type LicenseClaims = Record<string, unknown>;
3
+ export declare const LicenseClaimContext: Readonly<{
4
+ readonly SchemaOrg: "org.schema";
5
+ }>;
6
+ /**
7
+ * Canonical business categories currently used by GW licensing flows.
8
+ *
9
+ * Mapping:
10
+ * - employee -> professional
11
+ * - individual -> individual
12
+ */
13
+ export declare const LicenseCategories: Readonly<{
14
+ readonly Professional: "professional";
15
+ readonly Individual: "individual";
16
+ }>;
17
+ export type LicenseCategory = typeof LicenseCategories[keyof typeof LicenseCategories];
18
+ /**
19
+ * Stable helper-level request type ids for license-oriented batch entries.
20
+ *
21
+ * These are request metadata strings emitted by SDK/common-utils helpers so
22
+ * callers do not handcraft type labels inline.
23
+ */
24
+ export declare const LicenseEntryTypes: Readonly<{
25
+ readonly Issue: "License-issue-request-v1.0";
26
+ readonly Purchase: "License-purchase-request-v1.0";
27
+ readonly Search: "License-search-request-v1.0";
28
+ }>;
29
+ export declare const LicenseEntryOperations: Readonly<{
30
+ readonly Issue: "IndividualProduct:Issue";
31
+ readonly Purchase: "IndividualProduct:Purchase";
32
+ readonly Search: "IndividualProduct:Search";
33
+ }>;
34
+ export declare const LicenseRequestMethods: Readonly<{
35
+ readonly Post: "POST";
36
+ }>;
37
+ /**
38
+ * Runtime-facing lifecycle values commonly used when searching stored
39
+ * `DeviceLicense` documents.
40
+ *
41
+ * These are not schema.org claims. They are operational filters that runtime
42
+ * layers may attach alongside the canonical schema.org claims.
43
+ */
44
+ export declare const LicenseStatuses: Readonly<{
45
+ readonly Available: "available";
46
+ readonly Issued: "issued";
47
+ readonly Active: "active";
48
+ readonly Inactive: "inactive";
49
+ }>;
50
+ export type LicenseStatus = typeof LicenseStatuses[keyof typeof LicenseStatuses];
51
+ export type LicenseIssueInput = Readonly<{
52
+ email: string;
53
+ role: string;
54
+ userClass?: DeviceUserClass;
55
+ type?: DeviceAppType;
56
+ additionalClaims?: LicenseClaims;
57
+ }>;
58
+ export type LicensePurchaseInput = Readonly<{
59
+ quantity: number;
60
+ userClass?: DeviceUserClass;
61
+ type?: DeviceAppType;
62
+ serialNumbers?: readonly string[];
63
+ additionalClaims?: LicenseClaims;
64
+ }>;
65
+ export type LicenseSearchInput = Readonly<{
66
+ serialNumbers?: readonly string[];
67
+ userClass?: DeviceUserClass;
68
+ type?: DeviceAppType;
69
+ email?: string;
70
+ role?: string;
71
+ status?: LicenseStatus;
72
+ subjectId?: string;
73
+ additionalClaims?: LicenseClaims;
74
+ }>;
75
+ /**
76
+ * Maps one canonical runtime user class to the schema.org-compatible
77
+ * `IndividualProduct.category` value used by current GW flows.
78
+ */
79
+ export declare function mapLicenseCategoryFromUserClass(userClass?: DeviceUserClass): LicenseCategory;
80
+ /**
81
+ * Converts a list of seat ids / serial numbers into the compact string form
82
+ * currently stored in `org.schema.Offer.serialNumber`.
83
+ */
84
+ export declare function serializeLicenseSerialNumbers(serialNumbers?: readonly string[]): string | undefined;
85
+ /**
86
+ * Builds canonical `org.schema.*` claims for `License/_issue`.
87
+ *
88
+ * The resulting payload keeps current GW semantics:
89
+ * - recipient identity/role comes from `Person.*`
90
+ * - seat/app semantics come from `IndividualProduct.*`
91
+ */
92
+ export declare function buildLicenseIssueClaims(input: LicenseIssueInput): LicenseClaims;
93
+ /**
94
+ * Builds the canonical batch entry for `License/_issue`.
95
+ */
96
+ export declare function buildLicenseIssueEntry(input: LicenseIssueInput): {
97
+ type: string;
98
+ request: {
99
+ method: 'POST';
100
+ };
101
+ meta: {
102
+ claims: LicenseClaims;
103
+ };
104
+ };
105
+ /**
106
+ * Builds canonical schema.org claims for a license purchase / seat-creation
107
+ * request.
108
+ *
109
+ * Current business semantics:
110
+ * - `Offer.eligibleQuantity.value` requests how many seats must exist
111
+ * - `Offer.serialNumber` can optionally carry the explicit seat ids when they
112
+ * are already known or preallocated
113
+ * - `IndividualProduct.*` classifies the target seat family/form factor
114
+ */
115
+ export declare function buildLicensePurchaseClaims(input: LicensePurchaseInput): LicenseClaims;
116
+ /**
117
+ * Builds the canonical batch entry for a purchase/order-oriented license
118
+ * request.
119
+ */
120
+ export declare function buildLicensePurchaseEntry(input: LicensePurchaseInput): {
121
+ type: string;
122
+ request: {
123
+ method: 'POST';
124
+ };
125
+ meta: {
126
+ claims: LicenseClaims;
127
+ };
128
+ };
129
+ /**
130
+ * Builds the canonical search envelope for license listing.
131
+ *
132
+ * Split of concerns:
133
+ * - schema.org-compatible selectors stay in `meta.claims`
134
+ * - document lifecycle stays in `meta.status`, mirroring the current
135
+ * `ConfidentialStorageDoc.status` usage
136
+ * - `subjectId` remains an explicit side-field until a canonical indexed claim
137
+ * is introduced for that lookup
138
+ */
139
+ export declare function buildLicenseSearchEntry(input: LicenseSearchInput): {
140
+ type: string;
141
+ request: {
142
+ method: 'POST';
143
+ };
144
+ meta: {
145
+ claims: LicenseClaims;
146
+ status?: LicenseStatus;
147
+ subjectId?: string;
148
+ };
149
+ };
@@ -0,0 +1,188 @@
1
+ import { DeviceAppTypes, DeviceUserClasses } from '../constants/device.js';
2
+ import { ClaimsIndividualProductSchemaorg, ClaimsOfferSchemaorg, ClaimsPersonSchemaorg, } from '../constants/schemaorg.js';
3
+ export const LicenseClaimContext = Object.freeze({
4
+ SchemaOrg: 'org.schema',
5
+ });
6
+ /**
7
+ * Canonical business categories currently used by GW licensing flows.
8
+ *
9
+ * Mapping:
10
+ * - employee -> professional
11
+ * - individual -> individual
12
+ */
13
+ export const LicenseCategories = Object.freeze({
14
+ Professional: 'professional',
15
+ Individual: 'individual',
16
+ });
17
+ /**
18
+ * Stable helper-level request type ids for license-oriented batch entries.
19
+ *
20
+ * These are request metadata strings emitted by SDK/common-utils helpers so
21
+ * callers do not handcraft type labels inline.
22
+ */
23
+ export const LicenseEntryTypes = Object.freeze({
24
+ Issue: 'License-issue-request-v1.0',
25
+ Purchase: 'License-purchase-request-v1.0',
26
+ Search: 'License-search-request-v1.0',
27
+ });
28
+ export const LicenseEntryOperations = Object.freeze({
29
+ Issue: 'IndividualProduct:Issue',
30
+ Purchase: 'IndividualProduct:Purchase',
31
+ Search: 'IndividualProduct:Search',
32
+ });
33
+ export const LicenseRequestMethods = Object.freeze({
34
+ Post: 'POST',
35
+ });
36
+ /**
37
+ * Runtime-facing lifecycle values commonly used when searching stored
38
+ * `DeviceLicense` documents.
39
+ *
40
+ * These are not schema.org claims. They are operational filters that runtime
41
+ * layers may attach alongside the canonical schema.org claims.
42
+ */
43
+ export const LicenseStatuses = Object.freeze({
44
+ Available: 'available',
45
+ Issued: 'issued',
46
+ Active: 'active',
47
+ Inactive: 'inactive',
48
+ });
49
+ function cloneClaims(claims) {
50
+ return { ...(claims || {}) };
51
+ }
52
+ /**
53
+ * Maps one canonical runtime user class to the schema.org-compatible
54
+ * `IndividualProduct.category` value used by current GW flows.
55
+ */
56
+ export function mapLicenseCategoryFromUserClass(userClass = DeviceUserClasses.Employee) {
57
+ return userClass === DeviceUserClasses.Individual
58
+ ? LicenseCategories.Individual
59
+ : LicenseCategories.Professional;
60
+ }
61
+ /**
62
+ * Converts a list of seat ids / serial numbers into the compact string form
63
+ * currently stored in `org.schema.Offer.serialNumber`.
64
+ */
65
+ export function serializeLicenseSerialNumbers(serialNumbers) {
66
+ const normalized = (serialNumbers || [])
67
+ .map((value) => String(value || '').trim())
68
+ .filter(Boolean);
69
+ return normalized.length > 0 ? normalized.join(',') : undefined;
70
+ }
71
+ /**
72
+ * Builds canonical `org.schema.*` claims for `License/_issue`.
73
+ *
74
+ * The resulting payload keeps current GW semantics:
75
+ * - recipient identity/role comes from `Person.*`
76
+ * - seat/app semantics come from `IndividualProduct.*`
77
+ */
78
+ export function buildLicenseIssueClaims(input) {
79
+ const claims = {
80
+ '@context': LicenseClaimContext.SchemaOrg,
81
+ ...cloneClaims(input.additionalClaims),
82
+ [ClaimsPersonSchemaorg.email]: input.email.trim(),
83
+ [ClaimsPersonSchemaorg.hasOccupationalRoleValue]: input.role.trim(),
84
+ [ClaimsIndividualProductSchemaorg.category]: mapLicenseCategoryFromUserClass(input.userClass || DeviceUserClasses.Employee),
85
+ [ClaimsIndividualProductSchemaorg.additionalType]: input.type || DeviceAppTypes.Mobile,
86
+ };
87
+ return claims;
88
+ }
89
+ /**
90
+ * Builds the canonical batch entry for `License/_issue`.
91
+ */
92
+ export function buildLicenseIssueEntry(input) {
93
+ return {
94
+ type: LicenseEntryTypes.Issue,
95
+ request: { method: LicenseRequestMethods.Post },
96
+ meta: {
97
+ claims: {
98
+ ...buildLicenseIssueClaims(input),
99
+ '@type': LicenseEntryOperations.Issue,
100
+ },
101
+ },
102
+ };
103
+ }
104
+ /**
105
+ * Builds canonical schema.org claims for a license purchase / seat-creation
106
+ * request.
107
+ *
108
+ * Current business semantics:
109
+ * - `Offer.eligibleQuantity.value` requests how many seats must exist
110
+ * - `Offer.serialNumber` can optionally carry the explicit seat ids when they
111
+ * are already known or preallocated
112
+ * - `IndividualProduct.*` classifies the target seat family/form factor
113
+ */
114
+ export function buildLicensePurchaseClaims(input) {
115
+ const claims = {
116
+ '@context': LicenseClaimContext.SchemaOrg,
117
+ ...cloneClaims(input.additionalClaims),
118
+ [ClaimsIndividualProductSchemaorg.category]: mapLicenseCategoryFromUserClass(input.userClass || DeviceUserClasses.Employee),
119
+ [ClaimsIndividualProductSchemaorg.additionalType]: input.type || DeviceAppTypes.Mobile,
120
+ [ClaimsOfferSchemaorg.eligibleQuantityValue]: input.quantity,
121
+ };
122
+ const serializedSerialNumbers = serializeLicenseSerialNumbers(input.serialNumbers);
123
+ if (serializedSerialNumbers) {
124
+ claims[ClaimsOfferSchemaorg.serialNumber] = serializedSerialNumbers;
125
+ }
126
+ return claims;
127
+ }
128
+ /**
129
+ * Builds the canonical batch entry for a purchase/order-oriented license
130
+ * request.
131
+ */
132
+ export function buildLicensePurchaseEntry(input) {
133
+ return {
134
+ type: LicenseEntryTypes.Purchase,
135
+ request: { method: LicenseRequestMethods.Post },
136
+ meta: {
137
+ claims: {
138
+ ...buildLicensePurchaseClaims(input),
139
+ '@type': LicenseEntryOperations.Purchase,
140
+ },
141
+ },
142
+ };
143
+ }
144
+ /**
145
+ * Builds the canonical search envelope for license listing.
146
+ *
147
+ * Split of concerns:
148
+ * - schema.org-compatible selectors stay in `meta.claims`
149
+ * - document lifecycle stays in `meta.status`, mirroring the current
150
+ * `ConfidentialStorageDoc.status` usage
151
+ * - `subjectId` remains an explicit side-field until a canonical indexed claim
152
+ * is introduced for that lookup
153
+ */
154
+ export function buildLicenseSearchEntry(input) {
155
+ const claims = {
156
+ '@context': LicenseClaimContext.SchemaOrg,
157
+ ...cloneClaims(input.additionalClaims),
158
+ };
159
+ if (input.serialNumbers && input.serialNumbers.length > 0) {
160
+ claims[ClaimsOfferSchemaorg.serialNumber] = serializeLicenseSerialNumbers(input.serialNumbers);
161
+ }
162
+ if (input.userClass) {
163
+ claims[ClaimsIndividualProductSchemaorg.category] = mapLicenseCategoryFromUserClass(input.userClass);
164
+ }
165
+ if (input.type) {
166
+ claims[ClaimsIndividualProductSchemaorg.additionalType] = input.type;
167
+ }
168
+ if (typeof input.email === 'string' && input.email.trim()) {
169
+ claims[ClaimsPersonSchemaorg.email] = input.email.trim();
170
+ }
171
+ if (typeof input.role === 'string' && input.role.trim()) {
172
+ claims[ClaimsPersonSchemaorg.hasOccupationalRoleValue] = input.role.trim();
173
+ }
174
+ return {
175
+ type: LicenseEntryTypes.Search,
176
+ request: { method: LicenseRequestMethods.Post },
177
+ meta: {
178
+ claims: {
179
+ ...claims,
180
+ '@type': LicenseEntryOperations.Search,
181
+ },
182
+ ...(input.status ? { status: input.status } : {}),
183
+ ...(typeof input.subjectId === 'string' && input.subjectId.trim()
184
+ ? { subjectId: input.subjectId.trim() }
185
+ : {}),
186
+ },
187
+ };
188
+ }
@@ -0,0 +1,16 @@
1
+ export declare const HL7_ACT_REASON_CODE_SYSTEM = "http://terminology.hl7.org/CodeSystem/v3-ActReason";
2
+ export type ServiceActReasonCoding = Readonly<{
3
+ system: string;
4
+ code: string;
5
+ }>;
6
+ /**
7
+ * Parses the compact CSV form used by flat GDC claims for `Service.additionalType`.
8
+ *
9
+ * Accepted examples:
10
+ * - `http://terminology.hl7.org/CodeSystem/v3-ActReason|METAMGT,HRESCH`
11
+ * - `http://terminology.hl7.org/CodeSystem/v3-ActReason|METAMGT,http://terminology.hl7.org/CodeSystem/v3-ActReason|HRESCH`
12
+ */
13
+ export declare function parseServiceActReasonCodings(value: unknown): ServiceActReasonCoding[];
14
+ export declare function parseServiceActReasonCodes(value: unknown): string[];
15
+ export declare function serializeServiceActReasonCodings(values: ReadonlyArray<ServiceActReasonCoding | undefined | null>): string | undefined;
16
+ export declare function serializeServiceActReasonCodes(codes: ReadonlyArray<string | undefined | null>, system?: string): string | undefined;
@@ -0,0 +1,68 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ export const HL7_ACT_REASON_CODE_SYSTEM = 'http://terminology.hl7.org/CodeSystem/v3-ActReason';
3
+ function normalizeText(value) {
4
+ return typeof value === 'string' ? value.trim() : '';
5
+ }
6
+ function normalizeCoding(input) {
7
+ const system = normalizeText(input?.system);
8
+ const code = normalizeText(input?.code).toUpperCase();
9
+ if (!system || !code)
10
+ return undefined;
11
+ return { system, code };
12
+ }
13
+ /**
14
+ * Parses the compact CSV form used by flat GDC claims for `Service.additionalType`.
15
+ *
16
+ * Accepted examples:
17
+ * - `http://terminology.hl7.org/CodeSystem/v3-ActReason|METAMGT,HRESCH`
18
+ * - `http://terminology.hl7.org/CodeSystem/v3-ActReason|METAMGT,http://terminology.hl7.org/CodeSystem/v3-ActReason|HRESCH`
19
+ */
20
+ export function parseServiceActReasonCodings(value) {
21
+ const raw = normalizeText(value);
22
+ if (!raw)
23
+ return [];
24
+ let currentSystem = '';
25
+ const codings = [];
26
+ for (const token of raw.split(',')) {
27
+ const normalizedToken = normalizeText(token);
28
+ if (!normalizedToken)
29
+ continue;
30
+ const pipeIndex = normalizedToken.indexOf('|');
31
+ if (pipeIndex >= 0) {
32
+ currentSystem = normalizeText(normalizedToken.slice(0, pipeIndex));
33
+ const coding = normalizeCoding({
34
+ system: currentSystem,
35
+ code: normalizedToken.slice(pipeIndex + 1),
36
+ });
37
+ if (coding)
38
+ codings.push(coding);
39
+ continue;
40
+ }
41
+ const coding = normalizeCoding({
42
+ system: currentSystem,
43
+ code: normalizedToken,
44
+ });
45
+ if (coding)
46
+ codings.push(coding);
47
+ }
48
+ return Array.from(new Map(codings.map((coding) => [`${coding.system}|${coding.code}`, coding])).values());
49
+ }
50
+ export function parseServiceActReasonCodes(value) {
51
+ return parseServiceActReasonCodings(value).map((coding) => coding.code);
52
+ }
53
+ export function serializeServiceActReasonCodings(values) {
54
+ const normalized = values
55
+ .map((value) => normalizeCoding(value || undefined))
56
+ .filter((value) => Boolean(value));
57
+ if (!normalized.length)
58
+ return undefined;
59
+ const unique = Array.from(new Map(normalized.map((coding) => [`${coding.system}|${coding.code}`, coding])).values());
60
+ const firstSystem = unique[0]?.system || '';
61
+ if (firstSystem && unique.every((coding) => coding.system === firstSystem)) {
62
+ return `${firstSystem}|${unique.map((coding) => coding.code).join(',')}`;
63
+ }
64
+ return unique.map((coding) => `${coding.system}|${coding.code}`).join(',');
65
+ }
66
+ export function serializeServiceActReasonCodes(codes, system = HL7_ACT_REASON_CODE_SYSTEM) {
67
+ return serializeServiceActReasonCodings(codes.map((code) => ({ system, code: normalizeText(code).toUpperCase() })));
68
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "1.20.0",
3
+ "version": "1.20.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },