@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.
Files changed (2) hide show
  1. package/dist/v1/index.d.ts +693 -3
  2. package/package.json +3 -3
@@ -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
- installationStatus: "new" | "active" | "failed";
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
- declare const setAppInstallationStatus: (status: "active" | "failed") => Promise<void>;
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, setAppInstallationStatus, timers, ui };
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",
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.12.2",
13
- "rollup": "^4.41.1",
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
  },