gdc-sdk-node-ts 2.4.13 → 2.4.16

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.
@@ -297,7 +297,7 @@ export declare class HttpRuntimeClient implements NodeRuntimeClient {
297
297
  * route with optional filters.
298
298
  */
299
299
  listOrganizationLicenses(ctx: RouteContext, input?: LicenseListRuntimeSearchInput): Promise<SubmitAndPollResult>;
300
- /** Adds zero-cost employee seats when the target GW is a test-network. */
300
+ /** @deprecated Professional seats use Employee Offer and confirmed Order. */
301
301
  addFreeOrganizationEmployeeLicenses(ctx: RouteContext, input: OrganizationEmployeeLicenseAddInput): Promise<SubmitAndPollResult>;
302
302
  searchOrganizationLicenseOffers(ctx: RouteContext, input: LicenseOfferRuntimeSearchInput): Promise<SubmitAndPollResult>;
303
303
  listOrganizationLicenseOffers(ctx: RouteContext, input?: LicenseOfferRuntimeSearchInput): Promise<SubmitAndPollResult>;
@@ -371,6 +371,7 @@ export class HttpRuntimeClient {
371
371
  async provisionOrganizationEmployee(ctx, input) {
372
372
  return provisionOrganizationEmployeeWithDeps(ctx, input, {
373
373
  createEmployee: (routeContext, creation) => this.createOrganizationEmployee(routeContext, creation),
374
+ confirmLicenseOrder: (routeContext, licenseOrder) => this.confirmOrganizationLicenseOrder(routeContext, licenseOrder),
374
375
  issueLicense: (routeContext, invitation) => this.issueOrganizationEmployeeLicense(routeContext, invitation),
375
376
  });
376
377
  }
@@ -473,7 +474,7 @@ export class HttpRuntimeClient {
473
474
  submitAndPoll: this.submitAndPoll.bind(this),
474
475
  });
475
476
  }
476
- /** Adds zero-cost employee seats when the target GW is a test-network. */
477
+ /** @deprecated Professional seats use Employee Offer and confirmed Order. */
477
478
  async addFreeOrganizationEmployeeLicenses(ctx, input) {
478
479
  return addFreeOrganizationEmployeeLicensesWithDeps(ctx, input, {
479
480
  organizationLicenseActionPath: this.paths.organizationLicenseActionPath.bind(this.paths),
@@ -83,7 +83,7 @@ export declare class OrganizationControllerSdk {
83
83
  * Lists organization-owned license seats with optional filters.
84
84
  */
85
85
  listLicenses(ctx: RouteContext, input?: LicenseListRuntimeSearchInput): Promise<SubmitAndPollResult>;
86
- /** Adds zero-cost employee seats on an explicitly configured test network. */
86
+ /** @deprecated Professional seats use Employee Offer and confirmed Order. */
87
87
  addFreeEmployeeLicenses(ctx: RouteContext, input: OrganizationEmployeeLicenseAddInput): Promise<SubmitAndPollResult>;
88
88
  /**
89
89
  * Searches commercial license offers known for the current organization.
@@ -111,7 +111,7 @@ export class OrganizationControllerSdk {
111
111
  listLicenses(ctx, input = {}) {
112
112
  return requireClientMethod(this.client, 'listOrganizationLicenses')(ctx, input);
113
113
  }
114
- /** Adds zero-cost employee seats on an explicitly configured test network. */
114
+ /** @deprecated Professional seats use Employee Offer and confirmed Order. */
115
115
  addFreeEmployeeLicenses(ctx, input) {
116
116
  return requireClientMethod(this.client, 'addFreeOrganizationEmployeeLicenses')(ctx, input);
117
117
  }
@@ -1,26 +1,47 @@
1
1
  import { type EmployeeActivationGrant, type OrganizationEmployeeLifecycleRecord } from 'gdc-common-utils-ts';
2
2
  import type { RouteContext } from './individual-onboarding.js';
3
3
  import type { SubmitAndPollResult } from './orchestration/client-port.js';
4
+ import type { OrganizationLicenseOrderConfirmInput } from './organization-license-order.js';
4
5
  import type { ServerProfileEnrollmentInput, ServerProfileRecord } from './server-profile-session.js';
5
6
  import type { LicenseListRuntimeSearchInput, OrganizationEmployeeCreationInput, OrganizationEmployeeLicenseInvitationInput, OrganizationEmployeeSearchInput } from './resource-operations.js';
6
- /** Input for the atomic controller-facing create plus seat issuance workflow. */
7
+ /**
8
+ * Input for atomic interactive provisioning. Every current employee create is
9
+ * seat-required. Supplying `licenseOrder` authorizes the SDK to close a
10
+ * GW-issued Offer before retrying creation and issuing the activation
11
+ * credential. A future batch import requires a separate explicit operation.
12
+ */
7
13
  export type OrganizationEmployeeProvisioningInput = Readonly<{
8
14
  creation: OrganizationEmployeeCreationInput;
9
15
  invitation: OrganizationEmployeeLicenseInvitationInput;
16
+ licenseOrder?: Omit<OrganizationLicenseOrderConfirmInput, 'offerId'>;
10
17
  }>;
11
18
  /** Results required by a portal to persist routing and display the credential once. */
12
19
  export type OrganizationEmployeeProvisioningResult = Readonly<{
13
20
  employee: SubmitAndPollResult;
14
21
  license: SubmitAndPollResult;
22
+ licenseOrder?: SubmitAndPollResult;
15
23
  activationCode: string;
16
24
  }>;
17
25
  /** Shared dependencies for the create plus seat issuance workflow. */
18
26
  export type OrganizationEmployeeProvisioningDeps = Readonly<{
19
27
  createEmployee(routeContext: RouteContext, input: OrganizationEmployeeCreationInput): Promise<SubmitAndPollResult>;
20
28
  issueLicense(routeContext: RouteContext, input: OrganizationEmployeeLicenseInvitationInput): Promise<SubmitAndPollResult>;
29
+ confirmLicenseOrder?(routeContext: RouteContext, input: OrganizationLicenseOrderConfirmInput): Promise<SubmitAndPollResult>;
21
30
  }>;
22
- /** Creates the employee, reserves its seat and returns the exact GW credential. */
31
+ /**
32
+ * Creates the employee, closing Offer/Order first when the strict request has
33
+ * no available seat, then reserves that seat through `License/_issue` and
34
+ * returns the exact GW activation credential.
35
+ */
23
36
  export declare function provisionOrganizationEmployeeWithDeps(routeContext: RouteContext, input: OrganizationEmployeeProvisioningInput, deps: OrganizationEmployeeProvisioningDeps): Promise<OrganizationEmployeeProvisioningResult>;
37
+ /**
38
+ * Reads the pending seat Offer from an employee-create response.
39
+ *
40
+ * Portal BFFs use this boundary when payment is asynchronous: persist the
41
+ * server-owned creation intent, redirect to hosted Checkout, then submit the
42
+ * paid reference in Order before retrying employee creation and License/_issue.
43
+ */
44
+ export declare function readEmployeeLicenseOfferId(value: unknown): string | undefined;
24
45
  /**
25
46
  * Rejects failed outer HTTP responses and failed entries hidden inside a
26
47
  * successful async poll envelope before a caller starts the next mutation.
@@ -2,17 +2,59 @@
2
2
  import { createHash } from 'node:crypto';
3
3
  import { ActorKinds, EmployeeActivationGrantVersions, projectOrganizationEmployeeLifecycle, readEmployeeActivationCode, } from 'gdc-common-utils-ts';
4
4
  import { buildStableActorIdentifier } from 'gdc-common-utils-ts/utils/actor-identifier';
5
- /** Creates the employee, reserves its seat and returns the exact GW credential. */
5
+ import { ClaimsOfferSchemaorg } from 'gdc-common-utils-ts/constants/schemaorg';
6
+ /**
7
+ * Creates the employee, closing Offer/Order first when the strict request has
8
+ * no available seat, then reserves that seat through `License/_issue` and
9
+ * returns the exact GW activation credential.
10
+ */
6
11
  export async function provisionOrganizationEmployeeWithDeps(routeContext, input, deps) {
7
- const employee = await deps.createEmployee(routeContext, input.creation);
12
+ const creation = input.creation;
13
+ let employee = await deps.createEmployee(routeContext, creation);
8
14
  assertSuccessfulEmployeeOperation('employee creation', employee);
15
+ let licenseOrder;
16
+ const offerId = readEmployeeLicenseOfferId(employee.poll.body);
17
+ if (offerId) {
18
+ if (!input.licenseOrder || !deps.confirmLicenseOrder) {
19
+ throw new Error('provisionOrganizationEmployee: employee creation requires Offer/Order confirmation before licence issue.');
20
+ }
21
+ licenseOrder = await deps.confirmLicenseOrder(routeContext, {
22
+ ...input.licenseOrder,
23
+ offerId,
24
+ });
25
+ assertSuccessfulEmployeeOperation('licence order confirmation', licenseOrder);
26
+ employee = await deps.createEmployee(routeContext, creation);
27
+ assertSuccessfulEmployeeOperation('employee creation retry', employee);
28
+ if (readEmployeeLicenseOfferId(employee.poll.body)) {
29
+ throw new Error('provisionOrganizationEmployee: employee creation still requires a licence after Order confirmation.');
30
+ }
31
+ }
9
32
  const license = await deps.issueLicense(routeContext, input.invitation);
10
33
  assertSuccessfulEmployeeOperation('licence issue', license);
11
34
  const activationCode = readEmployeeActivationCode(license.poll.body);
12
35
  if (!activationCode) {
13
36
  throw new Error('provisionOrganizationEmployee: GW response did not contain an employee activation credential.');
14
37
  }
15
- return { employee, license, activationCode };
38
+ return { employee, license, licenseOrder, activationCode };
39
+ }
40
+ /**
41
+ * Reads the pending seat Offer from an employee-create response.
42
+ *
43
+ * Portal BFFs use this boundary when payment is asynchronous: persist the
44
+ * server-owned creation intent, redirect to hosted Checkout, then submit the
45
+ * paid reference in Order before retrying employee creation and License/_issue.
46
+ */
47
+ export function readEmployeeLicenseOfferId(value) {
48
+ for (const candidate of nestedRecords(value)) {
49
+ if (candidate.type !== 'Employee-license-offer-v1.0')
50
+ continue;
51
+ const meta = record(candidate.meta);
52
+ const claims = record(meta?.claims);
53
+ const offerId = String(claims?.[ClaimsOfferSchemaorg.identifier] || '').trim();
54
+ if (offerId)
55
+ return offerId;
56
+ }
57
+ return undefined;
16
58
  }
17
59
  /**
18
60
  * Rejects failed outer HTTP responses and failed entries hidden inside a
@@ -669,14 +669,8 @@ type OrganizationLicenseMutationDeps = {
669
669
  organizationLicenseActionPollPath: (ctx: RouteContext, action: string) => string;
670
670
  submitAndPoll: IndividualLicenseMutationDeps['submitAndPoll'];
671
671
  };
672
- /**
673
- * Requests additional zero-cost employee seats from a GW test-network.
674
- * Production callers must use the provider-payment and confirmed Order flow;
675
- * the gateway enforces that environment boundary independently of this SDK.
676
- * The request keeps the canonical outer DIDComm identity fields because secure
677
- * transports sign this envelope before encrypting it for the gateway.
678
- */
679
- export declare function addFreeOrganizationEmployeeLicensesWithDeps(routeCtx: RouteContext, input: OrganizationEmployeeLicenseAddInput, deps: OrganizationLicenseMutationDeps): Promise<SubmitAndPollResult>;
672
+ /** @deprecated Professional seats always use Employee Offer and confirmed Order. */
673
+ export declare function addFreeOrganizationEmployeeLicensesWithDeps(_routeCtx: RouteContext, _input: OrganizationEmployeeLicenseAddInput, _deps: OrganizationLicenseMutationDeps): Promise<SubmitAndPollResult>;
680
674
  /**
681
675
  * Reserves one available household/member seat for a selected contact.
682
676
  *
@@ -243,29 +243,9 @@ export async function searchIndividualLicensesWithDeps(routeCtx, input, deps) {
243
243
  export async function listIndividualLicensesWithDeps(routeCtx, input, deps) {
244
244
  return searchIndividualLicensesWithDeps(routeCtx, input || {}, deps);
245
245
  }
246
- /**
247
- * Requests additional zero-cost employee seats from a GW test-network.
248
- * Production callers must use the provider-payment and confirmed Order flow;
249
- * the gateway enforces that environment boundary independently of this SDK.
250
- * The request keeps the canonical outer DIDComm identity fields because secure
251
- * transports sign this envelope before encrypting it for the gateway.
252
- */
253
- export async function addFreeOrganizationEmployeeLicensesWithDeps(routeCtx, input, deps) {
254
- const entry = buildLicensePurchaseEntry({
255
- quantity: input.quantity,
256
- userClass: 'employee',
257
- type: 'web',
258
- price: 0,
259
- priceCurrency: 'EUR',
260
- });
261
- return deps.submitAndPoll(deps.organizationLicenseActionPath(routeCtx, '_add'), deps.organizationLicenseActionPollPath(routeCtx, '_add'), {
262
- jti: `jti-${createRuntimeUuid()}`,
263
- iss: routeCtx.tenantId,
264
- aud: routeCtx.tenantId,
265
- type: 'application/didcomm-plain+json',
266
- thid: input.requestThid || `organization-license-add-${createRuntimeUuid()}`,
267
- body: { resourceType: 'Bundle', type: 'batch', data: [entry] },
268
- }, input.pollOptions);
246
+ /** @deprecated Professional seats always use Employee Offer and confirmed Order. */
247
+ export async function addFreeOrganizationEmployeeLicensesWithDeps(_routeCtx, _input, _deps) {
248
+ throw new Error('Professional seats require Employee Offer and Order payment verification; License/_add is not supported.');
269
249
  }
270
250
  /**
271
251
  * Reserves one available household/member seat for a selected contact.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-sdk-node-ts",
3
- "version": "2.4.13",
3
+ "version": "2.4.16",
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.",
@@ -12,6 +12,7 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "build": "tsc -p tsconfig.json",
15
+ "prepare": "npm run build",
15
16
  "type-check": "tsc -p tsconfig.json --noEmit",
16
17
  "check:product-neutrality": "node scripts/check-product-neutrality.mjs",
17
18
  "prepublishOnly": "npm run check:product-neutrality && npm run type-check && npm test",
@@ -37,7 +38,7 @@
37
38
  "test:e2e:live-gw:clean": "bash ./scripts/run-live-gw-clean.sh"
38
39
  },
39
40
  "dependencies": {
40
- "gdc-common-utils-ts": "2.5.7",
41
+ "gdc-common-utils-ts": "2.5.12",
41
42
  "gdc-sdk-core-ts": "~2.3.18"
42
43
  },
43
44
  "devDependencies": {