@relazio/plugin-sdk 0.1.0 → 0.2.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 +96 -0
- package/README.md +206 -200
- package/dist/core/types.d.ts +8 -8
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -1
- package/dist/utils/builders.d.ts +147 -0
- package/dist/utils/builders.d.ts.map +1 -0
- package/dist/utils/builders.js +316 -0
- package/dist/utils/id-generator.d.ts +81 -0
- package/dist/utils/id-generator.d.ts.map +1 -0
- package/dist/utils/id-generator.js +122 -0
- package/dist/utils/result-builder.d.ts +109 -0
- package/dist/utils/result-builder.d.ts.map +1 -0
- package/dist/utils/result-builder.js +185 -0
- package/docs/builders-guide.md +520 -0
- package/docs/examples.md +238 -0
- package/docs/quick-start.md +287 -0
- package/docs/response-format.md +515 -0
- package/package.json +5 -4
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# External Plugin Response Format
|
|
2
|
+
|
|
3
|
+
This document defines the required response format for external plugins when returning entities and edges.
|
|
4
|
+
|
|
5
|
+
## Response Modes
|
|
6
|
+
|
|
7
|
+
External plugins can return results in two modes:
|
|
8
|
+
|
|
9
|
+
1. **Synchronous (sync)**: Immediate results
|
|
10
|
+
2. **Asynchronous (async)**: Background job with webhook completion
|
|
11
|
+
|
|
12
|
+
## Response Structure
|
|
13
|
+
|
|
14
|
+
### Synchronous Response
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
{
|
|
18
|
+
"async": false,
|
|
19
|
+
"result": {
|
|
20
|
+
"success": true,
|
|
21
|
+
"entities": [...], // Array of entities (format below)
|
|
22
|
+
"edges": [...], // Array of edges (format below)
|
|
23
|
+
"message": "Optional success message",
|
|
24
|
+
"metadata": {} // Optional additional metadata
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Asynchronous Response
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
{
|
|
33
|
+
"async": true,
|
|
34
|
+
"jobId": "your-plugin-job-id-123",
|
|
35
|
+
"estimatedTime": 30 // Estimated time in seconds (optional)
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Entity Format
|
|
40
|
+
|
|
41
|
+
### Structure
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
{
|
|
45
|
+
"id": string, // REQUIRED - Unique ID generated by plugin
|
|
46
|
+
"type": string, // REQUIRED - Entity type (see list below)
|
|
47
|
+
"value": string, // REQUIRED - Short entity value
|
|
48
|
+
"label": string, // OPTIONAL - Display text (default: value)
|
|
49
|
+
"metadata": object // OPTIONAL - Additional metadata
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Required Rules
|
|
54
|
+
|
|
55
|
+
1. **`id` must be unique** across the entire response
|
|
56
|
+
2. **`type` must be valid** (see list below)
|
|
57
|
+
3. **`value` must be a non-empty string**
|
|
58
|
+
4. **Use `metadata` only** - not `properties`
|
|
59
|
+
5. **IDs should be stable** and deterministic when possible
|
|
60
|
+
|
|
61
|
+
### Supported Entity Types
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
type EntityType =
|
|
65
|
+
| 'email' // Email address
|
|
66
|
+
| 'domain' // Domain name
|
|
67
|
+
| 'ip' // IP address
|
|
68
|
+
| 'person' // Person name
|
|
69
|
+
| 'username' // Username/handle
|
|
70
|
+
| 'phone' // Phone number
|
|
71
|
+
| 'organization' // Organization/company
|
|
72
|
+
| 'hash' // Hash/checksum
|
|
73
|
+
| 'credential' // Credential pair
|
|
74
|
+
| 'social' // Social media profile
|
|
75
|
+
| 'document' // Document
|
|
76
|
+
| 'note' // Text note
|
|
77
|
+
| 'image' // Image
|
|
78
|
+
| 'video' // Video
|
|
79
|
+
| 'location' // Geographic location
|
|
80
|
+
| 'wallet' // Crypto wallet
|
|
81
|
+
| 'transaction' // Transaction
|
|
82
|
+
| 'exchange' // Exchange
|
|
83
|
+
| 'url' // URL
|
|
84
|
+
| 'maps' // Map view
|
|
85
|
+
| 'custom'; // Custom entity
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Entity Examples
|
|
89
|
+
|
|
90
|
+
**IP Address**:
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"id": "ip-8.8.8.8",
|
|
94
|
+
"type": "ip",
|
|
95
|
+
"value": "8.8.8.8",
|
|
96
|
+
"label": "Google DNS",
|
|
97
|
+
"metadata": {
|
|
98
|
+
"country": "US",
|
|
99
|
+
"isp": "Google LLC"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Location**:
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"id": "loc-mountain-view-ca",
|
|
108
|
+
"type": "location",
|
|
109
|
+
"value": "Mountain View, CA",
|
|
110
|
+
"metadata": {
|
|
111
|
+
"latitude": 37.386,
|
|
112
|
+
"longitude": -122.084,
|
|
113
|
+
"country": "United States"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Organization**:
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"id": "org-google-llc",
|
|
122
|
+
"type": "organization",
|
|
123
|
+
"value": "Google LLC",
|
|
124
|
+
"metadata": {
|
|
125
|
+
"industry": "Technology",
|
|
126
|
+
"website": "google.com"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Note with Markdown**:
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"id": "note-ip-analysis-1",
|
|
135
|
+
"type": "note",
|
|
136
|
+
"value": "IP Analysis",
|
|
137
|
+
"label": "## IP: 8.8.8.8\n\n**Location**: Mountain View, CA\n**ISP**: Google LLC",
|
|
138
|
+
"metadata": {
|
|
139
|
+
"tags": ["ip-lookup", "analysis"],
|
|
140
|
+
"format": "markdown"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Edge Format
|
|
146
|
+
|
|
147
|
+
### Structure
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
{
|
|
151
|
+
"id": string, // REQUIRED - Unique edge ID
|
|
152
|
+
"sourceId": string, // REQUIRED - Source entity ID
|
|
153
|
+
"targetId": string, // REQUIRED - Target entity ID
|
|
154
|
+
"label": string, // REQUIRED - Visible label
|
|
155
|
+
"relationship": string, // OPTIONAL - Relationship type (default: label)
|
|
156
|
+
"metadata": object // OPTIONAL - Additional metadata
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Required Rules
|
|
161
|
+
|
|
162
|
+
1. **`id` must be unique** across the entire response
|
|
163
|
+
2. **`sourceId` must match an entity ID** (or input entity ID)
|
|
164
|
+
3. **`targetId` must match an entity ID** returned
|
|
165
|
+
4. **Use `sourceId/targetId` only** - not `from/to`
|
|
166
|
+
5. **`label` is required** and visible in the graph
|
|
167
|
+
|
|
168
|
+
### Edge Examples
|
|
169
|
+
|
|
170
|
+
**IP to Location**:
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"id": "edge-ip-to-location-1",
|
|
174
|
+
"sourceId": "input-node-id",
|
|
175
|
+
"targetId": "loc-mountain-view-ca",
|
|
176
|
+
"label": "located in",
|
|
177
|
+
"relationship": "geolocation"
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**IP to Organization**:
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"id": "edge-ip-to-org-1",
|
|
185
|
+
"sourceId": "input-node-id",
|
|
186
|
+
"targetId": "org-google-llc",
|
|
187
|
+
"label": "assigned by",
|
|
188
|
+
"relationship": "isp_assignment"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Complete Example
|
|
193
|
+
|
|
194
|
+
### Input Transform
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"transformId": "lookup-ip",
|
|
199
|
+
"input": {
|
|
200
|
+
"entity": {
|
|
201
|
+
"id": "node-abc123",
|
|
202
|
+
"type": "ip",
|
|
203
|
+
"value": "8.8.8.8"
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
"callbackUrl": "https://app.example.com/api/webhooks/transforms/job-xyz?token=..."
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Synchronous Output
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"async": false,
|
|
215
|
+
"result": {
|
|
216
|
+
"success": true,
|
|
217
|
+
"entities": [
|
|
218
|
+
{
|
|
219
|
+
"id": "loc-mountain-view-ca",
|
|
220
|
+
"type": "location",
|
|
221
|
+
"value": "Mountain View, CA",
|
|
222
|
+
"metadata": {
|
|
223
|
+
"latitude": 37.386,
|
|
224
|
+
"longitude": -122.084,
|
|
225
|
+
"country": "United States"
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"id": "org-google-llc",
|
|
230
|
+
"type": "organization",
|
|
231
|
+
"value": "Google LLC",
|
|
232
|
+
"metadata": {
|
|
233
|
+
"type": "isp",
|
|
234
|
+
"asn": "AS15169"
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
],
|
|
238
|
+
"edges": [
|
|
239
|
+
{
|
|
240
|
+
"id": "edge-ip-to-loc",
|
|
241
|
+
"sourceId": "node-abc123",
|
|
242
|
+
"targetId": "loc-mountain-view-ca",
|
|
243
|
+
"label": "located in",
|
|
244
|
+
"relationship": "geolocation"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"id": "edge-ip-to-org",
|
|
248
|
+
"sourceId": "node-abc123",
|
|
249
|
+
"targetId": "org-google-llc",
|
|
250
|
+
"label": "assigned by",
|
|
251
|
+
"relationship": "isp_assignment"
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
"message": "Successfully analyzed IP 8.8.8.8"
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Asynchronous Output
|
|
260
|
+
|
|
261
|
+
```json
|
|
262
|
+
{
|
|
263
|
+
"async": true,
|
|
264
|
+
"jobId": "scan-job-12345",
|
|
265
|
+
"estimatedTime": 60
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Webhook for Async Jobs
|
|
270
|
+
|
|
271
|
+
When an async job completes, the plugin must send a webhook to the provided callback URL.
|
|
272
|
+
|
|
273
|
+
### Required Headers
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
Content-Type: application/json
|
|
277
|
+
X-Plugin-Signature: sha256={hmac_signature}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The HMAC signature is calculated on the JSON body using the plugin's webhook secret.
|
|
281
|
+
|
|
282
|
+
### Webhook Body - Completion
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
{
|
|
286
|
+
"jobId": "scan-job-12345",
|
|
287
|
+
"status": "completed",
|
|
288
|
+
"progress": 100,
|
|
289
|
+
"progressMessage": "Scan complete",
|
|
290
|
+
"result": {
|
|
291
|
+
"success": true,
|
|
292
|
+
"entities": [
|
|
293
|
+
{
|
|
294
|
+
"id": "...",
|
|
295
|
+
"type": "...",
|
|
296
|
+
"value": "..."
|
|
297
|
+
}
|
|
298
|
+
],
|
|
299
|
+
"edges": [
|
|
300
|
+
{
|
|
301
|
+
"id": "...",
|
|
302
|
+
"sourceId": "...",
|
|
303
|
+
"targetId": "...",
|
|
304
|
+
"label": "..."
|
|
305
|
+
}
|
|
306
|
+
]
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Webhook Body - Progress Update
|
|
312
|
+
|
|
313
|
+
```json
|
|
314
|
+
{
|
|
315
|
+
"jobId": "scan-job-12345",
|
|
316
|
+
"status": "processing",
|
|
317
|
+
"progress": 45,
|
|
318
|
+
"progressMessage": "Scanning ports..."
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Webhook Body - Failure
|
|
323
|
+
|
|
324
|
+
```json
|
|
325
|
+
{
|
|
326
|
+
"jobId": "scan-job-12345",
|
|
327
|
+
"status": "failed",
|
|
328
|
+
"error": "Connection timeout"
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## ID Generation Best Practices
|
|
333
|
+
|
|
334
|
+
### Deterministic IDs
|
|
335
|
+
|
|
336
|
+
Use predictable, deterministic formats:
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
const entityId = `${type}-${hashValue(value)}`;
|
|
340
|
+
// Example: "location-a3f42bc1"
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Unique IDs
|
|
344
|
+
|
|
345
|
+
Ensure every entity has a unique ID:
|
|
346
|
+
|
|
347
|
+
```javascript
|
|
348
|
+
const entityId = `${type}-${normalizedValue}-${Date.now()}`;
|
|
349
|
+
const edgeId = `edge-${sourceId}-${targetId}-${Date.now()}`;
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Clear Prefixes
|
|
353
|
+
|
|
354
|
+
Use prefixes to identify the type:
|
|
355
|
+
|
|
356
|
+
```javascript
|
|
357
|
+
"ip-8.8.8.8"
|
|
358
|
+
"loc-nyc-us"
|
|
359
|
+
"org-google"
|
|
360
|
+
"note-analysis-1"
|
|
361
|
+
"edge-ip-to-loc-1"
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Input Entity ID
|
|
365
|
+
|
|
366
|
+
Always use the input entity ID in edges as `sourceId`:
|
|
367
|
+
|
|
368
|
+
```javascript
|
|
369
|
+
// Input contains:
|
|
370
|
+
{
|
|
371
|
+
"entity": {
|
|
372
|
+
"id": "node-abc123", // Use this as sourceId
|
|
373
|
+
"type": "ip",
|
|
374
|
+
"value": "8.8.8.8"
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Edges must use this ID:
|
|
379
|
+
{
|
|
380
|
+
"id": "edge-1",
|
|
381
|
+
"sourceId": "node-abc123", // Input entity ID
|
|
382
|
+
"targetId": "loc-nyc",
|
|
383
|
+
"label": "located in"
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## SDK Example
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
import crypto from 'crypto';
|
|
391
|
+
|
|
392
|
+
function generateEntityId(type: string, value: string): string {
|
|
393
|
+
const hash = crypto.createHash('md5')
|
|
394
|
+
.update(value.toLowerCase().trim())
|
|
395
|
+
.digest('hex')
|
|
396
|
+
.substring(0, 8);
|
|
397
|
+
return `${type}-${hash}`;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function generateEdgeId(sourceId: string, targetId: string): string {
|
|
401
|
+
const hash = crypto.createHash('md5')
|
|
402
|
+
.update(`${sourceId}-${targetId}`)
|
|
403
|
+
.digest('hex')
|
|
404
|
+
.substring(0, 8);
|
|
405
|
+
return `edge-${hash}`;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async function executeTransform(input) {
|
|
409
|
+
const { entity } = input;
|
|
410
|
+
|
|
411
|
+
const lookupResult = await lookupIP(entity.value);
|
|
412
|
+
|
|
413
|
+
const entities = [
|
|
414
|
+
{
|
|
415
|
+
id: generateEntityId('location', lookupResult.city),
|
|
416
|
+
type: 'location',
|
|
417
|
+
value: lookupResult.city,
|
|
418
|
+
metadata: {
|
|
419
|
+
latitude: lookupResult.lat,
|
|
420
|
+
longitude: lookupResult.lon
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
id: generateEntityId('organization', lookupResult.isp),
|
|
425
|
+
type: 'organization',
|
|
426
|
+
value: lookupResult.isp,
|
|
427
|
+
metadata: {
|
|
428
|
+
asn: lookupResult.asn
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
];
|
|
432
|
+
|
|
433
|
+
const edges = [
|
|
434
|
+
{
|
|
435
|
+
id: generateEdgeId(entity.id, entities[0].id),
|
|
436
|
+
sourceId: entity.id,
|
|
437
|
+
targetId: entities[0].id,
|
|
438
|
+
label: 'located in',
|
|
439
|
+
relationship: 'geolocation'
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
id: generateEdgeId(entity.id, entities[1].id),
|
|
443
|
+
sourceId: entity.id,
|
|
444
|
+
targetId: entities[1].id,
|
|
445
|
+
label: 'assigned by',
|
|
446
|
+
relationship: 'isp_assignment'
|
|
447
|
+
}
|
|
448
|
+
];
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
async: false,
|
|
452
|
+
result: {
|
|
453
|
+
success: true,
|
|
454
|
+
entities,
|
|
455
|
+
edges,
|
|
456
|
+
message: `Analyzed IP ${entity.value}`
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Validation
|
|
463
|
+
|
|
464
|
+
The Relazio system validates responses and rejects non-compliant ones.
|
|
465
|
+
|
|
466
|
+
### Applied Validations
|
|
467
|
+
|
|
468
|
+
- Each entity must have `id`, `type`, `value`
|
|
469
|
+
- Each edge must have `id`, `sourceId`, `targetId`, `label`
|
|
470
|
+
- `type` must be a valid type
|
|
471
|
+
- All IDs must be unique
|
|
472
|
+
- Edge `targetId` must correspond to a returned entity
|
|
473
|
+
- Edge `sourceId` must be the input entity ID or a returned entity
|
|
474
|
+
|
|
475
|
+
### Common Errors
|
|
476
|
+
|
|
477
|
+
```
|
|
478
|
+
"Entity missing required field: id"
|
|
479
|
+
"Edge missing required field: sourceId"
|
|
480
|
+
"Invalid entity type: unknown"
|
|
481
|
+
"Edge references non-existent entity: xyz"
|
|
482
|
+
"Duplicate entity ID: loc-1"
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## HMAC Security
|
|
486
|
+
|
|
487
|
+
For webhooks, calculate the HMAC signature:
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
import crypto from 'crypto';
|
|
491
|
+
|
|
492
|
+
function generateHMACSignature(payload: string, secret: string): string {
|
|
493
|
+
return crypto
|
|
494
|
+
.createHmac('sha256', secret)
|
|
495
|
+
.update(payload)
|
|
496
|
+
.digest('hex');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Usage:
|
|
500
|
+
const body = JSON.stringify(webhookPayload);
|
|
501
|
+
const signature = generateHMACSignature(body, webhookSecret);
|
|
502
|
+
|
|
503
|
+
// Send with header:
|
|
504
|
+
headers: {
|
|
505
|
+
'Content-Type': 'application/json',
|
|
506
|
+
'X-Plugin-Signature': `sha256=${signature}`
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## References
|
|
511
|
+
|
|
512
|
+
- [Quick Start Guide](./quick-start.md)
|
|
513
|
+
- [Builders Guide](./builders-guide.md)
|
|
514
|
+
- [Examples Documentation](./examples.md)
|
|
515
|
+
- [Main README](../README.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relazio/plugin-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Official SDK for building external plugins for Relazio",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,11 +22,13 @@
|
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|
|
25
|
-
"url": "https://github.com/relazio/plugin-sdk.git"
|
|
25
|
+
"url": "git+https://github.com/relazio/plugin-sdk.git"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"dist",
|
|
29
|
+
"docs",
|
|
29
30
|
"README.md",
|
|
31
|
+
"CHANGELOG.md",
|
|
30
32
|
"LICENSE"
|
|
31
33
|
],
|
|
32
34
|
"publishConfig": {
|
|
@@ -43,10 +45,9 @@
|
|
|
43
45
|
"@typescript-eslint/parser": "^6.17.0",
|
|
44
46
|
"eslint": "^8.56.0",
|
|
45
47
|
"typescript": "^5.3.3",
|
|
46
|
-
"vitest": "^
|
|
48
|
+
"vitest": "^4.0.16"
|
|
47
49
|
},
|
|
48
50
|
"engines": {
|
|
49
51
|
"node": ">=18.0.0"
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
|
-
|