gdc-common-utils-ts 2.0.2 → 2.0.4

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
@@ -10,6 +10,26 @@ Short rule:
10
10
  - do not add ad hoc literals in `101` tests when `gdc-common-utils-ts` can own
11
11
  the reusable value instead
12
12
 
13
+ ## Shared Workspace
14
+
15
+ Recommended local layout for the shared ICA/GDC repos and fixture PDFs:
16
+
17
+ ```text
18
+ ~/GITS/gdc-workspace/
19
+ dataspace-ica-ts/
20
+ ica-client-sdk-ts/
21
+ gdc-common-utils-ts/
22
+ examples/
23
+ <example-pdf-1>.pdf
24
+ <example-pdf-2>.pdf
25
+ ```
26
+
27
+ This is recommended because:
28
+
29
+ - cross-repo docs and fixture-based tests often refer to sibling repos
30
+ - real PDF examples are expected under `~/GITS/gdc-workspace/examples/`
31
+ - keeping a single shared workspace reduces path drift between repos
32
+
13
33
  Employee shared examples live in `src/examples/employee.ts`.
14
34
  Employee pure helper functions live in `src/utils/employee.ts`.
15
35
 
@@ -61,6 +81,31 @@ They are not interchangeable:
61
81
  Production-grade flows should prefer ICA-issued representative VCs that carry
62
82
  both dimensions.
63
83
 
84
+ ## Legal Organization Verification Transaction
85
+
86
+ The first host-side legal-organization onboarding step now has one canonical
87
+ shared payload builder in this package:
88
+
89
+ - `buildLegalOrganizationVerificationTransactionBundle(...)`
90
+ - `EXAMPLE_LEGAL_ORGANIZATION_VERIFICATION_TRANSACTION_BUNDLE`
91
+
92
+ This builder owns the business payload only:
93
+
94
+ - signed PDF evidence attachment references
95
+ - `controller.publicKeyJwk` as the controller business binding key
96
+ - optional `organization.publicKeyJwk`
97
+ - legal representative payload
98
+ - `meta.claims` business claims
99
+
100
+ It intentionally does not own:
101
+
102
+ - `fetch`
103
+ - polling
104
+ - JOSE transport execution
105
+ - BFF/frontend runtime crypto
106
+
107
+ Those runtime concerns belong in `gdc-sdk-node-ts`, `gdc-sdk-front-ts`, or GW.
108
+
64
109
  Step by step:
65
110
 
66
111
  1. ICA verifies the signed PDF and emits the representative VC.
@@ -136,6 +136,7 @@ export declare const EXAMPLE_VERIFY_RESPONSE_PERSON_PUBLIC_KEY_JWK: Readonly<{
136
136
  alg: "ES384";
137
137
  kid: "controller-es384-001";
138
138
  }>;
139
+ export declare const EXAMPLE_VERIFY_RESPONSE_ORG_AUTHORIZED_CATEGORY: "health-care";
139
140
  export declare const EXAMPLE_VERIFY_RESPONSE_ORG_CREDENTIAL: Readonly<{
140
141
  id: "urn:uuid:org-vc-001";
141
142
  '@context': ("https://www.w3.org/ns/credentials/v2" | "https://schema.org")[];
@@ -154,6 +155,10 @@ export declare const EXAMPLE_VERIFY_RESPONSE_ORG_CREDENTIAL: Readonly<{
154
155
  url: "health-care.provider.example.org";
155
156
  alternateName: string;
156
157
  additionalType: "sector=onehealth;section=dataprovider;kind=clinic;action=_index-provider,_research-provider";
158
+ makesOffer: {
159
+ '@type': string;
160
+ category: "health-care";
161
+ };
157
162
  address: {
158
163
  '@type': "PostalAddress";
159
164
  addressCountry: "ES";
@@ -2,6 +2,7 @@
2
2
  // Always create JSDoc, do not use strings inline in keys nor values, use types instead, and reuse the data test examples.
3
3
  import { ActivationCredentialTypes, W3cCredentialContexts, W3cCredentialTypes, } from '../constants/verifiable-credentials.js';
4
4
  import { ResourceTypesFhirR4 } from '../constants/fhir-resource-types.js';
5
+ import { DataspaceSectors } from '../constants/sectors.js';
5
6
  import { IssueSeverity, IssueType } from '../models/issue.js';
6
7
  import { EXAMPLE_DEFAULT_ICA_DID, EXAMPLE_BUNDLE_RESOURCE_TYPE, EXAMPLE_PROVIDER_DOMAIN, EXAMPLE_PROVIDER_LEGAL_NAME, EXAMPLE_PROVIDER_TAX_ID, EXAMPLE_TENANT_SERVICE_DID, } from './shared.js';
7
8
  import { EXAMPLE_ORG_CONTROLLER_SIGNING_KEY_ID, EXAMPLE_REPRESENTATIVE_IDENTIFIER, EXAMPLE_REPRESENTATIVE_SAME_AS, EXAMPLE_REPRESENTATIVE_SUBJECT_URN, } from './ica-activation-proof.js';
@@ -102,6 +103,7 @@ export const EXAMPLE_VERIFY_RESPONSE_PERSON_PUBLIC_KEY_JWK = Object.freeze({
102
103
  alg: 'ES384',
103
104
  kid: EXAMPLE_VERIFY_RESPONSE_PERSON_KID,
104
105
  });
106
+ export const EXAMPLE_VERIFY_RESPONSE_ORG_AUTHORIZED_CATEGORY = DataspaceSectors.HealthCare;
105
107
  export const EXAMPLE_VERIFY_RESPONSE_ORG_CREDENTIAL = Object.freeze({
106
108
  id: EXAMPLE_VERIFY_RESPONSE_ORG_VC_ID,
107
109
  '@context': [W3cCredentialContexts.V2, EXAMPLE_VERIFY_RESPONSE_SCHEMA_ORG_CONTEXT],
@@ -120,6 +122,10 @@ export const EXAMPLE_VERIFY_RESPONSE_ORG_CREDENTIAL = Object.freeze({
120
122
  url: EXAMPLE_PROVIDER_DOMAIN,
121
123
  alternateName: 'example-provider',
122
124
  additionalType: EXAMPLE_VERIFY_RESPONSE_ORG_ADDITIONAL_TYPE,
125
+ makesOffer: {
126
+ '@type': 'Offer',
127
+ category: EXAMPLE_VERIFY_RESPONSE_ORG_AUTHORIZED_CATEGORY,
128
+ },
123
129
  address: {
124
130
  '@type': EXAMPLE_VERIFY_RESPONSE_ADDRESS_TYPE,
125
131
  addressCountry: EXAMPLE_VERIFY_RESPONSE_ADDRESS_COUNTRY,
@@ -3,6 +3,8 @@ export * from './ica-activation-proof';
3
3
  export * from './ica-verify-response';
4
4
  export * from './dataspace-discovery';
5
5
  export * from './organization-controller';
6
+ export * from './organization-did-binding';
7
+ export * from './legal-organization-verification-transaction';
6
8
  export * from './individual-controller';
7
9
  export * from './professional';
8
10
  export * from './employee';
@@ -3,6 +3,8 @@ export * from './ica-activation-proof.js';
3
3
  export * from './ica-verify-response.js';
4
4
  export * from './dataspace-discovery.js';
5
5
  export * from './organization-controller.js';
6
+ export * from './organization-did-binding.js';
7
+ export * from './legal-organization-verification-transaction.js';
6
8
  export * from './individual-controller.js';
7
9
  export * from './professional.js';
8
10
  export * from './employee.js';
@@ -0,0 +1 @@
1
+ export declare const EXAMPLE_LEGAL_ORGANIZATION_VERIFICATION_TRANSACTION_BUNDLE: import("..").BundleJsonApi<import("..").ErrorEntry | import("..").BundleEntry>;
@@ -0,0 +1,66 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ import { ClaimsOrganizationSchemaorg, ClaimsPersonSchemaorg, ClaimsServiceSchemaorg, } from '../constants/schemaorg.js';
3
+ import { buildLegalOrganizationVerificationTransactionBundle, } from '../utils/legal-organization-verification-transaction.js';
4
+ import { EXAMPLE_CONTROLLER_BINDING, EXAMPLE_EMAIL_CONTROLLER_ORG, EXAMPLE_JURISDICTION, EXAMPLE_ORGANIZATION_LEGAL_NAME, EXAMPLE_LEGAL_ORGANIZATION_TAX_ID, EXAMPLE_SECTOR, EXAMPLE_SIGNED_TERMS_PDF_URL, EXAMPLE_TENANT_SERVICE_DID, } from './shared.js';
5
+ /**
6
+ * Canonical host-onboarding transaction example for the new ICA verification
7
+ * first step.
8
+ *
9
+ * Why this exists:
10
+ * - BFF/portal integrations need one stable example of the GW CORE request
11
+ * that wraps an ICA `_verify`
12
+ * - the example keeps the controller business binding separate from any
13
+ * technical DIDComm communication key
14
+ */
15
+ const exampleControllerBinding = {
16
+ did: EXAMPLE_CONTROLLER_BINDING.did,
17
+ sameAs: EXAMPLE_CONTROLLER_BINDING.sameAs,
18
+ publicKeyJwk: { ...EXAMPLE_CONTROLLER_BINDING.publicKeyJwk },
19
+ ...(EXAMPLE_CONTROLLER_BINDING.jwks
20
+ ? {
21
+ jwks: {
22
+ keys: EXAMPLE_CONTROLLER_BINDING.jwks.keys.map((item) => ({ ...item })),
23
+ },
24
+ }
25
+ : {}),
26
+ };
27
+ export const EXAMPLE_LEGAL_ORGANIZATION_VERIFICATION_TRANSACTION_BUNDLE = buildLegalOrganizationVerificationTransactionBundle({
28
+ claims: {
29
+ '@context': 'org.schema',
30
+ [ClaimsOrganizationSchemaorg.legalName]: EXAMPLE_ORGANIZATION_LEGAL_NAME,
31
+ [ClaimsOrganizationSchemaorg.identifierType]: 'taxID',
32
+ [ClaimsOrganizationSchemaorg.identifierValue]: EXAMPLE_LEGAL_ORGANIZATION_TAX_ID,
33
+ [ClaimsOrganizationSchemaorg.taxId]: EXAMPLE_LEGAL_ORGANIZATION_TAX_ID,
34
+ [ClaimsOrganizationSchemaorg.addressCountry]: EXAMPLE_JURISDICTION,
35
+ [ClaimsPersonSchemaorg.email]: EXAMPLE_EMAIL_CONTROLLER_ORG,
36
+ [ClaimsServiceSchemaorg.category]: EXAMPLE_SECTOR,
37
+ [ClaimsServiceSchemaorg.identifier]: EXAMPLE_TENANT_SERVICE_DID,
38
+ [ClaimsServiceSchemaorg.url]: `https://operator.example.net/acme/cds-${String(EXAMPLE_JURISDICTION).toLowerCase()}/v1/${EXAMPLE_SECTOR}`,
39
+ },
40
+ controller: exampleControllerBinding,
41
+ organization: {
42
+ did: EXAMPLE_TENANT_SERVICE_DID,
43
+ publicKeyJwk: {
44
+ kid: 'organization-signing-es384-001',
45
+ kty: 'EC',
46
+ crv: 'P-384',
47
+ x: '<organization-sign-x>',
48
+ y: '<organization-sign-y>',
49
+ alg: 'ES384',
50
+ use: 'sig',
51
+ },
52
+ },
53
+ legalRepresentativePayload: {
54
+ email: EXAMPLE_EMAIL_CONTROLLER_ORG,
55
+ },
56
+ verification: {
57
+ resourceType: 'contract',
58
+ },
59
+ attachments: [{
60
+ id: 'signed-terms-pdf-001',
61
+ media_type: 'application/pdf',
62
+ data: {
63
+ links: [EXAMPLE_SIGNED_TERMS_PDF_URL],
64
+ },
65
+ }],
66
+ });
@@ -0,0 +1 @@
1
+ export declare const EXAMPLE_ORGANIZATION_DID_BINDING_BUNDLE: import("..").BundleJsonApi<import("..").ErrorEntry | import("..").BundleEntry>;
@@ -0,0 +1,14 @@
1
+ // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
+ import { buildOrganizationDidBindingBundle } from '../utils/organization-did-binding.js';
3
+ import { EXAMPLE_CONTROLLER_BINDING, EXAMPLE_TENANT_SERVICE_DID, } from './shared.js';
4
+ export const EXAMPLE_ORGANIZATION_DID_BINDING_BUNDLE = buildOrganizationDidBindingBundle({
5
+ organization: {
6
+ url: [
7
+ 'https://provider.example.org',
8
+ EXAMPLE_TENANT_SERVICE_DID,
9
+ ],
10
+ },
11
+ controller: {
12
+ sameAs: EXAMPLE_CONTROLLER_BINDING.sameAs,
13
+ },
14
+ });
@@ -17,6 +17,8 @@ export declare const EXAMPLE_NETWORK_TYPE: "test";
17
17
  export declare const EXAMPLE_ROUTE_VERSION: "v1";
18
18
  export declare const EXAMPLE_SECTOR: "health-care";
19
19
  export declare const EXAMPLE_EMAIL_CONTROLLER_ORG: "controller@acme.org";
20
+ export declare const EXAMPLE_ORGANIZATION_LEGAL_NAME: "ACME HEALTH SL";
21
+ export declare const EXAMPLE_LEGAL_ORGANIZATION_TAX_ID: "VATES-B00112233";
20
22
  export declare const EXAMPLE_EMAIL_CONTROLLER_INDIVIDUAL: "ana.parent@example.org";
21
23
  export declare const EXAMPLE_INTEROPERABLE_CONTEXT_FHIR_API: "org.hl7.fhir.api";
22
24
  export declare const EXAMPLE_SELF_REGISTERED_INDIVIDUAL_ALTERNATE_NAME: "Jane";
@@ -27,6 +29,7 @@ export declare const EXAMPLE_SELF_REGISTERED_INDIVIDUAL_BIRTH_YEAR: "1980";
27
29
  export declare const EXAMPLE_REGISTERED_SUBJECT_ALTERNATE_NAME: "Doraemon";
28
30
  export declare const EXAMPLE_REGISTERED_SUBJECT_BIRTH_YEAR: "2020";
29
31
  export declare const EXAMPLE_PDF_CONSENT_DATE: "2026-06-08";
32
+ export declare const EXAMPLE_SIGNED_TERMS_PDF_URL: "https://portal.example.org/files/legal-organization-signed-terms.pdf";
30
33
  /**
31
34
  * Public provider domain selected during autodiscovery.
32
35
  *
@@ -32,6 +32,8 @@ export const EXAMPLE_NETWORK_TYPE = HostNetworkTypes.Test;
32
32
  export const EXAMPLE_ROUTE_VERSION = 'v1';
33
33
  export const EXAMPLE_SECTOR = DataspaceSectors.HealthCare;
34
34
  export const EXAMPLE_EMAIL_CONTROLLER_ORG = 'controller@acme.org';
35
+ export const EXAMPLE_ORGANIZATION_LEGAL_NAME = 'ACME HEALTH SL';
36
+ export const EXAMPLE_LEGAL_ORGANIZATION_TAX_ID = 'VATES-B00112233';
35
37
  export const EXAMPLE_EMAIL_CONTROLLER_INDIVIDUAL = 'ana.parent@example.org';
36
38
  export const EXAMPLE_INTEROPERABLE_CONTEXT_FHIR_API = Format.FHIR_API;
37
39
  export const EXAMPLE_SELF_REGISTERED_INDIVIDUAL_ALTERNATE_NAME = 'Jane';
@@ -42,6 +44,7 @@ export const EXAMPLE_SELF_REGISTERED_INDIVIDUAL_BIRTH_YEAR = '1980';
42
44
  export const EXAMPLE_REGISTERED_SUBJECT_ALTERNATE_NAME = 'Doraemon';
43
45
  export const EXAMPLE_REGISTERED_SUBJECT_BIRTH_YEAR = '2020';
44
46
  export const EXAMPLE_PDF_CONSENT_DATE = '2026-06-08';
47
+ export const EXAMPLE_SIGNED_TERMS_PDF_URL = 'https://portal.example.org/files/legal-organization-signed-terms.pdf';
45
48
  /**
46
49
  * Public provider domain selected during autodiscovery.
47
50
  *
@@ -49,6 +49,7 @@ export * from './family-registration-test-data';
49
49
  export * from './individual-form-pdf';
50
50
  export * from './individual-organization-claims';
51
51
  export * from './organization-lifecycle';
52
+ export * from './organization-did-binding';
52
53
  export * from './individual-organization-lifecycle';
53
54
  export * from './individual-onboarding-editor';
54
55
  export * from './individual-onboarding-document-reference';
@@ -60,6 +61,7 @@ export * from './interoperable-resource-operation';
60
61
  export * from './jwt';
61
62
  export * from './jwk-thumbprint';
62
63
  export * from './legal-organization-onboarding';
64
+ export * from './legal-organization-verification-transaction';
63
65
  export * from './license';
64
66
  export * from './license-commercial-search';
65
67
  export * from './license-list-search';
@@ -49,6 +49,7 @@ export * from './family-registration-test-data.js';
49
49
  export * from './individual-form-pdf.js';
50
50
  export * from './individual-organization-claims.js';
51
51
  export * from './organization-lifecycle.js';
52
+ export * from './organization-did-binding.js';
52
53
  export * from './individual-organization-lifecycle.js';
53
54
  export * from './individual-onboarding-editor.js';
54
55
  export * from './individual-onboarding-document-reference.js';
@@ -60,6 +61,7 @@ export * from './interoperable-resource-operation.js';
60
61
  export * from './jwt.js';
61
62
  export * from './jwk-thumbprint.js';
62
63
  export * from './legal-organization-onboarding.js';
64
+ export * from './legal-organization-verification-transaction.js';
63
65
  export * from './license.js';
64
66
  export * from './license-commercial-search.js';
65
67
  export * from './license-list-search.js';
@@ -0,0 +1,90 @@
1
+ import type { BundleJsonApi } from '../models/bundle';
2
+ import type { ClaimsRecord } from '../models/resource-document';
3
+ /**
4
+ * Canonical business entry type for the first host-side onboarding step that
5
+ * asks GW CORE to forward a legal-organization verification request to ICA.
6
+ *
7
+ * Responsibilities of this transaction:
8
+ * - carry the signed PDF evidence or PDF URL attachment
9
+ * - carry the controller business binding key
10
+ * - optionally carry the organization VC-signing public key chosen by the host
11
+ * - carry the legal organization claims that GW CORE should keep through the
12
+ * verification/onboarding pipeline
13
+ *
14
+ * Non-responsibilities:
15
+ * - it is not the same thing as host `_activate`
16
+ * - it does not model Fabric/CSR enrollment
17
+ */
18
+ export declare const LegalOrganizationVerificationTransactionEntryTypes: Readonly<{
19
+ readonly Request: "Organization-verification-transaction-request-v1.0";
20
+ readonly Response: "Organization-verification-transaction-response-v1.0";
21
+ }>;
22
+ export type LegalOrganizationVerificationTransactionEntryType = typeof LegalOrganizationVerificationTransactionEntryTypes[keyof typeof LegalOrganizationVerificationTransactionEntryTypes];
23
+ /**
24
+ * Explicit controller binding material that ICA should project into the legal
25
+ * representative VC.
26
+ */
27
+ export type LegalOrganizationVerificationTransactionController = Readonly<{
28
+ did?: string;
29
+ sameAs?: string;
30
+ publicKeyJwk?: Record<string, unknown>;
31
+ jwks?: {
32
+ keys: Array<Record<string, unknown>>;
33
+ };
34
+ }>;
35
+ /**
36
+ * Optional organization-side public key material that the hosting operator may
37
+ * already know before the final activation/publication step.
38
+ */
39
+ export type LegalOrganizationVerificationTransactionOrganization = Readonly<{
40
+ did?: string;
41
+ url?: string;
42
+ publicKeyJwk?: Record<string, unknown>;
43
+ jwks?: {
44
+ keys: Array<Record<string, unknown>>;
45
+ };
46
+ }>;
47
+ /**
48
+ * Additional legal-representative payload fields forwarded to ICA `_verify`.
49
+ *
50
+ * These values help ICA derive `sameAs` when the signed document itself does
51
+ * not expose that contact value in a directly reusable way.
52
+ */
53
+ export type LegalOrganizationVerificationRepresentativePayload = Readonly<{
54
+ email?: string;
55
+ sameAs?: string;
56
+ }>;
57
+ /**
58
+ * Optional verification routing hints for ICA.
59
+ *
60
+ * `resourceType` defaults to `contract`, which matches the current
61
+ * legal-organization terms flow in ICA.
62
+ */
63
+ export type LegalOrganizationVerificationRouting = Readonly<{
64
+ resourceType?: string;
65
+ }>;
66
+ /**
67
+ * Canonical input accepted by shared SDK/GW helpers when building the first
68
+ * host onboarding transaction that wraps an ICA `_verify` request.
69
+ */
70
+ export type LegalOrganizationVerificationTransactionInput = Readonly<{
71
+ claims: ClaimsRecord;
72
+ controller: LegalOrganizationVerificationTransactionController;
73
+ organization?: LegalOrganizationVerificationTransactionOrganization;
74
+ legalRepresentativePayload?: LegalOrganizationVerificationRepresentativePayload;
75
+ verification?: LegalOrganizationVerificationRouting;
76
+ attachments?: unknown[];
77
+ }>;
78
+ /**
79
+ * Builds the canonical Bundle payload for host-side legal organization
80
+ * verification transactions.
81
+ *
82
+ * Contract notes:
83
+ * - business claims remain in `meta.claims`
84
+ * - controller binding material remains in `resource.controller.*`
85
+ * - organization signing material remains in `resource.organization.*`
86
+ * - PDF evidence or URL attachments stay at the DIDComm-message level
87
+ * - `Service.category` is the canonical business-sector input later used by GW
88
+ * to target the appropriate ICA verification route
89
+ */
90
+ export declare function buildLegalOrganizationVerificationTransactionBundle(input: LegalOrganizationVerificationTransactionInput): BundleJsonApi;
@@ -0,0 +1,65 @@
1
+ import { ClaimsServiceSchemaorg } from '../constants/schemaorg.js';
2
+ /**
3
+ * Canonical business entry type for the first host-side onboarding step that
4
+ * asks GW CORE to forward a legal-organization verification request to ICA.
5
+ *
6
+ * Responsibilities of this transaction:
7
+ * - carry the signed PDF evidence or PDF URL attachment
8
+ * - carry the controller business binding key
9
+ * - optionally carry the organization VC-signing public key chosen by the host
10
+ * - carry the legal organization claims that GW CORE should keep through the
11
+ * verification/onboarding pipeline
12
+ *
13
+ * Non-responsibilities:
14
+ * - it is not the same thing as host `_activate`
15
+ * - it does not model Fabric/CSR enrollment
16
+ */
17
+ export const LegalOrganizationVerificationTransactionEntryTypes = Object.freeze({
18
+ Request: 'Organization-verification-transaction-request-v1.0',
19
+ Response: 'Organization-verification-transaction-response-v1.0',
20
+ });
21
+ function normalizeText(value) {
22
+ return typeof value === 'string' ? value.trim() : '';
23
+ }
24
+ /**
25
+ * Builds the canonical Bundle payload for host-side legal organization
26
+ * verification transactions.
27
+ *
28
+ * Contract notes:
29
+ * - business claims remain in `meta.claims`
30
+ * - controller binding material remains in `resource.controller.*`
31
+ * - organization signing material remains in `resource.organization.*`
32
+ * - PDF evidence or URL attachments stay at the DIDComm-message level
33
+ * - `Service.category` is the canonical business-sector input later used by GW
34
+ * to target the appropriate ICA verification route
35
+ */
36
+ export function buildLegalOrganizationVerificationTransactionBundle(input) {
37
+ const businessSector = normalizeText(input.claims?.[ClaimsServiceSchemaorg.category]);
38
+ if (!businessSector) {
39
+ throw new Error(`buildLegalOrganizationVerificationTransactionBundle requires ${ClaimsServiceSchemaorg.category}.`);
40
+ }
41
+ return {
42
+ resourceType: 'Bundle',
43
+ type: 'collection',
44
+ total: 1,
45
+ data: [{
46
+ type: LegalOrganizationVerificationTransactionEntryTypes.Request,
47
+ meta: {
48
+ claims: input.claims,
49
+ },
50
+ resource: {
51
+ controller: input.controller,
52
+ ...(input.organization ? { organization: input.organization } : {}),
53
+ ...(input.legalRepresentativePayload
54
+ ? { legalRepresentativePayload: input.legalRepresentativePayload }
55
+ : {}),
56
+ verification: {
57
+ resourceType: normalizeText(input.verification?.resourceType) || 'contract',
58
+ },
59
+ },
60
+ }],
61
+ ...(Array.isArray(input.attachments) && input.attachments.length > 0
62
+ ? { attachments: input.attachments }
63
+ : {}),
64
+ };
65
+ }
@@ -0,0 +1,60 @@
1
+ import type { BundleJsonApi } from '../models/bundle';
2
+ export declare const OrganizationDidBindingEntryTypes: Readonly<{
3
+ readonly Request: "Organization-did-binding-request-v1.0";
4
+ readonly Response: "Organization-did-binding-response-v1.0";
5
+ }>;
6
+ export type OrganizationDidBindingErrorCode = 'MISSING_ORGANIZATION_URL' | 'UNSUPPORTED_ORGANIZATION_LOCATOR';
7
+ export type OrganizationDidBindingValidationError = Readonly<{
8
+ code: OrganizationDidBindingErrorCode;
9
+ message: string;
10
+ claimPaths: string[];
11
+ }>;
12
+ export type OrganizationDidBindingControllerInput = Readonly<{
13
+ sameAs?: string;
14
+ }>;
15
+ export type OrganizationDidBindingOrganizationInput = Readonly<{
16
+ url: string | string[];
17
+ taxID?: string;
18
+ taxId?: string;
19
+ identifier?: string;
20
+ }>;
21
+ export type OrganizationDidBindingInput = Readonly<{
22
+ organization: OrganizationDidBindingOrganizationInput;
23
+ controller?: OrganizationDidBindingControllerInput;
24
+ }>;
25
+ export type ValidateOrganizationDidBindingInputResult = Readonly<{
26
+ ok: boolean;
27
+ errors: OrganizationDidBindingValidationError[];
28
+ normalizedInput: Readonly<{
29
+ organization: {
30
+ url: string[];
31
+ };
32
+ controller?: OrganizationDidBindingControllerInput;
33
+ }>;
34
+ }>;
35
+ /**
36
+ * Validates the tenant-scoped organization DID binding request.
37
+ *
38
+ * Canonical contract:
39
+ * - the organization is already identified by the tenant path in GW CORE
40
+ * - callers send one or more public aliases in `organization.url`
41
+ * - `controller.sameAs` is optional additional evidence, not a new key-binding
42
+ * bootstrap step
43
+ *
44
+ * Current version limits:
45
+ * - no `taxID` / `identifier` locator is required
46
+ * - sending those legacy locator fields is rejected so callers do not mix path
47
+ * identity with payload identity
48
+ */
49
+ export declare function validateOrganizationDidBindingInput(input: OrganizationDidBindingInput): ValidateOrganizationDidBindingInputResult;
50
+ /**
51
+ * Builds the canonical bundle payload for one tenant-scoped DID binding
52
+ * request.
53
+ *
54
+ * Result contract:
55
+ * - `resource.organization.url` carries the public alias replacement list
56
+ * - `resource.controller.sameAs` is optional corroborating identity material
57
+ * - organization identity is resolved from the tenant path, not from payload
58
+ * locators
59
+ */
60
+ export declare function buildOrganizationDidBindingBundle(input: OrganizationDidBindingInput): BundleJsonApi;
@@ -0,0 +1,103 @@
1
+ export const OrganizationDidBindingEntryTypes = Object.freeze({
2
+ Request: 'Organization-did-binding-request-v1.0',
3
+ Response: 'Organization-did-binding-response-v1.0',
4
+ });
5
+ function normalizeOptionalText(value) {
6
+ if (typeof value !== 'string')
7
+ return undefined;
8
+ const trimmed = value.trim();
9
+ return trimmed.length ? trimmed : undefined;
10
+ }
11
+ function normalizeOrganizationUrls(value) {
12
+ if (Array.isArray(value)) {
13
+ const urls = value
14
+ .map((item) => normalizeOptionalText(item))
15
+ .filter((item) => Boolean(item));
16
+ return urls.length ? urls : undefined;
17
+ }
18
+ const raw = normalizeOptionalText(value);
19
+ if (!raw)
20
+ return undefined;
21
+ const urls = raw
22
+ .split(',')
23
+ .map((item) => item.trim())
24
+ .filter(Boolean);
25
+ return urls.length ? urls : undefined;
26
+ }
27
+ /**
28
+ * Validates the tenant-scoped organization DID binding request.
29
+ *
30
+ * Canonical contract:
31
+ * - the organization is already identified by the tenant path in GW CORE
32
+ * - callers send one or more public aliases in `organization.url`
33
+ * - `controller.sameAs` is optional additional evidence, not a new key-binding
34
+ * bootstrap step
35
+ *
36
+ * Current version limits:
37
+ * - no `taxID` / `identifier` locator is required
38
+ * - sending those legacy locator fields is rejected so callers do not mix path
39
+ * identity with payload identity
40
+ */
41
+ export function validateOrganizationDidBindingInput(input) {
42
+ const errors = [];
43
+ const normalizedUrls = normalizeOrganizationUrls(input?.organization?.url);
44
+ const taxID = normalizeOptionalText(input?.organization?.taxID || input?.organization?.taxId);
45
+ const identifier = normalizeOptionalText(input?.organization?.identifier);
46
+ const controllerSameAs = normalizeOptionalText(input?.controller?.sameAs);
47
+ if (!normalizedUrls?.length) {
48
+ errors.push({
49
+ code: 'MISSING_ORGANIZATION_URL',
50
+ message: 'Organization DID binding requires at least one organization.url value.',
51
+ claimPaths: ['organization.url'],
52
+ });
53
+ }
54
+ if (taxID || identifier) {
55
+ errors.push({
56
+ code: 'UNSUPPORTED_ORGANIZATION_LOCATOR',
57
+ message: 'Organization DID binding in GW CORE uses the tenant path as locator and does not accept organization.taxID or organization.identifier in this version.',
58
+ claimPaths: ['organization.taxID', 'organization.identifier'],
59
+ });
60
+ }
61
+ return {
62
+ ok: errors.length === 0,
63
+ errors,
64
+ normalizedInput: {
65
+ organization: {
66
+ url: normalizedUrls || [],
67
+ },
68
+ ...(controllerSameAs ? { controller: { sameAs: controllerSameAs } } : {}),
69
+ },
70
+ };
71
+ }
72
+ /**
73
+ * Builds the canonical bundle payload for one tenant-scoped DID binding
74
+ * request.
75
+ *
76
+ * Result contract:
77
+ * - `resource.organization.url` carries the public alias replacement list
78
+ * - `resource.controller.sameAs` is optional corroborating identity material
79
+ * - organization identity is resolved from the tenant path, not from payload
80
+ * locators
81
+ */
82
+ export function buildOrganizationDidBindingBundle(input) {
83
+ const validation = validateOrganizationDidBindingInput(input);
84
+ if (!validation.ok) {
85
+ throw new Error(validation.errors.map((item) => item.message).join(' '));
86
+ }
87
+ return {
88
+ resourceType: 'Bundle',
89
+ type: 'collection',
90
+ total: 1,
91
+ data: [{
92
+ type: OrganizationDidBindingEntryTypes.Request,
93
+ resource: {
94
+ organization: {
95
+ url: validation.normalizedInput.organization.url,
96
+ },
97
+ ...(validation.normalizedInput.controller
98
+ ? { controller: validation.normalizedInput.controller }
99
+ : {}),
100
+ },
101
+ }],
102
+ };
103
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-common-utils-ts",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },