dataspace-client-sdk-node 0.1.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 (56) hide show
  1. package/README.md +310 -0
  2. package/SDK_PARITY_MAP.md +120 -0
  3. package/TODO_PROMPT_NEXT_STEPS.md +185 -0
  4. package/artifacts/update-smart-wallet.js +1016 -0
  5. package/dist/builders.d.ts +12 -0
  6. package/dist/builders.js +17 -0
  7. package/dist/client.d.ts +333 -0
  8. package/dist/client.js +1229 -0
  9. package/dist/consent/pdfSignatureVerification.d.ts +18 -0
  10. package/dist/consent/pdfSignatureVerification.js +23 -0
  11. package/dist/index.d.ts +4 -0
  12. package/dist/index.js +8 -0
  13. package/dist/sdk/dataspace-wallet-sdk-node/MultiWalletClient.d.ts +9 -0
  14. package/dist/sdk/dataspace-wallet-sdk-node/MultiWalletClient.js +21 -0
  15. package/dist/sdk/dataspace-wallet-sdk-node/WalletClient.d.ts +26 -0
  16. package/dist/sdk/dataspace-wallet-sdk-node/WalletClient.js +36 -0
  17. package/dist/sdk/dataspace-wallet-sdk-node/index.d.ts +6 -0
  18. package/dist/sdk/dataspace-wallet-sdk-node/index.js +6 -0
  19. package/dist/sdk/dataspace-wallet-sdk-node/provider.d.ts +24 -0
  20. package/dist/sdk/dataspace-wallet-sdk-node/provider.js +1 -0
  21. package/dist/sdk/dataspace-wallet-sdk-node/providers/memory-provider.d.ts +41 -0
  22. package/dist/sdk/dataspace-wallet-sdk-node/providers/memory-provider.js +216 -0
  23. package/dist/sdk/dataspace-wallet-sdk-node/providers/seed-provider.d.ts +22 -0
  24. package/dist/sdk/dataspace-wallet-sdk-node/providers/seed-provider.js +28 -0
  25. package/dist/sdk/dataspace-wallet-sdk-node/types.d.ts +51 -0
  26. package/dist/sdk/dataspace-wallet-sdk-node/types.js +1 -0
  27. package/dist/types.d.ts +445 -0
  28. package/dist/types.js +1 -0
  29. package/docs/API.md +745 -0
  30. package/docs/DATA_MODEL_ALIGNMENT.md +31 -0
  31. package/docs/DATA_PLANES_SCOPE_MATRIX.md +51 -0
  32. package/docs/DEVELOPER_USE_CASES.md +253 -0
  33. package/docs/E2E_BOOTSTRAP.md +54 -0
  34. package/docs/TODO_SMART_EHR_COMPAT.md +58 -0
  35. package/examples/backend-pkce-auth.mjs +119 -0
  36. package/examples/conversion-upload.mjs +52 -0
  37. package/examples/e2e-bootstrap-tenant.mjs +126 -0
  38. package/examples/e2e-individual-flow.mjs +43 -0
  39. package/examples/host-activate-and-employee.mjs +75 -0
  40. package/package.json +26 -0
  41. package/src/builders.ts +28 -0
  42. package/src/client.ts +1626 -0
  43. package/src/consent/pdfSignatureVerification.ts +41 -0
  44. package/src/index.ts +8 -0
  45. package/src/sdk/dataspace-wallet-sdk-node/MultiWalletClient.ts +25 -0
  46. package/src/sdk/dataspace-wallet-sdk-node/WalletClient.ts +63 -0
  47. package/src/sdk/dataspace-wallet-sdk-node/index.ts +6 -0
  48. package/src/sdk/dataspace-wallet-sdk-node/provider.ts +44 -0
  49. package/src/sdk/dataspace-wallet-sdk-node/providers/memory-provider.ts +310 -0
  50. package/src/sdk/dataspace-wallet-sdk-node/providers/seed-provider.ts +31 -0
  51. package/src/sdk/dataspace-wallet-sdk-node/types.ts +61 -0
  52. package/src/types.ts +497 -0
  53. package/tests/client.test.mjs +892 -0
  54. package/tests/uc5-org-onboarding.flow.test.mjs +145 -0
  55. package/tests/uc5-subject-data.flow.test.mjs +198 -0
  56. package/tsconfig.json +13 -0
@@ -0,0 +1,126 @@
1
+ import { DataspaceNodeClient } from '../dist/index.js';
2
+
3
+ function parseCsv(value, fallback = []) {
4
+ const items = String(value || '')
5
+ .split(',')
6
+ .map((item) => item.trim())
7
+ .filter(Boolean);
8
+ return items.length ? items : fallback;
9
+ }
10
+
11
+ function parseBool(value, fallback = false) {
12
+ if (value === undefined) return fallback;
13
+ const normalized = String(value).trim().toLowerCase();
14
+ if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) return true;
15
+ if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) return false;
16
+ return fallback;
17
+ }
18
+
19
+ function requiredEnv(name) {
20
+ const value = String(process.env[name] || '').trim();
21
+ if (!value) throw new Error(`Missing required env var: ${name}`);
22
+ return value;
23
+ }
24
+
25
+ function readOptionalJson(envName) {
26
+ const raw = String(process.env[envName] || '').trim();
27
+ if (!raw) return undefined;
28
+ return JSON.parse(raw);
29
+ }
30
+
31
+ function pickDiagnostics(result) {
32
+ return (
33
+ result?.poll?.body?.issues?.issue?.[0]?.diagnostics ||
34
+ result?.poll?.body?.body?.issues?.issue?.[0]?.diagnostics ||
35
+ ''
36
+ );
37
+ }
38
+
39
+ const baseUrl = process.env.BASE_URL || 'http://localhost:3000';
40
+ const authMode = (process.env.AUTH_MODE || 'demo').trim().toLowerCase();
41
+ const hostCtx = {
42
+ jurisdiction: process.env.JURISDICTION || 'ES',
43
+ sector: process.env.HOST_REGISTRY_SECTOR || 'test',
44
+ };
45
+ const tenantCtx = {
46
+ tenantId: process.env.TENANT_ID || 'acme',
47
+ jurisdiction: process.env.JURISDICTION || 'ES',
48
+ sector: process.env.SECTOR || 'health-care',
49
+ };
50
+
51
+ const pollOptions = {
52
+ timeoutMs: Number(process.env.POLL_TIMEOUT_MS || 120000),
53
+ intervalMs: Number(process.env.POLL_INTERVAL_MS || 2000),
54
+ };
55
+
56
+ async function resolveClient() {
57
+ if (authMode === 'pkce') {
58
+ const authClient = new DataspaceNodeClient({ baseUrl });
59
+ const auth = await authClient.authenticateBackendPkceAndExchange({
60
+ ctx: tenantCtx,
61
+ apiKey: requiredEnv('GW_API_KEY'),
62
+ controllerPublicJwk: readOptionalJson('GW_CONTROLLER_PUBLIC_JWK_SIGN') || readOptionalJson('GW_PUBLIC_JWK'),
63
+ scopes: parseCsv(process.env.GW_AUTH_SCOPES, ['onboarding', 'employee.write']),
64
+ endpointId: process.env.GW_AUTH_ENDPOINT_ID || 'e2e-bootstrap',
65
+ codeVerifier: process.env.GW_PKCE_CODE_VERIFIER,
66
+ pollOptions,
67
+ });
68
+ if (auth.status === 'failed' || !auth.accessToken) {
69
+ throw new Error(`PKCE auth failed at step ${auth.step || 'unknown'}.`);
70
+ }
71
+ return new DataspaceNodeClient({ baseUrl, bearerToken: auth.accessToken });
72
+ }
73
+
74
+ return new DataspaceNodeClient({
75
+ baseUrl,
76
+ bearerToken: process.env.AUTH_BEARER || 'demo-token',
77
+ });
78
+ }
79
+
80
+ async function main() {
81
+ const client = await resolveClient();
82
+
83
+ const activation = await client.activateOrganizationInGatewayFromIcaProof(
84
+ hostCtx,
85
+ {
86
+ vpToken: requiredEnv('VP_TOKEN'),
87
+ organizationVc: process.env.ORGANIZATION_VC_JWT,
88
+ legalRepresentativeVc: process.env.LEGAL_REPRESENTATIVE_VC_JWT,
89
+ },
90
+ pollOptions,
91
+ );
92
+
93
+ console.log(`[activate] http=${activation.poll.status}`);
94
+ if (activation.poll.status !== 200) {
95
+ const diagnostics = pickDiagnostics(activation);
96
+ throw new Error(`Organization activation failed (status=${activation.poll.status})${diagnostics ? `: ${diagnostics}` : ''}`);
97
+ }
98
+
99
+ if (!parseBool(process.env.CREATE_CONTROLLER_EMPLOYEE, false)) {
100
+ console.log('[employee] skipped (CREATE_CONTROLLER_EMPLOYEE=false)');
101
+ return;
102
+ }
103
+
104
+ const employee = await client.createOrganizationEmployee(
105
+ tenantCtx,
106
+ {
107
+ employeeClaims: {
108
+ '@context': 'org.schema',
109
+ 'org.schema.Person.email': process.env.CONTROLLER_EMAIL || 'controller@example.com',
110
+ 'org.schema.Person.hasOccupation': process.env.CONTROLLER_ROLE || 'ISCO-08|1342',
111
+ },
112
+ },
113
+ pollOptions,
114
+ );
115
+
116
+ console.log(`[employee] http=${employee.poll.status}`);
117
+ if (employee.poll.status !== 200) {
118
+ const diagnostics = pickDiagnostics(employee);
119
+ throw new Error(`Controller employee creation failed (status=${employee.poll.status})${diagnostics ? `: ${diagnostics}` : ''}`);
120
+ }
121
+ }
122
+
123
+ main().catch((error) => {
124
+ console.error('[e2e-bootstrap-tenant] ERROR:', error?.message || error);
125
+ process.exit(1);
126
+ });
@@ -0,0 +1,43 @@
1
+ import { DataspaceNodeClient, createDidcommPlainMessage } from '../dist/index.js';
2
+
3
+ const baseUrl = process.env.BASE_URL || 'http://localhost:3000';
4
+ const bearerToken = process.env.AUTH_BEARER || 'demo-token';
5
+
6
+ const client = new DataspaceNodeClient({ baseUrl, bearerToken });
7
+ const ctx = {
8
+ tenantId: process.env.TENANT_ID || 'acme',
9
+ jurisdiction: process.env.JURISDICTION || 'ES',
10
+ sector: process.env.SECTOR || 'health-care',
11
+ };
12
+
13
+ const payload = createDidcommPlainMessage({
14
+ iss: 'adult1@example.com',
15
+ aud: 'did:web:api.acme.org',
16
+ body: {
17
+ data: [
18
+ {
19
+ type: 'Family-registration-form-v1.0',
20
+ meta: {
21
+ claims: {
22
+ '@context': 'org.schema',
23
+ '@type': 'template',
24
+ 'org.schema.Organization.address.addressCountry': 'ES',
25
+ 'org.schema.Organization.identifier.additionalType': 'UUID',
26
+ 'org.schema.Organization.identifier.value': `family-${Date.now()}`,
27
+ 'org.schema.Person.email': 'adult1@example.com',
28
+ 'org.schema.Service.category': ctx.sector,
29
+ 'org.schema.Service.identifier': 'did:web:api-provider.example.com',
30
+ 'org.schema.Service.serviceType': 'http://terminology.hl7.org/CodeSystem/v3-ActReason|SRVC',
31
+ 'org.schema.Service.termsOfService': 'https://provider.example.com/terms',
32
+ },
33
+ },
34
+ },
35
+ ],
36
+ },
37
+ });
38
+
39
+ const submitPath = client.individualFamilyOrganizationBatchPath(ctx);
40
+ const pollPath = client.individualFamilyOrganizationPollPath(ctx);
41
+ const result = await client.submitAndPoll(submitPath, pollPath, payload, { timeoutMs: 30000, intervalMs: 2000 });
42
+
43
+ console.log(JSON.stringify(result, null, 2));
@@ -0,0 +1,75 @@
1
+ import { DataspaceNodeClient, createDidcommPlainMessage } from '../dist/index.js';
2
+
3
+ const baseUrl = process.env.BASE_URL || 'http://localhost:3000';
4
+ const bearerToken = process.env.AUTH_BEARER || 'demo-token';
5
+
6
+ const client = new DataspaceNodeClient({ baseUrl, bearerToken });
7
+
8
+ const hostCtx = {
9
+ jurisdiction: process.env.JURISDICTION || 'ES',
10
+ sector: process.env.HOST_REGISTRY_SECTOR || 'test-network',
11
+ };
12
+
13
+ const tenantCtx = {
14
+ tenantId: process.env.TENANT_ID || 'acme',
15
+ jurisdiction: process.env.JURISDICTION || 'ES',
16
+ sector: process.env.SECTOR || 'health-care',
17
+ };
18
+
19
+ // 1) Host organization activation
20
+ const activatePayload = createDidcommPlainMessage({
21
+ iss: 'did:web:controller.example.com',
22
+ aud: 'did:web:host.example.com',
23
+ body: {
24
+ data: [
25
+ {
26
+ type: 'Organization-activation-request-v1.0',
27
+ meta: {
28
+ claims: {
29
+ '@context': 'org.schema',
30
+ 'Organization.identifier': process.env.TENANT_URN || 'urn:example:tenant',
31
+ vp_token: process.env.VP_TOKEN || '<vp-token-placeholder>',
32
+ },
33
+ },
34
+ },
35
+ ],
36
+ },
37
+ });
38
+
39
+ const activateSubmitPath = client.hostRegistryOrganizationActivatePath(hostCtx);
40
+ const activatePollPath = client.hostRegistryOrganizationActivatePollPath(hostCtx);
41
+ const activation = await client.submitAndPoll(activateSubmitPath, activatePollPath, activatePayload, {
42
+ timeoutMs: 120000,
43
+ intervalMs: 5000,
44
+ });
45
+
46
+ console.log('Activation status:', activation.poll.status);
47
+
48
+ // 2) Employee creation under tenant
49
+ const employeePayload = createDidcommPlainMessage({
50
+ iss: 'did:web:api.acme.org:employee:admin1@acme.org:device:<uuid>',
51
+ aud: 'did:web:api.acme.org',
52
+ body: {
53
+ data: [
54
+ {
55
+ type: 'Employee-registration-request-v1.0',
56
+ meta: {
57
+ claims: {
58
+ '@context': 'org.schema',
59
+ 'org.schema.Person.email': process.env.EMPLOYEE_EMAIL || 'doctor1@acme.org',
60
+ 'org.schema.Person.hasOccupation': process.env.EMPLOYEE_ROLE || 'ISCO-08|2211',
61
+ },
62
+ },
63
+ },
64
+ ],
65
+ },
66
+ });
67
+
68
+ const employeeSubmitPath = client.employeeBatchPath(tenantCtx);
69
+ const employeePollPath = client.employeePollPath(tenantCtx);
70
+ const employee = await client.submitAndPoll(employeeSubmitPath, employeePollPath, employeePayload, {
71
+ timeoutMs: 120000,
72
+ intervalMs: 5000,
73
+ });
74
+
75
+ console.log('Employee status:', employee.poll.status);
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "dataspace-client-sdk-node",
3
+ "version": "0.1.1",
4
+ "description": "Node.js SDK skeleton for GW/UNID DIDComm plain batch + async polling flows",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc -p tsconfig.json",
10
+ "type-check": "tsc -p tsconfig.json --noEmit",
11
+ "test": "npm run build && node --test tests/*.test.mjs",
12
+ "example:e2e-bootstrap-tenant": "npm run build && node examples/e2e-bootstrap-tenant.mjs"
13
+ },
14
+ "engines": {
15
+ "node": ">=22"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^25.6.0",
19
+ "typescript": "^5.1.6"
20
+ },
21
+ "dependencies": {
22
+ "@noble/post-quantum": "^0.6.1",
23
+ "gdc-common-utils-ts": "^1.4.8",
24
+ "node-fetch": "^3.3.2"
25
+ }
26
+ }
@@ -0,0 +1,28 @@
1
+ import type { DidcommPlainMessage } from './types.js';
2
+
3
+ export function createDidcommPlainMessage(input: {
4
+ iss: string;
5
+ aud: string;
6
+ body: Record<string, unknown>;
7
+ type?: string;
8
+ jti?: string;
9
+ thid?: string;
10
+ }): DidcommPlainMessage {
11
+ const random = Math.random().toString(36).slice(2, 10);
12
+ const now = Date.now();
13
+ const thid = input.thid ?? `thid-${now}-${random}`;
14
+ const jti = input.jti ?? `jti-${now}-${random}`;
15
+
16
+ return {
17
+ jti,
18
+ thid,
19
+ iss: input.iss,
20
+ aud: input.aud,
21
+ type: input.type ?? 'application/api+json',
22
+ body: input.body,
23
+ };
24
+ }
25
+
26
+ export function buildAsyncPollRequest(thid: string): { thid: string } {
27
+ return { thid };
28
+ }