gdc-common-utils-ts 1.24.3 → 2.0.1

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 (40) hide show
  1. package/README.md +39 -0
  2. package/dist/constants/index.d.ts +1 -0
  3. package/dist/constants/index.js +1 -0
  4. package/dist/constants/profile-runtime.d.ts +33 -0
  5. package/dist/constants/profile-runtime.js +30 -0
  6. package/dist/examples/frontend-session.js +2 -1
  7. package/dist/examples/index.d.ts +1 -0
  8. package/dist/examples/index.js +1 -0
  9. package/dist/examples/lifecycle.js +5 -5
  10. package/dist/examples/organization-controller.d.ts +0 -1
  11. package/dist/examples/organization-controller.js +0 -1
  12. package/dist/examples/profile-runtime.d.ts +16 -0
  13. package/dist/examples/profile-runtime.js +18 -0
  14. package/dist/interfaces/Cryptography.types.d.ts +15 -0
  15. package/dist/models/identity-bootstrap.d.ts +9 -0
  16. package/dist/utils/activation-policy.d.ts +22 -3
  17. package/dist/utils/activation-policy.js +22 -3
  18. package/dist/utils/activation-request.d.ts +43 -1
  19. package/dist/utils/activation-request.js +74 -0
  20. package/dist/utils/communication-search-editor.d.ts +3 -3
  21. package/dist/utils/communication-search-editor.js +1 -1
  22. package/dist/utils/index.d.ts +3 -0
  23. package/dist/utils/index.js +3 -0
  24. package/dist/utils/individual-organization-lifecycle.d.ts +15 -5
  25. package/dist/utils/individual-organization-lifecycle.js +53 -6
  26. package/dist/utils/interoperable-resource-operation.d.ts +4 -4
  27. package/dist/utils/interoperable-resource-operation.js +22 -22
  28. package/dist/utils/jwk-thumbprint.d.ts +40 -0
  29. package/dist/utils/jwk-thumbprint.js +57 -0
  30. package/dist/utils/legal-organization-onboarding.d.ts +97 -0
  31. package/dist/utils/legal-organization-onboarding.js +128 -0
  32. package/dist/utils/license-commercial-search.d.ts +6 -6
  33. package/dist/utils/license-commercial-search.js +2 -2
  34. package/dist/utils/license-list-search.d.ts +7 -7
  35. package/dist/utils/license-list-search.js +23 -23
  36. package/dist/utils/license-offer-order.d.ts +19 -19
  37. package/dist/utils/license-offer-order.js +68 -68
  38. package/dist/utils/organization-lifecycle.d.ts +59 -0
  39. package/dist/utils/organization-lifecycle.js +155 -0
  40. package/package.json +2 -2
@@ -47,7 +47,7 @@ export type IndividualOrganizationLifecyclePayload = Readonly<{
47
47
  data: IndividualOrganizationLifecycleDataEntry[];
48
48
  };
49
49
  }>;
50
- export type IndividualOrganizationLifecycleDraftState = Readonly<{
50
+ export type IndividualOrganizationLifecycleEditorState = Readonly<{
51
51
  operation: IndividualOrganizationLifecycleOperation;
52
52
  claims: IndividualOrganizationLifecycleClaims;
53
53
  resourceId?: string;
@@ -76,7 +76,7 @@ export declare function buildCurrentIndividualOrganizationLifecycleDataEntry(inp
76
76
  */
77
77
  export declare function buildCurrentIndividualOrganizationLifecyclePayload(input: IndividualOrganizationLifecyclePayloadInput): IndividualOrganizationLifecyclePayload;
78
78
  /**
79
- * High-level chainable draft for hosted individual/family lifecycle work.
79
+ * High-level chainable editor for hosted individual/family lifecycle work.
80
80
  *
81
81
  * Teaching goal:
82
82
  * - authors one canonical flat `org.schema.Organization.*` claim set
@@ -84,11 +84,12 @@ export declare function buildCurrentIndividualOrganizationLifecyclePayload(input
84
84
  * - can emit either a semantic lifecycle message or the current GW CORE
85
85
  * payload shape used by `sdk-node`
86
86
  */
87
- export declare class IndividualOrganizationLifecycleDraft {
87
+ export declare class IndividualOrganizationLifecycleEditor {
88
88
  private draft;
89
- constructor(initial?: Partial<IndividualOrganizationLifecycleDraftState>);
89
+ constructor(initial?: Partial<IndividualOrganizationLifecycleEditorState>);
90
90
  mergeClaims(claims: IndividualOrganizationLifecycleClaims): this;
91
91
  setClaims(claims: IndividualOrganizationLifecycleClaims): this;
92
+ setContext(context: string): this;
92
93
  setIdentifier(identifier: string): this;
93
94
  setAlternateName(alternateName: string): this;
94
95
  setOwnerEmail(email: string): this;
@@ -96,8 +97,17 @@ export declare class IndividualOrganizationLifecycleDraft {
96
97
  setResourceId(resourceId?: string): this;
97
98
  setRequestType(requestType: string): this;
98
99
  setThreadId(thid: string): this;
100
+ getIdentifier(): string | undefined;
101
+ getContext(): string | undefined;
102
+ getAlternateName(): string | undefined;
103
+ getOwnerEmail(): string | undefined;
104
+ getOperation(): IndividualOrganizationLifecycleOperation;
105
+ getResourceId(): string | undefined;
106
+ getRequestType(): string | undefined;
107
+ getThreadId(): string | undefined;
99
108
  getClaims(): IndividualOrganizationLifecycleClaims;
100
- getDraft(): IndividualOrganizationLifecycleDraftState;
109
+ getState(): IndividualOrganizationLifecycleEditorState;
110
+ getEditorState(): IndividualOrganizationLifecycleEditorState;
101
111
  toSemanticMessage(): IndividualOrganizationLifecycleSemanticMessage;
102
112
  buildCurrentGwDataEntry(): IndividualOrganizationLifecycleDataEntry;
103
113
  buildCurrentGwPayload(): IndividualOrganizationLifecyclePayload;
@@ -17,7 +17,10 @@ function normalizeText(value) {
17
17
  function normalizeDraft(draft) {
18
18
  return {
19
19
  operation: draft?.operation || IndividualOrganizationLifecycleOperations.Disable,
20
- claims: cloneClaims(draft?.claims),
20
+ claims: {
21
+ '@context': 'org.schema',
22
+ ...cloneClaims(draft?.claims),
23
+ },
21
24
  resourceId: normalizeText(draft?.resourceId),
22
25
  requestType: normalizeText(draft?.requestType),
23
26
  thid: normalizeText(draft?.thid),
@@ -75,7 +78,7 @@ export function buildCurrentIndividualOrganizationLifecyclePayload(input) {
75
78
  };
76
79
  }
77
80
  /**
78
- * High-level chainable draft for hosted individual/family lifecycle work.
81
+ * High-level chainable editor for hosted individual/family lifecycle work.
79
82
  *
80
83
  * Teaching goal:
81
84
  * - authors one canonical flat `org.schema.Organization.*` claim set
@@ -83,7 +86,7 @@ export function buildCurrentIndividualOrganizationLifecyclePayload(input) {
83
86
  * - can emit either a semantic lifecycle message or the current GW CORE
84
87
  * payload shape used by `sdk-node`
85
88
  */
86
- export class IndividualOrganizationLifecycleDraft {
89
+ export class IndividualOrganizationLifecycleEditor {
87
90
  draft;
88
91
  constructor(initial) {
89
92
  this.draft = normalizeDraft(initial);
@@ -96,6 +99,15 @@ export class IndividualOrganizationLifecycleDraft {
96
99
  this.draft = patchDraft(this.draft, { claims });
97
100
  return this;
98
101
  }
102
+ setContext(context) {
103
+ this.draft = patchDraft(this.draft, {
104
+ claims: {
105
+ ...this.draft.claims,
106
+ '@context': String(context).trim(),
107
+ },
108
+ });
109
+ return this;
110
+ }
99
111
  setIdentifier(identifier) {
100
112
  this.draft = patchDraft(this.draft, {
101
113
  claims: {
@@ -139,12 +151,47 @@ export class IndividualOrganizationLifecycleDraft {
139
151
  this.draft = patchDraft(this.draft, { thid });
140
152
  return this;
141
153
  }
154
+ getIdentifier() {
155
+ const value = this.draft.claims[ClaimsOrganizationSchemaorg.identifier];
156
+ const normalized = normalizeText(value);
157
+ return normalized || undefined;
158
+ }
159
+ getContext() {
160
+ const value = this.draft.claims['@context'];
161
+ const normalized = normalizeText(value);
162
+ return normalized || undefined;
163
+ }
164
+ getAlternateName() {
165
+ const value = this.draft.claims[ClaimsOrganizationSchemaorg.alternateName];
166
+ const normalized = normalizeText(value);
167
+ return normalized || undefined;
168
+ }
169
+ getOwnerEmail() {
170
+ const value = this.draft.claims[ClaimsOrganizationSchemaorg.ownerEmail];
171
+ const normalized = normalizeText(value);
172
+ return normalized || undefined;
173
+ }
174
+ getOperation() {
175
+ return this.draft.operation;
176
+ }
177
+ getResourceId() {
178
+ return normalizeText(this.draft.resourceId) || undefined;
179
+ }
180
+ getRequestType() {
181
+ return normalizeText(this.draft.requestType) || undefined;
182
+ }
183
+ getThreadId() {
184
+ return normalizeText(this.draft.thid) || undefined;
185
+ }
142
186
  getClaims() {
143
187
  return cloneClaims(this.draft.claims);
144
188
  }
145
- getDraft() {
189
+ getState() {
146
190
  return normalizeDraft(this.draft);
147
191
  }
192
+ getEditorState() {
193
+ return this.getState();
194
+ }
148
195
  toSemanticMessage() {
149
196
  return {
150
197
  operation: this.draft.operation,
@@ -156,7 +203,7 @@ export class IndividualOrganizationLifecycleDraft {
156
203
  buildCurrentGwDataEntry() {
157
204
  const requestType = normalizeText(this.draft.requestType);
158
205
  if (!requestType) {
159
- throw new Error('IndividualOrganizationLifecycleDraft.buildCurrentGwDataEntry requires requestType.');
206
+ throw new Error('IndividualOrganizationLifecycleEditor.buildCurrentGwDataEntry requires requestType.');
160
207
  }
161
208
  return buildCurrentIndividualOrganizationLifecycleDataEntry({
162
209
  claims: this.getClaims(),
@@ -167,7 +214,7 @@ export class IndividualOrganizationLifecycleDraft {
167
214
  buildCurrentGwPayload() {
168
215
  const requestType = normalizeText(this.draft.requestType);
169
216
  if (!requestType) {
170
- throw new Error('IndividualOrganizationLifecycleDraft.buildCurrentGwPayload requires requestType.');
217
+ throw new Error('IndividualOrganizationLifecycleEditor.buildCurrentGwPayload requires requestType.');
171
218
  }
172
219
  return {
173
220
  thid: this.draft.thid || createLifecycleThreadId(this.draft.operation),
@@ -16,7 +16,7 @@ export declare const InteroperableLifecycleStatuses: Readonly<{
16
16
  }>;
17
17
  export type InteroperableLifecycleStatus = typeof InteroperableLifecycleStatuses[keyof typeof InteroperableLifecycleStatuses];
18
18
  export type InteroperableSearchParams = Readonly<Record<string, SearchParameterPrimitive | undefined>>;
19
- export type InteroperableResourceOperationDraft = Readonly<{
19
+ export type InteroperableResourceOperationState = Readonly<{
20
20
  resourceType: string;
21
21
  identifierClaimKey: string;
22
22
  identifierValue?: string;
@@ -45,7 +45,7 @@ export interface InteroperableResourceOperationEditor {
45
45
  mergeClaims(claims: Record<string, unknown>): InteroperableResourceOperationEditor;
46
46
  setLifecycleStatus(value: InteroperableLifecycleStatus): InteroperableResourceOperationEditor;
47
47
  importFhirResource(resource: FhirResource): InteroperableResourceOperationEditor;
48
- getDraft(): InteroperableResourceOperationDraft;
48
+ getState(): InteroperableResourceOperationState;
49
49
  getBusinessIdentifier(): string | undefined;
50
50
  getClaims(): Record<string, unknown>;
51
51
  buildLifecycleResource(): {
@@ -140,7 +140,7 @@ export declare function buildInteroperableSearchPath(resourceType: string): stri
140
140
  * - `resource.meta.status` carries lifecycle state without overloading
141
141
  * resource-specific FHIR fields such as `status` or `active`
142
142
  */
143
- export declare function buildLifecycleOperationResource(draft: InteroperableResourceOperationDraft): {
143
+ export declare function buildLifecycleOperationResource(state: InteroperableResourceOperationState): {
144
144
  resourceType: string;
145
145
  id?: string;
146
146
  identifier?: Array<{
@@ -155,4 +155,4 @@ export declare function buildLifecycleOperationResource(draft: InteroperableReso
155
155
  /**
156
156
  * Creates the shared chainable editor for search/disable/purge request shapes.
157
157
  */
158
- export declare function createInteroperableResourceOperationEditor(initial?: Partial<InteroperableResourceOperationDraft>): InteroperableResourceOperationEditor;
158
+ export declare function createInteroperableResourceOperationEditor(initial?: Partial<InteroperableResourceOperationState>): InteroperableResourceOperationEditor;
@@ -24,23 +24,23 @@ function cloneClaims(claims) {
24
24
  function resolveDefaultIdentifierClaimKey(resourceType) {
25
25
  return `${String(resourceType || '').trim()}.identifier`;
26
26
  }
27
- function normalizeDraft(draft) {
28
- const resourceType = normalizeText(draft?.resourceType) || 'Resource';
27
+ function normalizeDraft(state) {
28
+ const resourceType = normalizeText(state?.resourceType) || 'Resource';
29
29
  return {
30
30
  resourceType,
31
- identifierClaimKey: normalizeText(draft?.identifierClaimKey) || resolveDefaultIdentifierClaimKey(resourceType),
32
- identifierValue: normalizeText(draft?.identifierValue),
33
- identifierSystem: normalizeText(draft?.identifierSystem),
34
- resourceId: normalizeText(draft?.resourceId),
35
- claims: cloneClaims(draft?.claims),
36
- lifecycleStatus: draft?.lifecycleStatus,
31
+ identifierClaimKey: normalizeText(state?.identifierClaimKey) || resolveDefaultIdentifierClaimKey(resourceType),
32
+ identifierValue: normalizeText(state?.identifierValue),
33
+ identifierSystem: normalizeText(state?.identifierSystem),
34
+ resourceId: normalizeText(state?.resourceId),
35
+ claims: cloneClaims(state?.claims),
36
+ lifecycleStatus: state?.lifecycleStatus,
37
37
  };
38
38
  }
39
- function patchDraft(draft, patch) {
39
+ function patchDraft(state, patch) {
40
40
  return normalizeDraft({
41
- ...draft,
41
+ ...state,
42
42
  ...patch,
43
- claims: patch.claims ? cloneClaims(patch.claims) : cloneClaims(draft.claims),
43
+ claims: patch.claims ? cloneClaims(patch.claims) : cloneClaims(state.claims),
44
44
  });
45
45
  }
46
46
  /**
@@ -118,23 +118,23 @@ export function buildInteroperableSearchPath(resourceType) {
118
118
  * - `resource.meta.status` carries lifecycle state without overloading
119
119
  * resource-specific FHIR fields such as `status` or `active`
120
120
  */
121
- export function buildLifecycleOperationResource(draft) {
122
- const claims = cloneClaims(draft.claims);
123
- if (draft.identifierValue && !normalizeText(claims[draft.identifierClaimKey])) {
124
- claims[draft.identifierClaimKey] = draft.identifierValue;
121
+ export function buildLifecycleOperationResource(state) {
122
+ const claims = cloneClaims(state.claims);
123
+ if (state.identifierValue && !normalizeText(claims[state.identifierClaimKey])) {
124
+ claims[state.identifierClaimKey] = state.identifierValue;
125
125
  }
126
126
  return {
127
- resourceType: draft.resourceType,
128
- ...(draft.resourceId ? { id: draft.resourceId } : {}),
129
- ...(draft.identifierValue ? {
127
+ resourceType: state.resourceType,
128
+ ...(state.resourceId ? { id: state.resourceId } : {}),
129
+ ...(state.identifierValue ? {
130
130
  identifier: [{
131
- value: draft.identifierValue,
132
- ...(draft.identifierSystem ? { system: draft.identifierSystem } : {}),
131
+ value: state.identifierValue,
132
+ ...(state.identifierSystem ? { system: state.identifierSystem } : {}),
133
133
  }],
134
134
  } : {}),
135
135
  meta: {
136
136
  claims,
137
- ...(draft.lifecycleStatus ? { status: draft.lifecycleStatus } : {}),
137
+ ...(state.lifecycleStatus ? { status: state.lifecycleStatus } : {}),
138
138
  },
139
139
  };
140
140
  }
@@ -192,7 +192,7 @@ export function createInteroperableResourceOperationEditor(initial) {
192
192
  });
193
193
  return editor;
194
194
  },
195
- getDraft() {
195
+ getState() {
196
196
  return normalizeDraft(draft);
197
197
  },
198
198
  getBusinessIdentifier() {
@@ -0,0 +1,40 @@
1
+ import { PublicJwk } from '../interfaces/Cryptography.types';
2
+ /**
3
+ * Minimal RSA public JWK shape needed for RFC 7638 thumbprint derivation.
4
+ */
5
+ export type RsaPublicJwk = {
6
+ kty: 'RSA';
7
+ e: string;
8
+ n: string;
9
+ kid?: string;
10
+ alg?: string;
11
+ use?: string;
12
+ };
13
+ /**
14
+ * Public JWK shapes supported by the RFC 7638 thumbprint helpers.
15
+ *
16
+ * Notes:
17
+ * - classic EC keys are identified by `kty: 'EC'` and use `crv`, `x`, `y`
18
+ * - this includes `secp256k1`, represented as `crv: 'secp256k1'`
19
+ * - ML-DSA keys are represented in this codebase as `kty: 'AKP'` and use
20
+ * `alg` plus `pub`; they do not use `crv`
21
+ */
22
+ export type ThumbprintableJwk = PublicJwk | RsaPublicJwk;
23
+ /**
24
+ * Computes the RFC 7638 JWK thumbprint as a bare base64url string.
25
+ *
26
+ * Canonical fields per supported key family:
27
+ * - `EC`: `kty`, `crv`, `x`, `y`
28
+ * - `RSA`: `kty`, `e`, `n`
29
+ * - `OKP`: `kty`, `crv`, `x`
30
+ * - `AKP` / ML-DSA: `kty`, `alg`, `pub`
31
+ *
32
+ * For classical EC keys, the curve is taken from `crv`, so keys such as
33
+ * `secp256k1` are handled naturally through `crv: 'secp256k1'`.
34
+ */
35
+ export declare function computeRfc7638JwkThumbprint(jwk: ThumbprintableJwk): string;
36
+ /**
37
+ * Returns the RFC 9278 thumbprint URI form:
38
+ * `urn:ietf:params:oauth:jwk-thumbprint:sha-256:<base64url>`
39
+ */
40
+ export declare function toJwkThumbprintSha256Urn(jwk: ThumbprintableJwk): string;
@@ -0,0 +1,57 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { UrnPrefixes } from '../constants/urn.js';
3
+ function canonicalizeForThumbprint(jwk) {
4
+ const keys = Object.keys(jwk).sort();
5
+ const parts = keys.map((key) => `"${key}":${JSON.stringify(jwk[key])}`);
6
+ return `{${parts.join(',')}}`;
7
+ }
8
+ function toBaseThumbprintJwk(jwk) {
9
+ if (jwk.kty === 'EC') {
10
+ const { crv, x, y } = jwk;
11
+ if (!crv || !x || !y)
12
+ throw new Error('EC JWK thumbprint requires crv, x and y.');
13
+ return { kty: 'EC', crv, x, y };
14
+ }
15
+ if (jwk.kty === 'RSA') {
16
+ const { e, n } = jwk;
17
+ if (!e || !n)
18
+ throw new Error('RSA JWK thumbprint requires e and n.');
19
+ return { kty: 'RSA', e, n };
20
+ }
21
+ if (jwk.kty === 'OKP') {
22
+ const { crv, x } = jwk;
23
+ if (!crv || !x)
24
+ throw new Error('OKP JWK thumbprint requires crv and x.');
25
+ return { kty: 'OKP', crv, x };
26
+ }
27
+ if (jwk.kty === 'AKP') {
28
+ const { alg, pub } = jwk;
29
+ if (!alg || !pub)
30
+ throw new Error('AKP JWK thumbprint requires alg and pub.');
31
+ return { kty: 'AKP', alg, pub };
32
+ }
33
+ throw new Error(`Unsupported JWK kty for RFC7638 thumbprint: ${jwk.kty || 'unknown'}`);
34
+ }
35
+ /**
36
+ * Computes the RFC 7638 JWK thumbprint as a bare base64url string.
37
+ *
38
+ * Canonical fields per supported key family:
39
+ * - `EC`: `kty`, `crv`, `x`, `y`
40
+ * - `RSA`: `kty`, `e`, `n`
41
+ * - `OKP`: `kty`, `crv`, `x`
42
+ * - `AKP` / ML-DSA: `kty`, `alg`, `pub`
43
+ *
44
+ * For classical EC keys, the curve is taken from `crv`, so keys such as
45
+ * `secp256k1` are handled naturally through `crv: 'secp256k1'`.
46
+ */
47
+ export function computeRfc7638JwkThumbprint(jwk) {
48
+ const canonical = canonicalizeForThumbprint(toBaseThumbprintJwk(jwk));
49
+ return createHash('sha256').update(canonical).digest('base64url');
50
+ }
51
+ /**
52
+ * Returns the RFC 9278 thumbprint URI form:
53
+ * `urn:ietf:params:oauth:jwk-thumbprint:sha-256:<base64url>`
54
+ */
55
+ export function toJwkThumbprintSha256Urn(jwk) {
56
+ return `${UrnPrefixes.JwkThumbprintSha256KeyId}${computeRfc7638JwkThumbprint(jwk)}`;
57
+ }
@@ -0,0 +1,97 @@
1
+ import { ClaimsOrganizationSchemaorg } from '../constants/schemaorg';
2
+ import { ClaimsRecord } from '../models/resource-document';
3
+ export type LegalOrganizationOnboardingErrorCode = 'MISSING_IDENTIFIER_OR_TAX_ID' | 'EXPLICIT_ALTERNATE_NAME_NOT_ALLOWED';
4
+ export type LegalOrganizationOnboardingValidationError = {
5
+ code: LegalOrganizationOnboardingErrorCode;
6
+ message: string;
7
+ claimPaths: string[];
8
+ };
9
+ export type ValidateLegalOrganizationOnboardingClaimsOptions = {
10
+ /**
11
+ * When `false` (default), `alternateName` is treated as a compatibility alias
12
+ * derived from the canonical legal identifier. Callers may still send an
13
+ * explicit value, but it must match the final normalized
14
+ * `Organization.identifier.value`.
15
+ *
16
+ * When `true`, callers may keep an explicit `alternateName` that differs from
17
+ * `Organization.identifier.value`.
18
+ */
19
+ allowExplicitAlternateNameForTenantId?: boolean;
20
+ };
21
+ export type ValidateLegalOrganizationOnboardingClaimsResult = {
22
+ ok: boolean;
23
+ errors: LegalOrganizationOnboardingValidationError[];
24
+ missingClaims: string[];
25
+ normalizedClaims: ClaimsRecord;
26
+ derived: {
27
+ identifierValueFromTaxId: boolean;
28
+ alternateNameFromIdentifierValue: boolean;
29
+ };
30
+ };
31
+ /**
32
+ * JSON Schema for legal-organization onboarding claims used by high-level SDK
33
+ * forms and assistants before they submit anything to GW CORE.
34
+ *
35
+ * Contract:
36
+ * - one of `Organization.identifier.value` or `Organization.taxID` must be
37
+ * provided
38
+ * - `alternateName` is optional at input time because current GW compatibility
39
+ * may derive it from the canonical legal identifier
40
+ * - callers that want stricter tenant-alias behavior must still run the
41
+ * validator below because JSON Schema alone cannot express the
42
+ * runtime option `allowExplicitAlternateNameForTenantId`
43
+ */
44
+ export declare const LEGAL_ORGANIZATION_ONBOARDING_JSON_SCHEMA: {
45
+ readonly $schema: "https://json-schema.org/draft/2020-12/schema";
46
+ readonly $id: "https://gdc/common-utils/legal-organization-onboarding.schema.json";
47
+ readonly title: "Legal Organization Onboarding Claims";
48
+ readonly type: "object";
49
+ readonly properties: {
50
+ readonly '@context': {
51
+ readonly type: "string";
52
+ readonly enum: readonly ["org.schema"];
53
+ };
54
+ readonly "org.schema.Organization.identifier.value": {
55
+ readonly type: "string";
56
+ readonly minLength: 1;
57
+ readonly description: "Canonical legal identifier used by onboarding and tenant normalization.";
58
+ };
59
+ readonly "org.schema.Organization.taxID": {
60
+ readonly type: "string";
61
+ readonly minLength: 1;
62
+ readonly description: "Compatibility tax identifier that may backfill Organization.identifier.value.";
63
+ };
64
+ readonly "org.schema.Organization.alternateName": {
65
+ readonly type: "string";
66
+ readonly minLength: 1;
67
+ readonly description: "Optional compatibility alias. If omitted, callers may derive it from Organization.identifier.value.";
68
+ };
69
+ };
70
+ readonly oneOf: readonly [{
71
+ readonly required: readonly [ClaimsOrganizationSchemaorg.identifierValue];
72
+ }, {
73
+ readonly required: readonly [ClaimsOrganizationSchemaorg.taxId];
74
+ }];
75
+ readonly additionalProperties: true;
76
+ };
77
+ /**
78
+ * Validates and normalizes legal-organization onboarding claims for SDKs,
79
+ * forms, and assistant-style data collection flows.
80
+ *
81
+ * Main behavior:
82
+ * - requires at least one of `Organization.identifier.value` or
83
+ * `Organization.taxID`
84
+ * - if `identifier.value` is missing and `taxID` exists, copies
85
+ * `taxID -> identifier.value`
86
+ * - if `alternateName` is missing and a canonical identifier exists, copies
87
+ * `identifier.value -> alternateName`
88
+ * - when `allowExplicitAlternateNameForTenantId` is `false` (default),
89
+ * rejects explicit `alternateName` values that differ from the final
90
+ * canonical `identifier.value`
91
+ *
92
+ * The result shape is intentionally assistant-friendly:
93
+ * - `missingClaims` tells UI/voice flows what is still required
94
+ * - `normalizedClaims` shows the post-derivation claim set
95
+ * - `derived` explains which values were filled automatically
96
+ */
97
+ export declare function validateLegalOrganizationOnboardingClaims(claims: ClaimsRecord, options?: ValidateLegalOrganizationOnboardingClaimsOptions): ValidateLegalOrganizationOnboardingClaimsResult;
@@ -0,0 +1,128 @@
1
+ import { ClaimsOrganizationSchemaorg } from '../constants/schemaorg.js';
2
+ /**
3
+ * JSON Schema for legal-organization onboarding claims used by high-level SDK
4
+ * forms and assistants before they submit anything to GW CORE.
5
+ *
6
+ * Contract:
7
+ * - one of `Organization.identifier.value` or `Organization.taxID` must be
8
+ * provided
9
+ * - `alternateName` is optional at input time because current GW compatibility
10
+ * may derive it from the canonical legal identifier
11
+ * - callers that want stricter tenant-alias behavior must still run the
12
+ * validator below because JSON Schema alone cannot express the
13
+ * runtime option `allowExplicitAlternateNameForTenantId`
14
+ */
15
+ export const LEGAL_ORGANIZATION_ONBOARDING_JSON_SCHEMA = {
16
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
17
+ $id: 'https://gdc/common-utils/legal-organization-onboarding.schema.json',
18
+ title: 'Legal Organization Onboarding Claims',
19
+ type: 'object',
20
+ properties: {
21
+ '@context': {
22
+ type: 'string',
23
+ enum: ['org.schema'],
24
+ },
25
+ [ClaimsOrganizationSchemaorg.identifierValue]: {
26
+ type: 'string',
27
+ minLength: 1,
28
+ description: 'Canonical legal identifier used by onboarding and tenant normalization.',
29
+ },
30
+ [ClaimsOrganizationSchemaorg.taxId]: {
31
+ type: 'string',
32
+ minLength: 1,
33
+ description: 'Compatibility tax identifier that may backfill Organization.identifier.value.',
34
+ },
35
+ [ClaimsOrganizationSchemaorg.alternateName]: {
36
+ type: 'string',
37
+ minLength: 1,
38
+ description: 'Optional compatibility alias. If omitted, callers may derive it from Organization.identifier.value.',
39
+ },
40
+ },
41
+ oneOf: [
42
+ { required: [ClaimsOrganizationSchemaorg.identifierValue] },
43
+ { required: [ClaimsOrganizationSchemaorg.taxId] },
44
+ ],
45
+ additionalProperties: true,
46
+ };
47
+ function normalizeOptionalString(value) {
48
+ if (typeof value !== 'string') {
49
+ return undefined;
50
+ }
51
+ const trimmed = value.trim();
52
+ return trimmed.length ? trimmed : undefined;
53
+ }
54
+ /**
55
+ * Validates and normalizes legal-organization onboarding claims for SDKs,
56
+ * forms, and assistant-style data collection flows.
57
+ *
58
+ * Main behavior:
59
+ * - requires at least one of `Organization.identifier.value` or
60
+ * `Organization.taxID`
61
+ * - if `identifier.value` is missing and `taxID` exists, copies
62
+ * `taxID -> identifier.value`
63
+ * - if `alternateName` is missing and a canonical identifier exists, copies
64
+ * `identifier.value -> alternateName`
65
+ * - when `allowExplicitAlternateNameForTenantId` is `false` (default),
66
+ * rejects explicit `alternateName` values that differ from the final
67
+ * canonical `identifier.value`
68
+ *
69
+ * The result shape is intentionally assistant-friendly:
70
+ * - `missingClaims` tells UI/voice flows what is still required
71
+ * - `normalizedClaims` shows the post-derivation claim set
72
+ * - `derived` explains which values were filled automatically
73
+ */
74
+ export function validateLegalOrganizationOnboardingClaims(claims, options = {}) {
75
+ const normalizedClaims = { ...(claims || {}) };
76
+ const errors = [];
77
+ const missingClaims = [];
78
+ const allowExplicitAlternateNameForTenantId = options.allowExplicitAlternateNameForTenantId === true;
79
+ const identifierValue = normalizeOptionalString(normalizedClaims[ClaimsOrganizationSchemaorg.identifierValue]);
80
+ const taxId = normalizeOptionalString(normalizedClaims[ClaimsOrganizationSchemaorg.taxId]);
81
+ const explicitAlternateName = normalizeOptionalString(normalizedClaims[ClaimsOrganizationSchemaorg.alternateName]);
82
+ let finalIdentifierValue = identifierValue;
83
+ let identifierValueFromTaxId = false;
84
+ let alternateNameFromIdentifierValue = false;
85
+ if (!finalIdentifierValue && taxId) {
86
+ finalIdentifierValue = taxId;
87
+ normalizedClaims[ClaimsOrganizationSchemaorg.identifierValue] = taxId;
88
+ identifierValueFromTaxId = true;
89
+ }
90
+ if (!finalIdentifierValue) {
91
+ missingClaims.push(ClaimsOrganizationSchemaorg.identifierValue, ClaimsOrganizationSchemaorg.taxId);
92
+ errors.push({
93
+ code: 'MISSING_IDENTIFIER_OR_TAX_ID',
94
+ message: 'Legal organization onboarding requires Organization.identifier.value or Organization.taxID.',
95
+ claimPaths: [
96
+ ClaimsOrganizationSchemaorg.identifierValue,
97
+ ClaimsOrganizationSchemaorg.taxId,
98
+ ],
99
+ });
100
+ }
101
+ if (!explicitAlternateName && finalIdentifierValue) {
102
+ normalizedClaims[ClaimsOrganizationSchemaorg.alternateName] = finalIdentifierValue;
103
+ alternateNameFromIdentifierValue = true;
104
+ }
105
+ if (explicitAlternateName
106
+ && finalIdentifierValue
107
+ && !allowExplicitAlternateNameForTenantId
108
+ && explicitAlternateName !== finalIdentifierValue) {
109
+ errors.push({
110
+ code: 'EXPLICIT_ALTERNATE_NAME_NOT_ALLOWED',
111
+ message: 'Explicit Organization.alternateName is not allowed unless it matches Organization.identifier.value or the caller enables allowExplicitAlternateNameForTenantId.',
112
+ claimPaths: [
113
+ ClaimsOrganizationSchemaorg.alternateName,
114
+ ClaimsOrganizationSchemaorg.identifierValue,
115
+ ],
116
+ });
117
+ }
118
+ return {
119
+ ok: errors.length === 0,
120
+ errors,
121
+ missingClaims,
122
+ normalizedClaims,
123
+ derived: {
124
+ identifierValueFromTaxId,
125
+ alternateNameFromIdentifierValue,
126
+ },
127
+ };
128
+ }
@@ -8,14 +8,14 @@ export declare const LicenseCommercialSearchOperation: Readonly<{
8
8
  readonly Offer: "Offer:Search";
9
9
  readonly Order: "Order:Search";
10
10
  }>;
11
- export type LicenseOfferSearchDraft = Readonly<{
11
+ export type LicenseOfferSearchState = Readonly<{
12
12
  offerId?: string;
13
13
  status?: string;
14
14
  category?: string;
15
15
  customerType?: string;
16
16
  additionalClaims: LicenseClaims;
17
17
  }>;
18
- export type LicenseOrderSearchDraft = Readonly<{
18
+ export type LicenseOrderSearchState = Readonly<{
19
19
  acceptedOfferId?: string;
20
20
  invoiceId?: string;
21
21
  paymentMethod?: string;
@@ -43,13 +43,13 @@ export type LicenseOrderRecord = Readonly<{
43
43
  */
44
44
  export declare class LicenseOfferSearchEditor {
45
45
  private draft;
46
- constructor(initial?: Partial<LicenseOfferSearchDraft>);
46
+ constructor(initial?: Partial<LicenseOfferSearchState>);
47
47
  setOfferId(value: string): this;
48
48
  setStatus(value: string): this;
49
49
  setCategory(value: string): this;
50
50
  setCustomerType(value: string): this;
51
51
  mergeClaims(claims: LicenseClaims): this;
52
- getDraft(): LicenseOfferSearchDraft;
52
+ getState(): LicenseOfferSearchState;
53
53
  buildSearchEntry(): {
54
54
  type: string;
55
55
  request: {
@@ -72,13 +72,13 @@ export declare class LicenseOfferSearchEditor {
72
72
  */
73
73
  export declare class LicenseOrderSearchEditor {
74
74
  private draft;
75
- constructor(initial?: Partial<LicenseOrderSearchDraft>);
75
+ constructor(initial?: Partial<LicenseOrderSearchState>);
76
76
  setAcceptedOfferId(value: string): this;
77
77
  setInvoiceId(value: string): this;
78
78
  setPaymentMethod(value: string): this;
79
79
  setStatus(value: string): this;
80
80
  mergeClaims(claims: LicenseClaims): this;
81
- getDraft(): LicenseOrderSearchDraft;
81
+ getState(): LicenseOrderSearchState;
82
82
  buildSearchEntry(): {
83
83
  type: string;
84
84
  request: {
@@ -68,7 +68,7 @@ export class LicenseOfferSearchEditor {
68
68
  });
69
69
  return this;
70
70
  }
71
- getDraft() {
71
+ getState() {
72
72
  return cloneOfferSearchDraft(this.draft);
73
73
  }
74
74
  buildSearchEntry() {
@@ -131,7 +131,7 @@ export class LicenseOrderSearchEditor {
131
131
  });
132
132
  return this;
133
133
  }
134
- getDraft() {
134
+ getState() {
135
135
  return cloneOrderSearchDraft(this.draft);
136
136
  }
137
137
  buildSearchEntry() {