@slflows/sdk 0.0.3 → 0.0.5
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/dist/v1/index.d.ts +693 -3
- package/package.json +3 -3
package/dist/v1/index.d.ts
CHANGED
|
@@ -1,15 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main schema interface for defining a Flows app.
|
|
3
|
+
*
|
|
4
|
+
* An app consists of blocks (entities) that can process events, manage state,
|
|
5
|
+
* expose HTTP endpoints, and handle scheduled tasks. Apps are the fundamental
|
|
6
|
+
* unit of deployment in Flows.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* export default defineApp({
|
|
11
|
+
* name: "Data Processor",
|
|
12
|
+
* blocks: {
|
|
13
|
+
* processor: {
|
|
14
|
+
* name: "Process Data",
|
|
15
|
+
* description: "Transforms incoming data",
|
|
16
|
+
* inputs: {
|
|
17
|
+
* data: {
|
|
18
|
+
* name: "Input Data",
|
|
19
|
+
* onEvent: async (input) => {
|
|
20
|
+
* // Process the event
|
|
21
|
+
* await events.emit(processedData);
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* },
|
|
25
|
+
* outputs: {
|
|
26
|
+
* processed: {
|
|
27
|
+
* name: "Processed Data",
|
|
28
|
+
* default: true
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
1
36
|
interface AppSchema {
|
|
37
|
+
/** Display name for the app */
|
|
2
38
|
name?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Instructions shown to users when installing the app.
|
|
41
|
+
* Supports Markdown formatting.
|
|
42
|
+
*/
|
|
3
43
|
installationInstructions?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Configuration fields for the app. These are set during installation
|
|
46
|
+
* and available to all blocks within the app.
|
|
47
|
+
*/
|
|
4
48
|
config?: Record<string, AppConfigField>;
|
|
49
|
+
/**
|
|
50
|
+
* Signals exported by the app.
|
|
51
|
+
* These are available to all blocks within the app.
|
|
52
|
+
*/
|
|
53
|
+
signals?: Record<string, AppSignal>;
|
|
54
|
+
/**
|
|
55
|
+
* Lifecycle hook called when the app needs to synchronize its state.
|
|
56
|
+
* Use this to provision resources, validate configuration, or perform setup.
|
|
57
|
+
*/
|
|
5
58
|
onSync?: (input: AppInput) => Promise<AppLifecycleCallbackOutput>;
|
|
59
|
+
/**
|
|
60
|
+
* Lifecycle hook called when the app is being drained (shut down).
|
|
61
|
+
* Use this to clean up resources, save state, or perform teardown.
|
|
62
|
+
*/
|
|
6
63
|
onDrain?: (input: AppInput) => Promise<AppLifecycleCallbackOutput>;
|
|
64
|
+
/** Custom status description shown when the app is in draft mode */
|
|
7
65
|
draftCustomStatusDescription?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Handler for internal messages sent to the app from blocks.
|
|
68
|
+
* Used for app-level coordination and communication.
|
|
69
|
+
*/
|
|
8
70
|
onInternalMessage?: (input: AppOnInternalMessageInput) => Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Map of block definitions. Each block becomes an entity that can be
|
|
73
|
+
* placed on the flow canvas. Key is the block type ID.
|
|
74
|
+
*/
|
|
9
75
|
blocks: Record<string, AppBlock>;
|
|
76
|
+
/** HTTP server configuration for app-level endpoints */
|
|
10
77
|
http?: AppHTTPComponent;
|
|
78
|
+
/** UI component configuration for custom app interfaces */
|
|
11
79
|
ui?: AppUIComponent;
|
|
80
|
+
/**
|
|
81
|
+
* Declarative schedules that trigger at app level.
|
|
82
|
+
* Use for periodic maintenance, data synchronization, etc.
|
|
83
|
+
*/
|
|
12
84
|
schedules?: Record<string, AppSchedule>;
|
|
85
|
+
/**
|
|
86
|
+
* Handler for imperative timers set by the app.
|
|
87
|
+
* Called when a timer created with timers.set() expires.
|
|
88
|
+
*/
|
|
13
89
|
onTimer?: (input: AppOnTimerInput) => Promise<void>;
|
|
14
90
|
}
|
|
15
91
|
interface AppUIComponent {
|
|
@@ -38,6 +114,12 @@ interface AppConfigField {
|
|
|
38
114
|
fixed?: boolean;
|
|
39
115
|
required: boolean;
|
|
40
116
|
default?: unknown;
|
|
117
|
+
sensitive?: boolean;
|
|
118
|
+
}
|
|
119
|
+
interface AppSignal {
|
|
120
|
+
name: string;
|
|
121
|
+
description: string;
|
|
122
|
+
sensitive?: boolean;
|
|
41
123
|
}
|
|
42
124
|
interface AppHTTPComponent {
|
|
43
125
|
onRequest: (input: AppOnHTTPRequestInput) => Promise<void>;
|
|
@@ -86,6 +168,7 @@ interface AppBlockConfigField {
|
|
|
86
168
|
required: boolean;
|
|
87
169
|
default?: unknown;
|
|
88
170
|
fieldKey?: string;
|
|
171
|
+
sensitive?: boolean;
|
|
89
172
|
}
|
|
90
173
|
interface AppBlockComponentInput {
|
|
91
174
|
name?: string;
|
|
@@ -117,6 +200,7 @@ interface AppBlockUIComponent {
|
|
|
117
200
|
interface AppBlockSignal {
|
|
118
201
|
name: string;
|
|
119
202
|
description: string;
|
|
203
|
+
sensitive?: boolean;
|
|
120
204
|
}
|
|
121
205
|
interface AppBlockSchedule {
|
|
122
206
|
description?: string;
|
|
@@ -130,27 +214,61 @@ interface EntityView {
|
|
|
130
214
|
type: EntityViewType;
|
|
131
215
|
}
|
|
132
216
|
type EntityViewType = "default" | "fullScreen" | "regular";
|
|
217
|
+
/**
|
|
218
|
+
* Input context provided to app-level handlers.
|
|
219
|
+
* Contains app configuration and runtime information.
|
|
220
|
+
*/
|
|
133
221
|
interface AppInput {
|
|
222
|
+
/** App installation context and configuration */
|
|
134
223
|
app: AppContext;
|
|
135
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* App runtime context containing configuration and endpoints.
|
|
227
|
+
* Available in all app-level handlers (onSync, onDrain, onInternalMessage, etc.).
|
|
228
|
+
*/
|
|
136
229
|
interface AppContext {
|
|
230
|
+
/** App configuration values set during installation */
|
|
137
231
|
config: Record<string, any>;
|
|
138
|
-
|
|
232
|
+
/** Current status of the app installation */
|
|
233
|
+
status: "draft" | "in_progress" | "ready" | "failed" | "draining" | "draining_failed" | "drained";
|
|
234
|
+
/** HTTP endpoint information for this app */
|
|
139
235
|
http: AppHTTPEndpoint;
|
|
236
|
+
/** URL for managing this app installation */
|
|
140
237
|
installationUrl: string;
|
|
238
|
+
/** Signals exported by this app */
|
|
239
|
+
signals: Record<string, any>;
|
|
141
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* HTTP endpoint information for app-level HTTP handlers.
|
|
243
|
+
*/
|
|
142
244
|
interface AppHTTPEndpoint {
|
|
245
|
+
/** Base URL for this app's HTTP endpoints */
|
|
143
246
|
url: string;
|
|
144
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Input context provided to block-level handlers.
|
|
250
|
+
* Extends AppInput with block-specific context.
|
|
251
|
+
*/
|
|
145
252
|
interface EntityInput extends AppInput {
|
|
253
|
+
/** Block (entity) instance context and configuration */
|
|
146
254
|
block: EntityContext;
|
|
147
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Block (entity) runtime context containing configuration and state.
|
|
258
|
+
* Available in all block-level handlers (onEvent, onSync, onDrain, etc.).
|
|
259
|
+
*/
|
|
148
260
|
interface EntityContext {
|
|
261
|
+
/** Unique identifier for this block instance */
|
|
149
262
|
id: string;
|
|
263
|
+
/** Display name of this block instance */
|
|
150
264
|
name: string;
|
|
265
|
+
/** Description of this block instance */
|
|
151
266
|
description: string;
|
|
267
|
+
/** Block configuration values set in the flow */
|
|
152
268
|
config: Record<string, any>;
|
|
269
|
+
/** Lifecycle state information (null if block has no lifecycle) */
|
|
153
270
|
lifecycle: EntityLifecycleComponent | null;
|
|
271
|
+
/** HTTP endpoint information (null if block has no HTTP handler) */
|
|
154
272
|
http: EntityHTTPEndpoint | null;
|
|
155
273
|
}
|
|
156
274
|
interface EntityLifecycleComponent {
|
|
@@ -161,14 +279,31 @@ type EntityLifecycleStatus = "draft" | "in_progress" | "ready" | "drifted" | "fa
|
|
|
161
279
|
interface EntityHTTPEndpoint {
|
|
162
280
|
url: string;
|
|
163
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Input context provided to event handlers (onEvent).
|
|
284
|
+
* Extends EntityInput with event-specific information.
|
|
285
|
+
*/
|
|
164
286
|
interface EventInput extends EntityInput {
|
|
287
|
+
/** Event data and metadata */
|
|
165
288
|
event: EventContext;
|
|
166
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* Event context containing the event data and metadata.
|
|
292
|
+
* Available in all onEvent handlers when processing incoming events.
|
|
293
|
+
*/
|
|
167
294
|
interface EventContext {
|
|
295
|
+
/** Unique identifier for this event */
|
|
168
296
|
id: string;
|
|
297
|
+
/** Configuration values for the specific input that received this event */
|
|
169
298
|
inputConfig: Record<string, any>;
|
|
299
|
+
/**
|
|
300
|
+
* Echo information for request-response patterns.
|
|
301
|
+
* Present when this event was emitted with echo: true.
|
|
302
|
+
*/
|
|
170
303
|
echo?: {
|
|
304
|
+
/** The event body that was echoed */
|
|
171
305
|
body: any;
|
|
306
|
+
/** The output key where the original event was emitted */
|
|
172
307
|
outputKey: string;
|
|
173
308
|
};
|
|
174
309
|
}
|
|
@@ -259,68 +394,209 @@ interface UIRequest {
|
|
|
259
394
|
payload?: any;
|
|
260
395
|
}
|
|
261
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Key-value pair for storage operations.
|
|
399
|
+
* Supports TTL (time-to-live) and optimistic locking for safe concurrent access.
|
|
400
|
+
*/
|
|
262
401
|
interface KVPair {
|
|
402
|
+
/** The storage key */
|
|
263
403
|
key: string;
|
|
404
|
+
/**
|
|
405
|
+
* The stored value. May be undefined (key doesn't exist),
|
|
406
|
+
* null (explicitly set to null), or any other value.
|
|
407
|
+
*/
|
|
264
408
|
value?: any;
|
|
409
|
+
/** Unix timestamp of when this key was last updated */
|
|
265
410
|
updatedAt?: number;
|
|
411
|
+
/** Time-to-live in seconds. If set, key will expire after this duration */
|
|
266
412
|
ttl?: number;
|
|
413
|
+
/** Optional lock information for atomic operations */
|
|
267
414
|
lock?: {
|
|
415
|
+
/** Unique lock identifier */
|
|
268
416
|
id: string;
|
|
417
|
+
/** Lock timeout in seconds */
|
|
269
418
|
timeout?: number;
|
|
270
419
|
};
|
|
271
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* Input parameters for listing key-value pairs with optional pagination.
|
|
423
|
+
* Used with kv.app.list() and kv.block.list() to retrieve multiple keys.
|
|
424
|
+
*/
|
|
272
425
|
interface KVListInput {
|
|
426
|
+
/** Prefix to filter keys (e.g., "user:" to get all user-related keys) */
|
|
273
427
|
keyPrefix: string;
|
|
428
|
+
/** Starting key for pagination (from previous nextStartingKey) */
|
|
274
429
|
startingKey?: string;
|
|
275
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* Output from key-value list operations with pagination support.
|
|
433
|
+
* Contains the retrieved key-value pairs and pagination information.
|
|
434
|
+
*/
|
|
276
435
|
interface KVListOutput {
|
|
436
|
+
/** Array of key-value pairs matching the prefix */
|
|
277
437
|
pairs: Array<KVPair>;
|
|
438
|
+
/** Next starting key for pagination (undefined if no more results) */
|
|
278
439
|
nextStartingKey?: string;
|
|
279
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Input parameters for listing blocks within the same app installation.
|
|
443
|
+
* Used to discover other blocks for coordination and messaging.
|
|
444
|
+
*/
|
|
280
445
|
interface ListBlocksInput {
|
|
446
|
+
/** Filter by specific block type IDs (omit to list all blocks) */
|
|
281
447
|
typeIds?: string[];
|
|
282
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* Output from blocks.list() operation containing discovered blocks.
|
|
451
|
+
* Provides information about other blocks in the same app installation.
|
|
452
|
+
*/
|
|
283
453
|
interface ListBlocksOutput {
|
|
454
|
+
/** Array of blocks found in the app installation */
|
|
284
455
|
blocks: {
|
|
456
|
+
/** Unique identifier of the block instance */
|
|
285
457
|
id: string;
|
|
458
|
+
/** Type ID of the block (from the app's block definitions) */
|
|
286
459
|
typeId: string;
|
|
460
|
+
/** Current configuration values of the block */
|
|
287
461
|
config: Record<string, any>;
|
|
288
462
|
}[];
|
|
289
463
|
}
|
|
290
|
-
|
|
464
|
+
/**
|
|
465
|
+
* Options for emitting events to downstream blocks.
|
|
466
|
+
*/
|
|
291
467
|
interface EmitOptions {
|
|
468
|
+
/** The output key to emit on (defaults to default output) */
|
|
292
469
|
outputKey?: string;
|
|
470
|
+
/** Parent event ID for lineage tracking */
|
|
293
471
|
parentEventId?: string;
|
|
472
|
+
/** Additional parent event IDs for complex lineage */
|
|
294
473
|
secondaryParentEventIds?: string[];
|
|
474
|
+
/**
|
|
475
|
+
* Whether to echo this event back for request-response patterns.
|
|
476
|
+
*
|
|
477
|
+
* When enabled, if this block later receives an event that has one of its own
|
|
478
|
+
* events in its ancestry, the original event will be automatically made available
|
|
479
|
+
* in input.event.echo. This eliminates the need for manual tracking of
|
|
480
|
+
* request-response relationships.
|
|
481
|
+
*
|
|
482
|
+
* The echo contains:
|
|
483
|
+
* - body: The original event payload
|
|
484
|
+
* - outputKey: The output ID where the original event was emitted
|
|
485
|
+
*
|
|
486
|
+
* Particularly useful for:
|
|
487
|
+
* - HTTP request/response patterns
|
|
488
|
+
* - Multi-step workflows where responses need to be matched to original requests
|
|
489
|
+
* - Any scenario where maintaining context across multiple events is important
|
|
490
|
+
*/
|
|
295
491
|
echo?: boolean;
|
|
492
|
+
/** Pending event ID to complete when emitting */
|
|
296
493
|
complete?: string;
|
|
297
494
|
}
|
|
495
|
+
/**
|
|
496
|
+
* Options for creating a pending event.
|
|
497
|
+
*/
|
|
298
498
|
interface CreatePendingEventOptions {
|
|
499
|
+
/** Optional predicted event payload */
|
|
299
500
|
event?: Record<string, any>;
|
|
501
|
+
/** Which output this will emit on (optional) */
|
|
300
502
|
outputId?: string;
|
|
503
|
+
/** Parent event (auto-populated in handlers) */
|
|
301
504
|
parentEventId?: string;
|
|
505
|
+
/** Additional parent event IDs for complex lineage */
|
|
302
506
|
secondaryParentEventIds?: string[];
|
|
507
|
+
/** User-readable status message */
|
|
303
508
|
statusDescription: string | null;
|
|
304
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Options for updating an existing pending event.
|
|
512
|
+
* Used with updatePendingEvent() to modify pending event data or status.
|
|
513
|
+
*/
|
|
305
514
|
interface UpdatePendingEventOptions {
|
|
515
|
+
/** Updated event data (replaces previous event data) */
|
|
306
516
|
event?: Record<string, any>;
|
|
517
|
+
/** Updated output ID to emit on when completed */
|
|
307
518
|
outputId?: string;
|
|
519
|
+
/** Updated parent event ID for lineage tracking */
|
|
308
520
|
parentEventId?: string;
|
|
521
|
+
/** Updated additional parent event IDs */
|
|
309
522
|
secondaryParentEventIds?: string[];
|
|
523
|
+
/** Updated status description shown while pending */
|
|
310
524
|
statusDescription?: string | null;
|
|
311
525
|
}
|
|
526
|
+
/**
|
|
527
|
+
* Options for creating user prompts in approval workflows.
|
|
528
|
+
* Used with createPrompt() to request manual user input or approval.
|
|
529
|
+
*/
|
|
312
530
|
interface PromptOptions {
|
|
531
|
+
/** Redirect configuration for the approval interface */
|
|
313
532
|
redirect: {
|
|
533
|
+
/** URL where users will be redirected for approval */
|
|
314
534
|
url: string;
|
|
535
|
+
/** HTTP method for the approval request */
|
|
315
536
|
method: "GET" | "POST";
|
|
537
|
+
/** Form fields to include with POST requests */
|
|
316
538
|
formFields?: Record<string, string>;
|
|
317
539
|
};
|
|
318
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* Metadata about the current function invocation.
|
|
543
|
+
* Provides context for logging, debugging, and correlation.
|
|
544
|
+
*/
|
|
319
545
|
interface InvocationMetadata {
|
|
546
|
+
/** Unique identifier for this invocation */
|
|
320
547
|
invocationId: string;
|
|
548
|
+
/** Unix timestamp when the invocation started */
|
|
321
549
|
timestamp: number;
|
|
322
550
|
}
|
|
551
|
+
/**
|
|
552
|
+
* Gets metadata about the current function invocation.
|
|
553
|
+
*
|
|
554
|
+
* Provides information about when and how the current handler was invoked.
|
|
555
|
+
* Useful for logging, debugging, and correlation across distributed operations.
|
|
556
|
+
*
|
|
557
|
+
* @returns Promise resolving to invocation metadata
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* ```typescript
|
|
561
|
+
* const metadata = await getInvocationMetadata();
|
|
562
|
+
* console.log(`Invocation ${metadata.invocationId} started at ${new Date(metadata.timestamp)}`);
|
|
563
|
+
*
|
|
564
|
+
* // Use in logging for correlation
|
|
565
|
+
* console.log(`[${metadata.invocationId}] Processing event:`, input.event.body);
|
|
566
|
+
* ```
|
|
567
|
+
*/
|
|
323
568
|
declare const getInvocationMetadata: () => Promise<InvocationMetadata>;
|
|
569
|
+
/**
|
|
570
|
+
* Events service for emitting and managing events within flows.
|
|
571
|
+
*
|
|
572
|
+
* Events are the primary way blocks communicate with each other.
|
|
573
|
+
* The events service provides both immediate emission and pending event management
|
|
574
|
+
* for handling long-running operations.
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* ```typescript
|
|
578
|
+
* // Simple event emission
|
|
579
|
+
* await events.emit({ message: "Hello World" });
|
|
580
|
+
*
|
|
581
|
+
* // Emit to specific output
|
|
582
|
+
* await events.emit(data, { outputId: "success" });
|
|
583
|
+
*
|
|
584
|
+
* // Create pending event for long operation
|
|
585
|
+
* const pendingId = await events.createPending({
|
|
586
|
+
* statusDescription: "Processing data...",
|
|
587
|
+
* event: { progress: 0 }
|
|
588
|
+
* });
|
|
589
|
+
*
|
|
590
|
+
* // Update progress
|
|
591
|
+
* await events.updatePending(pendingId, {
|
|
592
|
+
* event: { progress: 50 },
|
|
593
|
+
* statusDescription: "Half complete..."
|
|
594
|
+
* });
|
|
595
|
+
*
|
|
596
|
+
* // Complete the operation
|
|
597
|
+
* await events.completePending(pendingId);
|
|
598
|
+
* ```
|
|
599
|
+
*/
|
|
324
600
|
declare const events: {
|
|
325
601
|
emit: (event: any, options?: EmitOptions) => Promise<void>;
|
|
326
602
|
createPending: (options: CreatePendingEventOptions) => Promise<string>;
|
|
@@ -328,7 +604,112 @@ declare const events: {
|
|
|
328
604
|
cancelPending: (pendingEventId: string, reason: string) => Promise<void>;
|
|
329
605
|
completePending: (pendingEventId: string) => Promise<void>;
|
|
330
606
|
};
|
|
607
|
+
/**
|
|
608
|
+
* Key-Value storage service for persisting data at block and app level.
|
|
609
|
+
*
|
|
610
|
+
* KV storage provides persistent, scoped storage with TTL support and optimistic locking.
|
|
611
|
+
* Block-level storage is isolated per block instance, while app-level storage is shared
|
|
612
|
+
* across all blocks in the app installation.
|
|
613
|
+
*
|
|
614
|
+
* ## Locking Mechanism
|
|
615
|
+
*
|
|
616
|
+
* The KV service includes a robust locking system for coordinating concurrent access:
|
|
617
|
+
*
|
|
618
|
+
* - **Lock Acquisition**: Use unique lock IDs to acquire exclusive access to keys
|
|
619
|
+
* - **Timeout Management**: Locks automatically expire after specified duration
|
|
620
|
+
* - **Lock Ownership**: Only the exact lock ID can modify or release the lock
|
|
621
|
+
* - **Graceful Failures**: Lock conflicts return `false` instead of throwing errors
|
|
622
|
+
* - **Automatic Cleanup**: Expired locks are automatically cleared
|
|
623
|
+
* - **Database-Level Safety**: Uses PostgreSQL row-level locks for atomicity
|
|
624
|
+
*
|
|
625
|
+
* ## Lock Behavior Details
|
|
626
|
+
*
|
|
627
|
+
* - **Conflict Resolution**: If a key is already locked by a different lock ID,
|
|
628
|
+
* operations return `success: false` rather than throwing errors
|
|
629
|
+
* - **Lock Expiration**: Locks with timeouts are automatically considered expired
|
|
630
|
+
* when `lock_timeout < NOW()`, allowing new acquisitions
|
|
631
|
+
* - **Lock Renewal**: Extend lock duration using `renewLock()` with the same lock ID
|
|
632
|
+
* - **Permanent Locks**: Omit timeout for locks that persist until explicitly released
|
|
633
|
+
*
|
|
634
|
+
* @example
|
|
635
|
+
* ```typescript
|
|
636
|
+
* // Block-level storage (scoped to this block)
|
|
637
|
+
* await kv.block.set({
|
|
638
|
+
* key: "counter",
|
|
639
|
+
* value: 42,
|
|
640
|
+
* ttl: 3600 // 1 hour TTL
|
|
641
|
+
* });
|
|
642
|
+
* const counter = await kv.block.get("counter");
|
|
643
|
+
*
|
|
644
|
+
* // App-level storage (shared across all blocks)
|
|
645
|
+
* await kv.app.set({
|
|
646
|
+
* key: "shared-config",
|
|
647
|
+
* value: { apiKey: "secret" }
|
|
648
|
+
* });
|
|
649
|
+
*
|
|
650
|
+
* // Safe concurrent access with locking
|
|
651
|
+
* import { randomUUID } from "crypto";
|
|
652
|
+
*
|
|
653
|
+
* const lockId = randomUUID();
|
|
654
|
+
* const acquired = await kv.app.set({
|
|
655
|
+
* key: "shared-counter",
|
|
656
|
+
* value: "processing",
|
|
657
|
+
* lock: { id: lockId, timeout: 30 } // 30 second timeout
|
|
658
|
+
* });
|
|
659
|
+
*
|
|
660
|
+
* if (acquired) {
|
|
661
|
+
* try {
|
|
662
|
+
* // Perform atomic read-modify-write operation
|
|
663
|
+
* const current = await kv.app.get("shared-counter");
|
|
664
|
+
*
|
|
665
|
+
* // Simulate some processing time
|
|
666
|
+
* await new Promise(resolve => setTimeout(resolve, 1000));
|
|
667
|
+
*
|
|
668
|
+
* // Update with same lock ID
|
|
669
|
+
* const updated = await kv.app.set({
|
|
670
|
+
* key: "shared-counter",
|
|
671
|
+
* value: current.value + 1,
|
|
672
|
+
* lock: { id: lockId } // Same lock ID required for updates
|
|
673
|
+
* });
|
|
674
|
+
*
|
|
675
|
+
* if (!updated) {
|
|
676
|
+
* throw new Error("Lock was lost during operation");
|
|
677
|
+
* }
|
|
678
|
+
* } finally {
|
|
679
|
+
* // Always release the lock to avoid blocking other operations
|
|
680
|
+
* await kv.app.releaseLock({ key: "shared-counter", lockId });
|
|
681
|
+
* }
|
|
682
|
+
* } else {
|
|
683
|
+
* // Lock acquisition failed - another process has the lock
|
|
684
|
+
* console.log("Resource is currently locked, will retry later");
|
|
685
|
+
*
|
|
686
|
+
* // Implement retry logic with exponential backoff
|
|
687
|
+
* setTimeout(() => { yourRetryLogic }, 1000);
|
|
688
|
+
* }
|
|
689
|
+
*
|
|
690
|
+
* // Renew lock for long-running operations
|
|
691
|
+
* const lockId2 = randomUUID();
|
|
692
|
+
* await kv.app.set({
|
|
693
|
+
* key: "long-process",
|
|
694
|
+
* value: "starting",
|
|
695
|
+
* lock: { id: lockId2, timeout: 30 }
|
|
696
|
+
* });
|
|
697
|
+
*
|
|
698
|
+
* // Extend lock before it expires
|
|
699
|
+
* const renewed = await kv.app.renewLock({
|
|
700
|
+
* key: "long-process",
|
|
701
|
+
* lock: { id: lockId2, timeout: 60 } // Extend for 60 more seconds
|
|
702
|
+
* });
|
|
703
|
+
*
|
|
704
|
+
* if (renewed) {
|
|
705
|
+
* console.log("Lock extended successfully");
|
|
706
|
+
* } else {
|
|
707
|
+
* console.log("Lock renewal failed - may have expired or been released");
|
|
708
|
+
* }
|
|
709
|
+
* ```
|
|
710
|
+
*/
|
|
331
711
|
declare const kv: {
|
|
712
|
+
/** Block-scoped key-value storage (isolated per block instance) */
|
|
332
713
|
block: {
|
|
333
714
|
getMany: (keys: string[]) => Promise<KVPair[]>;
|
|
334
715
|
get: (key: string) => Promise<KVPair>;
|
|
@@ -348,6 +729,7 @@ declare const kv: {
|
|
|
348
729
|
};
|
|
349
730
|
}) => Promise<boolean>;
|
|
350
731
|
};
|
|
732
|
+
/** App-scoped key-value storage (shared across all blocks in the app) */
|
|
351
733
|
app: {
|
|
352
734
|
getMany: (keys: string[]) => Promise<KVPair[]>;
|
|
353
735
|
get: (key: string) => Promise<KVPair>;
|
|
@@ -368,6 +750,27 @@ declare const kv: {
|
|
|
368
750
|
}) => Promise<boolean>;
|
|
369
751
|
};
|
|
370
752
|
};
|
|
753
|
+
/**
|
|
754
|
+
* Messaging service for internal communication between app components.
|
|
755
|
+
*
|
|
756
|
+
* Enables blocks to send messages to other blocks or to the app level,
|
|
757
|
+
* bypassing the normal event flow. Useful for coordination, service discovery,
|
|
758
|
+
* and notifications.
|
|
759
|
+
*
|
|
760
|
+
* @example
|
|
761
|
+
* ```typescript
|
|
762
|
+
* // Send message to specific blocks
|
|
763
|
+
* await messaging.sendToBlocks({
|
|
764
|
+
* body: { action: "refresh", data: newConfig },
|
|
765
|
+
* blockIds: ["block-1", "block-2"]
|
|
766
|
+
* });
|
|
767
|
+
*
|
|
768
|
+
* // Send message to app level
|
|
769
|
+
* await messaging.sendToApp({
|
|
770
|
+
* body: { type: "status-update", status: "ready" }
|
|
771
|
+
* });
|
|
772
|
+
* ```
|
|
773
|
+
*/
|
|
371
774
|
declare const messaging: {
|
|
372
775
|
sendToBlocks: (input: {
|
|
373
776
|
body: unknown;
|
|
@@ -377,9 +780,36 @@ declare const messaging: {
|
|
|
377
780
|
body: unknown;
|
|
378
781
|
}) => Promise<void>;
|
|
379
782
|
};
|
|
783
|
+
/**
|
|
784
|
+
* Blocks service for querying other blocks within the same app installation.
|
|
785
|
+
*
|
|
786
|
+
* @example
|
|
787
|
+
* ```typescript
|
|
788
|
+
* // List all blocks
|
|
789
|
+
* const allBlocks = await blocks.list();
|
|
790
|
+
*
|
|
791
|
+
* // List specific block types
|
|
792
|
+
* const processors = await blocks.list({
|
|
793
|
+
* typeIds: ["dataProcessor", "validator"]
|
|
794
|
+
* });
|
|
795
|
+
* ```
|
|
796
|
+
*/
|
|
380
797
|
declare const blocks: {
|
|
381
798
|
list: (input?: ListBlocksInput) => Promise<ListBlocksOutput>;
|
|
382
799
|
};
|
|
800
|
+
/**
|
|
801
|
+
* HTTP service for responding to HTTP requests.
|
|
802
|
+
*
|
|
803
|
+
* @example
|
|
804
|
+
* ```typescript
|
|
805
|
+
* // In an HTTP handler
|
|
806
|
+
* await http.respond(input.request.requestId, {
|
|
807
|
+
* statusCode: 200,
|
|
808
|
+
* headers: { "Content-Type": "application/json" },
|
|
809
|
+
* body: { success: true, data: result }
|
|
810
|
+
* });
|
|
811
|
+
* ```
|
|
812
|
+
*/
|
|
383
813
|
declare const http: {
|
|
384
814
|
respond: (requestId: string, response: {
|
|
385
815
|
statusCode?: number;
|
|
@@ -387,6 +817,190 @@ declare const http: {
|
|
|
387
817
|
body?: unknown;
|
|
388
818
|
}) => Promise<void>;
|
|
389
819
|
};
|
|
820
|
+
/**
|
|
821
|
+
* Lifecycle service for managing block state, resource provisioning, and user prompts.
|
|
822
|
+
*
|
|
823
|
+
* The lifecycle service enables blocks to manage their state in a controlled, observable way.
|
|
824
|
+
* It provides mechanisms for blocks to reconcile their desired state with actual state,
|
|
825
|
+
* report status, and share computed values with other blocks through signals.
|
|
826
|
+
*
|
|
827
|
+
* ## Core Concepts
|
|
828
|
+
*
|
|
829
|
+
* - **State Management**: Track and update block status through defined lifecycle phases
|
|
830
|
+
* - **Resource Provisioning**: Create, update, and cleanup external resources (cloud, APIs, etc.)
|
|
831
|
+
* - **Signal System**: Share computed values between blocks for reactive dependencies
|
|
832
|
+
* - **Status Reporting**: Provide meaningful status descriptions for monitoring and debugging
|
|
833
|
+
* - **Approval Workflows**: Implement manual approval steps using prompts and proceed operations
|
|
834
|
+
*
|
|
835
|
+
* ## Lifecycle States
|
|
836
|
+
*
|
|
837
|
+
* - `draft`: Initial state, block configured but hasn't processed state yet
|
|
838
|
+
* - `in_progress`: Block currently computing or updating its state
|
|
839
|
+
* - `ready`: All calculations/operations complete, block ready for use
|
|
840
|
+
* - `failed`: Block failed to compute, update, or maintain its state
|
|
841
|
+
* - `draining`: Block currently cleaning up resources or state
|
|
842
|
+
* - `draining_failed`: Cleanup operations failed
|
|
843
|
+
* - `drained`: All cleanup successfully completed
|
|
844
|
+
*
|
|
845
|
+
* ## Implementation Pattern
|
|
846
|
+
*
|
|
847
|
+
* Blocks with lifecycle implement `onSync` and `onDrain` handlers:
|
|
848
|
+
* - **onSync**: Reconcile desired state, provision resources, update signals
|
|
849
|
+
* - **onDrain**: Clean up resources when block is removed or app is deleted
|
|
850
|
+
* - **signals**: Export computed values for other blocks to reference
|
|
851
|
+
*
|
|
852
|
+
* ## Signal Dependencies
|
|
853
|
+
*
|
|
854
|
+
* Signals create reactive dependencies between blocks. When a block updates its signals,
|
|
855
|
+
* dependent blocks automatically trigger their sync operations to adapt to changes.
|
|
856
|
+
* This enables building complex reactive systems where changes cascade automatically.
|
|
857
|
+
*
|
|
858
|
+
* @example
|
|
859
|
+
* ```typescript
|
|
860
|
+
* // Complete lifecycle example with resource management
|
|
861
|
+
* export const cloudResourceBlock: AppBlock = {
|
|
862
|
+
* name: "Cloud Storage Bucket",
|
|
863
|
+
* description: "Manages a cloud storage bucket with lifecycle",
|
|
864
|
+
*
|
|
865
|
+
* config: {
|
|
866
|
+
* bucketName: {
|
|
867
|
+
* name: "Bucket Name",
|
|
868
|
+
* type: "string",
|
|
869
|
+
* required: true
|
|
870
|
+
* },
|
|
871
|
+
* region: {
|
|
872
|
+
* name: "Region",
|
|
873
|
+
* type: "string",
|
|
874
|
+
* required: true,
|
|
875
|
+
* default: "us-west-2"
|
|
876
|
+
* }
|
|
877
|
+
* },
|
|
878
|
+
*
|
|
879
|
+
* // Values shared with other blocks
|
|
880
|
+
* signals: {
|
|
881
|
+
* bucketId: {
|
|
882
|
+
* name: "Bucket ID",
|
|
883
|
+
* description: "The unique identifier of the created bucket"
|
|
884
|
+
* },
|
|
885
|
+
* bucketUrl: {
|
|
886
|
+
* name: "Bucket URL",
|
|
887
|
+
* description: "The access URL for the bucket"
|
|
888
|
+
* }
|
|
889
|
+
* },
|
|
890
|
+
*
|
|
891
|
+
* // Provision resources and maintain state
|
|
892
|
+
* onSync: async (input) => {
|
|
893
|
+
* const config = input.block.config;
|
|
894
|
+
*
|
|
895
|
+
* // Check if bucket already exists
|
|
896
|
+
* const existingBucket = await kv.block.get("bucketId");
|
|
897
|
+
*
|
|
898
|
+
* if (!existingBucket?.value) {
|
|
899
|
+
* // Create new bucket
|
|
900
|
+
* const bucketId = await cloudApi.createBucket({
|
|
901
|
+
* name: config.bucketName,
|
|
902
|
+
* region: config.region
|
|
903
|
+
* });
|
|
904
|
+
*
|
|
905
|
+
* await kv.block.set({ key: "bucketId", value: bucketId });
|
|
906
|
+
*
|
|
907
|
+
* return {
|
|
908
|
+
* newStatus: "in_progress",
|
|
909
|
+
* customStatusDescription: "Bucket creation in progress",
|
|
910
|
+
* nextScheduleDelay: 30 // Check again in 30 seconds
|
|
911
|
+
* };
|
|
912
|
+
* }
|
|
913
|
+
*
|
|
914
|
+
* // Check bucket status
|
|
915
|
+
* const bucketStatus = await cloudApi.getBucketStatus(existingBucket.value);
|
|
916
|
+
*
|
|
917
|
+
* if (bucketStatus === "ready") {
|
|
918
|
+
* const bucketDetails = await cloudApi.getBucketDetails(existingBucket.value);
|
|
919
|
+
*
|
|
920
|
+
* return {
|
|
921
|
+
* newStatus: "ready",
|
|
922
|
+
* signalUpdates: {
|
|
923
|
+
* bucketId: existingBucket.value,
|
|
924
|
+
* bucketUrl: bucketDetails.url
|
|
925
|
+
* }
|
|
926
|
+
* };
|
|
927
|
+
* }
|
|
928
|
+
*
|
|
929
|
+
* return {
|
|
930
|
+
* newStatus: "in_progress",
|
|
931
|
+
* customStatusDescription: "Waiting for bucket to be ready",
|
|
932
|
+
* nextScheduleDelay: 30
|
|
933
|
+
* };
|
|
934
|
+
* },
|
|
935
|
+
*
|
|
936
|
+
* // Clean up resources when block is removed
|
|
937
|
+
* onDrain: async (input) => {
|
|
938
|
+
* const bucketId = await kv.block.get("bucketId");
|
|
939
|
+
*
|
|
940
|
+
* if (bucketId?.value) {
|
|
941
|
+
* await cloudApi.deleteBucket(bucketId.value);
|
|
942
|
+
* await kv.block.delete(["bucketId"]);
|
|
943
|
+
* }
|
|
944
|
+
*
|
|
945
|
+
* return {
|
|
946
|
+
* newStatus: "drained",
|
|
947
|
+
* signalUpdates: {
|
|
948
|
+
* bucketId: null,
|
|
949
|
+
* bucketUrl: null
|
|
950
|
+
* }
|
|
951
|
+
* };
|
|
952
|
+
* },
|
|
953
|
+
*
|
|
954
|
+
* // Trigger sync when configuration changes
|
|
955
|
+
* inputs: {
|
|
956
|
+
* configChange: {
|
|
957
|
+
* name: "Configuration Change",
|
|
958
|
+
* onEvent: async (input) => {
|
|
959
|
+
* // Configuration updated, trigger sync
|
|
960
|
+
* await lifecycle.sync();
|
|
961
|
+
* }
|
|
962
|
+
* }
|
|
963
|
+
* }
|
|
964
|
+
* };
|
|
965
|
+
*
|
|
966
|
+
* // Approval workflow example
|
|
967
|
+
* export const approvalBlock: AppBlock = {
|
|
968
|
+
* name: "Production Deployment Approval",
|
|
969
|
+
*
|
|
970
|
+
* http: {
|
|
971
|
+
* onRequest: async (input) => {
|
|
972
|
+
* if (input.request.path === "/approve") {
|
|
973
|
+
* // Manual approval received
|
|
974
|
+
* await lifecycle.proceed();
|
|
975
|
+
*
|
|
976
|
+
* await http.respond(input.request.requestId, {
|
|
977
|
+
* statusCode: 200,
|
|
978
|
+
* body: { message: "Deployment approved" }
|
|
979
|
+
* });
|
|
980
|
+
* }
|
|
981
|
+
* }
|
|
982
|
+
* },
|
|
983
|
+
*
|
|
984
|
+
* onSync: async (input) => {
|
|
985
|
+
* // Create approval prompt if needed
|
|
986
|
+
* const promptId = await lifecycle.prompt.create(
|
|
987
|
+
* "Please approve production deployment",
|
|
988
|
+
* {
|
|
989
|
+
* redirect: {
|
|
990
|
+
* url: `${input.block.http.url}/approve`,
|
|
991
|
+
* method: "POST"
|
|
992
|
+
* }
|
|
993
|
+
* }
|
|
994
|
+
* );
|
|
995
|
+
*
|
|
996
|
+
* return {
|
|
997
|
+
* newStatus: "in_progress",
|
|
998
|
+
* customStatusDescription: "Waiting for manual approval"
|
|
999
|
+
* };
|
|
1000
|
+
* }
|
|
1001
|
+
* };
|
|
1002
|
+
* ```
|
|
1003
|
+
*/
|
|
390
1004
|
declare const lifecycle: {
|
|
391
1005
|
sync: () => Promise<void>;
|
|
392
1006
|
proceed: () => Promise<void>;
|
|
@@ -395,6 +1009,21 @@ declare const lifecycle: {
|
|
|
395
1009
|
delete: (promptId: string) => Promise<void>;
|
|
396
1010
|
};
|
|
397
1011
|
};
|
|
1012
|
+
/**
|
|
1013
|
+
* Timers service for scheduling delayed execution.
|
|
1014
|
+
*
|
|
1015
|
+
* @example
|
|
1016
|
+
* ```typescript
|
|
1017
|
+
* // Set a timer for 30 seconds
|
|
1018
|
+
* const timerId = await timers.set(30, {
|
|
1019
|
+
* inputPayload: { action: "retry" },
|
|
1020
|
+
* description: "Retry failed operation"
|
|
1021
|
+
* });
|
|
1022
|
+
*
|
|
1023
|
+
* // Cancel the timer
|
|
1024
|
+
* await timers.unset(timerId);
|
|
1025
|
+
* ```
|
|
1026
|
+
*/
|
|
398
1027
|
declare const timers: {
|
|
399
1028
|
set: (delaySeconds: number, options: {
|
|
400
1029
|
inputPayload?: unknown;
|
|
@@ -404,6 +1033,23 @@ declare const timers: {
|
|
|
404
1033
|
}) => Promise<string>;
|
|
405
1034
|
unset: (id: string) => Promise<void>;
|
|
406
1035
|
};
|
|
1036
|
+
/**
|
|
1037
|
+
* UI service for managing custom block widgets.
|
|
1038
|
+
*
|
|
1039
|
+
* @example
|
|
1040
|
+
* ```typescript
|
|
1041
|
+
* // Set widget elements
|
|
1042
|
+
* await ui.widget.set({
|
|
1043
|
+
* elements: [
|
|
1044
|
+
* { type: "text", content: "Status: Active" },
|
|
1045
|
+
* { type: "button", label: "Refresh", action: "refresh" }
|
|
1046
|
+
* ]
|
|
1047
|
+
* });
|
|
1048
|
+
*
|
|
1049
|
+
* // Get current widget
|
|
1050
|
+
* const elements = await ui.widget.get();
|
|
1051
|
+
* ```
|
|
1052
|
+
*/
|
|
407
1053
|
declare const ui: {
|
|
408
1054
|
widget: {
|
|
409
1055
|
set: (options: {
|
|
@@ -413,6 +1059,50 @@ declare const ui: {
|
|
|
413
1059
|
get: (blockId?: any) => Promise<any[]>;
|
|
414
1060
|
};
|
|
415
1061
|
};
|
|
1062
|
+
/**
|
|
1063
|
+
* Define a Flows app with proper TypeScript typing and validation.
|
|
1064
|
+
*
|
|
1065
|
+
* This function provides type safety and IDE support while developing apps.
|
|
1066
|
+
* It should be the default export of your app module.
|
|
1067
|
+
*
|
|
1068
|
+
* @param app - The app schema defining blocks, configuration, and behaviors
|
|
1069
|
+
* @returns The same app schema for runtime use
|
|
1070
|
+
*
|
|
1071
|
+
* @example
|
|
1072
|
+
* ```typescript
|
|
1073
|
+
* export default defineApp({
|
|
1074
|
+
* name: "My App",
|
|
1075
|
+
* installationInstructions: "Configure your API credentials below.",
|
|
1076
|
+
* config: {
|
|
1077
|
+
* apiKey: {
|
|
1078
|
+
* name: "API Key",
|
|
1079
|
+
* type: "string",
|
|
1080
|
+
* required: true
|
|
1081
|
+
* }
|
|
1082
|
+
* },
|
|
1083
|
+
* blocks: {
|
|
1084
|
+
* processor: {
|
|
1085
|
+
* name: "Data Processor",
|
|
1086
|
+
* inputs: {
|
|
1087
|
+
* data: {
|
|
1088
|
+
* name: "Input Data",
|
|
1089
|
+
* onEvent: async (input) => {
|
|
1090
|
+
* // Your processing logic here
|
|
1091
|
+
* await events.emit(processedData);
|
|
1092
|
+
* }
|
|
1093
|
+
* }
|
|
1094
|
+
* },
|
|
1095
|
+
* outputs: {
|
|
1096
|
+
* result: {
|
|
1097
|
+
* name: "Processed Result",
|
|
1098
|
+
* default: true
|
|
1099
|
+
* }
|
|
1100
|
+
* }
|
|
1101
|
+
* }
|
|
1102
|
+
* }
|
|
1103
|
+
* });
|
|
1104
|
+
* ```
|
|
1105
|
+
*/
|
|
416
1106
|
declare function defineApp(app: AppSchema): AppSchema;
|
|
417
1107
|
|
|
418
|
-
export { type AppBlock, type AppBlockComponentInput, type AppBlockComponentOutput, type AppBlockConfigField, type AppBlockHTTPComponent, type AppBlockSchedule, type AppBlockSignal, type AppBlockUIComponent, type AppConfigField, type AppContext, type AppHTTPComponent, type AppHTTPEndpoint, type AppInput, type AppLifecycleCallbackOutput, type AppLifecycleStatus, type AppOnCreateOutput, type AppOnHTTPRequestInput, type AppOnInternalMessageInput, type AppOnTimerInput, type AppOnTriggerInput, type AppOnUIRequestInput, type AppSchedule, type AppSchema, type AppUIComponent, type EntityContext, type EntityHTTPEndpoint, type EntityInput, type EntityLifecycleCallbackOutput, type EntityLifecycleComponent, type EntityLifecycleStatus, type EntityOnHTTPRequestInput, type EntityOnInternalMessageInput, type EntityOnTimerInput, type EntityOnTriggerInput, type EntityOnUIRequestInput, type EntityView, type EntityViewType, type EventContext, type EventInput, type HTTPRequest, type ScheduleDefinition, type UIRequest, blocks, defineApp, events, getInvocationMetadata, http, kv, lifecycle, messaging,
|
|
1108
|
+
export { type AppBlock, type AppBlockComponentInput, type AppBlockComponentOutput, type AppBlockConfigField, type AppBlockHTTPComponent, type AppBlockSchedule, type AppBlockSignal, type AppBlockUIComponent, type AppConfigField, type AppContext, type AppHTTPComponent, type AppHTTPEndpoint, type AppInput, type AppLifecycleCallbackOutput, type AppLifecycleStatus, type AppOnCreateOutput, type AppOnHTTPRequestInput, type AppOnInternalMessageInput, type AppOnTimerInput, type AppOnTriggerInput, type AppOnUIRequestInput, type AppSchedule, type AppSchema, type AppSignal, type AppUIComponent, type EntityContext, type EntityHTTPEndpoint, type EntityInput, type EntityLifecycleCallbackOutput, type EntityLifecycleComponent, type EntityLifecycleStatus, type EntityOnHTTPRequestInput, type EntityOnInternalMessageInput, type EntityOnTimerInput, type EntityOnTriggerInput, type EntityOnUIRequestInput, type EntityView, type EntityViewType, type EventContext, type EventInput, type HTTPRequest, type ScheduleDefinition, type UIRequest, blocks, defineApp, events, getInvocationMetadata, http, kv, lifecycle, messaging, timers, ui };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slflows/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist"
|
|
6
6
|
],
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"./v1": "./dist/v1/index.d.ts"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"pkgroll": "^2.
|
|
13
|
-
"rollup": "^4.
|
|
12
|
+
"pkgroll": "^2.13.1",
|
|
13
|
+
"rollup": "^4.44.1",
|
|
14
14
|
"rollup-plugin-dts": "^6.2.1",
|
|
15
15
|
"typescript": "^5.8.3"
|
|
16
16
|
},
|