2dai-sdk 1.4.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 ADDED
@@ -0,0 +1,1287 @@
1
+ # 2dai-sdk
2
+
3
+ Official TypeScript/JavaScript SDK for 2DAI AI Generation API
4
+
5
+ Generate images, videos, and text using state-of-the-art AI models through a simple and intuitive API.
6
+
7
+ [![Version](https://img.shields.io/badge/version-1.4.0-blue)](.)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue)](.)
9
+ [![Node](https://img.shields.io/badge/node-%3E%3D18.0.0-green)](.)
10
+
11
+ ---
12
+
13
+ ## Table of Contents
14
+
15
+ - [Features](#features)
16
+ - [What's New](#whats-new)
17
+ - [Installation](#installation)
18
+ - [Getting an API Key](#getting-an-api-key)
19
+ - [Quick Start](#quick-start)
20
+ - [Documentation](#documentation)
21
+ - [Initialization](#initialization)
22
+ - [Image Generation](#image-generation)
23
+ - [Image Editing](#image-editing)
24
+ - [Image Upscaling](#image-upscaling)
25
+ - [Video Generation](#video-generation)
26
+ - [Text Generation (LLM)](#text-generation-llm)
27
+ - [CDN Operations](#cdn-operations)
28
+ - [Watermarking](#watermarking)
29
+ - [Settings & Usage Tracking](#settings--usage-tracking)
30
+ - [Advanced Usage](#advanced-usage)
31
+ - [Complete Examples](#complete-examples)
32
+ - [API Reference](#api-reference)
33
+ - [Best Practices](#best-practices)
34
+ - [Troubleshooting](#troubleshooting)
35
+ - [Migration Guide](#migration-guide)
36
+
37
+ ---
38
+
39
+ ## Features
40
+
41
+ - **Text-to-Image**: Generate stunning images from text prompts
42
+ - **Image-to-Image**: Edit and transform existing images
43
+ - **AI Image Upscale**: Upscale images 2-4x using AI
44
+ - **Image-to-Video**: Create videos from static images
45
+ - **LLM Text Generation**: Generate text with context, memory, and JSON support
46
+ - **Image Description (Vision)**: Analyze images with LLM for structured data extraction
47
+ - **12 Style Presets**: Realistic, anime, manga, cinematic, and more
48
+ - **7 Format Presets**: Portrait, landscape, profile, story, post, smartphone, banner
49
+ - **Type-Safe**: Full TypeScript support with comprehensive type definitions
50
+ - **Built-in Watermarking**: Apply custom watermarks to generated content
51
+ - **WebSocket Support**: Real-time generation with progress updates
52
+ - **Usage Tracking**: Real-time usage monitoring with remaining quota and reset times
53
+ - **Rate Limiting**: Built-in rate limit tracking for requests and tokens (LLM)
54
+ - **CDN Format Conversion**: Convert between image formats (JPG, PNG, GIF) and extract frames from videos
55
+ - **Comprehensive Tests**: Full test coverage for all API endpoints and WebSocket operations
56
+
57
+ ---
58
+
59
+ ## What's New
60
+
61
+ ### v1.4.0 (Latest)
62
+
63
+ - **AI Image Upscale** - New upscale methods for AI-powered image upscaling
64
+ - `upscaleImage()` - REST API upscaling with factor 2-4x
65
+ - `wsUpscaleImage()` - WebSocket upscaling for real-time operations
66
+ - **Image Description (Vision)** - Analyze images with LLM
67
+ - Pass `imageId` to `generateText()` or `wsGenerateLlm()` for image analysis
68
+ - Extract structured JSON data from images
69
+ - **New Types** - `UpscaleOptions`, `UpscaleResult`, `WsUpscaleRequest`, `WsUpscaleResponse`
70
+ - **New Constants** - `UPSCALE_FACTOR` (MIN: 2, MAX: 4, DEFAULT: 2)
71
+
72
+ ### v1.3.0
73
+
74
+ - **Full WebSocket Client** - Complete WebSocket implementation
75
+ - `wsConnect()`, `wsGenerateImage()`, `wsGenerateVideo()`, `wsGenerateLlm()`
76
+ - Auto-reconnect with exponential backoff
77
+ - Ping/pong keepalive to prevent timeouts
78
+ - Request tracking with `requestId`
79
+ - **Image Resize for img2img** - New `width` and `height` parameters for `editImage()`
80
+ - Resize output to custom dimensions (320-1344) during image editing
81
+ - **JSON Output Enhancement** - `TextGenerationResult.json` field for parsed JSON output
82
+ - **WebSocket Types** - New exported types for WebSocket requests/responses
83
+
84
+ ### v1.2.0
85
+
86
+ - **Video Frame Extraction** - Extract frames from videos with `?seek=<ms>` CDN parameter
87
+ - **Video Metadata** - `duration` and `fps` in video generation responses
88
+ - **WatermarkPosition Constants** - Type-safe watermark positioning
89
+
90
+ ### v1.1.0
91
+
92
+ - **Style Update** - Replaced `legacy` style with new `text` style
93
+ - Updated style presets to match Gen6 improvements
94
+
95
+ ### v1.0.3
96
+
97
+ - **Markdown Formatting** - New `useMarkdown` option for LLM text generation
98
+ - When enabled, responses include markdown formatting (headers, bullet points, code blocks)
99
+ - Works with both REST API and WebSocket connections
100
+
101
+ ### v1.0.2
102
+
103
+ - **JSON Format Fix** - Fixed `jsonFormat: true` responses returning empty strings
104
+ - LLM text generation now correctly returns JSON responses when using `jsonFormat` and `jsonTemplate` options
105
+
106
+ ### v1.0.1
107
+
108
+ - **GIF File Format Support** - Full GIF upload, download, and processing via CDN
109
+ - **Format Conversion** - Convert between image formats (PNG, JPG, GIF) and extract video frames as GIF
110
+ - **GIF Watermarking** - Apply watermarks to GIF files with position control
111
+ - **Enhanced Debug Logging** - Improved watermark operation debugging
112
+
113
+ ### v1.0.0
114
+
115
+ - Initial public release
116
+ - Text-to-image generation with 12 style presets
117
+ - Image-to-image editing with strength control
118
+ - Image-to-video generation
119
+ - LLM text generation with memory and JSON format support
120
+ - 7 format presets for different aspect ratios
121
+ - WebSocket and REST API support
122
+ - Built-in rate limiting and watermarking support
123
+
124
+ ---
125
+
126
+ ## Installation
127
+
128
+ ```bash
129
+ npm install 2dai-sdk
130
+ ```
131
+
132
+ or
133
+
134
+ ```bash
135
+ yarn add 2dai-sdk
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Getting an API Key
141
+
142
+ To use this SDK, you need an API key.
143
+
144
+ **Request Access:**
145
+ 1. Fill out the [API Access Request Form](https://YOUR_2DAI_FORM_LINK)
146
+ 2. We'll review your request within 24-48 hours
147
+ 3. Once approved, you'll receive your API key via email
148
+
149
+ **Contact:**
150
+ - Twitter/X: [@2DAICommunity](https://x.com/2DAICommunity)
151
+ - Telegram: [Token2dAI](https://t.me/Token2dAI)
152
+
153
+ ---
154
+
155
+ ## Quick Start
156
+
157
+ ```typescript
158
+ import { createClient, STYLES, FORMATS } from '2dai-sdk';
159
+
160
+ // Initialize the client
161
+ const client = createClient('2dai_pk_your_api_key_here');
162
+
163
+ // Generate an image (REST API)
164
+ const image = await client.generateImage({
165
+ prompt: 'a majestic lion in the savanna at sunset',
166
+ style: STYLES.cine.id,
167
+ format: FORMATS.landscape.id
168
+ });
169
+
170
+ // Construct CDN URL from image ID
171
+ const imageUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${image.imageId}.jpg`;
172
+ console.log(`Image URL: ${imageUrl}`);
173
+
174
+ // Or use WebSocket for real-time generation
175
+ await client.wsConnect();
176
+ const wsImage = await client.wsGenerateImage({
177
+ prompt: 'a futuristic city at night',
178
+ style: STYLES.cine.id,
179
+ format: FORMATS.landscape.id
180
+ });
181
+ console.log(`WebSocket Image ID: ${wsImage.result.imageId}`);
182
+ await client.close();
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Documentation
188
+
189
+ ### Initialization
190
+
191
+ ```typescript
192
+ import { createClient } from '2dai-sdk';
193
+
194
+ const client = createClient('2dai_pk_your_api_key_here', {
195
+ baseUrl: 'https://apiv2.2dai.io:800', // Optional, defaults to production
196
+ timeout: 300000, // Optional, 5 minutes default
197
+ debug: false // Optional, enable debug logging
198
+ });
199
+ ```
200
+
201
+ ### Image Generation
202
+
203
+ #### Generate from Text Prompt
204
+
205
+ ```typescript
206
+ import { STYLES, FORMATS } from '2dai-sdk';
207
+
208
+ const result = await client.generateImage({
209
+ prompt: 'a cyberpunk cityscape at night with neon lights',
210
+ negativePrompt: 'blurry, low quality, distorted',
211
+ style: STYLES.cine.id,
212
+ format: FORMATS.landscape.id,
213
+ seed: 12345 // Optional, for reproducibility
214
+ });
215
+
216
+ console.log(result);
217
+ // {
218
+ // success: true,
219
+ // imageId: '550e8400-e29b-41d4-a716-446655440000',
220
+ // width: 1344,
221
+ // height: 768,
222
+ // seed: 12345
223
+ // }
224
+
225
+ // Construct CDN URL from imageId
226
+ const imageUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${result.imageId}.jpg`;
227
+ console.log(`Access image at: ${imageUrl}`);
228
+ ```
229
+
230
+ #### Available Styles
231
+
232
+ ```typescript
233
+ import { STYLES } from '2dai-sdk';
234
+
235
+ STYLES.raw // Raw Gen6 defaults
236
+ STYLES.realistic // Realistic 2DAI generation
237
+ STYLES.text // Text & Clarity
238
+ STYLES.ciniji // Niji anime style with vibrant colors
239
+ STYLES.portrait // Gen6 Portrait
240
+ STYLES.cine // Gen6 Cinematic
241
+ STYLES.sport // Gen6 Sport
242
+ STYLES.fashion // Gen6 Fashion
243
+ STYLES.niji // Gen6 Anime Niji
244
+ STYLES.anime // Gen6 Anime
245
+ STYLES.manga // Gen6 Manga
246
+ STYLES.paint // Gen6 Paint
247
+ ```
248
+
249
+ #### Available Formats
250
+
251
+ ```typescript
252
+ import { FORMATS } from '2dai-sdk';
253
+
254
+ FORMATS.portrait // 9:16 vertical (768x1344)
255
+ FORMATS.landscape // 16:9 horizontal (1344x768)
256
+ FORMATS.profile // 1:1 profile picture (1024x1024)
257
+ FORMATS.story // 9:16 story format (720x1280)
258
+ FORMATS.post // 9:7 wide square (1152x896)
259
+ FORMATS.smartphone // Phone screen (640x1344)
260
+ FORMATS.banner // 3:1 wide screen (1472x448)
261
+ ```
262
+
263
+ ### Image Editing
264
+
265
+ ```typescript
266
+ // Edit using image ID
267
+ const edited = await client.editImage('550e8400-e29b-41d4-a716-446655440000', {
268
+ prompt: 'make the sky more dramatic with storm clouds'
269
+ });
270
+
271
+ // Edit with resize - output to custom dimensions
272
+ const resized = await client.editImage('550e8400-e29b-41d4-a716-446655440000', {
273
+ prompt: 'Seamlessly extend the image, remove the black background',
274
+ width: 768, // Custom output width (320-1344)
275
+ height: 1344 // Custom output height (320-1344)
276
+ });
277
+
278
+ console.log(resized);
279
+ // {
280
+ // success: true,
281
+ // imageId: 'new-image-id',
282
+ // width: 768,
283
+ // height: 1344,
284
+ // seed: 12345
285
+ // }
286
+
287
+ // Edit with resizePad - fit original in frame with black padding
288
+ const padded = await client.editImage('550e8400-e29b-41d4-a716-446655440000', {
289
+ prompt: 'Seamlessly extend the image',
290
+ width: 768,
291
+ height: 1344,
292
+ resizePad: true // Fits original image in resized frame, fills extra space with black
293
+ });
294
+
295
+ // Edit via WebSocket
296
+ await client.wsConnect();
297
+ const wsEdited = await client.wsGenerateImage({
298
+ imageId: '550e8400-e29b-41d4-a716-446655440000',
299
+ prompt: 'make the image black and white',
300
+ width: 1344,
301
+ height: 768
302
+ });
303
+ console.log(wsEdited.result.imageId);
304
+ ```
305
+
306
+ #### Resize Padding (`resizePad`)
307
+
308
+ When resizing images to different dimensions, the `resizePad` option controls how the original image is fitted:
309
+
310
+ | resizePad | Behavior |
311
+ |-----------|----------|
312
+ | `false` (default) | Image is cropped/stretched to fill the target dimensions |
313
+ | `true` | Original image fits entirely within the frame, extra space is filled with black |
314
+
315
+ ### Image Upscaling
316
+
317
+ AI-powered image upscaling. No user prompt needed.
318
+
319
+ ```typescript
320
+ import { UPSCALE_FACTOR } from '2dai-sdk';
321
+
322
+ // Upscale an image (REST API)
323
+ const upscaled = await client.upscaleImage('550e8400-e29b-41d4-a716-446655440000', {
324
+ factor: 2 // 2x, 3x, or 4x (default: 2)
325
+ });
326
+
327
+ console.log(upscaled);
328
+ // {
329
+ // success: true,
330
+ // imageId: 'upscaled-image-id',
331
+ // width: 2048,
332
+ // height: 2048,
333
+ // seed: 12345
334
+ // }
335
+
336
+ // Upscale via WebSocket
337
+ await client.wsConnect();
338
+ const wsUpscaled = await client.wsUpscaleImage({
339
+ imageId: '550e8400-e29b-41d4-a716-446655440000',
340
+ factor: 2
341
+ });
342
+ console.log(wsUpscaled.result.imageId);
343
+ await client.close();
344
+ ```
345
+
346
+ #### Upscale Factor Limits
347
+
348
+ ```typescript
349
+ import { UPSCALE_FACTOR } from '2dai-sdk';
350
+
351
+ UPSCALE_FACTOR.MIN // 2
352
+ UPSCALE_FACTOR.MAX // 4
353
+ UPSCALE_FACTOR.DEFAULT // 2
354
+ ```
355
+
356
+ ### Video Generation
357
+
358
+ ```typescript
359
+ // Generate video from image ID
360
+ const video = await client.generateVideo({
361
+ imageId: '550e8400-e29b-41d4-a716-446655440000',
362
+ duration: 5, // 1-10 seconds
363
+ fps: 16 // 8-32 fps
364
+ });
365
+
366
+ // Generate video from buffer
367
+ const video = await client.generateVideo({
368
+ imageBuffer,
369
+ duration: 5
370
+ });
371
+
372
+ console.log(video);
373
+ // {
374
+ // success: true,
375
+ // videoId: '660e8400-e29b-41d4-a716-446655440000',
376
+ // duration: 5,
377
+ // fps: 16
378
+ // }
379
+
380
+ // Construct CDN URL from videoId
381
+ const videoUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${video.videoId}.mp4`;
382
+ console.log(`Access video at: ${videoUrl}`);
383
+ ```
384
+
385
+ ### Text Generation (LLM)
386
+
387
+ ```typescript
388
+ // Simple text generation
389
+ const result = await client.generateText({
390
+ prompt: 'Write a short poem about AI and creativity'
391
+ });
392
+
393
+ console.log(result.response);
394
+
395
+ // With markdown formatting
396
+ const formatted = await client.generateText({
397
+ prompt: 'Explain what TypeScript is. Use headers and code examples.',
398
+ useMarkdown: true // Response includes markdown formatting
399
+ });
400
+
401
+ console.log(formatted.response); // Contains # headers, ```code blocks```, bullet points, etc.
402
+
403
+ // With system message and memory
404
+ const result = await client.generateText({
405
+ prompt: 'What were we discussing?',
406
+ system: 'You are a helpful assistant',
407
+ memory: [
408
+ 'User asked about AI capabilities',
409
+ 'Discussed image generation features'
410
+ ]
411
+ });
412
+
413
+ // JSON format response
414
+ const result = await client.generateText({
415
+ prompt: 'Analyze this product review',
416
+ jsonFormat: true,
417
+ jsonTemplate: {
418
+ sentiment: 'positive/negative/neutral',
419
+ score: '1-10',
420
+ summary: 'brief summary'
421
+ }
422
+ });
423
+
424
+ console.log(result.response); // Returns structured JSON
425
+
426
+ // With knowledge base
427
+ const result = await client.generateText({
428
+ prompt: 'What is our refund policy?',
429
+ askKnowledge: {
430
+ sources: ['docs', 'policies'],
431
+ query: 'refund policy'
432
+ }
433
+ });
434
+
435
+ // Image description (Vision)
436
+ const imageResult = await client.generateImage({
437
+ prompt: 'a futuristic city at night'
438
+ });
439
+
440
+ const description = await client.generateText({
441
+ prompt: 'Analyze this image and extract structured data',
442
+ system: 'Extract information from images into structured JSON format.',
443
+ imageId: imageResult.imageId, // Pass image for analysis
444
+ jsonFormat: true,
445
+ jsonTemplate: {
446
+ main_subject: 'string - primary subject of the image',
447
+ objects: 'array of strings - objects visible in the image',
448
+ colors: 'array of strings - dominant colors',
449
+ mood: 'string - overall mood/atmosphere'
450
+ }
451
+ });
452
+
453
+ console.log(description.json);
454
+ // Output: { main_subject: "futuristic cityscape", objects: ["buildings", "lights", ...], ... }
455
+ ```
456
+
457
+ ### CDN Operations
458
+
459
+ The CDN supports multiple file formats and operations including format conversion, resizing, and watermarking.
460
+
461
+ #### Supported File Formats
462
+
463
+ | Format | Extension | Description |
464
+ |--------|-----------|-------------|
465
+ | JPEG | `.jpg`, `.jpeg` | Standard image format |
466
+ | PNG | `.png` | Lossless image format with transparency |
467
+ | GIF | `.gif` | Static or animated image format |
468
+ | MP4 | `.mp4` | Video format |
469
+
470
+ #### CDN URL Construction
471
+
472
+ ```typescript
473
+ // Basic image URL
474
+ const imageUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${imageId}.jpg`;
475
+
476
+ // Image with resize
477
+ const resizedUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${imageId}.jpg?w=512&h=512`;
478
+
479
+ // Image with watermark
480
+ const watermarkedUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${imageId}.jpg?watermark=${watermarkId}&position=southeast`;
481
+
482
+ // Video URL
483
+ const videoUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${videoId}.mp4`;
484
+
485
+ // Extract first frame from video as GIF
486
+ const gifFromVideo = `https://apiv2.2dai.io:800/api/v1/cdn/${videoId}.gif`;
487
+
488
+ // GIF with resize
489
+ const resizedGif = `https://apiv2.2dai.io:800/api/v1/cdn/${videoId}.gif?w=256&h=256`;
490
+ ```
491
+
492
+ #### Format Conversion Matrix
493
+
494
+ | From \ To | PNG | JPG | JPEG | GIF |
495
+ |-----------|-----|-----|------|-----|
496
+ | PNG | - | Yes | Yes | Yes |
497
+ | JPG | Yes | - | Yes | Yes |
498
+ | JPEG | Yes | Yes | - | Yes |
499
+ | GIF | Yes | Yes | Yes | - |
500
+ | MP4 | Yes | Yes | Yes | Yes |
501
+
502
+ **Notes:**
503
+ - Converting animated GIF to other formats extracts the first frame
504
+ - MP4 to image formats extracts a single frame as still image
505
+ - Watermarking supported on all image formats including GIF
506
+
507
+ #### CDN Query Parameters
508
+
509
+ | Parameter | Description | Example |
510
+ |-----------|-------------|---------|
511
+ | `w` | Target width in pixels | `?w=1024` |
512
+ | `h` | Target height in pixels | `?h=768` |
513
+ | `watermark` | Watermark file ID | `?watermark=abc123` |
514
+ | `position` | Watermark position | `?position=southeast` |
515
+
516
+ **Watermark Positions:**
517
+ - Sharp gravity: `northwest`, `north`, `northeast`, `west`, `center`, `east`, `southwest`, `south`, `southeast`
518
+ - Human-readable: `top-left`, `top-center`, `top-right`, `middle-left`, `middle-center`, `middle-right`, `bottom-left`, `bottom-center`, `bottom-right`
519
+
520
+ #### Downloading Files
521
+
522
+ ```typescript
523
+ import axios from 'axios';
524
+ import fs from 'fs';
525
+
526
+ // Download image
527
+ const imageResponse = await axios.get(
528
+ `https://apiv2.2dai.io:800/api/v1/cdn/${imageId}.jpg`,
529
+ {
530
+ responseType: 'arraybuffer',
531
+ headers: { 'Authorization': `Bearer ${apiKey}` }
532
+ }
533
+ );
534
+ fs.writeFileSync('output.jpg', imageResponse.data);
535
+
536
+ // Download video
537
+ const videoResponse = await axios.get(
538
+ `https://apiv2.2dai.io:800/api/v1/cdn/${videoId}.mp4`,
539
+ {
540
+ responseType: 'arraybuffer',
541
+ headers: { 'Authorization': `Bearer ${apiKey}` }
542
+ }
543
+ );
544
+ fs.writeFileSync('output.mp4', videoResponse.data);
545
+
546
+ // Extract video frame as GIF
547
+ const gifResponse = await axios.get(
548
+ `https://apiv2.2dai.io:800/api/v1/cdn/${videoId}.gif`,
549
+ {
550
+ responseType: 'arraybuffer',
551
+ headers: { 'Authorization': `Bearer ${apiKey}` }
552
+ }
553
+ );
554
+ fs.writeFileSync('frame.gif', gifResponse.data);
555
+
556
+ // Download with resize
557
+ const resizedResponse = await axios.get(
558
+ `https://apiv2.2dai.io:800/api/v1/cdn/${imageId}.png?w=256&h=256`,
559
+ {
560
+ responseType: 'arraybuffer',
561
+ headers: { 'Authorization': `Bearer ${apiKey}` }
562
+ }
563
+ );
564
+ fs.writeFileSync('thumbnail.png', resizedResponse.data);
565
+ ```
566
+
567
+ ### Watermarking
568
+
569
+ ```typescript
570
+ // Upload watermark and set as default
571
+ const settings = await client.getSettings();
572
+ await client.updateWatermark('watermark-cdn-id');
573
+
574
+ // Apply watermark to generation
575
+ const image = await client.generateImage({
576
+ prompt: 'a beautiful landscape',
577
+ watermark: 'watermark-cdn-id',
578
+ watermarkPosition: 'southeast', // bottom-right
579
+ copyright: '2024 My Company'
580
+ });
581
+
582
+ // Tiled watermark (images only)
583
+ const image = await client.generateImage({
584
+ prompt: 'product photography',
585
+ watermark: 'watermark-cdn-id',
586
+ watermarkAsTiles: true
587
+ });
588
+
589
+ // Apply watermark via CDN URL
590
+ const watermarkedUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${imageId}.jpg?watermark=${watermarkId}&position=center`;
591
+ ```
592
+
593
+ ### Settings & Usage Tracking
594
+
595
+ ```typescript
596
+ // Get API key settings with current usage
597
+ const settings = await client.getSettings();
598
+
599
+ console.log('API Key:', settings.name);
600
+ console.log('Status:', settings.status);
601
+ console.log('\nRate Limits:');
602
+ console.log('Image:', settings.rateLimits.image);
603
+ console.log('Video:', settings.rateLimits.video);
604
+ console.log('LLM:', settings.rateLimits.llm);
605
+ console.log('CDN:', settings.rateLimits.cdn);
606
+
607
+ // Current usage with remaining quota and reset times
608
+ console.log('\nCurrent Usage:');
609
+ console.log('Image:', {
610
+ used15min: settings.currentUsage.image.current.requestsPer15Min,
611
+ usedDaily: settings.currentUsage.image.current.requestsPerDay,
612
+ remaining15min: settings.currentUsage.image.remaining.requestsPer15Min,
613
+ remainingDaily: settings.currentUsage.image.remaining.requestsPerDay,
614
+ resetAt: settings.currentUsage.image.resetAt
615
+ });
616
+
617
+ // LLM usage includes token tracking
618
+ console.log('LLM:', {
619
+ requests15min: settings.currentUsage.llm.current.requestsPer15Min,
620
+ requestsDaily: settings.currentUsage.llm.current.requestsPerDay,
621
+ tokens15min: settings.currentUsage.llm.current.tokensPer15Min,
622
+ tokensDaily: settings.currentUsage.llm.current.tokensPerDay,
623
+ resetAt: settings.currentUsage.llm.resetAt
624
+ });
625
+ ```
626
+
627
+ ### Rate Limit Checking
628
+
629
+ ```typescript
630
+ // Check current rate limits for all operations
631
+ const limits = await client.checkLimits();
632
+
633
+ limits.forEach(limit => {
634
+ console.log(`${limit.operation}:`);
635
+ console.log(` Requests: ${limit.current.requestsPer15Min}/${limit.limit.requestsPer15Min} (15min)`);
636
+ console.log(` Requests: ${limit.current.requestsPerDay}/${limit.limit.requestsPerDay} (daily)`);
637
+
638
+ if (limit.operation === 'llm') {
639
+ console.log(` Tokens: ${limit.current.tokensPer15Min}/${limit.limit.tokensPer15Min} (15min)`);
640
+ }
641
+ });
642
+ ```
643
+
644
+ ### Health Check
645
+
646
+ ```typescript
647
+ const health = await client.health();
648
+ console.log(health);
649
+ // {
650
+ // status: 'ok',
651
+ // timestamp: '2024-01-15T10:30:00.000Z'
652
+ // }
653
+ ```
654
+
655
+ ---
656
+
657
+ ## Advanced Usage
658
+
659
+ ### Custom Dimensions
660
+
661
+ ```typescript
662
+ const image = await client.generateImage({
663
+ prompt: 'a mountain landscape',
664
+ width: 1280,
665
+ height: 720
666
+ // Note: width/height must be between 320-1344
667
+ });
668
+ ```
669
+
670
+ ### Debug Mode
671
+
672
+ ```typescript
673
+ const client = createClient('2dai_pk_your_api_key_here', {
674
+ debug: true // Enable detailed logging
675
+ });
676
+ ```
677
+
678
+ ---
679
+
680
+ ## Complete Examples
681
+
682
+ ### Example 1: Generate Image with Watermark and Download
683
+
684
+ ```typescript
685
+ import { createClient, STYLES, FORMATS } from '2dai-sdk';
686
+ import axios from 'axios';
687
+ import fs from 'fs';
688
+
689
+ const client = createClient('2dai_pk_your_api_key_here');
690
+
691
+ async function generateAndDownload() {
692
+ try {
693
+ // Generate image
694
+ const result = await client.generateImage({
695
+ prompt: 'a futuristic city at sunset with flying cars',
696
+ style: STYLES.cine.id,
697
+ format: FORMATS.landscape.id,
698
+ watermark: 'your-watermark-id',
699
+ watermarkPosition: 'southeast'
700
+ });
701
+
702
+ console.log('Image generated:', result.imageId);
703
+
704
+ // Download the image
705
+ const imageUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${result.imageId}.jpg`;
706
+ const response = await axios.get(imageUrl, {
707
+ responseType: 'arraybuffer',
708
+ headers: { 'Authorization': `Bearer 2dai_pk_your_api_key_here` }
709
+ });
710
+
711
+ fs.writeFileSync('generated-image.jpg', response.data);
712
+ console.log('Image saved to generated-image.jpg');
713
+
714
+ } catch (error) {
715
+ console.error('Error:', error.message);
716
+ }
717
+ }
718
+
719
+ generateAndDownload();
720
+ ```
721
+
722
+ ### Example 2: Generate Video and Extract GIF Frame
723
+
724
+ ```typescript
725
+ import { createClient, STYLES, FORMATS } from '2dai-sdk';
726
+ import axios from 'axios';
727
+ import fs from 'fs';
728
+
729
+ const client = createClient('2dai_pk_your_api_key_here');
730
+
731
+ async function generateVideoAndExtractFrame() {
732
+ try {
733
+ // First generate an image
734
+ const image = await client.generateImage({
735
+ prompt: 'a dancing robot in a disco',
736
+ style: STYLES.anime.id,
737
+ format: FORMATS.profile.id
738
+ });
739
+ console.log('Image generated:', image.imageId);
740
+
741
+ // Generate video from image
742
+ const video = await client.generateVideo({
743
+ imageId: image.imageId,
744
+ duration: 3,
745
+ fps: 16
746
+ });
747
+ console.log('Video generated:', video.videoId);
748
+
749
+ // Download video as MP4
750
+ const mp4Url = `https://apiv2.2dai.io:800/api/v1/cdn/${video.videoId}.mp4`;
751
+ const mp4Response = await axios.get(mp4Url, {
752
+ responseType: 'arraybuffer',
753
+ headers: { 'Authorization': `Bearer 2dai_pk_your_api_key_here` }
754
+ });
755
+ fs.writeFileSync('video.mp4', mp4Response.data);
756
+ console.log('Video saved to video.mp4');
757
+
758
+ // Extract first frame as GIF
759
+ const gifUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${video.videoId}.gif`;
760
+ const gifResponse = await axios.get(gifUrl, {
761
+ responseType: 'arraybuffer',
762
+ headers: { 'Authorization': `Bearer 2dai_pk_your_api_key_here` }
763
+ });
764
+ fs.writeFileSync('thumbnail.gif', gifResponse.data);
765
+ console.log('GIF thumbnail saved to thumbnail.gif');
766
+
767
+ } catch (error) {
768
+ console.error('Error:', error.message);
769
+ }
770
+ }
771
+
772
+ generateVideoAndExtractFrame();
773
+ ```
774
+
775
+ ### Example 3: LLM with Memory and JSON Output
776
+
777
+ ```typescript
778
+ import { createClient } from '2dai-sdk';
779
+
780
+ const client = createClient('2dai_pk_your_api_key_here');
781
+
782
+ async function chatWithMemory() {
783
+ const memory: string[] = [];
784
+
785
+ // First message
786
+ const response1 = await client.generateText({
787
+ prompt: 'My name is Alice and I love painting',
788
+ system: 'You are a friendly art assistant'
789
+ });
790
+ console.log('Response 1:', response1.response);
791
+ memory.push('User: My name is Alice and I love painting');
792
+ memory.push(`Assistant: ${response1.response}`);
793
+
794
+ // Second message with memory
795
+ const response2 = await client.generateText({
796
+ prompt: 'What art supplies would you recommend for me?',
797
+ system: 'You are a friendly art assistant',
798
+ memory
799
+ });
800
+ console.log('Response 2:', response2.response);
801
+
802
+ // Get structured JSON output
803
+ const analysis = await client.generateText({
804
+ prompt: 'Analyze the conversation we just had',
805
+ system: 'Analyze conversations and return structured data',
806
+ memory,
807
+ jsonFormat: true,
808
+ jsonTemplate: {
809
+ userName: 'the user name mentioned',
810
+ interests: 'array of user interests',
811
+ recommendationsGiven: 'number of recommendations',
812
+ sentiment: 'positive/neutral/negative'
813
+ }
814
+ });
815
+ console.log('Analysis:', JSON.parse(analysis.response));
816
+ }
817
+
818
+ chatWithMemory();
819
+ ```
820
+
821
+ ### Example 4: Batch Image Generation with Rate Limit Checking
822
+
823
+ ```typescript
824
+ import { createClient, STYLES } from '2dai-sdk';
825
+
826
+ const client = createClient('2dai_pk_your_api_key_here');
827
+
828
+ async function batchGenerate(prompts: string[]) {
829
+ const results = [];
830
+
831
+ for (const prompt of prompts) {
832
+ // Check rate limits before each request
833
+ const limits = await client.checkLimits();
834
+ const imageLimit = limits.find(l => l.operation === 'image');
835
+
836
+ if (imageLimit && imageLimit.remaining.requestsPer15Min <= 0) {
837
+ console.log('Rate limit reached, waiting for reset...');
838
+ const resetTime = new Date(imageLimit.resetAt.window15Min);
839
+ const waitMs = resetTime.getTime() - Date.now();
840
+ await new Promise(resolve => setTimeout(resolve, waitMs + 1000));
841
+ }
842
+
843
+ try {
844
+ const result = await client.generateImage({
845
+ prompt,
846
+ style: STYLES.realistic.id
847
+ });
848
+ results.push({ prompt, imageId: result.imageId, success: true });
849
+ console.log(`Generated: ${prompt.substring(0, 30)}... -> ${result.imageId}`);
850
+ } catch (error: any) {
851
+ results.push({ prompt, error: error.message, success: false });
852
+ console.error(`Failed: ${prompt.substring(0, 30)}... -> ${error.message}`);
853
+ }
854
+ }
855
+
856
+ return results;
857
+ }
858
+
859
+ // Usage
860
+ const prompts = [
861
+ 'a red apple on a wooden table',
862
+ 'a blue ocean with waves',
863
+ 'a green forest with sunlight'
864
+ ];
865
+
866
+ batchGenerate(prompts).then(results => {
867
+ console.log('Batch complete:', results);
868
+ });
869
+ ```
870
+
871
+ ---
872
+
873
+ ## API Reference
874
+
875
+ ### Types
876
+
877
+ ```typescript
878
+ interface ImageGenerationOptions {
879
+ prompt: string;
880
+ style?: StylePreset | string;
881
+ format?: FormatPreset | string;
882
+ negativePrompt?: string;
883
+ seed?: number;
884
+ width?: number;
885
+ height?: number;
886
+ watermark?: string;
887
+ watermarkPosition?: WatermarkPosition;
888
+ watermarkAsTiles?: boolean;
889
+ copyright?: string;
890
+ }
891
+
892
+ interface VideoGenerationOptions {
893
+ imageId?: string;
894
+ imageBuffer?: Buffer;
895
+ duration?: number; // 1-10 seconds
896
+ fps?: number; // 8-32 fps
897
+ watermark?: string;
898
+ watermarkPosition?: WatermarkPosition;
899
+ }
900
+
901
+ interface TextGenerationOptions {
902
+ prompt: string;
903
+ system?: string;
904
+ memory?: string[];
905
+ jsonFormat?: boolean;
906
+ jsonTemplate?: { [key: string]: string };
907
+ useRandomSeed?: boolean;
908
+ askKnowledge?: {
909
+ sources?: string[];
910
+ query?: string;
911
+ };
912
+ useMarkdown?: boolean; // Enable markdown formatting in response (default: false)
913
+ imageId?: string; // Image ID from CDN for vision/image analysis
914
+ }
915
+
916
+ interface UpscaleOptions {
917
+ factor?: number; // 2, 3, or 4 (default: 2)
918
+ seed?: number; // Optional seed for reproducibility
919
+ }
920
+
921
+ interface UpscaleResult {
922
+ success: boolean;
923
+ imageId: string;
924
+ width: number;
925
+ height: number;
926
+ seed?: number;
927
+ }
928
+
929
+ interface OperationUsage {
930
+ current: {
931
+ requestsPer15Min: number;
932
+ requestsPerDay: number;
933
+ tokensPer15Min?: number; // Only for LLM operations
934
+ tokensPerDay?: number; // Only for LLM operations
935
+ };
936
+ remaining: {
937
+ requestsPer15Min: number;
938
+ requestsPerDay: number;
939
+ };
940
+ resetAt: {
941
+ window15Min: string; // ISO timestamp
942
+ daily: string; // ISO timestamp
943
+ };
944
+ }
945
+
946
+ interface APIKeySettings {
947
+ key: string;
948
+ name: string;
949
+ status: 'active' | 'suspended';
950
+ rateLimits: {
951
+ image: RateLimitConfig;
952
+ video: RateLimitConfig;
953
+ llm: LLMRateLimitConfig;
954
+ cdn: RateLimitConfig;
955
+ };
956
+ currentUsage?: {
957
+ image: OperationUsage;
958
+ video: OperationUsage;
959
+ llm: OperationUsage;
960
+ cdn: OperationUsage;
961
+ };
962
+ llmSettings?: any;
963
+ createdAt?: string;
964
+ lastUsedAt?: string;
965
+ }
966
+
967
+ type WatermarkPosition =
968
+ // Sharp gravity constants (recommended)
969
+ | 'center'
970
+ | 'northwest' | 'north' | 'northeast'
971
+ | 'west' | 'east'
972
+ | 'southwest' | 'south' | 'southeast'
973
+ // Human-readable alternatives
974
+ | 'top-left' | 'top-center' | 'top-right'
975
+ | 'middle-left' | 'middle-center' | 'middle-right'
976
+ | 'bottom-left' | 'bottom-center' | 'bottom-right';
977
+ ```
978
+
979
+ ---
980
+
981
+ ## Rate Limits
982
+
983
+ Default rate limits per API key:
984
+
985
+ | Operation | 15-Min Window | Daily Limit |
986
+ |-----------|--------------|-------------|
987
+ | Image Generation | 150 requests | 1,000 requests |
988
+ | Video Generation | 30 requests | 200 requests |
989
+ | LLM | 300 requests | 1,000 requests |
990
+ | LLM Tokens | 1,500,000 tokens | 5,000,000 tokens |
991
+ | CDN | 1,500 requests | 10,000 requests |
992
+
993
+ Custom rate limits can be configured per API key.
994
+
995
+ ---
996
+
997
+ ## Best Practices
998
+
999
+ ### 1. Check Rate Limits Before Batch Operations
1000
+
1001
+ ```typescript
1002
+ const limits = await client.checkLimits();
1003
+ const remaining = limits.find(l => l.operation === 'image')?.remaining.requestsPer15Min;
1004
+
1005
+ if (remaining && remaining < batchSize) {
1006
+ console.log(`Only ${remaining} requests available, reducing batch size`);
1007
+ }
1008
+ ```
1009
+
1010
+ ### 2. Use Appropriate Styles for Your Use Case
1011
+
1012
+ ```typescript
1013
+ import { STYLES } from '2dai-sdk';
1014
+
1015
+ // For realistic photos
1016
+ STYLES.realistic.id
1017
+
1018
+ // For artistic/creative content
1019
+ STYLES.paint.id
1020
+
1021
+ // For anime/manga content
1022
+ STYLES.anime.id
1023
+ STYLES.manga.id
1024
+
1025
+ // For cinematic shots
1026
+ STYLES.cine.id
1027
+ ```
1028
+
1029
+ ### 3. Handle Errors Gracefully
1030
+
1031
+ ```typescript
1032
+ try {
1033
+ const result = await client.generateImage({ prompt: 'test' });
1034
+ } catch (error: any) {
1035
+ if (error.message.includes('RATE_LIMIT_EXCEEDED')) {
1036
+ // Wait and retry
1037
+ await new Promise(r => setTimeout(r, 60000));
1038
+ } else if (error.message.includes('INVALID_API_KEY')) {
1039
+ // Check API key configuration
1040
+ } else {
1041
+ // Log and handle other errors
1042
+ console.error('Generation failed:', error.message);
1043
+ }
1044
+ }
1045
+ ```
1046
+
1047
+ ### 4. Use Negative Prompts for Better Results (not fully supported yet)
1048
+
1049
+ ```typescript
1050
+ const result = await client.generateImage({
1051
+ prompt: 'a beautiful portrait of a woman',
1052
+ negativePrompt: 'blurry, distorted, low quality, bad anatomy, extra limbs'
1053
+ });
1054
+ ```
1055
+
1056
+ ### 5. Cache Generated Content
1057
+
1058
+ ```typescript
1059
+ // Store image IDs for reuse
1060
+ const cache = new Map<string, string>();
1061
+
1062
+ async function getOrGenerate(prompt: string): Promise<string> {
1063
+ if (cache.has(prompt)) {
1064
+ return cache.get(prompt)!;
1065
+ }
1066
+
1067
+ const result = await client.generateImage({ prompt });
1068
+ cache.set(prompt, result.imageId);
1069
+ return result.imageId;
1070
+ }
1071
+ ```
1072
+
1073
+ ### 6. Use Seeds for Reproducibility
1074
+
1075
+ ```typescript
1076
+ // Same seed = same result (with same prompt and settings)
1077
+ const result1 = await client.generateImage({
1078
+ prompt: 'a red car',
1079
+ seed: 12345
1080
+ });
1081
+
1082
+ const result2 = await client.generateImage({
1083
+ prompt: 'a red car',
1084
+ seed: 12345
1085
+ });
1086
+
1087
+ // result1.imageId content will be identical to result2.imageId
1088
+ ```
1089
+
1090
+ ---
1091
+
1092
+ ## Troubleshooting
1093
+
1094
+ ### Common Issues
1095
+
1096
+ #### "RATE_LIMIT_EXCEEDED" Error
1097
+
1098
+ **Problem:** You've exceeded your rate limit for the current time window.
1099
+
1100
+ **Solution:**
1101
+ ```typescript
1102
+ const settings = await client.getSettings();
1103
+ const resetTime = settings.currentUsage?.image.resetAt.window15Min;
1104
+ console.log(`Rate limit resets at: ${resetTime}`);
1105
+
1106
+ // Wait for reset
1107
+ const waitMs = new Date(resetTime).getTime() - Date.now();
1108
+ await new Promise(r => setTimeout(r, waitMs + 1000));
1109
+ ```
1110
+
1111
+ #### "INVALID_API_KEY" Error
1112
+
1113
+ **Problem:** Your API key is invalid or expired.
1114
+
1115
+ **Solution:**
1116
+ - Verify your API key starts with `2dai_pk_`
1117
+ - Check that the key hasn't been revoked
1118
+ - Ensure you're using the correct environment (production vs staging)
1119
+
1120
+ #### Timeout Errors
1121
+
1122
+ **Problem:** Requests are timing out.
1123
+
1124
+ **Solution:**
1125
+ ```typescript
1126
+ const client = createClient('2dai_pk_...', {
1127
+ timeout: 600000 // Increase to 10 minutes for long operations
1128
+ });
1129
+ ```
1130
+
1131
+ #### Image Quality Issues
1132
+
1133
+ **Problem:** Generated images don't match expectations.
1134
+
1135
+ **Solution:**
1136
+ - Use more detailed, descriptive prompts (include lighting, style, mood, composition)
1137
+ - Add negative prompts to exclude unwanted elements (requires style support)
1138
+ - Try different styles for your use case (see [Available Styles](#available-styles))
1139
+ - Use specific dimensions with the `format` parameter
1140
+ - Experiment with different seeds for variations
1141
+
1142
+ **Need custom models or advanced features?** Contact us at [support@2dai.io](mailto:support@2dai.io) for:
1143
+ - Custom fine-tuned models for your brand/style
1144
+ - Higher rate limits and enterprise plans
1145
+ - Priority support and dedicated infrastructure
1146
+
1147
+ #### CDN Download Failures
1148
+
1149
+ **Problem:** Cannot download files from CDN.
1150
+
1151
+ **Solution:**
1152
+ ```typescript
1153
+ // Ensure Authorization header is included
1154
+ const response = await axios.get(cdnUrl, {
1155
+ headers: { 'Authorization': `Bearer ${apiKey}` },
1156
+ responseType: 'arraybuffer',
1157
+ timeout: 30000
1158
+ });
1159
+ ```
1160
+
1161
+ ### Debug Mode
1162
+
1163
+ Enable debug mode to see detailed request/response logs:
1164
+
1165
+ ```typescript
1166
+ const client = createClient('2dai_pk_...', {
1167
+ debug: true
1168
+ });
1169
+ ```
1170
+
1171
+ ---
1172
+
1173
+ ## Migration Guide
1174
+
1175
+ ### Breaking Changes in v1.0.0
1176
+
1177
+ **`imageUrl` and `videoUrl` have been removed from generation responses.**
1178
+
1179
+ Previously, the API returned full CDN URLs directly in the response. For security and architectural reasons, these have been removed. You now receive only file IDs and must construct CDN URLs yourself.
1180
+
1181
+ #### Before (Old Version)
1182
+ ```typescript
1183
+ const result = await client.generateImage({ prompt: "..." });
1184
+ console.log(result.imageUrl); // Full URL was provided
1185
+ // "https://cdn.2dai.com/file/xyz.jpg?watermark=..."
1186
+ ```
1187
+
1188
+ #### After (Current Version)
1189
+ ```typescript
1190
+ const result = await client.generateImage({ prompt: "..." });
1191
+ // Result now contains only imageId (no imageUrl property)
1192
+
1193
+ // Construct the CDN URL from the imageId
1194
+ const imageUrl = `https://apiv2.2dai.io:800/api/v1/cdn/${result.imageId}.jpg`;
1195
+ console.log(imageUrl);
1196
+ ```
1197
+
1198
+ **Why this change?**
1199
+ - **Security**: Prevents exposure of internal CDN structure and watermark IDs
1200
+ - **Flexibility**: Allows you to customize CDN parameters (size, watermark position, etc.)
1201
+ - **Future-proof**: CDN infrastructure can be updated without breaking client code
1202
+
1203
+ ---
1204
+
1205
+ ## Testing
1206
+
1207
+ This SDK includes comprehensive test suites covering all API functionality:
1208
+
1209
+ ```bash
1210
+ # Run all tests (REST + WebSocket)
1211
+ npm test
1212
+
1213
+ # Run REST API tests only
1214
+ npm run test:rest
1215
+
1216
+ # Run WebSocket tests only
1217
+ npm run test:ws
1218
+
1219
+ # Run tests with coverage report
1220
+ npm run test:coverage
1221
+ ```
1222
+
1223
+ ### Test Performance
1224
+
1225
+ | Test Suite | Tests | Expected Time | Notes |
1226
+ |------------|-------|---------------|-------|
1227
+ | REST API (`test:rest`) | 20 | ~6-7 min | Image/video generation dominates |
1228
+ | WebSocket (`test:ws`) | 26 | ~6 min | Similar generation overhead |
1229
+ | Full Suite (`test`) | 46 | ~12-13 min | Both suites combined |
1230
+
1231
+ **Time breakdown by operation type:**
1232
+ - Image Generation: ~25-65s per test (AI processing)
1233
+ - Video Generation: ~90s per test (longest operation)
1234
+ - Image Upscale: ~70-80s per test
1235
+ - LLM Text Generation: ~1-10s per test (fast)
1236
+ - CDN Operations: <1s per test (instant)
1237
+ - Error Handling: <1s per test (validation only)
1238
+
1239
+ ### Test Coverage
1240
+
1241
+ **REST API Tests (20 tests):**
1242
+ - Image Generation (2): Default settings, style + format
1243
+ - Image Editing (2): Basic edit, resize with dimensions
1244
+ - Image Upscale (1): AI upscale 2x
1245
+ - Video + CDN (4): Generation, MP4-to-GIF, resize
1246
+ - LLM Text (5): Simple prompt, system + memory, JSON, vision, markdown
1247
+ - Error Handling (3): Invalid API key, params, IDs
1248
+ - Settings (1): API key settings with usage
1249
+ - Frame Extraction (2): Multiple formats, resize + watermark
1250
+ - Watermarks (1): Position constants
1251
+
1252
+ **WebSocket Tests (26 tests):**
1253
+ - Connection & Auth (4): Connect, authenticate, reject invalid, ping/pong
1254
+ - Image Generation (2): Default, style + format
1255
+ - Image Editing (2): Basic edit, resize
1256
+ - Image Upscale (1): AI upscale via WS
1257
+ - Video Generation (1): 5-second video
1258
+ - GIF/CDN (3): MP4-to-GIF, resize, position
1259
+ - LLM Generation (4): Simple, system + memory + JSON, vision, markdown
1260
+ - Error Handling (3): Missing prompt, invalid ID, invalid dimensions
1261
+ - Connection Resilience (2): Graceful close, reconnection
1262
+ - Client SDK Options (4): Connection state, WS options, timeout, REST integration
1263
+
1264
+ ### Test Output
1265
+
1266
+ All tests display timestamps for performance monitoring:
1267
+ ```
1268
+ ================================================================================
1269
+ 🧪 TEST: Generate Image - Default Settings
1270
+ ⏱️ [20:23:05] Total: 0.0s | Since last: 0.0s
1271
+ ================================================================================
1272
+ ```
1273
+
1274
+ Generated files are saved to `tests/tmp/` for manual verification.
1275
+
1276
+ ---
1277
+
1278
+ ## Requirements
1279
+
1280
+ - Node.js 18 or higher
1281
+ - TypeScript 5.0+ (for TypeScript projects)
1282
+
1283
+ ---
1284
+
1285
+ ## License
1286
+
1287
+ MIT