@meistrari/tela-sdk-js 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,11 +11,16 @@ The Tela SDK for JavaScript provides a simple and powerful way to interact with
11
11
  - [Synchronous Execution](#synchronous-execution)
12
12
  - [Asynchronous Execution with Polling](#asynchronous-execution-with-polling)
13
13
  - [Streaming Execution](#streaming-execution)
14
+ - [Event-Driven Execution](#event-driven-execution)
14
15
  - [Schema Validation](#schema-validation)
15
16
  - [Multiple Files](#multiple-files)
16
17
  - [Webhook Notifications](#webhook-notifications)
18
+ - [Execution Tags](#execution-tags)
17
19
  - [File Handling](#file-handling)
18
- - [License](#license)
20
+ - [Migration Guide from v1.x to v2](#migration-guide-from-v1x-to-v2)
21
+ - [Breaking Changes](#breaking-changes)
22
+ - [Migration Checklist](#migration-checklist)
23
+ - [New Features in v2](#new-features-in-v2)
19
24
 
20
25
  ## Installation
21
26
 
@@ -131,6 +136,88 @@ for await (const chunk of canvas.execute(
131
136
  }
132
137
  ```
133
138
 
139
+ #### Event-Driven Execution
140
+
141
+ Monitor execution lifecycle with events. **Events are only useful with async executions** - synchronous and streaming executions complete too quickly for event-driven monitoring to be practical.
142
+
143
+ ```typescript
144
+ const canvas = await tela.canvas.get({
145
+ id: 'canvas-id',
146
+ input: schema => schema.object({
147
+ query: schema.string()
148
+ }),
149
+ output: schema => schema.object({
150
+ result: schema.string()
151
+ }),
152
+ })
153
+
154
+ // Start async execution to attach event listeners
155
+ const execution = await canvas.execute(
156
+ { query: 'What is the capital of France?' },
157
+ {
158
+ async: true,
159
+ pollingInterval: 1000,
160
+ pollingTimeout: 60000,
161
+ }
162
+ )
163
+
164
+ // Access current status at any time
165
+ console.log(execution.status) // 'created'
166
+
167
+ // Listen for status changes
168
+ execution.on('statusChange', (status) => {
169
+ console.log(`Status: ${status}`) // created → running → succeeded
170
+ })
171
+
172
+ // Listen for polling updates
173
+ execution.on('poll', (pollResult) => {
174
+ console.log(`Poll - Status: ${pollResult.status}`)
175
+ })
176
+
177
+ // Listen for successful completion
178
+ execution.on('success', (result) => {
179
+ console.log('Success!', result)
180
+ })
181
+
182
+ // Listen for errors (prevents throwing)
183
+ execution.on('error', (error) => {
184
+ console.error('Failed:', error.message)
185
+ // With error listener: error event is emitted, promise resolves to undefined
186
+ })
187
+
188
+ // Start event-driven polling (non-blocking)
189
+ execution.poll()
190
+
191
+ // Events will fire as polling progresses
192
+ // The result will be available in the success event callback
193
+ // Continue with other work while polling happens in the background...
194
+ ```
195
+
196
+ **Why Async Only?**
197
+
198
+ Events are designed for tracking progress over time. Synchronous executions complete immediately (blocking until done), so by the time you can access the execution object, all events have already fired. Streaming executions provide their own iteration mechanism via async generators, making events redundant.
199
+
200
+ **Status Property:**
201
+
202
+ The `status` property provides the current execution state:
203
+ - **Async**: `created` → `running` → `succeeded` or `failed`
204
+
205
+ Status is set to `succeeded` only after successful validation. If validation fails, status will be `failed` even if the API request succeeded.
206
+
207
+ **Available Events (Async Executions Only):**
208
+ - `success` - Emitted when execution completes successfully (after validation)
209
+ - `error` - Emitted on failure (if listeners exist, errors won't throw)
210
+ - `statusChange` - Emitted on status transitions
211
+ - `poll` - Emitted during polling (initial response + each poll)
212
+
213
+ **Error Handling:**
214
+ - **With error listener**: Errors emitted as events, promise resolves to `undefined`
215
+ - **Without error listener**: Errors throw and reject the promise
216
+
217
+ **Important:** You must call `execution.poll()` or `await execution.result` to start polling - events won't fire until polling begins.
218
+
219
+ See [Event Examples](./examples/events/) for detailed usage patterns.
220
+
134
221
  #### Schema Validation
135
222
 
136
223
  The Canvas API validates your inputs and outputs against the server schema:
@@ -238,6 +325,197 @@ const result = await canvas.execute(
238
325
  // OR wait for the webhook notification on your server instead of polling
239
326
  ```
240
327
 
328
+ #### Execution Tags
329
+
330
+ Add tags to your canvas executions for filtering, categorization, and analytics:
331
+
332
+ ```typescript
333
+ const canvas = await tela.canvas.get({
334
+ id: 'canvas-id',
335
+ input: schema => schema.object({
336
+ query: schema.string()
337
+ }),
338
+ output: schema => schema.object({
339
+ result: schema.string()
340
+ }),
341
+ })
342
+
343
+ // Execute with tags (works with all execution modes)
344
+ const result = await canvas.execute(
345
+ { query: 'Analyze customer feedback' },
346
+ {
347
+ tags: ['production', 'analytics', 'customer-feedback']
348
+ }
349
+ ).result
350
+
351
+ // Tags work with async executions
352
+ const asyncResult = await canvas.execute(
353
+ { query: 'Process large dataset' },
354
+ {
355
+ async: true,
356
+ tags: ['batch-processing', 'analytics']
357
+ }
358
+ ).result
359
+
360
+ // Tags work with streaming executions
361
+ for await (const chunk of canvas.execute(
362
+ { query: 'Generate report' },
363
+ {
364
+ stream: true,
365
+ tags: ['streaming', 'reports']
366
+ }
367
+ ).result) {
368
+ process.stdout.write(chunk.result || '')
369
+ }
370
+ ```
371
+
372
+ **Use Cases:**
373
+ - **Environment tracking**: Tag executions by environment (`production`, `staging`, `development`)
374
+ - **Feature categorization**: Organize executions by feature (`analytics`, `reporting`, `summarization`)
375
+ - **User segmentation**: Track executions by user tier or department
376
+ - **Cost allocation**: Attribute API usage to specific projects or teams
377
+ - **Performance monitoring**: Filter and analyze execution metrics by tag
378
+
379
+ Tags are sent to the Tela API and can be used for filtering and analytics in your Tela dashboard.
380
+
381
+ #### Fetching Existing Executions
382
+
383
+ Retrieve and monitor async executions by their ID, useful for job queues, resuming workflows, or multi-user dashboards:
384
+
385
+ ```typescript
386
+ // Start an async execution
387
+ const execution = await canvas.execute(
388
+ { query: 'Process this data' },
389
+ { async: true }
390
+ )
391
+
392
+ // Store the execution ID (e.g., in a database)
393
+ const executionId = execution.id
394
+
395
+ // Later, fetch the execution by ID
396
+ const fetchedExecution = await canvas.getExecution(executionId, {
397
+ pollingInterval: 2000, // Optional: custom polling interval
398
+ pollingTimeout: 120000 // Optional: custom timeout
399
+ })
400
+
401
+ console.log(fetchedExecution.status) // 'running', 'succeeded', or 'failed'
402
+
403
+ // Use event-driven polling for non-blocking progress tracking
404
+ // Note: poll() works for ANY async execution, not just fetched ones!
405
+ fetchedExecution.on('statusChange', (status) => {
406
+ console.log(`Status: ${status}`)
407
+ })
408
+
409
+ fetchedExecution.on('success', (result) => {
410
+ console.log('Completed!', result)
411
+ })
412
+
413
+ fetchedExecution.on('error', (error) => {
414
+ console.error('Failed:', error)
415
+ })
416
+
417
+ // Start polling without blocking (returns immediately)
418
+ fetchedExecution.poll()
419
+
420
+ // Continue with other work while polling runs in background...
421
+ console.log('Polling in background, doing other work...')
422
+
423
+ // Later, you can still await the result if needed
424
+ const result = await fetchedExecution.result
425
+ ```
426
+
427
+ **Alternative: Use `poll()` on newly started executions**
428
+
429
+ You can also use event-driven polling immediately when starting an execution:
430
+
431
+ ```typescript
432
+ const execution = await canvas.execute(
433
+ { query: 'Process this data' },
434
+ { async: true }
435
+ )
436
+
437
+ // Set up event listeners
438
+ execution.on('statusChange', status => console.log('Status:', status))
439
+ execution.on('success', result => console.log('Done!', result))
440
+ execution.on('error', error => console.error('Failed:', error))
441
+
442
+ // Start non-blocking polling
443
+ execution.poll()
444
+
445
+ // Do other work...
446
+ console.log('Execution polling in background')
447
+ ```
448
+
449
+ **Key Features:**
450
+
451
+ - **`canvas.getExecution(id, options?)`** - Fetch execution by UUID
452
+ - Only async executions have UUIDs and can be fetched
453
+ - Returns execution in current state (running, succeeded, or failed)
454
+ - Supports custom polling options
455
+
456
+ - **`execution.poll()`** - Start event-driven polling
457
+ - Returns `void` (non-blocking)
458
+ - Emits events: `statusChange`, `poll`, `success`, `error`
459
+ - Perfect for UI updates, dashboards, and concurrent monitoring
460
+
461
+ **Important Notes:**
462
+
463
+ ⚠️ **Events require polling or awaiting `.result`**: Events are only emitted when polling is active. You must either:
464
+ - Call `execution.poll()` to start event-driven polling, OR
465
+ - Await `execution.result` which starts polling automatically
466
+
467
+ ```typescript
468
+ // ❌ Events won't fire - no polling started
469
+ const execution = await canvas.execute({ query: 'test' }, { async: true })
470
+ execution.on('success', result => console.log('Done')) // This will never fire!
471
+
472
+ // ✅ Correct - poll() starts event-driven polling
473
+ const execution = await canvas.execute({ query: 'test' }, { async: true })
474
+ execution.on('success', result => console.log('Done'))
475
+ execution.poll() // Now events will fire
476
+
477
+ // ✅ Also correct - awaiting result starts polling
478
+ const execution = await canvas.execute({ query: 'test' }, { async: true })
479
+ execution.on('success', result => console.log('Done'))
480
+ await execution.result // Polling starts, events fire
481
+ ```
482
+
483
+ ⚠️ **Already completed executions don't emit events**: If you fetch an execution that has already finished (succeeded or failed), the `success` and `error` events won't fire because there's no polling needed. Instead, check the status and access the result directly:
484
+
485
+ ```typescript
486
+ const execution = await canvas.getExecution(executionId)
487
+
488
+ if (execution.status === 'succeeded') {
489
+ // Access result directly - success event won't fire
490
+ const result = await execution.result
491
+ console.log('Already completed:', result)
492
+ }
493
+ else if (execution.status === 'failed') {
494
+ // Handle failure - error event won't fire
495
+ try {
496
+ await execution.result
497
+ }
498
+ catch (error) {
499
+ console.error('Already failed:', error)
500
+ }
501
+ }
502
+ else {
503
+ // Still running - events will fire when you start polling
504
+ execution.on('success', result => console.log('Completed!', result))
505
+ execution.on('error', error => console.error('Failed:', error))
506
+ execution.poll()
507
+ }
508
+ ```
509
+
510
+ **Use Cases:**
511
+ - Job queue management systems
512
+ - Resuming monitoring after page refresh or server restart
513
+ - Multi-user dashboards showing execution status
514
+ - Background processing with real-time updates
515
+ - Retry mechanisms with persistent execution IDs
516
+
517
+ See [Fetch and Poll Examples](./examples/events/) for detailed usage patterns.
518
+
241
519
  ### File Handling
242
520
 
243
521
  The `TelaFile` API provides flexible file handling with support for various input types:
@@ -281,3 +559,495 @@ const fileWithRange = TelaFile.create(
281
559
  }
282
560
  )
283
561
  ```
562
+
563
+ ## Migration Guide from v1.x to v2
564
+
565
+ Version 2.0 of the Tela SDK introduces significant improvements to type safety, schema validation, and API design. This guide will help you migrate your code from v1.x to v2.
566
+
567
+ ### Breaking Changes
568
+
569
+ #### 1. API Surface Changed: `completions.create()` → `canvas.get()` + `execute()`
570
+
571
+ The v1 API used a single `completions.create()` method. V2 introduces a two-step pattern: first retrieve a canvas, then execute it.
572
+
573
+ **v1.x:**
574
+ ```typescript
575
+ const tela = new TelaSDK({ apiKey: 'your-api-key' })
576
+
577
+ const completion = await tela.completions.create({
578
+ canvasId: 'canvas-id',
579
+ variables: {
580
+ query: 'What is the capital of France?'
581
+ }
582
+ })
583
+
584
+ console.log(completion.choices[0].message.content)
585
+ ```
586
+
587
+ **v2:**
588
+ ```typescript
589
+ const tela = new TelaSDK({ apiKey: 'your-api-key' })
590
+
591
+ // First, get the canvas
592
+ const canvas = await tela.canvas.get({
593
+ id: 'canvas-id'
594
+ })
595
+
596
+ // Then execute it
597
+ const result = await canvas.execute({
598
+ query: 'What is the capital of France?'
599
+ }).result
600
+
601
+ console.log(result)
602
+ ```
603
+
604
+ #### 2. Schema Validation and Type Safety (Optional)
605
+
606
+ V2 introduces **optional** runtime and compile-time type safety with Zod schema validation. Schemas validate data at runtime (catching invalid data) and provide TypeScript types at compile-time. You can use the SDK without schemas if you prefer.
607
+
608
+ **v1.x (no schema validation):**
609
+ ```typescript
610
+ const completion = await tela.completions.create<
611
+ { query: string },
612
+ { result: string }
613
+ >({
614
+ canvasId: 'canvas-id',
615
+ variables: {
616
+ query: 'Hello'
617
+ }
618
+ })
619
+ ```
620
+
621
+ **v2 (without schemas - works just like v1.x):**
622
+ ```typescript
623
+ const canvas = await tela.canvas.get({
624
+ id: 'canvas-id'
625
+ })
626
+
627
+ const result = await canvas.execute({ query: 'Hello' }).result
628
+ // result is typed as unknown, no runtime validation
629
+ ```
630
+
631
+ **v2 (with optional schema validation for runtime + compile-time safety):**
632
+ ```typescript
633
+ const canvas = await tela.canvas.get({
634
+ id: 'canvas-id',
635
+ input: schema => schema.object({
636
+ query: schema.string()
637
+ }),
638
+ output: schema => schema.object({
639
+ result: schema.string()
640
+ })
641
+ })
642
+
643
+ // Runtime validation ensures data matches schema
644
+ // TypeScript knows the exact types at compile-time
645
+ const result = await canvas.execute({ query: 'Hello' }).result
646
+ // result.result is a string (validated and typed)
647
+ ```
648
+
649
+ #### 3. File Handling Changes
650
+
651
+ The `TelaFile` constructor now requires `mimeType` for `Uint8Array` and `ReadableStream` inputs (for compatibility with storage solutions).
652
+
653
+ **v1.x:**
654
+ ```typescript
655
+ import { TelaFile } from '@meistrari/tela-sdk-js'
656
+
657
+ const file = new TelaFile(blob, { range: [0, 5] })
658
+ const fileFromBytes = new TelaFile(new Uint8Array([/* ... */])) // mimeType optional
659
+ ```
660
+
661
+ **v2:**
662
+ ```typescript
663
+ import { TelaFile } from '@meistrari/tela-sdk-js'
664
+
665
+ // No change needed for Blob, File, or URL strings
666
+ const file = new TelaFile(blob, { range: [0, 5] })
667
+
668
+ // For Uint8Array or ReadableStream, mimeType is now required:
669
+ const fileFromBytes = new TelaFile(
670
+ new Uint8Array([/* ... */]),
671
+ {
672
+ mimeType: 'application/pdf',
673
+ name: 'document.pdf'
674
+ }
675
+ )
676
+
677
+ // Alternative: use tela.createFile() helper
678
+ const fileFromStream = tela.createFile(readableStream, { mimeType: 'image/jpeg' })
679
+ ```
680
+
681
+ #### 4. Streaming API Changes
682
+
683
+ Streaming now returns an async generator directly via the `.result` property.
684
+
685
+ **v1.x:**
686
+ ```typescript
687
+ const stream = await tela.completions.create({
688
+ canvasId: 'canvas-id',
689
+ stream: true,
690
+ variables: { query: 'Tell me a story' }
691
+ })
692
+
693
+ for await (const chunk of stream) {
694
+ process.stdout.write(chunk.message.content || '')
695
+ }
696
+ ```
697
+
698
+ **v2:**
699
+ ```typescript
700
+ const canvas = await tela.canvas.get({
701
+ id: 'canvas-id',
702
+ input: schema => schema.object({
703
+ query: schema.string()
704
+ }),
705
+ output: schema => schema.object({
706
+ response: schema.string()
707
+ })
708
+ })
709
+
710
+ const execution = canvas.execute(
711
+ { query: 'Tell me a story' },
712
+ { stream: true }
713
+ )
714
+
715
+ for await (const chunk of execution.result) {
716
+ process.stdout.write(chunk.response || '')
717
+ }
718
+ ```
719
+
720
+ #### 5. Async Execution with Polling
721
+
722
+ V2 introduces async execution with automatic polling. V1.x only supported synchronous execution (the request would block until completion).
723
+
724
+ **v1.x:**
725
+ ```typescript
726
+ // Synchronous only - blocks until the execution completes
727
+ const response = await tela.completions.create({
728
+ canvasId: 'canvas-id',
729
+ variables: { query: 'Analyze this' }
730
+ })
731
+ // This could take a very long time for long-running executions
732
+ console.log(response.choices[0].message.content)
733
+ ```
734
+
735
+ **v2:**
736
+ ```typescript
737
+ const canvas = await tela.canvas.get({
738
+ id: 'canvas-id',
739
+ input: schema => schema.object({ query: schema.string() }),
740
+ output: schema => schema.object({ result: schema.string() })
741
+ })
742
+
743
+ // Automatic polling
744
+ const execution = canvas.execute(
745
+ { query: 'Analyze this' },
746
+ {
747
+ async: true,
748
+ pollingInterval: 1000, // Poll every 1 second
749
+ pollingTimeout: 60000, // Timeout after 60 seconds
750
+ webhookUrl: 'https://your-server.com/webhook' // Optional
751
+ }
752
+ )
753
+
754
+ const result = await execution.result
755
+ console.log(result.result)
756
+ ```
757
+
758
+ #### 6. Response Structure Changes
759
+
760
+ The response structure has been simplified.
761
+
762
+ **v1.x:**
763
+ ```typescript
764
+ const completion = await tela.completions.create({
765
+ canvasId: 'canvas-id',
766
+ variables: { query: 'Hello' }
767
+ })
768
+
769
+ // Access via nested structure
770
+ console.log(completion.choices[0].message.content)
771
+ ```
772
+
773
+ **v2:**
774
+ ```typescript
775
+ const canvas = await tela.canvas.get({
776
+ id: 'canvas-id',
777
+ output: schema => schema.object({
778
+ answer: schema.string()
779
+ })
780
+ })
781
+
782
+ const response = await canvas.execute({ query: 'Hello' }).result
783
+ // Direct access to structured output
784
+ console.log(response.answer)
785
+ ```
786
+
787
+ ### Migration Checklist
788
+
789
+ - [ ] Replace `tela.completions.create()` with `tela.canvas.get()` + `canvas.execute()`
790
+ - [ ] (Optional) Add input/output schemas using the `schema` builder function for type safety
791
+ - [ ] Add `mimeType` parameter when creating `TelaFile` from `Uint8Array` or `ReadableStream`
792
+ - [ ] Update streaming code to use `.result` property for iteration
793
+ - [ ] Update async execution to use polling parameters instead of manual polling
794
+ - [ ] Update response access patterns to match new structure
795
+ - [ ] (If using schemas) Test schema validation warnings to ensure schemas match server configuration
796
+
797
+ ### New Features in v2
798
+
799
+ #### Event-Driven Execution Monitoring
800
+
801
+ V2 introduces a comprehensive event system for monitoring execution lifecycle across all execution modes (sync, async, streaming):
802
+
803
+ ```typescript
804
+ const execution = await canvas.execute(
805
+ { query: 'Analyze this' },
806
+ { async: true }
807
+ )
808
+
809
+ // Monitor progress with events
810
+ execution.on('statusChange', (status) => {
811
+ console.log(`Status: ${status}`)
812
+ })
813
+
814
+ execution.on('poll', (pollResult) => {
815
+ console.log(`Polling - ${pollResult.status}`)
816
+ })
817
+
818
+ execution.on('success', (result) => {
819
+ console.log('Completed!', result)
820
+ })
821
+
822
+ // Graceful error handling
823
+ execution.on('error', (error) => {
824
+ console.error('Failed:', error.message)
825
+ // With listener: no throw, promise resolves to undefined
826
+ })
827
+
828
+ const result = await execution.result
829
+ ```
830
+
831
+ **Available Events:**
832
+ - `success` - Completion with validated result
833
+ - `error` - Failure notification (prevents throwing if listener exists)
834
+ - `statusChange` - Status transitions
835
+ - `poll` - Polling updates (async only)
836
+
837
+ See [Event Examples](./examples/events/) for comprehensive patterns and use cases.
838
+
839
+ #### Promise-like Execution API
840
+
841
+ V2 introduces a flexible execution API that supports both direct result access and execution object retrieval:
842
+
843
+ ```typescript
844
+ // Direct result access (recommended for simple cases)
845
+ const result = await canvas.execute({ query: 'Hello' }).result
846
+
847
+ // Get execution object for more control
848
+ const execution = await canvas.execute({ query: 'Hello' })
849
+ const result2 = await execution.result
850
+ ```
851
+
852
+ #### Schema Validation Warnings
853
+
854
+ V2 validates your schemas against the server configuration and warns you about mismatches:
855
+
856
+ ```typescript
857
+ const canvas = await tela.canvas.get({
858
+ id: 'canvas-id',
859
+ input: schema => schema.object({
860
+ wrongField: schema.string() // Will warn if not in server schema
861
+ }),
862
+ skipSchemaValidation: false // Enable warnings (default)
863
+ })
864
+ ```
865
+
866
+ #### Vault File References
867
+
868
+ V2 supports vault file references:
869
+
870
+ ```typescript
871
+ const vaultFile = new TelaFile('vault://file-id')
872
+ ```
873
+
874
+ #### Skip Result Validation
875
+
876
+ When you have schemas defined, you can skip runtime validation for better performance (you'll still get TypeScript types):
877
+
878
+ ```typescript
879
+ const canvas = await tela.canvas.get({
880
+ id: 'canvas-id',
881
+ input: schema => schema.object({
882
+ query: schema.string()
883
+ }),
884
+ output: schema => schema.object({
885
+ answer: schema.string()
886
+ })
887
+ })
888
+
889
+ const execution = canvas.execute(
890
+ { query: 'Hello' },
891
+ { skipResultValidation: true } // Skip runtime validation, trust API response
892
+ )
893
+
894
+ // Still typed as { answer: string }, but no validation at runtime
895
+ const result = await execution.result
896
+ ```
897
+
898
+ #### Access Raw API Responses
899
+
900
+ V2 provides access to the raw, unprocessed API response alongside the parsed result:
901
+
902
+ ```typescript
903
+ const canvas = await tela.canvas.get({
904
+ id: 'canvas-id',
905
+ output: schema => schema.object({
906
+ answer: schema.string()
907
+ })
908
+ })
909
+
910
+ const execution = await canvas.execute({ query: 'Hello' })
911
+
912
+ // Get the parsed, validated result
913
+ const result = await execution.result
914
+ console.log(result.answer) // Type-safe access
915
+
916
+ // Get the raw API response
917
+ const rawResult = await execution.rawResult
918
+ console.log(rawResult) // Complete API response with all metadata
919
+
920
+ // You can await either one first - both will be available
921
+ const raw = await execution.rawResult // Triggers execution
922
+ const parsed = await execution.result // Reuses same execution
923
+ ```
924
+
925
+ This is useful when you need:
926
+ - Full API response metadata (execution IDs, timestamps, etc.)
927
+ - Debugging and logging complete responses
928
+ - Access to data not in your schema
929
+
930
+ #### Fetch Existing Executions and Event-Driven Polling
931
+
932
+ V2 introduces the ability to fetch existing async executions by ID and monitor them with event-driven polling:
933
+
934
+ ```typescript
935
+ // Start an async execution
936
+ const execution = await canvas.execute(
937
+ { query: 'Process this data' },
938
+ { async: true }
939
+ )
940
+
941
+ // Store the execution ID (e.g., in a database)
942
+ const executionId = execution.id
943
+
944
+ // Later, fetch the execution by ID
945
+ const fetchedExecution = await canvas.getExecution(executionId, {
946
+ pollingInterval: 2000,
947
+ pollingTimeout: 120000
948
+ })
949
+
950
+ // Use event-driven polling (non-blocking)
951
+ fetchedExecution.on('statusChange', (status) => {
952
+ console.log(`Status changed: ${status}`)
953
+ })
954
+
955
+ fetchedExecution.on('success', (result) => {
956
+ console.log('Completed!', result)
957
+ })
958
+
959
+ fetchedExecution.on('error', (error) => {
960
+ console.error('Failed:', error)
961
+ })
962
+
963
+ // Start polling without blocking
964
+ fetchedExecution.poll()
965
+
966
+ // Continue with other work...
967
+ console.log('Polling in background')
968
+ ```
969
+
970
+ This enables powerful use cases:
971
+ - **Job queue management**: Start jobs, store IDs, fetch later to check status
972
+ - **Resumable workflows**: Continue monitoring after page refresh or server restart
973
+ - **Multi-user dashboards**: Monitor executions started by different users
974
+ - **Real-time UI updates**: Non-blocking polling with event listeners
975
+ - **Background processing**: Track multiple executions concurrently
976
+
977
+ See [Fetch and Poll Examples](./examples/events/) for detailed patterns.
978
+
979
+ #### Execution Tags
980
+
981
+ V2 introduces support for tagging executions for filtering, categorization, and analytics:
982
+
983
+ ```typescript
984
+ // Add tags to any execution mode
985
+ const result = await canvas.execute(
986
+ { query: 'Analyze customer feedback' },
987
+ {
988
+ tags: ['production', 'analytics', 'customer-feedback']
989
+ }
990
+ ).result
991
+
992
+ // Tags work with async executions
993
+ const asyncResult = await canvas.execute(
994
+ { query: 'Process large dataset' },
995
+ {
996
+ async: true,
997
+ tags: ['batch-processing', 'analytics']
998
+ }
999
+ ).result
1000
+
1001
+ // Tags work with streaming executions
1002
+ for await (const chunk of canvas.execute(
1003
+ { query: 'Generate report' },
1004
+ {
1005
+ stream: true,
1006
+ tags: ['streaming', 'reports']
1007
+ }
1008
+ ).result) {
1009
+ process.stdout.write(chunk.result || '')
1010
+ }
1011
+ ```
1012
+
1013
+ **Use Cases:**
1014
+ - **Environment tracking**: Tag executions by environment (`production`, `staging`, `development`)
1015
+ - **Feature categorization**: Organize executions by feature (`analytics`, `reporting`, `summarization`)
1016
+ - **User segmentation**: Track executions by user tier or department
1017
+ - **Cost allocation**: Attribute API usage to specific projects or teams
1018
+ - **Performance monitoring**: Filter and analyze execution metrics by tag
1019
+
1020
+ #### Application Execution Labels
1021
+
1022
+ V2 adds support for setting workspace task titles when using deployed applications via `applicationId`:
1023
+
1024
+ ```typescript
1025
+ const canvas = await tela.canvas.get({
1026
+ applicationId: 'app-id' // Using deployed application instead of canvas ID
1027
+ })
1028
+
1029
+ // Set the title of the workspace task
1030
+ const result = await canvas.execute(
1031
+ { query: 'Process request' },
1032
+ {
1033
+ label: 'Customer Dashboard Query'
1034
+ }
1035
+ ).result
1036
+ ```
1037
+
1038
+ When you execute a canvas using `applicationId`, it creates a task in the application's workspace. The `label` field sets the title of that workspace task, making it easier to identify and track executions in your Tela workspace.
1039
+
1040
+ **Note:** The `label` field is only applicable when using `applicationId`. A warning will be logged if you provide a label without an `applicationId`.
1041
+
1042
+ **Use Cases:**
1043
+ - **Task identification**: Give meaningful titles to workspace tasks for easier tracking
1044
+ - **Execution categorization**: Organize tasks by feature or workflow in the workspace
1045
+ - **User-friendly naming**: Display clear task names instead of generic execution IDs
1046
+ - **Dashboard clarity**: Make workspace task lists more readable and searchable
1047
+
1048
+ ### Need Help?
1049
+
1050
+ If you encounter issues during migration, please:
1051
+ - Check the [examples](./examples/) directory for updated usage patterns
1052
+ - Review the [API documentation](./docs/)
1053
+ - Open an issue at [GitHub Issues](https://github.com/meistrari/tela-sdk-js/issues)