dataspace-client-sdk-node 0.1.1 → 0.1.2

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/src/types.ts CHANGED
@@ -195,6 +195,17 @@ export type FamilyOrganizationSummary = {
195
195
  updatedAt?: string;
196
196
  };
197
197
 
198
+ export type OfferPreview = {
199
+ offerId?: string;
200
+ amount?: string;
201
+ currency?: string;
202
+ seats?: number;
203
+ planName?: string;
204
+ sku?: string;
205
+ paymentMethod?: string;
206
+ checkoutUrl?: string;
207
+ };
208
+
198
209
  /**
199
210
  * Input for organization activation in GW using ICA-derived proof material.
200
211
  *
@@ -203,12 +214,43 @@ export type FamilyOrganizationSummary = {
203
214
  */
204
215
  export type GatewayOrganizationActivationInput = {
205
216
  vpToken: string;
217
+ /** Generic requested seats/members for initial offer sizing. Defaults to 2. */
218
+ numberOfMembers?: number;
219
+ organizationVc?: string;
220
+ legalRepresentativeVc?: string;
221
+ regulatoryEvidence?: Record<string, unknown>;
222
+ /** @deprecated Prefer `numberOfMembers` and explicit input fields. */
223
+ additionalClaims?: Record<string, unknown>;
224
+ };
225
+
226
+ export type GatewayOrganizationActivationSimpleInput = {
227
+ jurisdiction?: string;
228
+ sector?: string;
229
+ vpToken: string;
230
+ serviceProviderDidWeb?: string;
231
+ serviceProviderUrl?: string;
232
+ controllerEmail?: string;
233
+ controllerTelephone?: string;
234
+ controllerRole: string;
235
+ numberOfMembers?: number;
236
+ timeoutSeconds?: number;
237
+ intervalSeconds?: number;
206
238
  organizationVc?: string;
207
239
  legalRepresentativeVc?: string;
208
240
  regulatoryEvidence?: Record<string, unknown>;
209
241
  additionalClaims?: Record<string, unknown>;
210
242
  };
211
243
 
244
+ export type LegalOrganizationOrderSimpleInput = {
245
+ jurisdiction?: string;
246
+ sector?: string;
247
+ offerId: string;
248
+ timeoutSeconds?: number;
249
+ intervalSeconds?: number;
250
+ dataType?: string;
251
+ additionalClaims?: Record<string, unknown>;
252
+ };
253
+
212
254
  /**
213
255
  * Input for device activation based on activation code exchange + DCR.
214
256
  */
@@ -219,6 +261,17 @@ export type EmployeeDeviceActivationInput = {
219
261
  pollOptions?: PollOptions;
220
262
  };
221
263
 
264
+ export type EmployeeDeviceActivationSimpleInput = {
265
+ tenantId?: string;
266
+ jurisdiction?: string;
267
+ sector?: string;
268
+ activationCode: string;
269
+ idToken: string;
270
+ dcrPayload: Record<string, unknown>;
271
+ timeoutSeconds?: number;
272
+ intervalSeconds?: number;
273
+ };
274
+
222
275
  /**
223
276
  * Result of device activation flow.
224
277
  *
@@ -256,6 +309,40 @@ export type SubjectOrganizationBootstrapResult = {
256
309
  confirmation?: SubmitAndPollResult;
257
310
  };
258
311
 
312
+ export type IndividualOrganizationBootstrapSimpleInput = {
313
+ tenantId?: string;
314
+ jurisdiction?: string;
315
+ sector?: string;
316
+ alternateName: string;
317
+ controllerEmail?: string;
318
+ controllerTelephone?: string;
319
+ controllerRole?: string; // default org.hl7.v3.RoleCode|RESPRSN
320
+ timeoutSeconds?: number;
321
+ intervalSeconds?: number;
322
+ additionalClaims?: Record<string, unknown>;
323
+ };
324
+
325
+ export type IndividualOrganizationBootstrapSimpleResult = {
326
+ registration: SubmitAndPollResult;
327
+ offerId: string;
328
+ confirmation: SubmitAndPollResult;
329
+ };
330
+
331
+ export type IndividualOrganizationStartSimpleResult = {
332
+ registration: SubmitAndPollResult;
333
+ offerId: string;
334
+ offerPreview: OfferPreview;
335
+ };
336
+
337
+ export type IndividualOrganizationConfirmOrderSimpleInput = {
338
+ tenantId?: string;
339
+ jurisdiction?: string;
340
+ sector?: string;
341
+ offerId: string;
342
+ timeoutSeconds?: number;
343
+ intervalSeconds?: number;
344
+ };
345
+
259
346
  /**
260
347
  * Input for UC 5.5 IPS/FHIR import and index update.
261
348
  */
@@ -413,6 +500,8 @@ export type ClientOptions = {
413
500
  bearerToken?: string;
414
501
  defaultHeaders?: Record<string, string>;
415
502
  wallet?: WalletProvider;
503
+ /** Optional default tenant context so calls can omit ctx repeatedly. */
504
+ ctx?: RouteContext;
416
505
  };
417
506
 
418
507
  /**
@@ -487,6 +576,18 @@ export type SmartTokenExchangeInput = {
487
576
  path?: string;
488
577
  };
489
578
 
579
+ export type SmartTokenRequestSimpleInput = {
580
+ tenantId?: string;
581
+ jurisdiction?: string;
582
+ sector?: string;
583
+ idToken: string;
584
+ scopes: string[];
585
+ endpointId?: string;
586
+ timeoutSeconds?: number;
587
+ intervalSeconds?: number;
588
+ additionalClaims?: Record<string, unknown>;
589
+ };
590
+
490
591
  export type SmartTokenExchangeResult = {
491
592
  status: 'fetched' | 'cached' | 'failed';
492
593
  accessToken?: string;
@@ -268,6 +268,10 @@ test('activateOrganizationInGatewayFromIcaProof submits activation and polls to
268
268
  assert.equal(result.submit.status, 202);
269
269
  assert.equal(result.poll.status, 200);
270
270
  assert.equal(calls[0].url, 'http://localhost:3000/host/cds-ES/v1/test/registry/org.schema/Organization/_activate');
271
+ const submitPayload = JSON.parse(calls[0].options.body);
272
+ assert.equal(submitPayload.body.vp_token, 'vp-token-001');
273
+ assert.equal(submitPayload.body.organizationCredential, 'org-vc-jwt');
274
+ assert.equal(submitPayload.body.representativeCredential, 'legal-vc-jwt');
271
275
  } finally {
272
276
  globalThis.fetch = originalFetch;
273
277
  }
@@ -388,6 +392,90 @@ test('bootstrapSubjectOrganizationIndex runs registration and confirmation flow'
388
392
  }
389
393
  });
390
394
 
395
+ test('bootstrapIndividualOrganizationSimple registers individual org and confirms family order', async () => {
396
+ const calls = [];
397
+ const originalFetch = globalThis.fetch;
398
+
399
+ globalThis.fetch = async (url, options) => {
400
+ calls.push({ url: String(url), options, body: options?.body ? JSON.parse(options.body) : undefined });
401
+ if (calls.length === 1 || calls.length === 3) return jsonResponse({ accepted: true }, 202);
402
+ if (calls.length === 2) {
403
+ return jsonResponse({
404
+ body: { data: [{ meta: { claims: { 'org.schema.Offer.identifier': 'urn:offer:family-001' } } }] },
405
+ }, 200);
406
+ }
407
+ return jsonResponse({ status: 'COMPLETED' }, 200);
408
+ };
409
+
410
+ try {
411
+ const client = new DataspaceNodeClient({
412
+ baseUrl: 'http://localhost:3000',
413
+ bearerToken: 'controller-token',
414
+ ctx: { tenantId: 'acme', jurisdiction: 'ES', sector: 'health-care' },
415
+ });
416
+ const result = await client.bootstrapIndividualOrganizationSimple({
417
+ alternateName: 'ana',
418
+ controllerTelephone: 'tel:+34600111222',
419
+ });
420
+
421
+ assert.equal(result.offerId, 'urn:offer:family-001');
422
+ assert.equal(result.registration.poll.status, 200);
423
+ assert.equal(result.confirmation.poll.status, 200);
424
+ assert.equal(
425
+ calls[0].url,
426
+ 'http://localhost:3000/acme/cds-ES/v1/health-care/individual/org.schema/Organization/_batch',
427
+ );
428
+ assert.equal(
429
+ calls[2].url,
430
+ 'http://localhost:3000/acme/cds-ES/v1/health-care/individual/org.schema/Order/_batch',
431
+ );
432
+ const registrationClaims = calls[0].body?.body?.data?.[0]?.meta?.claims || {};
433
+ assert.equal(registrationClaims['org.schema.Organization.alternateName'], 'ana');
434
+ assert.equal(registrationClaims['org.schema.Service.category'], 'health-care');
435
+ assert.equal(registrationClaims['org.schema.Person.telephone'], 'tel:+34600111222');
436
+ assert.equal(registrationClaims['org.schema.Person.hasOccupation'], 'org.hl7.v3.RoleCode|RESPRSN');
437
+ } finally {
438
+ globalThis.fetch = originalFetch;
439
+ }
440
+ });
441
+
442
+ test('startIndividualOrganizationSimple + confirmIndividualOrganizationOrderSimple support explicit offer acceptance UX', async () => {
443
+ const calls = [];
444
+ const originalFetch = globalThis.fetch;
445
+
446
+ globalThis.fetch = async (url, options) => {
447
+ calls.push({ url: String(url), options, body: options?.body ? JSON.parse(options.body) : undefined });
448
+ if (calls.length === 1 || calls.length === 3) return jsonResponse({ accepted: true }, 202);
449
+ if (calls.length === 2) {
450
+ return jsonResponse({
451
+ body: { data: [{ meta: { claims: { 'org.schema.Offer.identifier': 'urn:offer:family-002', 'org.schema.Offer.price': '0.00' } } }] },
452
+ }, 200);
453
+ }
454
+ return jsonResponse({ status: 'COMPLETED' }, 200);
455
+ };
456
+
457
+ try {
458
+ const client = new DataspaceNodeClient({
459
+ baseUrl: 'http://localhost:3000',
460
+ bearerToken: 'controller-token',
461
+ ctx: { tenantId: 'acme', jurisdiction: 'ES', sector: 'health-care' },
462
+ });
463
+ const started = await client.startIndividualOrganizationSimple({
464
+ alternateName: 'maria',
465
+ controllerEmail: 'maria@example.com',
466
+ });
467
+ assert.equal(started.offerId, 'urn:offer:family-002');
468
+ assert.equal(started.offerPreview.amount, '0.00');
469
+
470
+ const confirmed = await client.confirmIndividualOrganizationOrderSimple({
471
+ offerId: started.offerId,
472
+ });
473
+ assert.equal(confirmed.poll.status, 200);
474
+ } finally {
475
+ globalThis.fetch = originalFetch;
476
+ }
477
+ });
478
+
391
479
  test('importIpsOrFhirAndUpdateIndex submits Composition in individual section', async () => {
392
480
  const calls = [];
393
481
  const originalFetch = globalThis.fetch;
@@ -523,6 +611,88 @@ test('requestSmartToken exchanges token in separate step', async () => {
523
611
  }
524
612
  });
525
613
 
614
+ test('requestSmartTokenSimple uses identity auth exchange async flow with default ctx', async () => {
615
+ const calls = [];
616
+ const originalFetch = globalThis.fetch;
617
+
618
+ globalThis.fetch = async (url, options) => {
619
+ calls.push({ url: String(url), options });
620
+ if (calls.length === 1) return jsonResponse({ accepted: true }, 202);
621
+ return jsonResponse({
622
+ access_token: 'smart-token-ctx-001',
623
+ token_type: 'Bearer',
624
+ scope: 'employee.healthcare.getIndexComposition',
625
+ expires_in: 3600,
626
+ }, 200);
627
+ };
628
+
629
+ try {
630
+ const client = new DataspaceNodeClient({
631
+ baseUrl: 'http://localhost:3000',
632
+ ctx: { tenantId: 'acme', jurisdiction: 'ES', sector: 'health-care' },
633
+ });
634
+
635
+ const result = await client.requestSmartTokenSimple({
636
+ idToken: 'employee-id-token-001',
637
+ scopes: ['employee.healthcare.getIndexComposition'],
638
+ timeoutSeconds: 5,
639
+ intervalSeconds: 0.001,
640
+ });
641
+
642
+ assert.equal(result.status, 'fetched');
643
+ assert.equal(result.accessToken, 'smart-token-ctx-001');
644
+ assert.equal(calls.length, 2);
645
+ assert.equal(calls[0].url, 'http://localhost:3000/host/cds-ES/v1/health-care/acme/identity/auth/_exchange');
646
+ assert.equal(calls[1].url, 'http://localhost:3000/host/cds-ES/v1/health-care/acme/identity/auth/_exchange-response');
647
+ } finally {
648
+ globalThis.fetch = originalFetch;
649
+ }
650
+ });
651
+
652
+ test('activateEmployeeDeviceWithActivationCodeSimple uses one-object input and default ctx', async () => {
653
+ const calls = [];
654
+ const originalFetch = globalThis.fetch;
655
+
656
+ globalThis.fetch = async (url, options) => {
657
+ calls.push({ url: String(url), options });
658
+ switch (calls.length) {
659
+ case 1:
660
+ return jsonResponse({ accepted: true }, 202); // exchange submit
661
+ case 2:
662
+ return jsonResponse({ body: { initial_access_token: 'initial-access-simple-001' } }, 200); // exchange poll
663
+ case 3:
664
+ return jsonResponse({ accepted: true }, 202); // dcr submit
665
+ default:
666
+ return jsonResponse({ body: { client_id: 'did:web:device-simple-001' } }, 200); // dcr poll
667
+ }
668
+ };
669
+
670
+ try {
671
+ const client = new DataspaceNodeClient({
672
+ baseUrl: 'http://localhost:3000',
673
+ ctx: { tenantId: 'acme', jurisdiction: 'ES', sector: 'health-care' },
674
+ });
675
+ const result = await client.activateEmployeeDeviceWithActivationCodeSimple({
676
+ activationCode: 'ACT-SIMPLE-001',
677
+ idToken: 'user-id-token-001',
678
+ dcrPayload: {
679
+ application_type: 'web',
680
+ client_name: 'Acme Portal',
681
+ token_endpoint_auth_method: 'private_key_jwt',
682
+ },
683
+ timeoutSeconds: 5,
684
+ intervalSeconds: 0.001,
685
+ });
686
+
687
+ assert.equal(result.initialAccessToken, 'initial-access-simple-001');
688
+ assert.equal(calls.length, 4);
689
+ assert.equal(calls[0].url, 'http://localhost:3000/host/cds-ES/v1/health-care/acme/identity/auth/_exchange');
690
+ assert.equal(calls[2].url, 'http://localhost:3000/host/cds-ES/v1/health-care/acme/identity/auth/_dcr');
691
+ } finally {
692
+ globalThis.fetch = originalFetch;
693
+ }
694
+ });
695
+
526
696
  test('generateDigitalTwinFromSubjectData submits digital twin Composition', async () => {
527
697
  const calls = [];
528
698
  const originalFetch = globalThis.fetch;
@@ -890,3 +1060,324 @@ test('searchFamilyOrganization returns null for not_found status', async () => {
890
1060
  globalThis.fetch = originalFetch;
891
1061
  }
892
1062
  });
1063
+
1064
+ test('offer helpers extract offerId and preview from activation-like response', () => {
1065
+ const client = new DataspaceNodeClient({ baseUrl: 'http://localhost:3000' });
1066
+ const result = {
1067
+ poll: {
1068
+ body: {
1069
+ body: {
1070
+ data: [{
1071
+ meta: {
1072
+ claims: {
1073
+ 'org.schema.Offer.identifier': 'urn:offer:abc',
1074
+ 'org.schema.Offer.price': '0.00',
1075
+ 'org.schema.Offer.priceCurrency': 'EUR',
1076
+ 'org.schema.Offer.eligibleQuantity.value': 2,
1077
+ 'org.schema.Offer.itemOffered.name': 'License Tier XS',
1078
+ 'org.schema.Offer.itemOffered.sku': 'portal-web',
1079
+ 'org.schema.Offer.acceptedPaymentMethod': 'Stripe',
1080
+ 'org.schema.Offer.checkoutPageURLTemplate': 'https://pay.example.com/checkout/123',
1081
+ },
1082
+ },
1083
+ }],
1084
+ },
1085
+ },
1086
+ },
1087
+ };
1088
+
1089
+ assert.equal(client.getOfferIdFromResponse(result), 'urn:offer:abc');
1090
+ const preview = client.getOfferPreviewFromResponse(result);
1091
+ assert.equal(preview.offerId, 'urn:offer:abc');
1092
+ assert.equal(preview.amount, '0.00');
1093
+ assert.equal(preview.currency, 'EUR');
1094
+ assert.equal(preview.seats, 2);
1095
+ assert.equal(preview.planName, 'License Tier XS');
1096
+ assert.equal(preview.sku, 'portal-web');
1097
+ assert.equal(preview.paymentMethod, 'Stripe');
1098
+ assert.equal(preview.checkoutUrl, 'https://pay.example.com/checkout/123');
1099
+ });
1100
+
1101
+ test('activateOrganizationInGatewaySimple maps single-object input and seconds options', async () => {
1102
+ const calls = [];
1103
+ const originalFetch = globalThis.fetch;
1104
+
1105
+ globalThis.fetch = async (url, options) => {
1106
+ calls.push({ url: String(url), options, body: options?.body ? JSON.parse(options.body) : undefined });
1107
+ if (calls.length === 1) return jsonResponse({ accepted: true }, 202);
1108
+ return jsonResponse({ status: 'COMPLETED' }, 200);
1109
+ };
1110
+
1111
+ try {
1112
+ const client = new DataspaceNodeClient({ baseUrl: 'http://localhost:3000', bearerToken: 'tok' });
1113
+ const result = await client.activateOrganizationInGatewaySimple({
1114
+ jurisdiction: 'ES',
1115
+ sector: 'health-care',
1116
+ vpToken: 'vp-001',
1117
+ serviceProviderUrl: 'https://api.acme.org',
1118
+ controllerEmail: 'admin@acme.org',
1119
+ controllerRole: 'ISCO-08|1112',
1120
+ numberOfMembers: 3,
1121
+ timeoutSeconds: 5,
1122
+ intervalSeconds: 1,
1123
+ });
1124
+
1125
+ assert.equal(result.poll.status, 200);
1126
+ assert.equal(calls[0].url, 'http://localhost:3000/host/cds-ES/v1/health-care/registry/org.schema/Organization/_activate');
1127
+ const claims = calls[0].body?.body?.data?.[0]?.meta?.claims || {};
1128
+ assert.equal(claims.vp_token, 'vp-001');
1129
+ assert.equal(claims['org.schema.Organization.numberOfEmployees'], 3);
1130
+ assert.equal(claims['org.schema.Service.category'], 'health-care');
1131
+ assert.equal(claims['org.schema.Service.identifier'], 'did:web:api.acme.org');
1132
+ assert.equal(claims['org.schema.Service.url'], 'https://api.acme.org');
1133
+ assert.equal(claims['org.schema.Person.email'], 'admin@acme.org');
1134
+ assert.equal(claims['org.schema.Person.hasOccupation'], 'ISCO-08|1112');
1135
+ } finally {
1136
+ globalThis.fetch = originalFetch;
1137
+ }
1138
+ });
1139
+
1140
+ test('confirmLegalOrganizationOrderSimple builds order payload from single object', async () => {
1141
+ const calls = [];
1142
+ const originalFetch = globalThis.fetch;
1143
+
1144
+ globalThis.fetch = async (url, options) => {
1145
+ calls.push({ url: String(url), options, body: options?.body ? JSON.parse(options.body) : undefined });
1146
+ if (calls.length === 1) return jsonResponse({ accepted: true }, 202);
1147
+ return jsonResponse({ status: 'COMPLETED' }, 200);
1148
+ };
1149
+
1150
+ try {
1151
+ const client = new DataspaceNodeClient({ baseUrl: 'http://localhost:3000', bearerToken: 'tok' });
1152
+ const result = await client.confirmLegalOrganizationOrderSimple({
1153
+ jurisdiction: 'ES',
1154
+ sector: 'health-care',
1155
+ offerId: 'urn:offer:xyz',
1156
+ timeoutSeconds: 5,
1157
+ intervalSeconds: 1,
1158
+ });
1159
+
1160
+ assert.equal(result.poll.status, 200);
1161
+ assert.equal(calls[0].url, 'http://localhost:3000/host/cds-ES/v1/health-care/registry/org.schema/Order/_batch');
1162
+ const claims = calls[0].body?.body?.data?.[0]?.meta?.claims || {};
1163
+ assert.equal(claims['Order.acceptedOffer.identifier'], 'urn:offer:xyz');
1164
+ } finally {
1165
+ globalThis.fetch = originalFetch;
1166
+ }
1167
+ });
1168
+
1169
+ test('constructor default ctx allows onboarding calls without passing ctx each time', async () => {
1170
+ const calls = [];
1171
+ const originalFetch = globalThis.fetch;
1172
+ globalThis.fetch = async (url, options) => {
1173
+ calls.push({ url: String(url), options, body: options?.body ? JSON.parse(options.body) : undefined });
1174
+ if (calls.length === 1) return jsonResponse({ accepted: true }, 202); // activate submit
1175
+ if (calls.length === 2) {
1176
+ return jsonResponse({
1177
+ body: { data: [{ meta: { claims: { 'org.schema.Offer.identifier': 'urn:offer:def' } } }] },
1178
+ }, 200); // activate poll
1179
+ }
1180
+ if (calls.length === 3) return jsonResponse({ accepted: true }, 202); // order submit
1181
+ return jsonResponse({ status: 'COMPLETED' }, 200); // order poll
1182
+ };
1183
+
1184
+ try {
1185
+ const client = new DataspaceNodeClient({
1186
+ baseUrl: 'http://localhost:3000',
1187
+ bearerToken: 'tok',
1188
+ ctx: { tenantId: 'acme', jurisdiction: 'ES', sector: 'health-care' },
1189
+ });
1190
+ const activation = await client.activateOrganizationInGatewayFromIcaProof(undefined, { vpToken: 'vp-ctx-001' }, { timeoutMs: 5000, intervalMs: 1 });
1191
+ const offerId = client.getOfferIdFromResponse(activation);
1192
+ assert.equal(offerId, 'urn:offer:def');
1193
+ const legalOrgOrder = await client.confirmLegalOrganizationOrderSimple({
1194
+ jurisdiction: 'ES',
1195
+ sector: 'health-care',
1196
+ offerId: offerId,
1197
+ timeoutSeconds: 5,
1198
+ intervalSeconds: 1,
1199
+ });
1200
+ assert.equal(legalOrgOrder.poll.status, 200);
1201
+ assert.equal(calls[0].url, 'http://localhost:3000/host/cds-ES/v1/health-care/registry/org.schema/Organization/_activate');
1202
+ assert.equal(calls[2].url, 'http://localhost:3000/host/cds-ES/v1/health-care/registry/org.schema/Order/_batch');
1203
+ } finally {
1204
+ globalThis.fetch = originalFetch;
1205
+ }
1206
+ });
1207
+
1208
+ test('setTenantId/setJurisdiction/setSector configure default ctx for simple methods', async () => {
1209
+ const calls = [];
1210
+ const originalFetch = globalThis.fetch;
1211
+
1212
+ globalThis.fetch = async (url, options) => {
1213
+ calls.push({ url: String(url), options });
1214
+ if (calls.length === 1) return jsonResponse({ accepted: true }, 202);
1215
+ if (calls.length === 2) {
1216
+ return jsonResponse({
1217
+ body: {
1218
+ data: [{
1219
+ meta: { claims: { 'org.schema.Offer.identifier': 'offer-setter-001' } },
1220
+ }],
1221
+ },
1222
+ }, 200);
1223
+ }
1224
+ if (calls.length === 3) return jsonResponse({ accepted: true }, 202);
1225
+ return jsonResponse({ status: 'COMPLETED' }, 200);
1226
+ };
1227
+
1228
+ try {
1229
+ const client = new DataspaceNodeClient({ baseUrl: 'http://localhost:3000', bearerToken: 'id-token' });
1230
+ client.setTenantId('acme').setJurisdiction('ES').setSector('health-care');
1231
+
1232
+ const activation = await client.activateOrganizationInGatewaySimple({
1233
+ vpToken: 'vp-token-001',
1234
+ serviceProviderDidWeb: 'did:web:api.acme.org',
1235
+ controllerEmail: 'owner@acme.org',
1236
+ controllerRole: 'ISCO-08|1112',
1237
+ });
1238
+ const offerId = client.getOfferIdFromResponse(activation);
1239
+ assert.equal(offerId, 'offer-setter-001');
1240
+
1241
+ const order = await client.confirmLegalOrganizationOrderSimple({
1242
+ offerId,
1243
+ });
1244
+ assert.equal(order.poll.status, 200);
1245
+ assert.equal(calls[0].url, 'http://localhost:3000/host/cds-ES/v1/health-care/registry/org.schema/Organization/_activate');
1246
+ assert.equal(calls[2].url, 'http://localhost:3000/host/cds-ES/v1/health-care/registry/org.schema/Order/_batch');
1247
+ } finally {
1248
+ globalThis.fetch = originalFetch;
1249
+ }
1250
+ });
1251
+
1252
+ test('setContextOrg configures default ctx for simple methods', async () => {
1253
+ const calls = [];
1254
+ const originalFetch = globalThis.fetch;
1255
+
1256
+ globalThis.fetch = async (url, options) => {
1257
+ calls.push({ url: String(url), options });
1258
+ if (calls.length === 1) return jsonResponse({ accepted: true }, 202);
1259
+ if (calls.length === 2) return jsonResponse({ body: { data: [{ meta: { claims: { 'org.schema.Offer.identifier': 'offer-orgctx-001' } } }] } }, 200);
1260
+ return jsonResponse({ status: 'COMPLETED' }, 200);
1261
+ };
1262
+
1263
+ try {
1264
+ const client = new DataspaceNodeClient({ baseUrl: 'http://localhost:3000', bearerToken: 'id-token' });
1265
+ client.setContextOrg({ tenantId: 'acme', jurisdiction: 'ES', sector: 'health-care' });
1266
+ const activation = await client.activateOrganizationInGatewaySimple({
1267
+ vpToken: 'vp-token-001',
1268
+ serviceProviderUrl: 'portal.acme.org',
1269
+ controllerTelephone: 'tel:+34600111222',
1270
+ controllerRole: 'ISCO-08|1112',
1271
+ });
1272
+ assert.equal(client.getOfferIdFromResponse(activation), 'offer-orgctx-001');
1273
+ assert.equal(calls[0].url, 'http://localhost:3000/host/cds-ES/v1/health-care/registry/org.schema/Organization/_activate');
1274
+ } finally {
1275
+ globalThis.fetch = originalFetch;
1276
+ }
1277
+ });
1278
+
1279
+ test('setDefaultTimeoutSeconds and setDefaultIntervalSeconds apply to simple methods', async () => {
1280
+ const calls = [];
1281
+ const originalFetch = globalThis.fetch;
1282
+
1283
+ globalThis.fetch = async (url, options) => {
1284
+ calls.push({ url: String(url), options });
1285
+ if (calls.length === 1) return jsonResponse({ accepted: true }, 202);
1286
+ if (calls.length === 2) return jsonResponse({ status: 'PENDING' }, 202);
1287
+ return jsonResponse({ status: 'COMPLETED' }, 200);
1288
+ };
1289
+
1290
+ try {
1291
+ const client = new DataspaceNodeClient({ baseUrl: 'http://localhost:3000', bearerToken: 'id-token' });
1292
+ client
1293
+ .setContextOrg({ tenantId: 'acme', jurisdiction: 'ES', sector: 'health-care' })
1294
+ .setDefaultTimeoutSeconds(5)
1295
+ .setDefaultIntervalSeconds(0.001);
1296
+
1297
+ const result = await client.activateOrganizationInGatewaySimple({
1298
+ vpToken: 'vp-token-001',
1299
+ serviceProviderDidWeb: 'did:web:api.acme.org',
1300
+ controllerEmail: 'owner@acme.org',
1301
+ controllerRole: 'ISCO-08|1112',
1302
+ });
1303
+
1304
+ assert.equal(result.poll.status, 200);
1305
+ assert.equal(calls.length, 3);
1306
+ } finally {
1307
+ globalThis.fetch = originalFetch;
1308
+ }
1309
+ });
1310
+
1311
+ test('activateOrganizationInGatewaySimple throws on business-level error in DIDComm entry', async () => {
1312
+ const calls = [];
1313
+ const originalFetch = globalThis.fetch;
1314
+
1315
+ globalThis.fetch = async (url, options) => {
1316
+ calls.push({ url: String(url), options });
1317
+ if (calls.length === 1) return jsonResponse({ accepted: true }, 202);
1318
+ return jsonResponse({
1319
+ data: [{
1320
+ type: 'Organization-activation-request-v1.0',
1321
+ response: {
1322
+ status: '400',
1323
+ outcome: { issue: [{ diagnostics: 'Missing ICA-issued organization credential.' }] },
1324
+ },
1325
+ }],
1326
+ resourceType: 'Bundle',
1327
+ type: 'batch-response',
1328
+ total: 1,
1329
+ }, 200);
1330
+ };
1331
+
1332
+ try {
1333
+ const client = new DataspaceNodeClient({ baseUrl: 'http://localhost:3000' });
1334
+ await assert.rejects(
1335
+ () => client.activateOrganizationInGatewaySimple({
1336
+ jurisdiction: 'ES',
1337
+ sector: 'test',
1338
+ vpToken: 'vp-token-001',
1339
+ serviceProviderDidWeb: 'did:web:api.acme.org',
1340
+ controllerEmail: 'owner@acme.org',
1341
+ controllerRole: 'ISCO-08|1112',
1342
+ }),
1343
+ /Missing ICA-issued organization credential\./,
1344
+ );
1345
+ } finally {
1346
+ globalThis.fetch = originalFetch;
1347
+ }
1348
+ });
1349
+
1350
+ test('activateOrganizationInGatewaySimple requires controller identity and role', async () => {
1351
+ const client = new DataspaceNodeClient({ baseUrl: 'http://localhost:3000' });
1352
+ await assert.rejects(
1353
+ () => client.activateOrganizationInGatewaySimple({
1354
+ jurisdiction: 'ES',
1355
+ sector: 'test',
1356
+ vpToken: 'vp-token-001',
1357
+ serviceProviderDidWeb: 'did:web:api.acme.org',
1358
+ controllerRole: 'ISCO-08|1112',
1359
+ }),
1360
+ /requires controllerEmail or controllerTelephone/i,
1361
+ );
1362
+ await assert.rejects(
1363
+ () => client.activateOrganizationInGatewaySimple({
1364
+ jurisdiction: 'ES',
1365
+ sector: 'test',
1366
+ vpToken: 'vp-token-001',
1367
+ serviceProviderDidWeb: 'did:web:api.acme.org',
1368
+ controllerEmail: 'admin@acme.org',
1369
+ controllerRole: '',
1370
+ }),
1371
+ /requires controllerRole/i,
1372
+ );
1373
+ await assert.rejects(
1374
+ () => client.activateOrganizationInGatewaySimple({
1375
+ jurisdiction: 'ES',
1376
+ sector: 'test',
1377
+ vpToken: 'vp-token-001',
1378
+ controllerEmail: 'admin@acme.org',
1379
+ controllerRole: 'ISCO-08|1112',
1380
+ }),
1381
+ /requires serviceProviderDidWeb or serviceProviderUrl/i,
1382
+ );
1383
+ });