@slflows/sdk 0.0.3 → 0.0.4

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