@weave-apps/sdk 0.9.0 → 0.11.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.
Files changed (37) hide show
  1. package/dist/WeaveAPIClient.d.ts +49 -0
  2. package/dist/WeaveAPIClient.d.ts.map +1 -1
  3. package/dist/WeaveDOMAPI.d.ts +66 -0
  4. package/dist/WeaveDOMAPI.d.ts.map +1 -1
  5. package/dist/WeaveDOMAPI.js +51 -0
  6. package/dist/apis/api-mono/services/interfaces/WorkflowsTriggersWorkflowCompanyIdPostPost.d.ts +224 -0
  7. package/dist/apis/api-mono/services/interfaces/WorkflowsTriggersWorkflowCompanyIdPostPost.d.ts.map +1 -0
  8. package/dist/apis/api-mono/services/interfaces/WorkflowsTriggersWorkflowCompanyIdPostPost.js +66 -0
  9. package/dist/app-sdk/src/WeaveAPIClient.d.ts +370 -0
  10. package/dist/app-sdk/src/WeaveAPIClient.d.ts.map +1 -0
  11. package/dist/app-sdk/src/WeaveAPIClient.js +361 -0
  12. package/dist/app-sdk/src/WeaveAppInstanceAPI.d.ts +237 -0
  13. package/dist/app-sdk/src/WeaveAppInstanceAPI.d.ts.map +1 -0
  14. package/dist/app-sdk/src/WeaveAppInstanceAPI.js +395 -0
  15. package/dist/app-sdk/src/WeaveBackgroundAPI.d.ts +81 -0
  16. package/dist/app-sdk/src/WeaveBackgroundAPI.d.ts.map +1 -0
  17. package/dist/app-sdk/src/WeaveBackgroundAPI.js +165 -0
  18. package/dist/app-sdk/src/WeaveBaseApp.d.ts +318 -0
  19. package/dist/app-sdk/src/WeaveBaseApp.d.ts.map +1 -0
  20. package/dist/app-sdk/src/WeaveBaseApp.js +434 -0
  21. package/dist/app-sdk/src/WeaveCronAPI.d.ts +68 -0
  22. package/dist/app-sdk/src/WeaveCronAPI.d.ts.map +1 -0
  23. package/dist/app-sdk/src/WeaveCronAPI.js +172 -0
  24. package/dist/app-sdk/src/WeaveDOMAPI.d.ts +593 -0
  25. package/dist/app-sdk/src/WeaveDOMAPI.d.ts.map +1 -0
  26. package/dist/app-sdk/src/WeaveDOMAPI.js +774 -0
  27. package/dist/app-sdk/src/global.d.ts +25 -0
  28. package/dist/app-sdk/src/global.d.ts.map +1 -0
  29. package/dist/app-sdk/src/global.js +23 -0
  30. package/dist/app-sdk/src/index.d.ts +14 -0
  31. package/dist/app-sdk/src/index.d.ts.map +1 -0
  32. package/dist/app-sdk/src/index.js +14 -0
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +1 -1
  36. package/package.json +3 -3
  37. package/templates/WEAVE_SPEC.md +417 -1
@@ -0,0 +1,774 @@
1
+ /**
2
+ * Weave DOM API
3
+ *
4
+ * Client-side API for iframe apps to interact with the parent page DOM
5
+ * through the secure DOM Bridge in the content script.
6
+ */
7
+ /**
8
+ * DOM operation types (must match content script)
9
+ */
10
+ var DOMOperation;
11
+ (function (DOMOperation) {
12
+ // Read operations
13
+ DOMOperation["QUERY"] = "QUERY";
14
+ DOMOperation["QUERY_ALL"] = "QUERY_ALL";
15
+ DOMOperation["GET_TEXT"] = "GET_TEXT";
16
+ DOMOperation["GET_ATTRIBUTE"] = "GET_ATTRIBUTE";
17
+ DOMOperation["GET_VALUE"] = "GET_VALUE";
18
+ DOMOperation["IS_CHECKED"] = "IS_CHECKED";
19
+ DOMOperation["HAS_CLASS"] = "HAS_CLASS";
20
+ DOMOperation["GET_PAGE_URL"] = "GET_PAGE_URL";
21
+ // Write operations
22
+ DOMOperation["SET_TEXT"] = "SET_TEXT";
23
+ DOMOperation["SET_ATTRIBUTE"] = "SET_ATTRIBUTE";
24
+ DOMOperation["SET_VALUE"] = "SET_VALUE";
25
+ DOMOperation["ADD_CLASS"] = "ADD_CLASS";
26
+ DOMOperation["REMOVE_CLASS"] = "REMOVE_CLASS";
27
+ DOMOperation["TOGGLE_CLASS"] = "TOGGLE_CLASS";
28
+ DOMOperation["SET_STYLE"] = "SET_STYLE";
29
+ // DOM manipulation
30
+ DOMOperation["INSERT_HTML"] = "INSERT_HTML";
31
+ DOMOperation["REMOVE_ELEMENT"] = "REMOVE_ELEMENT";
32
+ // Element injection with event listeners
33
+ DOMOperation["INJECT_ELEMENT"] = "INJECT_ELEMENT";
34
+ DOMOperation["REMOVE_INJECTED_ELEMENT"] = "REMOVE_INJECTED_ELEMENT";
35
+ // Form operations
36
+ DOMOperation["GET_FORM_DATA"] = "GET_FORM_DATA";
37
+ DOMOperation["START_FORM_CLICK_LISTENER"] = "START_FORM_CLICK_LISTENER";
38
+ DOMOperation["STOP_FORM_CLICK_LISTENER"] = "STOP_FORM_CLICK_LISTENER";
39
+ DOMOperation["SET_FORM_FIELD_VALUE"] = "SET_FORM_FIELD_VALUE";
40
+ // Element click listener operations
41
+ DOMOperation["START_ELEMENT_CLICK_LISTENER"] = "START_ELEMENT_CLICK_LISTENER";
42
+ DOMOperation["STOP_ELEMENT_CLICK_LISTENER"] = "STOP_ELEMENT_CLICK_LISTENER";
43
+ // Element hover listener operations
44
+ DOMOperation["START_ELEMENT_HOVER_LISTENER"] = "START_ELEMENT_HOVER_LISTENER";
45
+ DOMOperation["STOP_ELEMENT_HOVER_LISTENER"] = "STOP_ELEMENT_HOVER_LISTENER";
46
+ // Click operation
47
+ DOMOperation["CLICK_ELEMENT"] = "CLICK_ELEMENT";
48
+ // Element watching operations
49
+ DOMOperation["WATCH_ELEMENT"] = "WATCH_ELEMENT";
50
+ DOMOperation["UNWATCH_ELEMENT"] = "UNWATCH_ELEMENT";
51
+ // Navigation operations
52
+ DOMOperation["RELOAD_PAGE"] = "RELOAD_PAGE";
53
+ // Scroll operations
54
+ DOMOperation["SCROLL_INTO_VIEW"] = "SCROLL_INTO_VIEW";
55
+ // Dialog operations
56
+ DOMOperation["SHOW_ALERT"] = "SHOW_ALERT";
57
+ DOMOperation["SHOW_CONFIRM"] = "SHOW_CONFIRM";
58
+ })(DOMOperation || (DOMOperation = {}));
59
+ /**
60
+ * Weave DOM API
61
+ * Provides methods for iframe apps to interact with parent page DOM
62
+ */
63
+ export class WeaveDOMAPI {
64
+ constructor() {
65
+ this.pendingRequests = new Map();
66
+ this.messageListener = null;
67
+ this.requestCounter = 0;
68
+ this.timeout = 5000; // 5 second timeout
69
+ this.formClickCallback = null;
70
+ this.workflowSavedCallback = null;
71
+ this.triggerSavedCallback = null;
72
+ this.elementClickCallbacks = new Map();
73
+ this.elementSelectorClickCallbacks = new Map();
74
+ this.elementSelectorHoverCallbacks = new Map();
75
+ this.elementChangeCallbacks = new Map();
76
+ this.elementIdCounter = 0;
77
+ this.watcherIdCounter = 0;
78
+ this.initialize();
79
+ }
80
+ /**
81
+ * Initialize the API and start listening for responses
82
+ */
83
+ initialize() {
84
+ this.messageListener = (event) => {
85
+ this.handleResponse(event);
86
+ };
87
+ window.addEventListener('message', this.messageListener);
88
+ }
89
+ /**
90
+ * Cleanup
91
+ */
92
+ destroy() {
93
+ if (this.messageListener) {
94
+ window.removeEventListener('message', this.messageListener);
95
+ this.messageListener = null;
96
+ }
97
+ this.pendingRequests.clear();
98
+ }
99
+ /**
100
+ * Handle response from content script
101
+ */
102
+ handleResponse(event) {
103
+ const data = event.data;
104
+ // Only accept messages from parent (content script)
105
+ if (event.source !== window.parent) {
106
+ return;
107
+ }
108
+ // Handle form click events
109
+ if (data?.type === 'WEAVE_FORM_CLICKED') {
110
+ if (this.formClickCallback) {
111
+ this.formClickCallback({
112
+ formData: data.formData,
113
+ clickedElement: data.clickedElement,
114
+ });
115
+ }
116
+ return;
117
+ }
118
+ // Handle workflow saved events
119
+ if (data?.type === 'WORKFLOW_SAVED') {
120
+ if (this.workflowSavedCallback) {
121
+ this.workflowSavedCallback(data.payload);
122
+ }
123
+ return;
124
+ }
125
+ // Handle trigger saved events
126
+ if (data?.type === 'TRIGGER_SAVED') {
127
+ if (this.triggerSavedCallback) {
128
+ this.triggerSavedCallback(data.payload);
129
+ }
130
+ return;
131
+ }
132
+ // Handle element click events (for injected elements)
133
+ if (data?.type === 'WEAVE_ELEMENT_CLICKED') {
134
+ const callback = this.elementClickCallbacks.get(data.elementId);
135
+ if (callback) {
136
+ callback({
137
+ elementId: data.elementId,
138
+ event: data.event,
139
+ });
140
+ }
141
+ return;
142
+ }
143
+ // Handle element selector click events (for element click listeners)
144
+ if (data?.type === 'WEAVE_ELEMENT_SELECTOR_CLICKED') {
145
+ const callback = this.elementSelectorClickCallbacks.get(data.listenerId);
146
+ if (callback) {
147
+ callback({
148
+ element: data.element,
149
+ event: data.event,
150
+ });
151
+ }
152
+ return;
153
+ }
154
+ // Handle element selector hover events (for element hover listeners)
155
+ if (data?.type === 'WEAVE_ELEMENT_SELECTOR_HOVERED') {
156
+ const callback = this.elementSelectorHoverCallbacks.get(data.listenerId);
157
+ if (callback) {
158
+ callback({
159
+ element: data.element,
160
+ event: data.event,
161
+ });
162
+ }
163
+ return;
164
+ }
165
+ // Handle element change events (for element watchers)
166
+ if (data?.type === 'WEAVE_ELEMENT_CHANGED') {
167
+ const callback = this.elementChangeCallbacks.get(data.watcherId);
168
+ if (callback) {
169
+ callback({
170
+ changeType: data.changeType,
171
+ element: data.element,
172
+ attributeName: data.attributeName,
173
+ attributeValue: data.attributeValue,
174
+ });
175
+ }
176
+ return;
177
+ }
178
+ // Handle normal DOM operation responses
179
+ if (data?.type !== 'WEAVE_DOM_RESPONSE') {
180
+ return;
181
+ }
182
+ const response = data;
183
+ const pending = this.pendingRequests.get(response.id);
184
+ if (!pending) {
185
+ return;
186
+ }
187
+ this.pendingRequests.delete(response.id);
188
+ if (response.success) {
189
+ pending.resolve(response.data);
190
+ }
191
+ else {
192
+ pending.reject(new Error(response.error || 'Unknown error'));
193
+ }
194
+ }
195
+ /**
196
+ * Send request to content script
197
+ */
198
+ sendRequest(operation, payload) {
199
+ return new Promise((resolve, reject) => {
200
+ const id = `dom-request-${++this.requestCounter}`;
201
+ const request = {
202
+ type: 'WEAVE_DOM_REQUEST',
203
+ id,
204
+ operation,
205
+ payload,
206
+ };
207
+ // Store pending request
208
+ this.pendingRequests.set(id, { resolve, reject });
209
+ // Set timeout
210
+ const timeoutId = setTimeout(() => {
211
+ this.pendingRequests.delete(id);
212
+ reject(new Error(`Request timeout: ${operation}`));
213
+ }, this.timeout);
214
+ // Clear timeout on resolve/reject
215
+ const originalResolve = resolve;
216
+ const originalReject = reject;
217
+ this.pendingRequests.set(id, {
218
+ resolve: (value) => {
219
+ clearTimeout(timeoutId);
220
+ originalResolve(value);
221
+ },
222
+ reject: (error) => {
223
+ clearTimeout(timeoutId);
224
+ originalReject(error);
225
+ },
226
+ });
227
+ // Send to parent window (content script will intercept)
228
+ window.parent.postMessage(request, '*');
229
+ });
230
+ }
231
+ // ============================================================================
232
+ // Read Operations
233
+ // ============================================================================
234
+ /**
235
+ * Query a single element from parent page
236
+ */
237
+ async query(selector) {
238
+ return this.sendRequest(DOMOperation.QUERY, { selector });
239
+ }
240
+ /**
241
+ * Query all matching elements from parent page
242
+ */
243
+ async queryAll(selector) {
244
+ return this.sendRequest(DOMOperation.QUERY_ALL, { selector });
245
+ }
246
+ /**
247
+ * Get text content of an element
248
+ */
249
+ async getText(selector) {
250
+ return this.sendRequest(DOMOperation.GET_TEXT, { selector });
251
+ }
252
+ /**
253
+ * Get attribute value of an element
254
+ */
255
+ async getAttribute(selector, attribute) {
256
+ return this.sendRequest(DOMOperation.GET_ATTRIBUTE, {
257
+ selector,
258
+ attribute
259
+ });
260
+ }
261
+ /**
262
+ * Get value of an input element
263
+ */
264
+ async getValue(selector) {
265
+ return this.sendRequest(DOMOperation.GET_VALUE, { selector });
266
+ }
267
+ /**
268
+ * Check if a checkbox or radio button is checked
269
+ * @param selector - CSS selector for the checkbox/radio element
270
+ * @returns Promise with true if checked, false otherwise
271
+ *
272
+ * @example
273
+ * ```typescript
274
+ * const isChecked = await weaveDOM.isChecked('#my-checkbox');
275
+ * if (isChecked) {
276
+ * console.log('Checkbox is checked');
277
+ * }
278
+ * ```
279
+ */
280
+ async isChecked(selector) {
281
+ return this.sendRequest(DOMOperation.IS_CHECKED, { selector });
282
+ }
283
+ /**
284
+ * Check if element has a class
285
+ */
286
+ async hasClass(selector, className) {
287
+ return this.sendRequest(DOMOperation.HAS_CLASS, {
288
+ selector,
289
+ className
290
+ });
291
+ }
292
+ /**
293
+ * Get the current page URL
294
+ * Returns the parent page URL (not the iframe URL)
295
+ * @returns Promise with the current page URL
296
+ */
297
+ async getPageUrl() {
298
+ return this.sendRequest(DOMOperation.GET_PAGE_URL, {});
299
+ }
300
+ /**
301
+ * Reload the current page
302
+ * Triggers a full page reload of the parent page
303
+ */
304
+ async reloadPage() {
305
+ return this.sendRequest(DOMOperation.RELOAD_PAGE, {});
306
+ }
307
+ // ============================================================================
308
+ // Write Operations
309
+ // ============================================================================
310
+ /**
311
+ * Set text content of an element
312
+ */
313
+ async setText(selector, text) {
314
+ return this.sendRequest(DOMOperation.SET_TEXT, { selector, text });
315
+ }
316
+ /**
317
+ * Set attribute value of an element
318
+ */
319
+ async setAttribute(selector, attribute, value) {
320
+ return this.sendRequest(DOMOperation.SET_ATTRIBUTE, {
321
+ selector,
322
+ attribute,
323
+ value
324
+ });
325
+ }
326
+ /**
327
+ * Set value of an input element
328
+ */
329
+ async setValue(selector, value) {
330
+ return this.sendRequest(DOMOperation.SET_VALUE, { selector, value });
331
+ }
332
+ /**
333
+ * Add a class to an element
334
+ */
335
+ async addClass(selector, className) {
336
+ return this.sendRequest(DOMOperation.ADD_CLASS, { selector, className });
337
+ }
338
+ /**
339
+ * Remove a class from an element
340
+ */
341
+ async removeClass(selector, className) {
342
+ return this.sendRequest(DOMOperation.REMOVE_CLASS, { selector, className });
343
+ }
344
+ /**
345
+ * Toggle a class on an element
346
+ */
347
+ async toggleClass(selector, className) {
348
+ return this.sendRequest(DOMOperation.TOGGLE_CLASS, { selector, className });
349
+ }
350
+ /**
351
+ * Set a CSS style property on an element
352
+ */
353
+ async setStyle(selector, property, value) {
354
+ return this.sendRequest(DOMOperation.SET_STYLE, {
355
+ selector,
356
+ property,
357
+ value
358
+ });
359
+ }
360
+ /**
361
+ * Insert HTML relative to an element
362
+ */
363
+ async insertHTML(selector, position, html) {
364
+ return this.sendRequest(DOMOperation.INSERT_HTML, {
365
+ selector,
366
+ position,
367
+ html
368
+ });
369
+ }
370
+ /**
371
+ * Remove an element from the DOM
372
+ */
373
+ async removeElement(selector) {
374
+ return this.sendRequest(DOMOperation.REMOVE_ELEMENT, { selector });
375
+ }
376
+ /**
377
+ * Click an element on the parent page
378
+ *
379
+ * @param selector - CSS selector for the element to click
380
+ * @returns Promise that resolves when the click is complete
381
+ *
382
+ * @example
383
+ * // Click a button
384
+ * await weaveDOM.clickElement('button#submit');
385
+ *
386
+ * // Click a link
387
+ * await weaveDOM.clickElement('a.nav-item[href="/dashboard"]');
388
+ */
389
+ async clickElement(selector) {
390
+ return this.sendRequest(DOMOperation.CLICK_ELEMENT, { selector });
391
+ }
392
+ // ============================================================================
393
+ // Form Operations
394
+ // ============================================================================
395
+ /**
396
+ * Get form data from a form or an element inside a form
397
+ * @param selector - CSS selector for the form or an element inside the form
398
+ * @returns Promise with complete form data including all fields
399
+ */
400
+ async getFormData(selector) {
401
+ return this.sendRequest(DOMOperation.GET_FORM_DATA, { selector });
402
+ }
403
+ /**
404
+ * Start listening for clicks on form elements
405
+ * When a form element is clicked, the callback will be invoked with the form data
406
+ * @param callback - Function to call when a form element is clicked
407
+ */
408
+ async startFormClickListener(callback) {
409
+ this.formClickCallback = callback;
410
+ return this.sendRequest(DOMOperation.START_FORM_CLICK_LISTENER, {});
411
+ }
412
+ /**
413
+ * Stop listening for clicks on form elements
414
+ */
415
+ async stopFormClickListener() {
416
+ this.formClickCallback = null;
417
+ return this.sendRequest(DOMOperation.STOP_FORM_CLICK_LISTENER, {});
418
+ }
419
+ /**
420
+ * Start listening for clicks on elements matching a CSS selector
421
+ * When an element is clicked, the callback will be invoked with element data
422
+ *
423
+ * @param selector - CSS selector for elements to listen to (e.g., 'div.sortable-item[role="button"]')
424
+ * @param callback - Function to call when a matching element is clicked
425
+ * @param options - Optional configuration
426
+ * @param options.listenerId - Unique ID for this listener (auto-generated if not provided)
427
+ * @returns Promise<string> - The listener ID (for cleanup with stopElementClickListener)
428
+ *
429
+ * @example
430
+ * // Listen to tab buttons
431
+ * const listenerId = await weaveDOM.startElementClickListener(
432
+ * 'div.sortable-item[role="button"]',
433
+ * (data) => {
434
+ * console.log('Tab clicked:', data.element.textContent);
435
+ * console.log('Element:', data.element);
436
+ * }
437
+ * );
438
+ *
439
+ * // Later, stop listening
440
+ * await weaveDOM.stopElementClickListener(listenerId);
441
+ */
442
+ async startElementClickListener(selector, callback, options) {
443
+ const listenerId = options?.listenerId || `listener-${Date.now()}-${Math.random()}`;
444
+ // Store callback
445
+ this.elementSelectorClickCallbacks.set(listenerId, callback);
446
+ // Send request to content script
447
+ await this.sendRequest(DOMOperation.START_ELEMENT_CLICK_LISTENER, {
448
+ selector,
449
+ listenerId,
450
+ });
451
+ return listenerId;
452
+ }
453
+ /**
454
+ * Stop listening for clicks on elements
455
+ *
456
+ * @param listenerId - The ID of the listener to stop (returned from startElementClickListener)
457
+ *
458
+ * @example
459
+ * const listenerId = await weaveDOM.startElementClickListener('button', callback);
460
+ * // ... later ...
461
+ * await weaveDOM.stopElementClickListener(listenerId);
462
+ */
463
+ async stopElementClickListener(listenerId) {
464
+ // Remove callback
465
+ this.elementSelectorClickCallbacks.delete(listenerId);
466
+ // Send request to content script
467
+ return this.sendRequest(DOMOperation.STOP_ELEMENT_CLICK_LISTENER, {
468
+ listenerId,
469
+ });
470
+ }
471
+ /**
472
+ * Start listening for hover on elements matching a CSS selector
473
+ * When an element is hovered for the configured delay, the callback will be invoked with element data
474
+ *
475
+ * @param selector - CSS selector for elements to listen to
476
+ * @param callback - Function to call when a matching element is hovered
477
+ * @param options - Optional configuration
478
+ * @param options.listenerId - Unique ID for this listener (auto-generated if not provided)
479
+ * @param options.hoverDelayMs - Delay in milliseconds before hover callback fires (default: 1000)
480
+ * @returns Promise<string> - The listener ID (for cleanup with stopElementHoverListener)
481
+ */
482
+ async startElementHoverListener(selector, callback, options) {
483
+ const listenerId = options?.listenerId || `hover-listener-${Date.now()}-${Math.random()}`;
484
+ // Store callback
485
+ this.elementSelectorHoverCallbacks.set(listenerId, callback);
486
+ // Send request to content script
487
+ await this.sendRequest(DOMOperation.START_ELEMENT_HOVER_LISTENER, {
488
+ selector,
489
+ listenerId,
490
+ hoverDelayMs: options?.hoverDelayMs,
491
+ });
492
+ return listenerId;
493
+ }
494
+ /**
495
+ * Stop listening for hover on elements
496
+ *
497
+ * @param listenerId - The ID of the listener to stop (returned from startElementHoverListener)
498
+ */
499
+ async stopElementHoverListener(listenerId) {
500
+ // Remove callback
501
+ this.elementSelectorHoverCallbacks.delete(listenerId);
502
+ // Send request to content script
503
+ return this.sendRequest(DOMOperation.STOP_ELEMENT_HOVER_LISTENER, {
504
+ listenerId,
505
+ });
506
+ }
507
+ /**
508
+ * Set the value of a form field
509
+ * Handles all input types (text, checkbox, radio, select, textarea, etc.)
510
+ * Automatically triggers validation events
511
+ *
512
+ * @param selector - CSS selector for the form field
513
+ * @param value - Value to set (string for text inputs, boolean for checkboxes, array for multi-selects)
514
+ * @param scrollIntoView - Optional: if true, scrolls element to center of viewport before setting value (default: false)
515
+ *
516
+ * @example
517
+ * // Text input
518
+ * await weaveDOM.setFormFieldValue('input[name="email"]', 'user@example.com');
519
+ *
520
+ * // Checkbox with scroll
521
+ * await weaveDOM.setFormFieldValue('input[name="terms"]', true, true);
522
+ *
523
+ * // Radio button (set to the value of the radio to select)
524
+ * await weaveDOM.setFormFieldValue('input[name="gender"][value="female"]', 'female');
525
+ *
526
+ * // Select
527
+ * await weaveDOM.setFormFieldValue('select[name="country"]', 'US');
528
+ *
529
+ * // Multi-select with scroll to make it visible
530
+ * await weaveDOM.setFormFieldValue('select[name="interests"]', ['sports', 'music'], true);
531
+ */
532
+ async setFormFieldValue(selector, value, scrollIntoView = false) {
533
+ return this.sendRequest(DOMOperation.SET_FORM_FIELD_VALUE, {
534
+ selector,
535
+ value,
536
+ scrollIntoView
537
+ });
538
+ }
539
+ // ============================================================================
540
+ // Event Listeners
541
+ // ============================================================================
542
+ /**
543
+ * Register a callback for when a workflow is saved
544
+ * @param callback - Function to call when a workflow is saved from the content script
545
+ */
546
+ onWorkflowSaved(callback) {
547
+ this.workflowSavedCallback = callback;
548
+ }
549
+ /**
550
+ * Remove the workflow saved callback
551
+ */
552
+ offWorkflowSaved() {
553
+ this.workflowSavedCallback = null;
554
+ }
555
+ /**
556
+ * Register a callback for when a trigger is saved
557
+ * @param callback - Function to call when a trigger is saved from the content script
558
+ */
559
+ onTriggerSaved(callback) {
560
+ this.triggerSavedCallback = callback;
561
+ }
562
+ /**
563
+ * Remove the trigger saved callback
564
+ */
565
+ offTriggerSaved() {
566
+ this.triggerSavedCallback = null;
567
+ }
568
+ // ============================================================================
569
+ // Element Injection Operations
570
+ // ============================================================================
571
+ /**
572
+ * Inject an element onto the parent page with optional click event listener
573
+ *
574
+ * @param targetSelector - CSS selector for the element to inject relative to
575
+ * @param position - Position relative to target ('beforebegin', 'afterbegin', 'beforeend', 'afterend')
576
+ * @param html - HTML string to inject (will be sanitized)
577
+ * @param options - Optional configuration
578
+ * @param options.onClick - Callback function to invoke when element is clicked
579
+ * @param options.elementId - Custom element ID (auto-generated if not provided)
580
+ * @returns Promise with the element ID
581
+ *
582
+ * @example
583
+ * // Inject a button with click handler
584
+ * const elementId = await weaveDOM.injectElement(
585
+ * 'body',
586
+ * 'beforeend',
587
+ * '<button class="my-button">Click Me</button>',
588
+ * {
589
+ * onClick: (data) => {
590
+ * console.log('Button clicked!', data);
591
+ * }
592
+ * }
593
+ * );
594
+ *
595
+ * // Later, remove the element
596
+ * await weaveDOM.removeInjectedElement(elementId);
597
+ */
598
+ async injectElement(targetSelector, position, html, options) {
599
+ // Generate element ID if not provided
600
+ const elementId = options?.elementId || `weave-injected-${++this.elementIdCounter}`;
601
+ // Store click callback if provided
602
+ if (options?.onClick) {
603
+ this.elementClickCallbacks.set(elementId, options.onClick);
604
+ }
605
+ // Send injection request
606
+ const result = await this.sendRequest(DOMOperation.INJECT_ELEMENT, {
607
+ targetSelector,
608
+ position,
609
+ html,
610
+ elementId,
611
+ onClick: !!options?.onClick,
612
+ });
613
+ return result.elementId;
614
+ }
615
+ /**
616
+ * Remove an injected element from the parent page
617
+ *
618
+ * @param elementId - ID of the element to remove (returned from injectElement)
619
+ *
620
+ * @example
621
+ * await weaveDOM.removeInjectedElement('weave-injected-1');
622
+ */
623
+ async removeInjectedElement(elementId) {
624
+ // Remove click callback if it exists
625
+ this.elementClickCallbacks.delete(elementId);
626
+ // Send removal request
627
+ return this.sendRequest(DOMOperation.REMOVE_INJECTED_ELEMENT, {
628
+ elementId,
629
+ });
630
+ }
631
+ // ============================================================================
632
+ // Element Watching Operations
633
+ // ============================================================================
634
+ /**
635
+ * Watch an element for changes (attributes, removal, children)
636
+ *
637
+ * Sets up MutationObservers to monitor:
638
+ * - Attribute changes on the element
639
+ * - Element removal from DOM
640
+ * - Child node changes (optional)
641
+ *
642
+ * @param selector - CSS selector for the element to watch
643
+ * @param callback - Function to call when element changes
644
+ * @param options - Optional configuration
645
+ * @param options.watchAttributes - Watch for attribute changes (default: true)
646
+ * @param options.watchChildren - Watch for child node changes (default: false)
647
+ * @param options.attributeFilter - Optional array of specific attributes to watch
648
+ * @returns Promise with the watcher ID (use to stop watching)
649
+ *
650
+ * @example
651
+ * // Watch for attribute changes
652
+ * const watcherId = await weaveDOM.watchElement(
653
+ * '#my-element',
654
+ * (data) => {
655
+ * if (data.changeType === 'attribute') {
656
+ * console.log(`Attribute ${data.attributeName} changed to: ${data.attributeValue}`);
657
+ * } else if (data.changeType === 'removed') {
658
+ * console.log('Element was removed from DOM');
659
+ * }
660
+ * },
661
+ * {
662
+ * watchAttributes: true,
663
+ * attributeFilter: ['class', 'data-status'] // Only watch specific attributes
664
+ * }
665
+ * );
666
+ *
667
+ * // Later, stop watching
668
+ * await weaveDOM.unwatchElement(watcherId);
669
+ */
670
+ async watchElement(selector, callback, options) {
671
+ // Generate watcher ID
672
+ const watcherId = `weave-watcher-${++this.watcherIdCounter}`;
673
+ // Store callback
674
+ this.elementChangeCallbacks.set(watcherId, callback);
675
+ // Send watch request
676
+ await this.sendRequest(DOMOperation.WATCH_ELEMENT, {
677
+ selector,
678
+ watcherId,
679
+ watchAttributes: options?.watchAttributes,
680
+ watchChildren: options?.watchChildren,
681
+ attributeFilter: options?.attributeFilter,
682
+ });
683
+ return watcherId;
684
+ }
685
+ /**
686
+ * Stop watching an element
687
+ *
688
+ * @param watcherId - ID of the watcher to stop (returned from watchElement)
689
+ *
690
+ * @example
691
+ * await weaveDOM.unwatchElement('weave-watcher-1');
692
+ */
693
+ async unwatchElement(watcherId) {
694
+ // Remove callback
695
+ this.elementChangeCallbacks.delete(watcherId);
696
+ // Send unwatch request
697
+ return this.sendRequest(DOMOperation.UNWATCH_ELEMENT, {
698
+ watcherId,
699
+ });
700
+ }
701
+ // ============================================================================
702
+ // Scroll Operations
703
+ // ============================================================================
704
+ /**
705
+ * Scroll an element into view on the parent page
706
+ *
707
+ * Abstraction over Element.scrollIntoView(). Accepts either a boolean
708
+ * `alignToTop` parameter or a `ScrollIntoViewOptions` object.
709
+ *
710
+ * @param selector - CSS selector for the element to scroll into view
711
+ * @param arg - Optional. A boolean (alignToTop) or a ScrollIntoViewOptions object.
712
+ * - `true` (default): aligns the element to the top of the scrollable ancestor
713
+ * - `false`: aligns the element to the bottom
714
+ * - `ScrollIntoViewOptions`: e.g. `{ behavior: 'smooth', block: 'center' }`
715
+ *
716
+ * @example
717
+ * // Scroll element to top (default)
718
+ * await weaveDOM.scrollIntoView('#my-element');
719
+ *
720
+ * // Scroll element to bottom
721
+ * await weaveDOM.scrollIntoView('#my-element', false);
722
+ *
723
+ * // Scroll with smooth animation to center
724
+ * await weaveDOM.scrollIntoView('#my-element', { behavior: 'smooth', block: 'center' });
725
+ */
726
+ async scrollIntoView(selector, arg) {
727
+ const payload = { selector };
728
+ if (typeof arg === 'boolean') {
729
+ payload.alignToTop = arg;
730
+ }
731
+ else if (typeof arg === 'object' && arg !== null) {
732
+ payload.options = arg;
733
+ }
734
+ return this.sendRequest(DOMOperation.SCROLL_INTO_VIEW, payload);
735
+ }
736
+ // ============================================================================
737
+ // Dialog Operations
738
+ // ============================================================================
739
+ /**
740
+ * Show an alert dialog on the parent page
741
+ *
742
+ * This displays a native browser alert dialog with the specified message.
743
+ * The dialog blocks until the user clicks OK.
744
+ *
745
+ * @param message - Message to display in the alert
746
+ *
747
+ * @example
748
+ * await weaveDOM.alert('Please log in to continue.');
749
+ */
750
+ async alert(message) {
751
+ return this.sendRequest(DOMOperation.SHOW_ALERT, { message });
752
+ }
753
+ /**
754
+ * Show a confirm dialog on the parent page
755
+ *
756
+ * This displays a native browser confirm dialog with the specified message.
757
+ * Returns true if user clicked OK, false if user clicked Cancel.
758
+ *
759
+ * @param message - Message to display in the confirm dialog
760
+ * @returns Promise<boolean> - true if confirmed, false if cancelled
761
+ *
762
+ * @example
763
+ * const confirmed = await weaveDOM.confirm('Are you sure you want to proceed?');
764
+ * if (confirmed) {
765
+ * // User clicked OK
766
+ * } else {
767
+ * // User clicked Cancel
768
+ * }
769
+ */
770
+ async confirm(message) {
771
+ return this.sendRequest(DOMOperation.SHOW_CONFIRM, { message });
772
+ }
773
+ }
774
+ export default new WeaveDOMAPI();