dragdropdo-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/README.md +635 -0
- package/dist/src/client.d.ts +208 -0
- package/dist/src/client.js +621 -0
- package/dist/src/errors.d.ts +22 -0
- package/dist/src/errors.js +48 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.js +29 -0
- package/dist/src/types.d.ts +143 -0
- package/dist/src/types.js +5 -0
- package/dist/tests/client.e2e.test.d.ts +1 -0
- package/dist/tests/client.e2e.test.js +167 -0
- package/dist/tests/client.live.test.d.ts +1 -0
- package/dist/tests/client.live.test.js +104 -0
- package/package.json +42 -0
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* dragdropdo.com Business API Client
|
|
4
|
+
*
|
|
5
|
+
* A Node.js client library for interacting with the dragdropdo.com Business API.
|
|
6
|
+
* Provides methods for file uploads, operations, and status checking.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
42
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.Dragdropdo = void 0;
|
|
46
|
+
// Import statements - these will resolve when dependencies are installed
|
|
47
|
+
// @ts-ignore - Module resolution will work at runtime with installed dependencies
|
|
48
|
+
const axios_1 = __importDefault(require("axios"));
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const errors_1 = require("./errors");
|
|
54
|
+
class Dragdropdo {
|
|
55
|
+
/**
|
|
56
|
+
* Create a new DragDropDo Client instance
|
|
57
|
+
*
|
|
58
|
+
* @param config - Client configuration
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const client = new Dragdropdo({
|
|
62
|
+
* apiKey: 'your-api-key',
|
|
63
|
+
* baseURL: 'https://dragdropdo.com',
|
|
64
|
+
* timeout: 30000
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
constructor(config) {
|
|
69
|
+
if (!config.apiKey) {
|
|
70
|
+
throw new errors_1.D3ValidationError("API key is required");
|
|
71
|
+
}
|
|
72
|
+
this.apiKey = config.apiKey;
|
|
73
|
+
this.baseURL = config.baseURL || "https://dragdropdo.com";
|
|
74
|
+
this.timeout = config.timeout || 30000;
|
|
75
|
+
// Remove trailing slash from baseURL
|
|
76
|
+
this.baseURL = this.baseURL.replace(/\/$/, "");
|
|
77
|
+
// Create axios instance with default config
|
|
78
|
+
this.axiosInstance = axios_1.default.create({
|
|
79
|
+
baseURL: this.baseURL,
|
|
80
|
+
timeout: this.timeout,
|
|
81
|
+
headers: {
|
|
82
|
+
"Content-Type": "application/json",
|
|
83
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
84
|
+
...config.headers,
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
// Add response interceptor for error handling
|
|
88
|
+
this.axiosInstance.interceptors.response.use((response) => response, (error) => {
|
|
89
|
+
if (error.response) {
|
|
90
|
+
const { status, data } = error.response;
|
|
91
|
+
const message = data?.message ||
|
|
92
|
+
data?.error ||
|
|
93
|
+
error.message ||
|
|
94
|
+
"API request failed";
|
|
95
|
+
const code = data?.code;
|
|
96
|
+
throw new errors_1.D3APIError(message, status, code, data);
|
|
97
|
+
}
|
|
98
|
+
else if (error.request) {
|
|
99
|
+
throw new errors_1.DragdropdoError("Network error: No response received from server");
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
throw new errors_1.DragdropdoError(`Request error: ${error.message}`);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Upload a file to D3 storage
|
|
108
|
+
*
|
|
109
|
+
* This method handles the complete upload flow:
|
|
110
|
+
* 1. Request presigned URLs from the API
|
|
111
|
+
* 2. Upload file parts to presigned URLs
|
|
112
|
+
* 3. Return the file key for use in operations
|
|
113
|
+
*
|
|
114
|
+
* @param options - Upload options
|
|
115
|
+
* @returns Promise resolving to upload response with file key
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* // Upload from file path
|
|
120
|
+
* const result = await client.uploadFile({
|
|
121
|
+
* file: '/path/to/file.pdf',
|
|
122
|
+
* fileName: 'document.pdf',
|
|
123
|
+
* mimeType: 'application/pdf',
|
|
124
|
+
* onProgress: (progress) => {
|
|
125
|
+
* console.log(`Upload: ${progress.percentage}%`);
|
|
126
|
+
* }
|
|
127
|
+
* });
|
|
128
|
+
* console.log('File key:', result.fileKey);
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
async uploadFile(options) {
|
|
132
|
+
const { file, fileName, mimeType, parts, onProgress } = options;
|
|
133
|
+
if (!fileName) {
|
|
134
|
+
throw new errors_1.D3ValidationError("fileName is required");
|
|
135
|
+
}
|
|
136
|
+
// Determine file size
|
|
137
|
+
let fileSize;
|
|
138
|
+
if (typeof file !== "string") {
|
|
139
|
+
throw new errors_1.D3ValidationError("file must be a file path string");
|
|
140
|
+
}
|
|
141
|
+
if (!fs.existsSync(file)) {
|
|
142
|
+
throw new errors_1.D3ValidationError(`File not found: ${file}`);
|
|
143
|
+
}
|
|
144
|
+
const stats = fs.statSync(file);
|
|
145
|
+
fileSize = stats.size;
|
|
146
|
+
// Calculate parts if not provided
|
|
147
|
+
const chunkSize = 5 * 1024 * 1024; // 5MB per part
|
|
148
|
+
const calculatedParts = parts || Math.ceil(fileSize / chunkSize);
|
|
149
|
+
const actualParts = Math.max(1, Math.min(calculatedParts, 100)); // Limit to 100 parts
|
|
150
|
+
// Detect MIME type if not provided
|
|
151
|
+
let detectedMimeType = mimeType;
|
|
152
|
+
if (!detectedMimeType) {
|
|
153
|
+
const ext = path.extname(fileName).toLowerCase();
|
|
154
|
+
detectedMimeType = this.getMimeType(ext) || "application/octet-stream";
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
// Step 1: Request presigned URLs
|
|
158
|
+
const uploadResponse = await this.axiosInstance.post("/v1/biz/initiate-upload", {
|
|
159
|
+
file_name: fileName,
|
|
160
|
+
size: fileSize,
|
|
161
|
+
mime_type: detectedMimeType,
|
|
162
|
+
parts: actualParts,
|
|
163
|
+
});
|
|
164
|
+
const rawData = uploadResponse.data.data;
|
|
165
|
+
// Transform snake_case to camelCase
|
|
166
|
+
const transformed = this.toCamelCase(rawData);
|
|
167
|
+
const fileKey = transformed.fileKey || transformed.file_key;
|
|
168
|
+
const uploadId = transformed.uploadId || transformed.upload_id;
|
|
169
|
+
const presignedUrls = transformed.presignedUrls || transformed.presigned_urls || [];
|
|
170
|
+
const objectName = transformed.objectName || transformed.object_name;
|
|
171
|
+
if (presignedUrls.length !== actualParts) {
|
|
172
|
+
throw new errors_1.D3UploadError(`Mismatch: requested ${actualParts} parts but received ${presignedUrls.length} presigned URLs`);
|
|
173
|
+
}
|
|
174
|
+
if (!uploadId) {
|
|
175
|
+
throw new errors_1.D3UploadError("Upload ID not received from server");
|
|
176
|
+
}
|
|
177
|
+
// Step 2: Upload file parts and capture ETags
|
|
178
|
+
const chunkSizePerPart = Math.ceil(fileSize / actualParts);
|
|
179
|
+
let bytesUploaded = 0;
|
|
180
|
+
const uploadParts = [];
|
|
181
|
+
// Prepare file data for chunking
|
|
182
|
+
// Read entire file into memory for chunking (path-only uploads supported)
|
|
183
|
+
const fileBuffer = fs.readFileSync(file);
|
|
184
|
+
for (let i = 0; i < actualParts; i++) {
|
|
185
|
+
const start = i * chunkSizePerPart;
|
|
186
|
+
const end = Math.min(start + chunkSizePerPart, fileSize);
|
|
187
|
+
const partSize = end - start;
|
|
188
|
+
// Extract chunk from buffer (use subarray to avoid deprecated slice)
|
|
189
|
+
const chunk = fileBuffer.subarray(start, end);
|
|
190
|
+
// Upload chunk and capture ETag from response headers
|
|
191
|
+
const putResponse = await axios_1.default.put(presignedUrls[i], chunk, {
|
|
192
|
+
headers: {
|
|
193
|
+
"Content-Type": detectedMimeType,
|
|
194
|
+
},
|
|
195
|
+
maxContentLength: Infinity,
|
|
196
|
+
maxBodyLength: Infinity,
|
|
197
|
+
});
|
|
198
|
+
// Extract ETag from response (ETag may be quoted, so we'll store it as-is)
|
|
199
|
+
const etag = putResponse.headers.etag || putResponse.headers["ETag"] || "";
|
|
200
|
+
if (!etag) {
|
|
201
|
+
throw new errors_1.D3UploadError(`Failed to get ETag for part ${i + 1}`);
|
|
202
|
+
}
|
|
203
|
+
uploadParts.push({
|
|
204
|
+
etag: etag.replace(/^"|"$/g, ""), // Remove quotes if present
|
|
205
|
+
partNumber: i + 1,
|
|
206
|
+
});
|
|
207
|
+
bytesUploaded += partSize;
|
|
208
|
+
// Report progress
|
|
209
|
+
if (onProgress) {
|
|
210
|
+
onProgress({
|
|
211
|
+
currentPart: i + 1,
|
|
212
|
+
totalParts: actualParts,
|
|
213
|
+
bytesUploaded,
|
|
214
|
+
totalBytes: fileSize,
|
|
215
|
+
percentage: Math.round((bytesUploaded / fileSize) * 100),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Step 3: Complete the multipart upload
|
|
220
|
+
try {
|
|
221
|
+
await this.axiosInstance.post("/v1/biz/complete-upload", {
|
|
222
|
+
file_key: fileKey,
|
|
223
|
+
upload_id: uploadId,
|
|
224
|
+
object_name: objectName,
|
|
225
|
+
parts: uploadParts.map((part) => ({
|
|
226
|
+
etag: part.etag,
|
|
227
|
+
part_number: part.partNumber,
|
|
228
|
+
})),
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
catch (completeError) {
|
|
232
|
+
if (completeError instanceof errors_1.DragdropdoError ||
|
|
233
|
+
completeError instanceof errors_1.D3APIError) {
|
|
234
|
+
throw new errors_1.D3UploadError(`Failed to complete upload: ${completeError.message}`, completeError);
|
|
235
|
+
}
|
|
236
|
+
const message = completeError?.message || "Unknown error";
|
|
237
|
+
throw new errors_1.D3UploadError(`Failed to complete upload: ${message}`, completeError);
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
file_key: fileKey,
|
|
241
|
+
upload_id: uploadId,
|
|
242
|
+
presigned_urls: presignedUrls,
|
|
243
|
+
object_name: objectName,
|
|
244
|
+
// Provide camelCase aliases for backward compatibility
|
|
245
|
+
fileKey: fileKey,
|
|
246
|
+
uploadId: uploadId,
|
|
247
|
+
presignedUrls: presignedUrls,
|
|
248
|
+
objectName: objectName,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
if (error instanceof errors_1.DragdropdoError || error instanceof errors_1.D3APIError) {
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
const message = error?.message || "Unknown error";
|
|
256
|
+
throw new errors_1.D3UploadError(`Upload failed: ${message}`, error);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Check if an operation is supported for a file extension
|
|
261
|
+
*
|
|
262
|
+
* @param options - Supported operation options
|
|
263
|
+
* @returns Promise resolving to supported operation response
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* // Check all available actions for PDF
|
|
268
|
+
* const result = await client.checkSupportedOperation({
|
|
269
|
+
* ext: 'pdf'
|
|
270
|
+
* });
|
|
271
|
+
* console.log('Available actions:', result.availableActions);
|
|
272
|
+
*
|
|
273
|
+
* // Check if convert to PNG is supported
|
|
274
|
+
* const result = await client.checkSupportedOperation({
|
|
275
|
+
* ext: 'pdf',
|
|
276
|
+
* action: 'convert',
|
|
277
|
+
* parameters: { convert_to: 'png' }
|
|
278
|
+
* });
|
|
279
|
+
* console.log('Supported:', result.supported);
|
|
280
|
+
*
|
|
281
|
+
* // Get available compression levels
|
|
282
|
+
* const result = await client.checkSupportedOperation({
|
|
283
|
+
* ext: 'pdf',
|
|
284
|
+
* action: 'compress'
|
|
285
|
+
* });
|
|
286
|
+
* console.log('Compression levels:', result.parameters?.compression_value);
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
async checkSupportedOperation(options) {
|
|
290
|
+
if (!options.ext) {
|
|
291
|
+
throw new errors_1.D3ValidationError("Extension (ext) is required");
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
const response = await this.axiosInstance.post("/v1/biz/supported-operation", {
|
|
295
|
+
ext: options.ext,
|
|
296
|
+
action: options.action,
|
|
297
|
+
parameters: options.parameters,
|
|
298
|
+
});
|
|
299
|
+
return response.data.data;
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
if (error instanceof errors_1.DragdropdoError || error instanceof errors_1.D3APIError) {
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
const message = error?.message || "Unknown error";
|
|
306
|
+
throw new errors_1.DragdropdoError(`Failed to check supported operation: ${message}`, undefined, undefined, error);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Create a file operation (convert, compress, merge, zip, etc.)
|
|
311
|
+
*
|
|
312
|
+
* @param options - Operation options
|
|
313
|
+
* @returns Promise resolving to operation response with main task ID
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```typescript
|
|
317
|
+
* // Convert PDF to PNG
|
|
318
|
+
* const result = await client.createOperation({
|
|
319
|
+
* action: 'convert',
|
|
320
|
+
* fileKeys: ['file-key-123'],
|
|
321
|
+
* parameters: { convert_to: 'png' }
|
|
322
|
+
* });
|
|
323
|
+
*
|
|
324
|
+
* // Compress PDF
|
|
325
|
+
* const result = await client.createOperation({
|
|
326
|
+
* action: 'compress',
|
|
327
|
+
* fileKeys: ['file-key-123'],
|
|
328
|
+
* parameters: { compression_value: 'recommended' }
|
|
329
|
+
* });
|
|
330
|
+
*
|
|
331
|
+
* // Merge multiple PDFs
|
|
332
|
+
* const result = await client.createOperation({
|
|
333
|
+
* action: 'merge',
|
|
334
|
+
* fileKeys: ['file-key-1', 'file-key-2', 'file-key-3']
|
|
335
|
+
* });
|
|
336
|
+
*
|
|
337
|
+
* // Lock PDF with password
|
|
338
|
+
* const result = await client.createOperation({
|
|
339
|
+
* action: 'lock',
|
|
340
|
+
* fileKeys: ['file-key-123'],
|
|
341
|
+
* parameters: { password: 'secure-password' }
|
|
342
|
+
* });
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
async createOperation(options) {
|
|
346
|
+
if (!options.action) {
|
|
347
|
+
throw new errors_1.D3ValidationError("Action is required");
|
|
348
|
+
}
|
|
349
|
+
if (!options.fileKeys || options.fileKeys.length === 0) {
|
|
350
|
+
throw new errors_1.D3ValidationError("At least one file key is required");
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
const response = await this.axiosInstance.post("/v1/biz/do", {
|
|
354
|
+
action: options.action,
|
|
355
|
+
file_keys: options.fileKeys,
|
|
356
|
+
parameters: options.parameters,
|
|
357
|
+
notes: options.notes,
|
|
358
|
+
});
|
|
359
|
+
const rawData = response.data.data;
|
|
360
|
+
// Transform snake_case to camelCase
|
|
361
|
+
const transformed = this.toCamelCase(rawData);
|
|
362
|
+
return {
|
|
363
|
+
mainTaskId: transformed.mainTaskId || transformed.main_task_id,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
if (error instanceof errors_1.DragdropdoError || error instanceof errors_1.D3APIError) {
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
const message = error?.message || "Unknown error";
|
|
371
|
+
throw new errors_1.DragdropdoError(`Failed to create operation: ${message}`, undefined, undefined, error);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Convenience methods for specific operations
|
|
376
|
+
*/
|
|
377
|
+
/**
|
|
378
|
+
* Convert files to a different format
|
|
379
|
+
*/
|
|
380
|
+
async convert(fileKeys, convertTo, notes) {
|
|
381
|
+
return this.createOperation({
|
|
382
|
+
action: "convert",
|
|
383
|
+
fileKeys,
|
|
384
|
+
parameters: { convert_to: convertTo },
|
|
385
|
+
notes,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Compress files
|
|
390
|
+
*/
|
|
391
|
+
async compress(fileKeys, compressionValue = "recommended", notes) {
|
|
392
|
+
return this.createOperation({
|
|
393
|
+
action: "compress",
|
|
394
|
+
fileKeys,
|
|
395
|
+
parameters: { compression_value: compressionValue },
|
|
396
|
+
notes,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Merge multiple files
|
|
401
|
+
*/
|
|
402
|
+
async merge(fileKeys, notes) {
|
|
403
|
+
return this.createOperation({
|
|
404
|
+
action: "merge",
|
|
405
|
+
fileKeys,
|
|
406
|
+
notes,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Create a ZIP archive from files
|
|
411
|
+
*/
|
|
412
|
+
async zip(fileKeys, notes) {
|
|
413
|
+
return this.createOperation({
|
|
414
|
+
action: "zip",
|
|
415
|
+
fileKeys,
|
|
416
|
+
notes,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Share files (generate shareable links)
|
|
421
|
+
*/
|
|
422
|
+
async share(fileKeys, notes) {
|
|
423
|
+
return this.createOperation({
|
|
424
|
+
action: "share",
|
|
425
|
+
fileKeys,
|
|
426
|
+
notes,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Lock PDF with password
|
|
431
|
+
*/
|
|
432
|
+
async lockPdf(fileKeys, password, notes) {
|
|
433
|
+
return this.createOperation({
|
|
434
|
+
action: "lock",
|
|
435
|
+
fileKeys,
|
|
436
|
+
parameters: { password },
|
|
437
|
+
notes,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Unlock PDF with password
|
|
442
|
+
*/
|
|
443
|
+
async unlockPdf(fileKeys, password, notes) {
|
|
444
|
+
return this.createOperation({
|
|
445
|
+
action: "unlock",
|
|
446
|
+
fileKeys,
|
|
447
|
+
parameters: { password },
|
|
448
|
+
notes,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Reset PDF password
|
|
453
|
+
*/
|
|
454
|
+
async resetPdfPassword(fileKeys, oldPassword, newPassword, notes) {
|
|
455
|
+
return this.createOperation({
|
|
456
|
+
action: "reset_password",
|
|
457
|
+
fileKeys,
|
|
458
|
+
parameters: {
|
|
459
|
+
old_password: oldPassword,
|
|
460
|
+
new_password: newPassword,
|
|
461
|
+
},
|
|
462
|
+
notes,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Get operation status
|
|
467
|
+
*
|
|
468
|
+
* @param options - Status options
|
|
469
|
+
* @returns Promise resolving to status response
|
|
470
|
+
*
|
|
471
|
+
* @example
|
|
472
|
+
* ```typescript
|
|
473
|
+
* // Get main task status
|
|
474
|
+
* const status = await client.getStatus({
|
|
475
|
+
* mainTaskId: 'task-123'
|
|
476
|
+
* });
|
|
477
|
+
*
|
|
478
|
+
* // Get specific file task status
|
|
479
|
+
* const status = await client.getStatus({
|
|
480
|
+
* mainTaskId: 'task-123',
|
|
481
|
+
* fileTaskId: 'file-task-456'
|
|
482
|
+
* });
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
async getStatus(options) {
|
|
486
|
+
if (!options.mainTaskId) {
|
|
487
|
+
throw new errors_1.D3ValidationError("mainTaskId is required");
|
|
488
|
+
}
|
|
489
|
+
try {
|
|
490
|
+
let url = `/v1/biz/status/${options.mainTaskId}`;
|
|
491
|
+
if (options.fileTaskId) {
|
|
492
|
+
url += `/${options.fileTaskId}`;
|
|
493
|
+
}
|
|
494
|
+
const response = await this.axiosInstance.get(url);
|
|
495
|
+
const rawData = response.data.data;
|
|
496
|
+
// Transform snake_case to camelCase
|
|
497
|
+
const transformed = this.toCamelCase(rawData);
|
|
498
|
+
return {
|
|
499
|
+
operationStatus: transformed.operationStatus || transformed.operation_status,
|
|
500
|
+
filesData: (transformed.filesData || transformed.files_data || []).map((file) => ({
|
|
501
|
+
fileKey: file.fileKey || file.file_key,
|
|
502
|
+
status: file.status,
|
|
503
|
+
downloadLink: file.downloadLink || file.download_link,
|
|
504
|
+
errorCode: file.errorCode || file.error_code,
|
|
505
|
+
errorMessage: file.errorMessage || file.error_message,
|
|
506
|
+
})),
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
510
|
+
if (error instanceof errors_1.DragdropdoError || error instanceof errors_1.D3APIError) {
|
|
511
|
+
throw error;
|
|
512
|
+
}
|
|
513
|
+
const message = error?.message || "Unknown error";
|
|
514
|
+
throw new errors_1.DragdropdoError(`Failed to get status: ${message}`, undefined, undefined, error);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Poll operation status until completion or failure
|
|
519
|
+
*
|
|
520
|
+
* @param options - Poll status options
|
|
521
|
+
* @returns Promise resolving to final status response
|
|
522
|
+
*
|
|
523
|
+
* @example
|
|
524
|
+
* ```typescript
|
|
525
|
+
* const status = await client.pollStatus({
|
|
526
|
+
* mainTaskId: 'task-123',
|
|
527
|
+
* interval: 2000, // Check every 2 seconds
|
|
528
|
+
* timeout: 300000, // 5 minutes max
|
|
529
|
+
* onUpdate: (status) => {
|
|
530
|
+
* console.log('Status:', status.operationStatus);
|
|
531
|
+
* }
|
|
532
|
+
* });
|
|
533
|
+
*
|
|
534
|
+
* if (status.operationStatus === 'completed') {
|
|
535
|
+
* console.log('Download links:', status.filesData.map(f => f.downloadLink));
|
|
536
|
+
* }
|
|
537
|
+
* ```
|
|
538
|
+
*/
|
|
539
|
+
async pollStatus(options) {
|
|
540
|
+
const { mainTaskId, fileTaskId, interval = 2000, timeout = 300000, onUpdate, } = options;
|
|
541
|
+
const startTime = Date.now();
|
|
542
|
+
return new Promise((resolve, reject) => {
|
|
543
|
+
const poll = async () => {
|
|
544
|
+
try {
|
|
545
|
+
// Check timeout
|
|
546
|
+
if (Date.now() - startTime > timeout) {
|
|
547
|
+
reject(new errors_1.D3TimeoutError(`Polling timed out after ${timeout}ms`));
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
// Get status
|
|
551
|
+
const status = await this.getStatus({ mainTaskId, fileTaskId });
|
|
552
|
+
// Call update callback
|
|
553
|
+
if (onUpdate) {
|
|
554
|
+
onUpdate(status);
|
|
555
|
+
}
|
|
556
|
+
// Check if completed or failed
|
|
557
|
+
if (status.operationStatus === "completed" ||
|
|
558
|
+
status.operationStatus === "failed") {
|
|
559
|
+
resolve(status);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
// Continue polling
|
|
563
|
+
// setTimeout is available globally in Node.js
|
|
564
|
+
// @ts-ignore - setTimeout is available in Node.js runtime
|
|
565
|
+
setTimeout(poll, interval);
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
reject(error);
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
poll();
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Convert snake_case object keys to camelCase
|
|
576
|
+
*/
|
|
577
|
+
toCamelCase(obj) {
|
|
578
|
+
if (obj === null || obj === undefined) {
|
|
579
|
+
return obj;
|
|
580
|
+
}
|
|
581
|
+
if (Array.isArray(obj)) {
|
|
582
|
+
return obj.map((item) => this.toCamelCase(item));
|
|
583
|
+
}
|
|
584
|
+
if (typeof obj === "object") {
|
|
585
|
+
const camelObj = {};
|
|
586
|
+
for (const key in obj) {
|
|
587
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
588
|
+
const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
589
|
+
camelObj[camelKey] = this.toCamelCase(obj[key]);
|
|
590
|
+
// Also keep original key for backward compatibility
|
|
591
|
+
camelObj[key] = obj[key];
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return camelObj;
|
|
595
|
+
}
|
|
596
|
+
return obj;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Get MIME type from file extension
|
|
600
|
+
*/
|
|
601
|
+
getMimeType(ext) {
|
|
602
|
+
const mimeTypes = {
|
|
603
|
+
".pdf": "application/pdf",
|
|
604
|
+
".jpg": "image/jpeg",
|
|
605
|
+
".jpeg": "image/jpeg",
|
|
606
|
+
".png": "image/png",
|
|
607
|
+
".gif": "image/gif",
|
|
608
|
+
".webp": "image/webp",
|
|
609
|
+
".doc": "application/msword",
|
|
610
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
611
|
+
".xls": "application/vnd.ms-excel",
|
|
612
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
613
|
+
".zip": "application/zip",
|
|
614
|
+
".txt": "text/plain",
|
|
615
|
+
".mp4": "video/mp4",
|
|
616
|
+
".mp3": "audio/mpeg",
|
|
617
|
+
};
|
|
618
|
+
return mimeTypes[ext.toLowerCase()] || null;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
exports.Dragdropdo = Dragdropdo;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error classes for dragdropdo.com Business API Client
|
|
3
|
+
*/
|
|
4
|
+
import { D3Error } from "./types";
|
|
5
|
+
export declare class DragdropdoError extends Error implements D3Error {
|
|
6
|
+
statusCode?: number;
|
|
7
|
+
code?: number;
|
|
8
|
+
details?: any;
|
|
9
|
+
constructor(message: string, statusCode?: number, code?: number, details?: any);
|
|
10
|
+
}
|
|
11
|
+
export declare class D3APIError extends DragdropdoError {
|
|
12
|
+
constructor(message: string, statusCode: number, code?: number, details?: any);
|
|
13
|
+
}
|
|
14
|
+
export declare class D3ValidationError extends DragdropdoError {
|
|
15
|
+
constructor(message: string, details?: any);
|
|
16
|
+
}
|
|
17
|
+
export declare class D3UploadError extends DragdropdoError {
|
|
18
|
+
constructor(message: string, details?: any);
|
|
19
|
+
}
|
|
20
|
+
export declare class D3TimeoutError extends DragdropdoError {
|
|
21
|
+
constructor(message?: string);
|
|
22
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Custom error classes for dragdropdo.com Business API Client
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.D3TimeoutError = exports.D3UploadError = exports.D3ValidationError = exports.D3APIError = exports.DragdropdoError = void 0;
|
|
7
|
+
class DragdropdoError extends Error {
|
|
8
|
+
constructor(message, statusCode, code, details) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "DragdropdoError";
|
|
11
|
+
this.statusCode = statusCode;
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.details = details;
|
|
14
|
+
// Capture stack trace if available (Node.js)
|
|
15
|
+
if (typeof Error.captureStackTrace === "function") {
|
|
16
|
+
Error.captureStackTrace(this, this.constructor);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.DragdropdoError = DragdropdoError;
|
|
21
|
+
class D3APIError extends DragdropdoError {
|
|
22
|
+
constructor(message, statusCode, code, details) {
|
|
23
|
+
super(message, statusCode, code, details);
|
|
24
|
+
this.name = "D3APIError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.D3APIError = D3APIError;
|
|
28
|
+
class D3ValidationError extends DragdropdoError {
|
|
29
|
+
constructor(message, details) {
|
|
30
|
+
super(message, 400, undefined, details);
|
|
31
|
+
this.name = "D3ValidationError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.D3ValidationError = D3ValidationError;
|
|
35
|
+
class D3UploadError extends DragdropdoError {
|
|
36
|
+
constructor(message, details) {
|
|
37
|
+
super(message, undefined, undefined, details);
|
|
38
|
+
this.name = "D3UploadError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.D3UploadError = D3UploadError;
|
|
42
|
+
class D3TimeoutError extends DragdropdoError {
|
|
43
|
+
constructor(message = "Operation timed out") {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = "D3TimeoutError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.D3TimeoutError = D3TimeoutError;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DragDropDo SDK - Node.js Client Library
|
|
3
|
+
*
|
|
4
|
+
* Official Node.js client for the dragdropdo.com Business API
|
|
5
|
+
*/
|
|
6
|
+
export { Dragdropdo } from "./client";
|
|
7
|
+
export * from "./types";
|
|
8
|
+
export * from "./errors";
|
|
9
|
+
export { Dragdropdo as default } from "./client";
|