gdc-common-utils-ts 2.5.3 → 2.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/constants/data-capabilities.d.ts +7 -7
  2. package/dist/constants/device.d.ts +6 -0
  3. package/dist/constants/device.js +5 -0
  4. package/dist/constants/employee-lifecycle.d.ts +52 -0
  5. package/dist/constants/employee-lifecycle.js +48 -0
  6. package/dist/constants/healthcare.d.ts +2 -0
  7. package/dist/constants/healthcare.js +2 -0
  8. package/dist/constants/http.d.ts +28 -0
  9. package/dist/constants/http.js +28 -0
  10. package/dist/constants/identity-auth.d.ts +57 -0
  11. package/dist/constants/identity-auth.js +57 -0
  12. package/dist/constants/index.d.ts +4 -0
  13. package/dist/constants/index.js +4 -0
  14. package/dist/constants/organization-application.d.ts +27 -0
  15. package/dist/constants/organization-application.js +29 -0
  16. package/dist/constants/verifiable-credentials.d.ts +6 -2
  17. package/dist/constants/verifiable-credentials.js +6 -2
  18. package/dist/examples/inter-tenant-access-contract.d.ts +7 -7
  19. package/dist/examples/inter-tenant-access-contract.js +3 -2
  20. package/dist/examples/license.d.ts +111 -0
  21. package/dist/examples/license.js +85 -2
  22. package/dist/examples/shared.d.ts +33 -0
  23. package/dist/examples/shared.js +32 -0
  24. package/dist/models/device-license.d.ts +2 -2
  25. package/dist/models/index.d.ts +2 -0
  26. package/dist/models/index.js +2 -0
  27. package/dist/models/inter-tenant-access-contract.d.ts +1 -1
  28. package/dist/models/inter-tenant-access-contract.js +1 -1
  29. package/dist/models/organization-employee-lifecycle.d.ts +45 -0
  30. package/dist/models/organization-employee-lifecycle.js +1 -0
  31. package/dist/models/test-network-organization-registration-form.d.ts +71 -0
  32. package/dist/models/test-network-organization-registration-form.js +43 -0
  33. package/dist/utils/index.d.ts +3 -1
  34. package/dist/utils/index.js +3 -1
  35. package/dist/utils/jwk-thumbprint.d.ts +2 -0
  36. package/dist/utils/jwk-thumbprint.js +8 -0
  37. package/dist/utils/legal-organization-verification-transaction.d.ts +5 -0
  38. package/dist/utils/legal-organization-verification-transaction.js +3 -0
  39. package/dist/utils/license.js +3 -3
  40. package/dist/utils/organization-employee-lifecycle.d.ts +12 -0
  41. package/dist/utils/organization-employee-lifecycle.js +125 -0
  42. package/dist/utils/{organization-registration-authorization.d.ts → organization-test-network-credential.d.ts} +4 -4
  43. package/dist/utils/{organization-registration-authorization.js → organization-test-network-credential.js} +6 -6
  44. package/dist/utils/test-network-organization-credentials.d.ts +35 -0
  45. package/dist/utils/test-network-organization-credentials.js +144 -0
  46. package/package.json +1 -1
@@ -0,0 +1,125 @@
1
+ import { ClaimsIndividualProductSchemaorg, ClaimsPersonSchemaorg } from '../constants/schemaorg.js';
2
+ import { DeviceBindingStatuses } from '../constants/device.js';
3
+ import { EmployeeDirectoryStatuses } from '../constants/employee-lifecycle.js';
4
+ import { IdentityAuthRequestFields, IdentityAuthResponseEntryTypes, } from '../constants/identity-auth.js';
5
+ import { DEFAULT_LICENSE_DEVICE_ALLOWANCE } from './license.js';
6
+ /** Builds the exact identity/auth revoke request body from a typed target. */
7
+ export function buildEmployeeDeviceRevocationBody(target) {
8
+ return Object.freeze({
9
+ [IdentityAuthRequestFields.LicenseId]: target.licenseId,
10
+ [IdentityAuthRequestFields.ClientId]: target.clientId,
11
+ });
12
+ }
13
+ /** Reads an employee activation credential from canonical and legacy GW envelopes. */
14
+ export function readEmployeeActivationCode(value) {
15
+ for (const candidate of listNestedRecords(value)) {
16
+ if (candidate.type === IdentityAuthResponseEntryTypes.LicenseIssued) {
17
+ const issuedId = text(candidate.id);
18
+ if (issuedId)
19
+ return issuedId;
20
+ }
21
+ const claims = record(candidate.claims) || record(record(candidate.meta)?.claims);
22
+ const serial = text(claims?.[ClaimsIndividualProductSchemaorg.serialNumber]);
23
+ if (serial)
24
+ return serial;
25
+ }
26
+ return '';
27
+ }
28
+ /** Extracts GW search rows regardless of async job envelope depth. */
29
+ export function extractGwSearchResources(value) {
30
+ for (const candidate of listNestedRecords(value)) {
31
+ const data = record(candidate.resource)?.data || candidate.data;
32
+ if (Array.isArray(data))
33
+ return data.map(record).filter(isRecord);
34
+ }
35
+ return [];
36
+ }
37
+ /** Combines employee directory and license search results into one typed view. */
38
+ export function projectOrganizationEmployeeLifecycle(input) {
39
+ const licenses = extractGwSearchResources(input.licenseResponse);
40
+ return extractGwSearchResources(input.employeeResponse).map((employee) => {
41
+ const claims = record(employee.claims) || record(record(employee.meta)?.claims) || {};
42
+ const resourceId = text(employee.id);
43
+ const employeeDid = text(claims[ClaimsPersonSchemaorg.identifier]);
44
+ const license = licenses.find((candidate) => {
45
+ const meta = record(candidate.meta) || {};
46
+ const subjectId = text(meta.subjectId);
47
+ return subjectId === resourceId || subjectId === employeeDid;
48
+ });
49
+ const status = normalizeEmployeeDirectoryStatus(text(employee.status) || text(record(employee.meta)?.status));
50
+ if (!license)
51
+ return {
52
+ resourceId,
53
+ email: text(claims[ClaimsPersonSchemaorg.email]),
54
+ roleCode: text(claims[ClaimsPersonSchemaorg.hasOccupationalRoleValue]),
55
+ employeeDid,
56
+ status,
57
+ };
58
+ const meta = record(license.meta) || {};
59
+ const bindings = Array.isArray(meta.deviceBindings) ? meta.deviceBindings.map(record).filter(isRecord) : [];
60
+ const devices = bindings.map((binding) => {
61
+ const info = record(binding.deviceInfo) || {};
62
+ const clientId = text(binding.clientId);
63
+ return {
64
+ clientId,
65
+ name: text(info.model) || text(info.clientInstanceId) || clientId,
66
+ status: binding.status === DeviceBindingStatuses.Revoked
67
+ ? DeviceBindingStatuses.Revoked
68
+ : DeviceBindingStatuses.Active,
69
+ };
70
+ }).filter((device) => Boolean(device.clientId));
71
+ return {
72
+ resourceId,
73
+ email: text(claims[ClaimsPersonSchemaorg.email]),
74
+ roleCode: text(claims[ClaimsPersonSchemaorg.hasOccupationalRoleValue]),
75
+ employeeDid,
76
+ status,
77
+ license: {
78
+ id: text(license.id),
79
+ status: text(meta.status),
80
+ maxDevices: positiveInteger(meta.maxDevices) || DEFAULT_LICENSE_DEVICE_ALLOWANCE,
81
+ activeDevices: devices.filter((device) => device.status === DeviceBindingStatuses.Active).length,
82
+ devices,
83
+ },
84
+ };
85
+ }).filter((employee) => Boolean(employee.resourceId));
86
+ }
87
+ function normalizeEmployeeDirectoryStatus(value) {
88
+ return Object.values(EmployeeDirectoryStatuses).includes(value)
89
+ ? value
90
+ : EmployeeDirectoryStatuses.Unknown;
91
+ }
92
+ function listNestedRecords(root) {
93
+ const result = [];
94
+ const pending = [root];
95
+ const visited = new Set();
96
+ while (pending.length > 0) {
97
+ const value = pending.shift();
98
+ if (!value || typeof value !== 'object' || visited.has(value))
99
+ continue;
100
+ visited.add(value);
101
+ if (Array.isArray(value)) {
102
+ pending.push(...value);
103
+ continue;
104
+ }
105
+ const item = value;
106
+ result.push(item);
107
+ pending.push(...Object.values(item));
108
+ }
109
+ return result;
110
+ }
111
+ function record(value) {
112
+ return value && typeof value === 'object' && !Array.isArray(value)
113
+ ? value
114
+ : undefined;
115
+ }
116
+ function isRecord(value) {
117
+ return Boolean(value);
118
+ }
119
+ function text(value) {
120
+ return typeof value === 'string' ? value.trim() : '';
121
+ }
122
+ function positiveInteger(value) {
123
+ const parsed = Number(value);
124
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
125
+ }
@@ -1,5 +1,5 @@
1
1
  import type { VerifiableCredentialV2 } from '../models/verifiable-credential';
2
- /** Lifecycle of the one GW activation licence delivered to the legal postal address. */
2
+ /** Lifecycle of the one Test Network activation licence delivered to the legal postal address. */
3
3
  export declare const PostalActivationLicenseStatuses: Readonly<{
4
4
  readonly Issued: "issued";
5
5
  readonly Delivered: "delivered";
@@ -35,7 +35,7 @@ export type PostalActivationLicenseBinding = Readonly<{
35
35
  redeemedAt?: string;
36
36
  }>;
37
37
  /** Input for the credential returned out-of-band to the applicant controller. */
38
- export type OrganizationRegistrationAuthorizationCredentialInput = Readonly<{
38
+ export type OrganizationTestNetworkCredentialInput = Readonly<{
39
39
  issuerDid: string;
40
40
  subjectDid: string;
41
41
  credentialId: string;
@@ -56,7 +56,7 @@ export type OrganizationRegistrationAuthorizationCredentialInput = Readonly<{
56
56
  * to `Organization/_transaction`. The postal code itself never appears in the
57
57
  * credential; its confirmed, purpose-bound licence record is referenced.
58
58
  */
59
- export declare function buildOrganizationRegistrationAuthorizationCredential(input: OrganizationRegistrationAuthorizationCredentialInput): VerifiableCredentialV2;
59
+ export declare function buildOrganizationTestNetworkCredential(input: OrganizationTestNetworkCredentialInput): VerifiableCredentialV2;
60
60
  /**
61
61
  * Applies the only legal state transitions for the purpose-bound postal code.
62
62
  * Confirmation proves delivery but deliberately does not consume the code;
@@ -68,4 +68,4 @@ export declare function transitionPostalActivationLicense(current: PostalActivat
68
68
  * same credential payload with the complete `proof` property removed, so a
69
69
  * later counter-proof cannot invalidate an earlier one.
70
70
  */
71
- export declare function canonicalizeOrganizationRegistrationAuthorizationCredential(credential: VerifiableCredentialV2): string;
71
+ export declare function canonicalizeOrganizationTestNetworkCredential(credential: VerifiableCredentialV2): string;
@@ -1,5 +1,5 @@
1
1
  import { ContractCredentialTypes, W3cCredentialContexts, W3cCredentialTypes, } from '../constants/verifiable-credentials.js';
2
- /** Lifecycle of the one GW activation licence delivered to the legal postal address. */
2
+ /** Lifecycle of the one Test Network activation licence delivered to the legal postal address. */
3
3
  export const PostalActivationLicenseStatuses = Object.freeze({
4
4
  Issued: 'issued',
5
5
  Delivered: 'delivered',
@@ -18,7 +18,7 @@ function required(value, name) {
18
18
  * to `Organization/_transaction`. The postal code itself never appears in the
19
19
  * credential; its confirmed, purpose-bound licence record is referenced.
20
20
  */
21
- export function buildOrganizationRegistrationAuthorizationCredential(input) {
21
+ export function buildOrganizationTestNetworkCredential(input) {
22
22
  const applicationId = required(input.applicationId, 'applicationId');
23
23
  const organizationIdentifier = required(input.organizationIdentifier, 'organizationIdentifier');
24
24
  const controllerEmail = required(input.controllerEmail, 'controllerEmail').toLowerCase();
@@ -37,7 +37,7 @@ export function buildOrganizationRegistrationAuthorizationCredential(input) {
37
37
  id: required(input.credentialId, 'credentialId'),
38
38
  type: [
39
39
  W3cCredentialTypes.VerifiableCredential,
40
- ContractCredentialTypes.OrganizationRegistrationAuthorizationCredential,
40
+ ContractCredentialTypes.OrganizationTestNetworkCredential,
41
41
  ],
42
42
  issuer: required(input.issuerDid, 'issuerDid'),
43
43
  credentialSubject: {
@@ -109,9 +109,9 @@ function canonicalizeValue(value) {
109
109
  * same credential payload with the complete `proof` property removed, so a
110
110
  * later counter-proof cannot invalidate an earlier one.
111
111
  */
112
- export function canonicalizeOrganizationRegistrationAuthorizationCredential(credential) {
113
- if (!credential.type.includes(ContractCredentialTypes.OrganizationRegistrationAuthorizationCredential)) {
114
- throw new Error('Credential is not an organization registration authorization.');
112
+ export function canonicalizeOrganizationTestNetworkCredential(credential) {
113
+ if (!credential.type.includes(ContractCredentialTypes.OrganizationTestNetworkCredential)) {
114
+ throw new Error('Credential is not an organization Test Network admission.');
115
115
  }
116
116
  return JSON.stringify(canonicalizeValue(credential));
117
117
  }
@@ -0,0 +1,35 @@
1
+ import type { VerifiableCredentialV2 } from '../models/verifiable-credential';
2
+ export declare const TestNetworkOrganizationCredentialTypes: readonly ["OrganizationCredential", "LegalRepresentativeCredential", "ServiceControllerCredential"];
3
+ export type TestNetworkOrganizationCredentialSet = readonly [
4
+ VerifiableCredentialV2,
5
+ VerifiableCredentialV2,
6
+ VerifiableCredentialV2
7
+ ];
8
+ export type BuildTestNetworkOrganizationCredentialSetInput = Readonly<{
9
+ issuerDid: string;
10
+ organizationDid: string;
11
+ applicationId: string;
12
+ validFrom: string;
13
+ validUntil: string;
14
+ pdfSha256: string;
15
+ documentVersion: string;
16
+ legalName: string;
17
+ organizationIdentifier: string;
18
+ identifierType: string;
19
+ addressCountry: string;
20
+ serviceCategory: string;
21
+ legalRepresentativeEmail: string;
22
+ legalRepresentativeFullName: string;
23
+ controllerEmail: string;
24
+ controllerKeyMaterial: string;
25
+ }>;
26
+ /**
27
+ * Builds the three schema.org credentials used by organization activation in
28
+ * both environments. In this profile the reviewer/host-provider DID is the
29
+ * issuer, every subject is restricted to `test-network`, and the immutable PDF
30
+ * digest is common evidence. The function creates drafts; a reviewer proof
31
+ * must still be added to every returned credential.
32
+ */
33
+ export declare function buildTestNetworkOrganizationCredentialSet(input: BuildTestNetworkOrganizationCredentialSetInput): TestNetworkOrganizationCredentialSet;
34
+ /** Deterministic detached-proof payload for one of the three Test Network VCs. */
35
+ export declare function canonicalizeTestNetworkOrganizationCredential(credential: VerifiableCredentialV2): string;
@@ -0,0 +1,144 @@
1
+ import { ActivationCredentialTypes, EnvironmentCredentialTypes, W3cCredentialContexts, W3cCredentialTypes, } from '../constants/verifiable-credentials.js';
2
+ import { buildStableActorIdentifier } from './actor-identifier.js';
3
+ export const TestNetworkOrganizationCredentialTypes = Object.freeze([
4
+ ActivationCredentialTypes.OrganizationCredential,
5
+ ActivationCredentialTypes.LegalRepresentativeCredential,
6
+ ActivationCredentialTypes.ServiceControllerCredential,
7
+ ]);
8
+ function required(value, name) {
9
+ const normalized = String(value || '').trim();
10
+ if (!normalized)
11
+ throw new Error(`Test Network organization credentials require ${name}.`);
12
+ return normalized;
13
+ }
14
+ /**
15
+ * Builds the three schema.org credentials used by organization activation in
16
+ * both environments. In this profile the reviewer/host-provider DID is the
17
+ * issuer, every subject is restricted to `test-network`, and the immutable PDF
18
+ * digest is common evidence. The function creates drafts; a reviewer proof
19
+ * must still be added to every returned credential.
20
+ */
21
+ export function buildTestNetworkOrganizationCredentialSet(input) {
22
+ const issuer = required(input.issuerDid, 'issuerDid');
23
+ const organizationDid = required(input.organizationDid, 'organizationDid');
24
+ const applicationId = required(input.applicationId, 'applicationId');
25
+ const validFrom = required(input.validFrom, 'validFrom');
26
+ const validUntil = required(input.validUntil, 'validUntil');
27
+ const legalName = required(input.legalName, 'legalName');
28
+ const organizationIdentifier = required(input.organizationIdentifier, 'organizationIdentifier');
29
+ const identifierType = required(input.identifierType, 'identifierType');
30
+ const addressCountry = required(input.addressCountry, 'addressCountry').toUpperCase();
31
+ const serviceCategory = required(input.serviceCategory, 'serviceCategory');
32
+ const legalRepresentativeEmail = required(input.legalRepresentativeEmail, 'legalRepresentativeEmail').toLowerCase();
33
+ const legalRepresentativeFullName = required(input.legalRepresentativeFullName, 'legalRepresentativeFullName');
34
+ const controllerEmail = required(input.controllerEmail, 'controllerEmail').toLowerCase();
35
+ const evidence = [{
36
+ type: 'DocumentVerification',
37
+ id: `urn:sha256:${required(input.pdfSha256, 'pdfSha256')}`,
38
+ documentVersion: required(input.documentVersion, 'documentVersion'),
39
+ applicationId,
40
+ targetNetwork: 'test-network',
41
+ }];
42
+ const base = {
43
+ '@context': [W3cCredentialContexts.V2, 'https://schema.org'],
44
+ issuer,
45
+ validFrom,
46
+ validUntil,
47
+ evidence,
48
+ };
49
+ const organization = {
50
+ ...base,
51
+ id: `urn:uuid:${applicationId}:organization`,
52
+ type: [
53
+ W3cCredentialTypes.VerifiableCredential,
54
+ ActivationCredentialTypes.OrganizationCredential,
55
+ EnvironmentCredentialTypes.TestNetworkCredential,
56
+ ],
57
+ credentialSubject: {
58
+ id: organizationDid,
59
+ '@type': 'Organization',
60
+ legalName,
61
+ identifier: { additionalType: identifierType, value: organizationIdentifier },
62
+ ...(identifierType === 'taxID' ? { taxID: organizationIdentifier } : {}),
63
+ address: { '@type': 'PostalAddress', addressCountry },
64
+ makesOffer: { '@type': 'Offer', category: serviceCategory },
65
+ targetNetwork: 'test-network',
66
+ },
67
+ };
68
+ const representative = {
69
+ ...base,
70
+ id: `urn:uuid:${applicationId}:legal-representative`,
71
+ type: [
72
+ W3cCredentialTypes.VerifiableCredential,
73
+ ActivationCredentialTypes.PersonCredential,
74
+ ActivationCredentialTypes.LegalRepresentativeCredential,
75
+ EnvironmentCredentialTypes.TestNetworkCredential,
76
+ ],
77
+ credentialSubject: {
78
+ id: buildStableActorIdentifier({ contactKind: 'email', contact: legalRepresentativeEmail }),
79
+ '@type': 'Person',
80
+ name: legalRepresentativeFullName,
81
+ sameAs: buildStableActorIdentifier({ contactKind: 'email', contact: legalRepresentativeEmail }),
82
+ hasOccupation: { '@type': 'Occupation', occupationalCategory: 'ISCO-08|1120' },
83
+ memberOf: {
84
+ '@type': 'Organization',
85
+ legalName,
86
+ ...(identifierType === 'taxID'
87
+ ? { taxID: organizationIdentifier }
88
+ : { identifier: { additionalType: identifierType, value: organizationIdentifier } }),
89
+ },
90
+ targetNetwork: 'test-network',
91
+ },
92
+ };
93
+ const controller = {
94
+ ...base,
95
+ id: `urn:uuid:${applicationId}:service-controller`,
96
+ type: [
97
+ W3cCredentialTypes.VerifiableCredential,
98
+ 'ServiceCredential',
99
+ ActivationCredentialTypes.ServiceControllerCredential,
100
+ EnvironmentCredentialTypes.TestNetworkCredential,
101
+ ],
102
+ credentialSubject: {
103
+ id: organizationDid,
104
+ '@type': 'Service',
105
+ serviceType: 'OrganizationControllerService',
106
+ provider: {
107
+ '@type': 'Organization',
108
+ legalName,
109
+ ...(identifierType === 'taxID'
110
+ ? { taxID: organizationIdentifier }
111
+ : { identifier: { additionalType: identifierType, value: organizationIdentifier } }),
112
+ },
113
+ owner: {
114
+ '@type': 'Person',
115
+ additionalType: 'RESPRSN',
116
+ sameAs: buildStableActorIdentifier({ contactKind: 'email', contact: controllerEmail }),
117
+ hasOccupation: { '@type': 'Occupation', occupationalCategory: 'ISCO-08|1330' },
118
+ hasCredential: { material: required(input.controllerKeyMaterial, 'controllerKeyMaterial') },
119
+ },
120
+ targetNetwork: 'test-network',
121
+ },
122
+ };
123
+ return [organization, representative, controller];
124
+ }
125
+ function canonicalizeValue(value) {
126
+ if (Array.isArray(value))
127
+ return value.map(canonicalizeValue);
128
+ if (!value || typeof value !== 'object')
129
+ return value;
130
+ return Object.fromEntries(Object.entries(value)
131
+ .filter(([key]) => key !== 'proof')
132
+ .sort(([left], [right]) => left.localeCompare(right))
133
+ .map(([key, nested]) => [key, canonicalizeValue(nested)]));
134
+ }
135
+ /** Deterministic detached-proof payload for one of the three Test Network VCs. */
136
+ export function canonicalizeTestNetworkOrganizationCredential(credential) {
137
+ if (!TestNetworkOrganizationCredentialTypes.some(type => credential.type.includes(type))) {
138
+ throw new Error('Credential is not a Test Network organization credential.');
139
+ }
140
+ if (credential.credentialSubject?.targetNetwork !== 'test-network') {
141
+ throw new Error('Test Network organization credential requires targetNetwork=test-network.');
142
+ }
143
+ return JSON.stringify(canonicalizeValue(credential));
144
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "2.5.3",
3
+ "version": "2.5.6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },