gdc-sdk-node-ts 2.9.15 → 2.9.17

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.
@@ -1,4 +1,4 @@
1
- import { type FamilyOrganizationSummary } from 'gdc-common-utils-ts/utils/family-organization-summary';
1
+ import { type FamilyOrganizationSummary, type FamilyRegistrationStatus } from 'gdc-common-utils-ts/utils/family-organization-summary';
2
2
  import type { PollOptions, SubmitAndPollResult } from './orchestration/client-port.js';
3
3
  import type { RouteContext } from './individual-onboarding.js';
4
4
  export type FamilyOrganizationSearchInput = Readonly<{
@@ -19,7 +19,11 @@ export type OwnedFamilyOrganizationDirectoryInput = Readonly<{
19
19
  }>;
20
20
  export type OwnedFamilyOrganizationDirectoryEntry = Readonly<{
21
21
  resourceId: string;
22
- alternateName: string;
22
+ /** Missing only for an owner-private registration draft. */
23
+ alternateName?: string;
24
+ status?: FamilyRegistrationStatus;
25
+ birthDate?: string;
26
+ missingFields?: string[];
23
27
  claims: Readonly<Record<string, unknown>>;
24
28
  }>;
25
29
  type SearchFamilyOrganizationWithDeps = {
@@ -1,6 +1,6 @@
1
1
  // Copyright 2026 Antifraud Services Inc. under the Apache License, Version 2.0.
2
2
  import { readFamilyOrganizationSummaryFromResponseBody, } from 'gdc-common-utils-ts/utils/family-organization-summary';
3
- import { ClaimsOrganizationSchemaorg, ClaimsServiceSchemaorg } from 'gdc-common-utils-ts/constants';
3
+ import { ClaimsOrganizationSchemaorg, ClaimsPersonSchemaorg, ClaimsServiceSchemaorg } from 'gdc-common-utils-ts/constants';
4
4
  import { resolvePollOptionsFromSeconds } from './poll-options.js';
5
5
  /**
6
6
  * Searches one existing family/individual organization registration by the
@@ -100,11 +100,35 @@ export async function listOwnedFamilyOrganizationsWithDeps(routeCtx, input, deps
100
100
  if (!((email && ownerEmail === email) || (telephone && ownerTelephone === telephone)))
101
101
  return [];
102
102
  const alternateName = String(resourceClaims[ClaimsOrganizationSchemaorg.alternateName] || '').trim();
103
- if (!alternateName)
103
+ const rawStatus = String(resourceClaims['org.schema.FamilyRegistration.status'] || '').trim();
104
+ const status = isFamilyRegistrationStatus(rawStatus) ? rawStatus : undefined;
105
+ if (!alternateName && status !== 'draft_saved' && status !== 'resume_required')
104
106
  return [];
105
- return [{ resourceId: String(resource.id), alternateName, claims: resourceClaims }];
107
+ const birthDate = String(resourceClaims[ClaimsPersonSchemaorg.birthDate]
108
+ || resourceClaims['org.schema.Organization.foundingDate']
109
+ || '').trim();
110
+ const missingFields = Array.isArray(resourceClaims['org.schema.FamilyRegistration.missingFields'])
111
+ ? resourceClaims['org.schema.FamilyRegistration.missingFields']
112
+ .map((value) => String(value || '').trim())
113
+ .filter(Boolean)
114
+ : [];
115
+ return [{
116
+ resourceId: String(resource.id),
117
+ ...(alternateName ? { alternateName } : {}),
118
+ ...(status ? { status } : {}),
119
+ ...(birthDate ? { birthDate } : {}),
120
+ ...(missingFields.length ? { missingFields } : {}),
121
+ claims: resourceClaims,
122
+ }];
106
123
  });
107
124
  }
125
+ function isFamilyRegistrationStatus(value) {
126
+ return value === 'new_created'
127
+ || value === 'draft_saved'
128
+ || value === 'resume_required'
129
+ || value === 'already_exists'
130
+ || value === 'not_found';
131
+ }
108
132
  function createRuntimeUuid() {
109
133
  const fromCrypto = globalThis.crypto?.randomUUID?.();
110
134
  if (fromCrypto) {
@@ -6,6 +6,14 @@ import type { PollOptions, SubmitAndPollResult } from './orchestration/client-po
6
6
  import type { RouteContext } from './individual-onboarding.js';
7
7
  import type { OfferPreview } from './order-offer-summary.js';
8
8
  export type IndividualOrganizationRegistrationInput = {
9
+ /**
10
+ * Explicitly saves an owner-private, cardless registration draft.
11
+ *
12
+ * This is intended for accessible channels that captured a birth year/date
13
+ * but could not reliably capture the subject's name. It never authorizes an
14
+ * Order or public card by itself.
15
+ */
16
+ registrationIntent?: 'activate' | 'save-private-draft';
9
17
  /**
10
18
  * Preferred high-level input produced by `createIndividualOnboardingEditor()`.
11
19
  *
@@ -77,8 +85,17 @@ export type IndividualOrganizationRegistrationInput = {
77
85
  export type IndividualOrganizationBootstrapInput = IndividualOrganizationRegistrationInput;
78
86
  export type IndividualOrganizationRegistrationResult = {
79
87
  registration: SubmitAndPollResult;
80
- offerId: string;
81
- offerPreview: OfferPreview;
88
+ /**
89
+ * Owner-private Organization UUID returned by GW for a saved draft.
90
+ *
91
+ * It is not a public card identifier or DID and is present only when
92
+ * `registrationIntent` was `save-private-draft`.
93
+ */
94
+ draftId?: string;
95
+ /** Absent only for an explicitly requested owner-private draft. */
96
+ offerId?: string;
97
+ /** Absent only for an explicitly requested owner-private draft. */
98
+ offerPreview?: OfferPreview;
82
99
  /** Lifecycle state returned by GW for this registration receipt. */
83
100
  registrationStatus?: FamilyRegistrationStatus;
84
101
  /** False when the same family registration is already active. */
@@ -26,7 +26,8 @@ export async function registerIndividualOrganizationWithDeps(deps) {
26
26
  * routing/indexing contract for this flow.
27
27
  */
28
28
  const onboardingDraft = deps.input.onboardingDraft;
29
- if (onboardingDraft && !hasSignedPdfEvidence(onboardingDraft)) {
29
+ const savesPrivateDraft = deps.input.registrationIntent === 'save-private-draft';
30
+ if (onboardingDraft && !hasSignedPdfEvidence(onboardingDraft) && !savesPrivateDraft) {
30
31
  const subjectAlternateName = String(onboardingDraft.formFields.subjectAlternateName
31
32
  || onboardingDraft.claims?.[ClaimsOrganizationSchemaorg.alternateName]
32
33
  || '').trim();
@@ -35,9 +36,16 @@ export async function registerIndividualOrganizationWithDeps(deps) {
35
36
  }
36
37
  }
37
38
  const alternateName = String(deps.input.alternateName || '').trim();
38
- if (!onboardingDraft && !alternateName) {
39
+ if (!onboardingDraft && !alternateName && !savesPrivateDraft) {
39
40
  throw new Error('registerIndividualOrganization requires alternateName.');
40
41
  }
42
+ const draftClaims = {
43
+ ...(onboardingDraft?.claims || {}),
44
+ ...(deps.input.additionalClaims || {}),
45
+ };
46
+ if (!alternateName && savesPrivateDraft && !hasValidHumanBirthDate(draftClaims)) {
47
+ throw new Error('registerIndividualOrganization private draft requires a valid birth year or date.');
48
+ }
41
49
  const controllerEmail = String(deps.input.controllerEmail
42
50
  || onboardingDraft?.formFields.controllerEmail
43
51
  || onboardingDraft?.claims?.[ClaimsOrganizationSchemaorg.ownerEmail]
@@ -99,6 +107,23 @@ export async function registerIndividualOrganizationWithDeps(deps) {
99
107
  });
100
108
  const registration = await deps.submitAndPoll(deps.individualFamilyOrganizationBatchPath(deps.routeCtx), deps.individualFamilyOrganizationPollPath(deps.routeCtx), registrationPayload, pollOptions);
101
109
  deps.assertFirstDidcommEntrySuccess?.(registration, 'registerIndividualOrganization.registration');
110
+ const registrationSummary = readFamilyOrganizationSummaryFromResponseBody(registration.poll.body);
111
+ const registrationStatus = registrationSummary?.status;
112
+ if (savesPrivateDraft) {
113
+ if (registrationStatus !== 'draft_saved') {
114
+ throw new Error('registerIndividualOrganization failed: GW did not preserve the requested private draft.');
115
+ }
116
+ const draftId = String(registrationSummary?.organizationId || '').trim();
117
+ if (!draftId) {
118
+ throw new Error('registerIndividualOrganization failed: GW did not return the private draft id.');
119
+ }
120
+ return {
121
+ registration,
122
+ registrationStatus,
123
+ draftId,
124
+ orderConfirmationRequired: false,
125
+ };
126
+ }
102
127
  /**
103
128
  * Commercial contract for this SDK path:
104
129
  * - this helper targets the family/individual commercial bootstrap flow
@@ -112,12 +137,11 @@ export async function registerIndividualOrganizationWithDeps(deps) {
112
137
  if (!offerId) {
113
138
  throw new Error('registerIndividualOrganization failed: missing offerId in registration response.');
114
139
  }
115
- const registrationSummary = readFamilyOrganizationSummaryFromResponseBody(registration.poll.body);
116
140
  return {
117
141
  registration,
118
142
  offerId,
119
143
  offerPreview: deps.getOfferPreviewFromResponse(registration),
120
- registrationStatus: registrationSummary?.status,
144
+ registrationStatus,
121
145
  orderConfirmationRequired: registrationSummary?.status !== 'already_exists',
122
146
  identity: readIndividualOrganizationBootstrapIdentity(registration.poll.body, {
123
147
  controllerEmail,
@@ -125,6 +149,10 @@ export async function registerIndividualOrganizationWithDeps(deps) {
125
149
  }),
126
150
  };
127
151
  }
152
+ function hasValidHumanBirthDate(claims) {
153
+ const birthDate = String(claims?.[ClaimsPersonSchemaorg.birthDate] || '').trim();
154
+ return /^(?:19|20)\d{2}(?:-(?:0[1-9]|1[0-2])(?:-(?:0[1-9]|[12]\d|3[01]))?)?$/.test(birthDate);
155
+ }
128
156
  function canonicalControllerUuid(value) {
129
157
  const hexadecimal = normalizeUuid(String(value || '').trim());
130
158
  if (!hexadecimal) {
@@ -389,7 +389,10 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
389
389
  /** Lists all individual Organizations owned by one verified account contact. */
390
390
  listOwnedFamilyOrganizations(ctx: RouteContext, input: OwnedFamilyOrganizationDirectoryInput): Promise<Readonly<{
391
391
  resourceId: string;
392
- alternateName: string;
392
+ alternateName?: string;
393
+ status?: import("gdc-common-utils-ts").FamilyRegistrationStatus;
394
+ birthDate?: string;
395
+ missingFields?: string[];
393
396
  claims: Readonly<Record<string, unknown>>;
394
397
  }>[]>;
395
398
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-sdk-node-ts",
3
- "version": "2.9.15",
3
+ "version": "2.9.17",
4
4
  "description": "Next-generation Node runtime package for the GDC SDK family",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Antifraud Services Inc.",
@@ -38,8 +38,8 @@
38
38
  "test:e2e:live-gw:clean": "bash ./scripts/run-live-gw-clean.sh"
39
39
  },
40
40
  "dependencies": {
41
- "gdc-common-utils-ts": "2.9.14",
42
- "gdc-sdk-core-ts": "2.9.9"
41
+ "gdc-common-utils-ts": "2.9.16",
42
+ "gdc-sdk-core-ts": "2.9.10"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^20.14.10",
@@ -1,16 +0,0 @@
1
- import type { VerifiableCredentialV2 } from 'gdc-common-utils-ts/models/verifiable-credential';
2
- import type { IWallet, WalletExecutionContext, WalletKeySelection } from 'gdc-sdk-core-ts';
3
- /** Input required to add one human-authorizer proof to the out-of-band VC. */
4
- export type SignOrganizationRegistrationAuthorizationInput = Readonly<{
5
- credential: VerifiableCredentialV2;
6
- wallet: IWallet;
7
- context: WalletExecutionContext;
8
- key: WalletKeySelection;
9
- verificationMethod: string;
10
- createdAt?: string;
11
- }>;
12
- /**
13
- * Adds one detached JWS proof made by the already-unlocked professional
14
- * profile. The browser never receives the private key or clear wallet seed.
15
- */
16
- export declare function signOrganizationRegistrationAuthorizationCredential(input: SignOrganizationRegistrationAuthorizationInput): Promise<VerifiableCredentialV2>;
@@ -1,34 +0,0 @@
1
- import { canonicalizeOrganizationRegistrationAuthorizationCredential, } from 'gdc-common-utils-ts/utils/organization-registration-authorization';
2
- /**
3
- * Adds one detached JWS proof made by the already-unlocked professional
4
- * profile. The browser never receives the private key or clear wallet seed.
5
- */
6
- export async function signOrganizationRegistrationAuthorizationCredential(input) {
7
- if (!input.wallet.signDetachedJws) {
8
- throw new Error('Wallet does not support detached organization authorization proofs.');
9
- }
10
- const verificationMethod = String(input.verificationMethod || '').trim();
11
- if (!verificationMethod)
12
- throw new Error('Organization authorization proof requires verificationMethod.');
13
- const payload = canonicalizeOrganizationRegistrationAuthorizationCredential(input.credential);
14
- const jws = await input.wallet.signDetachedJws(input.context, {
15
- payload,
16
- header: { typ: 'application/vc+ld+json' },
17
- key: input.key,
18
- });
19
- const existingProofs = Array.isArray(input.credential.proof)
20
- ? input.credential.proof
21
- : input.credential.proof
22
- ? [input.credential.proof]
23
- : [];
24
- return {
25
- ...input.credential,
26
- proof: [...existingProofs, {
27
- type: 'JsonWebSignature2020',
28
- created: input.createdAt || new Date().toISOString(),
29
- proofPurpose: 'contractAgreement',
30
- verificationMethod,
31
- jws,
32
- }],
33
- };
34
- }