docwal-sdk 1.0.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ All notable changes to the DocWal Node.js SDK will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2024-01-10
9
+
10
+ ### Added
11
+ - Initial release of DocWal Node.js/TypeScript SDK
12
+ - Full TypeScript support with complete type definitions
13
+ - Credentials resource with full CRUD operations
14
+ - Template management (create, list, update, delete)
15
+ - Team member management (invite, list, update roles, deactivate, remove)
16
+ - API key management (generate, regenerate, revoke, info)
17
+ - Batch credential issuance (JSON array and ZIP upload)
18
+ - File upload support for PDF documents using FormData
19
+ - Comprehensive error handling with custom error classes
20
+ - Full documentation with TypeScript examples
21
+ - Express.js integration example
22
+
23
+ ### Features
24
+ - Issue single credentials with optional PDF attachments
25
+ - Batch issue up to 1000 credentials at once
26
+ - Batch upload with ZIP files (CSV/JSON + PDFs)
27
+ - List and filter credentials with pagination
28
+ - Revoke credentials with audit trail
29
+ - Resend claim links with configurable expiration
30
+ - Download credential files as Buffer
31
+ - Manage credential templates with schema validation
32
+ - Team collaboration with role-based permissions
33
+ - Secure API key authentication
34
+ - Async/await pattern throughout
35
+ - Axios-based HTTP client with interceptors
36
+
37
+ ### TypeScript
38
+ - Full type definitions included
39
+ - Interfaces for all request/response types
40
+ - Generic error types with proper inheritance
41
+ - IDE autocomplete support
42
+
43
+ ### Security
44
+ - API key-based authentication
45
+ - HTTPS-only communication
46
+ - Input validation for all requests
47
+ - Proper error handling without leaking sensitive data
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 DocWal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,409 @@
1
+ # DocWal Node.js SDK
2
+
3
+ Official Node.js/TypeScript SDK for DocWal API - Issue and manage verifiable digital credentials.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install docwal-sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { DocWalClient } from 'docwal-sdk';
15
+
16
+ // Initialize client
17
+ const client = new DocWalClient({
18
+ apiKey: 'docwal_live_xxxxx'
19
+ });
20
+
21
+ // Issue a credential
22
+ const result = await client.credentials.issue({
23
+ templateId: 'template-123',
24
+ individualEmail: 'student@example.com',
25
+ credentialData: {
26
+ student_name: 'John Doe',
27
+ degree: 'Bachelor of Science',
28
+ major: 'Computer Science',
29
+ graduation_date: '2024-05-15',
30
+ gpa: '3.8'
31
+ }
32
+ });
33
+
34
+ console.log(`Credential issued! Doc ID: ${result.doc_id}`);
35
+ console.log(`Claim token: ${result.claim_token}`);
36
+ ```
37
+
38
+ ## Authentication
39
+
40
+ Get your API key from your DocWal dashboard:
41
+ 1. Login to https://docwal.com
42
+ 2. Navigate to Settings → API Keys
43
+ 3. Click "Generate API Key"
44
+ 4. Copy and store securely
45
+
46
+ **Requirements:**
47
+ - Pilot tier or above
48
+ - Owner or Admin role
49
+
50
+ ## Environment Configuration
51
+
52
+ ```typescript
53
+ // Production (default)
54
+ const client = new DocWalClient({
55
+ apiKey: 'docwal_live_xxxxx'
56
+ });
57
+
58
+ // Staging
59
+ const client = new DocWalClient({
60
+ apiKey: 'docwal_test_xxxxx',
61
+ baseUrl: 'https://sandbox.docwal.com/api'
62
+ });
63
+
64
+ // Custom timeout
65
+ const client = new DocWalClient({
66
+ apiKey: 'docwal_live_xxxxx',
67
+ timeout: 60000 // milliseconds
68
+ });
69
+ ```
70
+
71
+ ## TypeScript Support
72
+
73
+ Full TypeScript support with type definitions included:
74
+
75
+ ```typescript
76
+ import {
77
+ DocWalClient,
78
+ IssueCredentialParams,
79
+ IssueCredentialResponse,
80
+ Credential,
81
+ Template
82
+ } from 'docwal-sdk';
83
+ ```
84
+
85
+ ## Usage Examples
86
+
87
+ ### Issue Single Credential
88
+
89
+ ```typescript
90
+ // Basic credential
91
+ const result = await client.credentials.issue({
92
+ templateId: 'template-123',
93
+ individualEmail: 'student@example.com',
94
+ credentialData: {
95
+ student_name: 'John Doe',
96
+ degree: 'Bachelor of Science',
97
+ graduation_date: '2024-05-15'
98
+ }
99
+ });
100
+
101
+ // With PDF attachment
102
+ import fs from 'fs';
103
+
104
+ const pdfBuffer = fs.readFileSync('certificate.pdf');
105
+ const result = await client.credentials.issue({
106
+ templateId: 'template-123',
107
+ individualEmail: 'student@example.com',
108
+ credentialData: { student_name: 'John Doe' },
109
+ documentFile: pdfBuffer,
110
+ claimTokenExpiresHours: 168 // 7 days
111
+ });
112
+ ```
113
+
114
+ ### Batch Issue Credentials
115
+
116
+ ```typescript
117
+ const credentialsList = [
118
+ {
119
+ individual_email: 'student1@example.com',
120
+ credential_data: {
121
+ student_name: 'Alice Smith',
122
+ degree: 'Bachelor of Arts',
123
+ graduation_date: '2024-05-15'
124
+ }
125
+ },
126
+ {
127
+ individual_email: 'student2@example.com',
128
+ credential_data: {
129
+ student_name: 'Bob Johnson',
130
+ degree: 'Bachelor of Science',
131
+ graduation_date: '2024-05-15'
132
+ }
133
+ }
134
+ ];
135
+
136
+ const result = await client.credentials.batchIssue(
137
+ 'template-123',
138
+ credentialsList,
139
+ true // send notifications
140
+ );
141
+
142
+ console.log(`Success: ${result.success_count}/${result.total_rows}`);
143
+ ```
144
+
145
+ ### Batch Upload with ZIP
146
+
147
+ ```typescript
148
+ import fs from 'fs';
149
+
150
+ // ZIP structure:
151
+ // batch_credentials.zip
152
+ // ├── credentials.csv
153
+ // └── documents/
154
+ // ├── student001.pdf
155
+ // ├── student002.pdf
156
+ // └── student003.pdf
157
+
158
+ const zipBuffer = fs.readFileSync('batch_credentials.zip');
159
+
160
+ const result = await client.credentials.batchUpload(
161
+ 'template-123',
162
+ zipBuffer,
163
+ true // send notifications
164
+ );
165
+
166
+ console.log(`Processed: ${result.total_rows}`);
167
+ console.log(`Success: ${result.success_count}`);
168
+ console.log(`Failed: ${result.failure_count}`);
169
+
170
+ for (const item of result.results) {
171
+ if (item.status === 'success') {
172
+ console.log(`Row ${item.row}: ${item.doc_id}`);
173
+ } else {
174
+ console.log(`Row ${item.row}: ${item.error}`);
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### List and Get Credentials
180
+
181
+ ```typescript
182
+ // List all credentials
183
+ const credentials = await client.credentials.list(50, 0);
184
+
185
+ for (const cred of credentials) {
186
+ console.log(`${cred.doc_id}: ${cred.template_name}`);
187
+ }
188
+
189
+ // Get specific credential
190
+ const credential = await client.credentials.get('DOC123456');
191
+ console.log(`Issued to: ${credential.ownership?.individual_email}`);
192
+ console.log(`Status: ${credential.ownership?.is_claimed ? 'Claimed' : 'Pending'}`);
193
+ ```
194
+
195
+ ### Revoke Credential
196
+
197
+ ```typescript
198
+ const result = await client.credentials.revoke(
199
+ 'DOC123456',
200
+ 'Student expelled for academic misconduct'
201
+ );
202
+ console.log(result.message);
203
+ ```
204
+
205
+ ### Resend Claim Link
206
+
207
+ ```typescript
208
+ const result = await client.credentials.resendClaimLink(
209
+ 'DOC123456',
210
+ 168 // 7 days
211
+ );
212
+
213
+ console.log(`Sent to: ${result.recipient_email}`);
214
+ console.log(`Expires: ${result.claim_token_expires}`);
215
+ ```
216
+
217
+ ### Download Credential File
218
+
219
+ ```typescript
220
+ import fs from 'fs';
221
+
222
+ // Download PDF file
223
+ const pdfBuffer = await client.credentials.download('DOC123456');
224
+ fs.writeFileSync('credential.pdf', pdfBuffer);
225
+ ```
226
+
227
+ ## Template Management
228
+
229
+ ```typescript
230
+ // List templates
231
+ const templates = await client.templates.list();
232
+
233
+ // Get template
234
+ const template = await client.templates.get('template-123');
235
+
236
+ // Create template
237
+ const template = await client.templates.create({
238
+ name: 'Bachelor Degree Certificate',
239
+ description: 'Template for bachelor degree graduation certificates',
240
+ credentialType: 'certificate',
241
+ schema: {
242
+ student_name: {
243
+ type: 'string',
244
+ required: true,
245
+ label: 'Student Name'
246
+ },
247
+ degree: {
248
+ type: 'string',
249
+ required: true,
250
+ label: 'Degree Program'
251
+ },
252
+ graduation_date: {
253
+ type: 'date',
254
+ required: true,
255
+ label: 'Graduation Date'
256
+ }
257
+ },
258
+ version: '1.0'
259
+ });
260
+
261
+ // Update template
262
+ await client.templates.update('template-123', {
263
+ description: 'Updated description'
264
+ });
265
+
266
+ // Delete template (soft delete)
267
+ await client.templates.delete('template-123');
268
+ ```
269
+
270
+ ## API Key Management
271
+
272
+ ```typescript
273
+ // Generate new API key (Owner/Admin only)
274
+ const result = await client.apiKeys.generate();
275
+ console.log(`New API key: ${result.api_key}`);
276
+ console.log('⚠️ Store securely - shown only once!');
277
+
278
+ // Get API key info
279
+ const info = await client.apiKeys.info();
280
+ console.log(`Masked key: ${info.api_key_masked}`);
281
+ console.log(`Created: ${info.created_at}`);
282
+ console.log(`Last used: ${info.last_used_at}`);
283
+
284
+ // Regenerate API key
285
+ const result = await client.apiKeys.regenerate();
286
+ console.log(`New API key: ${result.api_key}`);
287
+
288
+ // Revoke API key
289
+ await client.apiKeys.revoke();
290
+ ```
291
+
292
+ ## Team Management
293
+
294
+ ```typescript
295
+ // List team members
296
+ const team = await client.team.list();
297
+ console.log(`Active members: ${team.stats.active_members}`);
298
+ console.log(`Pending invitations: ${team.stats.pending_invitations}`);
299
+
300
+ // Check email before inviting
301
+ const check = await client.team.checkEmail('newmember@university.edu');
302
+ if (check.recommendation === 'add_directly') {
303
+ console.log('User exists - can add directly');
304
+ } else if (check.recommendation === 'send_invitation') {
305
+ console.log("User doesn't exist - must send invitation");
306
+ }
307
+
308
+ // Invite team member
309
+ const result = await client.team.invite({
310
+ email: 'newmember@university.edu',
311
+ role: 'issuer',
312
+ sendEmail: true
313
+ });
314
+
315
+ // Update member role
316
+ await client.team.updateRole('member-123', 'admin');
317
+
318
+ // Deactivate member
319
+ await client.team.deactivate('member-123', 'Employee on leave');
320
+
321
+ // Reactivate member
322
+ await client.team.reactivate('member-123');
323
+
324
+ // Remove member permanently
325
+ await client.team.remove('member-123');
326
+ ```
327
+
328
+ ## Error Handling
329
+
330
+ ```typescript
331
+ import {
332
+ DocWalClient,
333
+ AuthenticationError,
334
+ ValidationError,
335
+ NotFoundError,
336
+ RateLimitError
337
+ } from 'docwal-sdk';
338
+
339
+ const client = new DocWalClient({ apiKey: 'docwal_live_xxxxx' });
340
+
341
+ try {
342
+ const result = await client.credentials.issue({
343
+ templateId: 'invalid-template',
344
+ individualEmail: 'student@example.com',
345
+ credentialData: {}
346
+ });
347
+ } catch (error) {
348
+ if (error instanceof AuthenticationError) {
349
+ console.error('Authentication failed:', error.message);
350
+ console.error('Check your API key');
351
+ } else if (error instanceof ValidationError) {
352
+ console.error('Validation error:', error.message);
353
+ console.error('Check required fields');
354
+ } else if (error instanceof NotFoundError) {
355
+ console.error('Resource not found:', error.message);
356
+ } else if (error instanceof RateLimitError) {
357
+ console.error('Rate limit exceeded');
358
+ } else {
359
+ console.error('Error:', error);
360
+ }
361
+ }
362
+ ```
363
+
364
+ ## Rate Limits
365
+
366
+ - **Pilot**: 500 requests/hour
367
+ - **Standard**: 1,000 requests/hour
368
+ - **Enterprise**: Unlimited
369
+
370
+ When rate limit is exceeded, `RateLimitError` is thrown.
371
+
372
+ ## Express.js Integration Example
373
+
374
+ ```typescript
375
+ import express from 'express';
376
+ import { DocWalClient } from 'docwal-sdk';
377
+
378
+ const app = express();
379
+ const docwal = new DocWalClient({ apiKey: process.env.DOCWAL_API_KEY! });
380
+
381
+ app.use(express.json());
382
+
383
+ app.post('/api/issue-credential', async (req, res) => {
384
+ try {
385
+ const result = await docwal.credentials.issue({
386
+ templateId: req.body.template_id,
387
+ individualEmail: req.body.email,
388
+ credentialData: req.body.data
389
+ });
390
+
391
+ res.json({ success: true, data: result });
392
+ } catch (error) {
393
+ res.status(500).json({ success: false, error: error.message });
394
+ }
395
+ });
396
+
397
+ app.listen(3000);
398
+ ```
399
+
400
+ ## Support
401
+
402
+ - **Email**: support@docwal.com
403
+ - **Documentation**: https://docs.docwal.com
404
+ - **API Reference**: https://docwal.com/api/docs
405
+ - **GitHub**: https://github.com/docwal/docwal-nodejs
406
+
407
+ ## License
408
+
409
+ MIT License - see LICENSE file for details.
@@ -0,0 +1,250 @@
1
+ /**
2
+ * DocWal Node.js SDK
3
+ *
4
+ * Official Node.js SDK for DocWal API - Issue and manage verifiable digital credentials.
5
+ */
6
+ import { AxiosInstance } from 'axios';
7
+ import { Readable } from 'stream';
8
+ export interface DocWalConfig {
9
+ apiKey: string;
10
+ baseUrl?: string;
11
+ timeout?: number;
12
+ }
13
+ export interface CredentialData {
14
+ [key: string]: any;
15
+ }
16
+ export interface IssueCredentialParams {
17
+ templateId: string;
18
+ individualEmail: string;
19
+ credentialData: CredentialData;
20
+ documentFile?: Buffer | Readable;
21
+ expiresAt?: string;
22
+ claimTokenExpiresHours?: number;
23
+ }
24
+ export interface IssueCredentialResponse {
25
+ doc_id: string;
26
+ document_hash: string;
27
+ status: string;
28
+ claim_token: string;
29
+ }
30
+ export interface BatchCredential {
31
+ individual_email: string;
32
+ credential_data: CredentialData;
33
+ }
34
+ export interface BatchIssueResponse {
35
+ total_rows: number;
36
+ success_count: number;
37
+ failure_count: number;
38
+ results: Array<{
39
+ row: number;
40
+ status: string;
41
+ doc_id?: string;
42
+ error?: string;
43
+ }>;
44
+ }
45
+ export interface Credential {
46
+ doc_id: string;
47
+ document_hash: string;
48
+ issued_at: string;
49
+ expires_at?: string;
50
+ is_revoked: boolean;
51
+ revoked_at?: string;
52
+ revocation_reason?: string;
53
+ template_name: string;
54
+ credential_data: CredentialData;
55
+ ownership?: {
56
+ individual_email: string;
57
+ is_claimed: boolean;
58
+ claimed_at?: string;
59
+ claim_token_expires?: string;
60
+ };
61
+ has_document_file: boolean;
62
+ }
63
+ export interface Template {
64
+ id: string;
65
+ name: string;
66
+ description: string;
67
+ credential_type: string;
68
+ schema: Record<string, any>;
69
+ version: string;
70
+ is_active: boolean;
71
+ }
72
+ export interface TeamMember {
73
+ id: string;
74
+ user: {
75
+ email: string;
76
+ full_name: string;
77
+ };
78
+ role: 'owner' | 'admin' | 'issuer';
79
+ is_active: boolean;
80
+ created_at: string;
81
+ }
82
+ export declare class DocWalError extends Error {
83
+ statusCode?: number | undefined;
84
+ response?: any | undefined;
85
+ constructor(message: string, statusCode?: number | undefined, response?: any | undefined);
86
+ }
87
+ export declare class AuthenticationError extends DocWalError {
88
+ constructor(message: string, statusCode?: number, response?: any);
89
+ }
90
+ export declare class ValidationError extends DocWalError {
91
+ constructor(message: string, statusCode?: number, response?: any);
92
+ }
93
+ export declare class RateLimitError extends DocWalError {
94
+ constructor(message: string, statusCode?: number, response?: any);
95
+ }
96
+ export declare class NotFoundError extends DocWalError {
97
+ constructor(message: string, statusCode?: number, response?: any);
98
+ }
99
+ /**
100
+ * DocWal API Client
101
+ */
102
+ export declare class DocWalClient {
103
+ private client;
104
+ credentials: CredentialsResource;
105
+ templates: TemplatesResource;
106
+ apiKeys: APIKeysResource;
107
+ team: TeamResource;
108
+ constructor(config: DocWalConfig);
109
+ }
110
+ /**
111
+ * Credentials Resource
112
+ */
113
+ declare class CredentialsResource {
114
+ private client;
115
+ constructor(client: AxiosInstance);
116
+ /**
117
+ * Issue a single credential
118
+ */
119
+ issue(params: IssueCredentialParams): Promise<IssueCredentialResponse>;
120
+ /**
121
+ * Issue multiple credentials in batch
122
+ */
123
+ batchIssue(templateId: string, credentials: BatchCredential[], sendNotifications?: boolean): Promise<BatchIssueResponse>;
124
+ /**
125
+ * Batch upload with ZIP file
126
+ */
127
+ batchUpload(templateId: string, file: Buffer | Readable, sendNotifications?: boolean): Promise<BatchIssueResponse>;
128
+ /**
129
+ * List all credentials
130
+ */
131
+ list(limit?: number, offset?: number): Promise<Credential[]>;
132
+ /**
133
+ * Get credential by doc_id
134
+ */
135
+ get(docId: string): Promise<Credential>;
136
+ /**
137
+ * Revoke credential
138
+ */
139
+ revoke(docId: string, reason: string): Promise<{
140
+ message: string;
141
+ }>;
142
+ /**
143
+ * Resend claim link
144
+ */
145
+ resendClaimLink(docId: string, claimTokenExpiresHours?: number): Promise<{
146
+ message: string;
147
+ claim_token: string;
148
+ claim_token_expires: string;
149
+ recipient_email: string;
150
+ }>;
151
+ /**
152
+ * Download credential file
153
+ */
154
+ download(docId: string): Promise<Buffer>;
155
+ }
156
+ /**
157
+ * Templates Resource
158
+ */
159
+ declare class TemplatesResource {
160
+ private client;
161
+ constructor(client: AxiosInstance);
162
+ list(): Promise<Template[]>;
163
+ get(templateId: string): Promise<Template>;
164
+ create(params: {
165
+ name: string;
166
+ description: string;
167
+ credentialType: string;
168
+ schema: Record<string, any>;
169
+ version?: string;
170
+ }): Promise<Template>;
171
+ update(templateId: string, updates: Partial<Template>): Promise<Template>;
172
+ delete(templateId: string): Promise<{
173
+ message: string;
174
+ }>;
175
+ }
176
+ /**
177
+ * API Keys Resource
178
+ */
179
+ declare class APIKeysResource {
180
+ private client;
181
+ constructor(client: AxiosInstance);
182
+ generate(): Promise<{
183
+ api_key: string;
184
+ created_at: string;
185
+ warning: string;
186
+ }>;
187
+ info(): Promise<{
188
+ has_api_key: boolean;
189
+ api_key_masked?: string;
190
+ created_at?: string;
191
+ last_used_at?: string;
192
+ is_active: boolean;
193
+ }>;
194
+ regenerate(): Promise<{
195
+ api_key: string;
196
+ created_at: string;
197
+ warning: string;
198
+ }>;
199
+ revoke(): Promise<{
200
+ message: string;
201
+ revoked_at: string;
202
+ }>;
203
+ }
204
+ /**
205
+ * Team Resource
206
+ */
207
+ declare class TeamResource {
208
+ private client;
209
+ constructor(client: AxiosInstance);
210
+ list(): Promise<{
211
+ members: TeamMember[];
212
+ pending_invitations: any[];
213
+ stats: {
214
+ total_members: number;
215
+ active_members: number;
216
+ tier_limit: number | null;
217
+ };
218
+ }>;
219
+ checkEmail(email: string): Promise<{
220
+ valid: boolean;
221
+ domain_matches: boolean;
222
+ user_exists: boolean;
223
+ is_already_member: boolean;
224
+ recommendation: string;
225
+ }>;
226
+ invite(params: {
227
+ email: string;
228
+ role?: 'owner' | 'admin' | 'issuer';
229
+ sendEmail?: boolean;
230
+ addDirectly?: boolean;
231
+ }): Promise<{
232
+ message: string;
233
+ invitation?: any;
234
+ member?: TeamMember;
235
+ }>;
236
+ updateRole(memberId: string, role: string): Promise<{
237
+ message: string;
238
+ member: TeamMember;
239
+ }>;
240
+ deactivate(memberId: string, reason?: string): Promise<{
241
+ message: string;
242
+ }>;
243
+ reactivate(memberId: string): Promise<{
244
+ message: string;
245
+ }>;
246
+ remove(memberId: string): Promise<{
247
+ message: string;
248
+ }>;
249
+ }
250
+ export default DocWalClient;
package/dist/index.js ADDED
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ /**
3
+ * DocWal Node.js SDK
4
+ *
5
+ * Official Node.js SDK for DocWal API - Issue and manage verifiable digital credentials.
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.DocWalClient = exports.NotFoundError = exports.RateLimitError = exports.ValidationError = exports.AuthenticationError = exports.DocWalError = void 0;
12
+ const axios_1 = __importDefault(require("axios"));
13
+ const form_data_1 = __importDefault(require("form-data"));
14
+ // Errors
15
+ class DocWalError extends Error {
16
+ constructor(message, statusCode, response) {
17
+ super(message);
18
+ this.statusCode = statusCode;
19
+ this.response = response;
20
+ this.name = 'DocWalError';
21
+ }
22
+ }
23
+ exports.DocWalError = DocWalError;
24
+ class AuthenticationError extends DocWalError {
25
+ constructor(message, statusCode, response) {
26
+ super(message, statusCode, response);
27
+ this.name = 'AuthenticationError';
28
+ }
29
+ }
30
+ exports.AuthenticationError = AuthenticationError;
31
+ class ValidationError extends DocWalError {
32
+ constructor(message, statusCode, response) {
33
+ super(message, statusCode, response);
34
+ this.name = 'ValidationError';
35
+ }
36
+ }
37
+ exports.ValidationError = ValidationError;
38
+ class RateLimitError extends DocWalError {
39
+ constructor(message, statusCode, response) {
40
+ super(message, statusCode, response);
41
+ this.name = 'RateLimitError';
42
+ }
43
+ }
44
+ exports.RateLimitError = RateLimitError;
45
+ class NotFoundError extends DocWalError {
46
+ constructor(message, statusCode, response) {
47
+ super(message, statusCode, response);
48
+ this.name = 'NotFoundError';
49
+ }
50
+ }
51
+ exports.NotFoundError = NotFoundError;
52
+ /**
53
+ * DocWal API Client
54
+ */
55
+ class DocWalClient {
56
+ constructor(config) {
57
+ const { apiKey, baseUrl = 'https://docwal.com/api', timeout = 30000 } = config;
58
+ this.client = axios_1.default.create({
59
+ baseURL: baseUrl,
60
+ timeout,
61
+ headers: {
62
+ 'X-API-Key': apiKey,
63
+ 'Content-Type': 'application/json',
64
+ },
65
+ });
66
+ // Error interceptor
67
+ this.client.interceptors.response.use((response) => response, (error) => {
68
+ if (error.response) {
69
+ const status = error.response.status;
70
+ const data = error.response.data;
71
+ const message = data?.error || error.message;
72
+ if (status === 401) {
73
+ throw new AuthenticationError(message, status, error.response);
74
+ }
75
+ else if (status === 400) {
76
+ throw new ValidationError(message, status, error.response);
77
+ }
78
+ else if (status === 404) {
79
+ throw new NotFoundError(message, status, error.response);
80
+ }
81
+ else if (status === 429) {
82
+ throw new RateLimitError(message, status, error.response);
83
+ }
84
+ else {
85
+ throw new DocWalError(message, status, error.response);
86
+ }
87
+ }
88
+ else if (error.request) {
89
+ throw new DocWalError('No response from server');
90
+ }
91
+ else {
92
+ throw new DocWalError(error.message);
93
+ }
94
+ });
95
+ // Initialize resources
96
+ this.credentials = new CredentialsResource(this.client);
97
+ this.templates = new TemplatesResource(this.client);
98
+ this.apiKeys = new APIKeysResource(this.client);
99
+ this.team = new TeamResource(this.client);
100
+ }
101
+ }
102
+ exports.DocWalClient = DocWalClient;
103
+ /**
104
+ * Credentials Resource
105
+ */
106
+ class CredentialsResource {
107
+ constructor(client) {
108
+ this.client = client;
109
+ }
110
+ /**
111
+ * Issue a single credential
112
+ */
113
+ async issue(params) {
114
+ const { templateId, individualEmail, credentialData, documentFile, expiresAt, claimTokenExpiresHours = 720, } = params;
115
+ if (documentFile) {
116
+ const formData = new form_data_1.default();
117
+ formData.append('template_id', templateId);
118
+ formData.append('individual_email', individualEmail);
119
+ formData.append('credential_data', JSON.stringify(credentialData));
120
+ formData.append('claim_token_expires_hours', claimTokenExpiresHours.toString());
121
+ if (expiresAt) {
122
+ formData.append('expires_at', expiresAt);
123
+ }
124
+ formData.append('document_file', documentFile, 'document.pdf');
125
+ const response = await this.client.post('/credentials/issue/', formData, {
126
+ headers: formData.getHeaders(),
127
+ });
128
+ return response.data;
129
+ }
130
+ else {
131
+ const response = await this.client.post('/credentials/issue/', {
132
+ template_id: templateId,
133
+ individual_email: individualEmail,
134
+ credential_data: credentialData,
135
+ claim_token_expires_hours: claimTokenExpiresHours,
136
+ ...(expiresAt && { expires_at: expiresAt }),
137
+ });
138
+ return response.data;
139
+ }
140
+ }
141
+ /**
142
+ * Issue multiple credentials in batch
143
+ */
144
+ async batchIssue(templateId, credentials, sendNotifications = true) {
145
+ const response = await this.client.post('/credentials/batch/', {
146
+ template_id: templateId,
147
+ credentials,
148
+ send_notifications: sendNotifications,
149
+ });
150
+ return response.data;
151
+ }
152
+ /**
153
+ * Batch upload with ZIP file
154
+ */
155
+ async batchUpload(templateId, file, sendNotifications = true) {
156
+ const formData = new form_data_1.default();
157
+ formData.append('template_id', templateId);
158
+ formData.append('send_notifications', sendNotifications.toString());
159
+ formData.append('file', file, 'batch.zip');
160
+ const response = await this.client.post('/credentials/batch-upload/', formData, {
161
+ headers: formData.getHeaders(),
162
+ });
163
+ return response.data;
164
+ }
165
+ /**
166
+ * List all credentials
167
+ */
168
+ async list(limit = 100, offset = 0) {
169
+ const response = await this.client.get('/credentials/', {
170
+ params: { limit, offset },
171
+ });
172
+ return response.data;
173
+ }
174
+ /**
175
+ * Get credential by doc_id
176
+ */
177
+ async get(docId) {
178
+ const response = await this.client.get(`/credentials/${docId}/`);
179
+ return response.data;
180
+ }
181
+ /**
182
+ * Revoke credential
183
+ */
184
+ async revoke(docId, reason) {
185
+ const response = await this.client.post(`/credentials/${docId}/revoke/`, {
186
+ reason,
187
+ });
188
+ return response.data;
189
+ }
190
+ /**
191
+ * Resend claim link
192
+ */
193
+ async resendClaimLink(docId, claimTokenExpiresHours = 720) {
194
+ const response = await this.client.post(`/credentials/${docId}/resend-claim/`, {
195
+ claim_token_expires_hours: claimTokenExpiresHours,
196
+ });
197
+ return response.data;
198
+ }
199
+ /**
200
+ * Download credential file
201
+ */
202
+ async download(docId) {
203
+ const response = await this.client.get(`/credentials/${docId}/download/`, {
204
+ responseType: 'arraybuffer',
205
+ });
206
+ return Buffer.from(response.data);
207
+ }
208
+ }
209
+ /**
210
+ * Templates Resource
211
+ */
212
+ class TemplatesResource {
213
+ constructor(client) {
214
+ this.client = client;
215
+ }
216
+ async list() {
217
+ const response = await this.client.get('/templates/');
218
+ return response.data;
219
+ }
220
+ async get(templateId) {
221
+ const response = await this.client.get(`/templates/${templateId}/`);
222
+ return response.data;
223
+ }
224
+ async create(params) {
225
+ const response = await this.client.post('/templates/', {
226
+ name: params.name,
227
+ description: params.description,
228
+ credential_type: params.credentialType,
229
+ schema: params.schema,
230
+ version: params.version || '1.0',
231
+ });
232
+ return response.data;
233
+ }
234
+ async update(templateId, updates) {
235
+ const response = await this.client.patch(`/templates/${templateId}/`, updates);
236
+ return response.data;
237
+ }
238
+ async delete(templateId) {
239
+ const response = await this.client.delete(`/templates/${templateId}/`);
240
+ return response.data;
241
+ }
242
+ }
243
+ /**
244
+ * API Keys Resource
245
+ */
246
+ class APIKeysResource {
247
+ constructor(client) {
248
+ this.client = client;
249
+ }
250
+ async generate() {
251
+ const response = await this.client.post('/institutions/api-keys/generate/');
252
+ return response.data;
253
+ }
254
+ async info() {
255
+ const response = await this.client.get('/institutions/api-keys/info/');
256
+ return response.data;
257
+ }
258
+ async regenerate() {
259
+ const response = await this.client.post('/institutions/api-keys/regenerate/');
260
+ return response.data;
261
+ }
262
+ async revoke() {
263
+ const response = await this.client.post('/institutions/api-keys/revoke/');
264
+ return response.data;
265
+ }
266
+ }
267
+ /**
268
+ * Team Resource
269
+ */
270
+ class TeamResource {
271
+ constructor(client) {
272
+ this.client = client;
273
+ }
274
+ async list() {
275
+ const response = await this.client.get('/institutions/team/');
276
+ return response.data;
277
+ }
278
+ async checkEmail(email) {
279
+ const response = await this.client.post('/institutions/team/check-email/', {
280
+ email,
281
+ });
282
+ return response.data;
283
+ }
284
+ async invite(params) {
285
+ const response = await this.client.post('/institutions/team/invite/', {
286
+ email: params.email,
287
+ role: params.role || 'issuer',
288
+ send_email: params.sendEmail !== false,
289
+ add_directly: params.addDirectly || false,
290
+ });
291
+ return response.data;
292
+ }
293
+ async updateRole(memberId, role) {
294
+ const response = await this.client.patch(`/institutions/team/members/${memberId}/role/`, {
295
+ role,
296
+ });
297
+ return response.data;
298
+ }
299
+ async deactivate(memberId, reason) {
300
+ const response = await this.client.post(`/institutions/team/members/${memberId}/deactivate/`, {
301
+ ...(reason && { reason }),
302
+ });
303
+ return response.data;
304
+ }
305
+ async reactivate(memberId) {
306
+ const response = await this.client.post(`/institutions/team/members/${memberId}/reactivate/`);
307
+ return response.data;
308
+ }
309
+ async remove(memberId) {
310
+ const response = await this.client.delete(`/institutions/team/members/${memberId}/remove/`);
311
+ return response.data;
312
+ }
313
+ }
314
+ exports.default = DocWalClient;
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "docwal-sdk",
3
+ "version": "1.0.0",
4
+ "description": "Official Node.js SDK for DocWal API - Issue and manage verifiable digital credentials",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepublishOnly": "npm run build",
10
+ "test": "jest"
11
+ },
12
+ "keywords": [
13
+ "docwal",
14
+ "credentials",
15
+ "verifiable",
16
+ "digital",
17
+ "education",
18
+ "blockchain"
19
+ ],
20
+ "author": "DocWal <support@docwal.com>",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/docwal/docwal-nodejs"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/docwal/docwal-nodejs/issues"
28
+ },
29
+ "homepage": "https://docs.docwal.com",
30
+ "dependencies": {
31
+ "axios": "^1.6.0",
32
+ "form-data": "^4.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^20.0.0",
36
+ "typescript": "^5.0.0"
37
+ },
38
+ "engines": {
39
+ "node": ">=14.0.0"
40
+ }
41
+ }