@weave-apps/sdk 0.1.17 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/WeaveAppInstanceAPI.d.ts +237 -0
- package/dist/WeaveAppInstanceAPI.d.ts.map +1 -0
- package/dist/WeaveAppInstanceAPI.js +395 -0
- package/dist/WeaveBaseApp.d.ts +29 -0
- package/dist/WeaveBaseApp.d.ts.map +1 -1
- package/dist/WeaveBaseApp.js +35 -0
- package/dist/WeaveDOMAPI.d.ts +58 -1
- package/dist/WeaveDOMAPI.d.ts.map +1 -1
- package/dist/WeaveDOMAPI.js +40 -0
- package/dist/global.d.ts +2 -0
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js +2 -0
- package/package.json +1 -1
- package/scripts/copy-sdk-files.js +1 -0
- package/dist/SidekickAPIClient.d.ts +0 -231
- package/dist/SidekickAPIClient.d.ts.map +0 -1
- package/dist/SidekickAPIClient.js +0 -274
- package/dist/SidekickBaseApp.d.ts +0 -177
- package/dist/SidekickBaseApp.d.ts.map +0 -1
- package/dist/SidekickBaseApp.js +0 -228
- package/dist/SidekickDOMAPI.d.ts +0 -433
- package/dist/SidekickDOMAPI.d.ts.map +0 -1
- package/dist/SidekickDOMAPI.js +0 -620
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WeaveAppInstanceAPI
|
|
3
|
+
*
|
|
4
|
+
* Client-side API for Weave apps to discover and communicate with
|
|
5
|
+
* other instances of themselves across browser tabs.
|
|
6
|
+
*
|
|
7
|
+
* Use cases:
|
|
8
|
+
* - Prevent duplicate automation runs across tabs
|
|
9
|
+
* - Coordinate which tab is "controlling" vs "observing"
|
|
10
|
+
* - Send messages between app instances
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Register this instance
|
|
15
|
+
* const instances = await this.instances.register({ url: window.location.href });
|
|
16
|
+
*
|
|
17
|
+
* // Claim controller status (for automation)
|
|
18
|
+
* const claimed = await this.instances.claimController();
|
|
19
|
+
*
|
|
20
|
+
* // Listen for messages from other instances
|
|
21
|
+
* this.instances.onMessage((msg, sender) => {
|
|
22
|
+
* if (msg.type === 'YIELD_CONTROL') {
|
|
23
|
+
* // Another tab wants control
|
|
24
|
+
* }
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* // Broadcast to all other instances
|
|
28
|
+
* await this.instances.broadcast({ type: 'YIELD_CONTROL' });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Represents a single app instance in a tab
|
|
33
|
+
*/
|
|
34
|
+
export interface AppInstance {
|
|
35
|
+
appId: string;
|
|
36
|
+
tabId: number;
|
|
37
|
+
url: string;
|
|
38
|
+
isController: boolean;
|
|
39
|
+
registeredAt: number;
|
|
40
|
+
metadata?: Record<string, any>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Message sent between app instances
|
|
44
|
+
*/
|
|
45
|
+
export interface InstanceMessage {
|
|
46
|
+
type: string;
|
|
47
|
+
data?: any;
|
|
48
|
+
senderId: number;
|
|
49
|
+
timestamp: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Callback for instance messages
|
|
53
|
+
*/
|
|
54
|
+
export type InstanceMessageCallback = (message: InstanceMessage) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Callback for instance changes
|
|
57
|
+
*/
|
|
58
|
+
export type InstanceChangeCallback = (instances: AppInstance[]) => void;
|
|
59
|
+
/**
|
|
60
|
+
* WeaveAppInstanceAPI class
|
|
61
|
+
*
|
|
62
|
+
* Provides methods for apps to discover and communicate with
|
|
63
|
+
* other instances of themselves across browser tabs.
|
|
64
|
+
*/
|
|
65
|
+
export declare class WeaveAppInstanceAPI {
|
|
66
|
+
private appId;
|
|
67
|
+
private _tabId;
|
|
68
|
+
constructor(appId: string);
|
|
69
|
+
/**
|
|
70
|
+
* Get this instance's tab ID (available after register)
|
|
71
|
+
*/
|
|
72
|
+
get tabId(): number | null;
|
|
73
|
+
/**
|
|
74
|
+
* Register this app instance
|
|
75
|
+
* Call this when your app starts to make it discoverable by other instances.
|
|
76
|
+
*
|
|
77
|
+
* @param metadata - Optional metadata to associate with this instance
|
|
78
|
+
* @returns All current instances of this app (including this one)
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* const instances = await this.instances.register({
|
|
82
|
+
* url: window.location.href,
|
|
83
|
+
* startedAt: Date.now()
|
|
84
|
+
* });
|
|
85
|
+
* console.log(`There are ${instances.length} instances of this app`);
|
|
86
|
+
*/
|
|
87
|
+
register(metadata?: Record<string, any>): Promise<AppInstance[]>;
|
|
88
|
+
/**
|
|
89
|
+
* Unregister this app instance
|
|
90
|
+
* Call this when your app is being destroyed.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* await this.instances.unregister();
|
|
94
|
+
*/
|
|
95
|
+
unregister(): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Get all instances of this app
|
|
98
|
+
*
|
|
99
|
+
* @returns All current instances
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* const instances = await this.instances.getInstances();
|
|
103
|
+
* const otherInstances = instances.filter(i => i.tabId !== this.instances.tabId);
|
|
104
|
+
*/
|
|
105
|
+
getInstances(): Promise<AppInstance[]>;
|
|
106
|
+
/**
|
|
107
|
+
* Claim controller status for this instance
|
|
108
|
+
* Only one instance can be controller at a time.
|
|
109
|
+
*
|
|
110
|
+
* @param force - If true, forcibly take control from current controller
|
|
111
|
+
* @returns true if successfully claimed, false if another instance is controller
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* const claimed = await this.instances.claimController();
|
|
115
|
+
* if (claimed) {
|
|
116
|
+
* // This instance is now the controller
|
|
117
|
+
* this.startAutomation();
|
|
118
|
+
* } else {
|
|
119
|
+
* // Another instance is already controlling
|
|
120
|
+
* this.showObserverUI();
|
|
121
|
+
* }
|
|
122
|
+
*/
|
|
123
|
+
claimController(force?: boolean): Promise<boolean>;
|
|
124
|
+
/**
|
|
125
|
+
* Release controller status
|
|
126
|
+
* Call this when you want to give up control.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* await this.instances.releaseController();
|
|
130
|
+
*/
|
|
131
|
+
releaseController(): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Check if this instance is the controller
|
|
134
|
+
*
|
|
135
|
+
* @returns true if this instance is the controller
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* if (await this.instances.isController()) {
|
|
139
|
+
* // We're in control
|
|
140
|
+
* }
|
|
141
|
+
*/
|
|
142
|
+
isController(): Promise<boolean>;
|
|
143
|
+
/**
|
|
144
|
+
* Get the current controller instance
|
|
145
|
+
*
|
|
146
|
+
* @returns The controller instance, or null if none
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* const controller = await this.instances.getController();
|
|
150
|
+
* if (controller) {
|
|
151
|
+
* console.log(`Controller is in tab ${controller.tabId}`);
|
|
152
|
+
* }
|
|
153
|
+
*/
|
|
154
|
+
getController(): Promise<AppInstance | null>;
|
|
155
|
+
/**
|
|
156
|
+
* Broadcast a message to all other instances
|
|
157
|
+
*
|
|
158
|
+
* @param message - Message to send (type and optional data)
|
|
159
|
+
* @returns Array of tab IDs that received the message
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* await this.instances.broadcast({
|
|
163
|
+
* type: 'YIELD_CONTROL',
|
|
164
|
+
* data: { reason: 'User requested control' }
|
|
165
|
+
* });
|
|
166
|
+
*/
|
|
167
|
+
broadcast(message: {
|
|
168
|
+
type: string;
|
|
169
|
+
data?: any;
|
|
170
|
+
}): Promise<number[]>;
|
|
171
|
+
/**
|
|
172
|
+
* Send a message to the controller instance
|
|
173
|
+
*
|
|
174
|
+
* @param message - Message to send
|
|
175
|
+
* @returns Tab ID of the controller, or null if no controller
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* await this.instances.sendToController({
|
|
179
|
+
* type: 'REQUEST_STATUS',
|
|
180
|
+
* });
|
|
181
|
+
*/
|
|
182
|
+
sendToController(message: {
|
|
183
|
+
type: string;
|
|
184
|
+
data?: any;
|
|
185
|
+
}): Promise<number | null>;
|
|
186
|
+
/**
|
|
187
|
+
* Send a message to a specific tab
|
|
188
|
+
*
|
|
189
|
+
* @param targetTabId - Tab ID to send to
|
|
190
|
+
* @param message - Message to send
|
|
191
|
+
* @returns true if sent successfully
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* await this.instances.sendToTab(123, {
|
|
195
|
+
* type: 'PING',
|
|
196
|
+
* });
|
|
197
|
+
*/
|
|
198
|
+
sendToTab(targetTabId: number, message: {
|
|
199
|
+
type: string;
|
|
200
|
+
data?: any;
|
|
201
|
+
}): Promise<boolean>;
|
|
202
|
+
/**
|
|
203
|
+
* Listen for messages from other instances
|
|
204
|
+
*
|
|
205
|
+
* @param callback - Function to call when a message is received
|
|
206
|
+
* @returns Unsubscribe function
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* const unsubscribe = this.instances.onMessage((msg) => {
|
|
210
|
+
* if (msg.type === 'YIELD_CONTROL') {
|
|
211
|
+
* this.releaseController();
|
|
212
|
+
* this.showObserverUI();
|
|
213
|
+
* }
|
|
214
|
+
* });
|
|
215
|
+
*
|
|
216
|
+
* // Later, to stop listening:
|
|
217
|
+
* unsubscribe();
|
|
218
|
+
*/
|
|
219
|
+
onMessage(callback: InstanceMessageCallback): () => void;
|
|
220
|
+
/**
|
|
221
|
+
* Listen for instance changes (new instances, removed instances)
|
|
222
|
+
*
|
|
223
|
+
* @param callback - Function to call when instances change
|
|
224
|
+
* @returns Unsubscribe function
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* const unsubscribe = this.instances.onInstanceChange((instances) => {
|
|
228
|
+
* console.log(`Now ${instances.length} instances`);
|
|
229
|
+
* const controller = instances.find(i => i.isController);
|
|
230
|
+
* if (controller) {
|
|
231
|
+
* console.log(`Controller is tab ${controller.tabId}`);
|
|
232
|
+
* }
|
|
233
|
+
* });
|
|
234
|
+
*/
|
|
235
|
+
onInstanceChange(callback: InstanceChangeCallback): () => void;
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=WeaveAppInstanceAPI.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WeaveAppInstanceAPI.d.ts","sourceRoot":"","sources":["../src/WeaveAppInstanceAPI.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAeH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAoBD;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;AAqHxE;;;;;GAKG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAuB;gBAEzB,KAAK,EAAE,MAAM;IAIzB;;OAEG;IACH,IAAI,KAAK,IAAI,MAAM,GAAG,IAAI,CAEzB;IAED;;;;;;;;;;;;;OAaG;IACG,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAetE;;;;;;OAMG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC;;;;;;;;OAQG;IACG,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAO5C;;;;;;;;;;;;;;;;OAgBG;IACG,eAAe,CAAC,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAQ/D;;;;;;OAMG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxC;;;;;;;;;OASG;IACG,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;IAOtC;;;;;;;;;;OAUG;IACG,aAAa,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAOlD;;;;;;;;;;;OAWG;IACG,SAAS,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQzE;;;;;;;;;;OAUG;IACG,gBAAgB,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQrF;;;;;;;;;;;OAWG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAS7F;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,CAAC,QAAQ,EAAE,uBAAuB,GAAG,MAAM,IAAI;IAsBxD;;;;;;;;;;;;;;OAcG;IACH,gBAAgB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,IAAI;CAqB/D"}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WeaveAppInstanceAPI
|
|
3
|
+
*
|
|
4
|
+
* Client-side API for Weave apps to discover and communicate with
|
|
5
|
+
* other instances of themselves across browser tabs.
|
|
6
|
+
*
|
|
7
|
+
* Use cases:
|
|
8
|
+
* - Prevent duplicate automation runs across tabs
|
|
9
|
+
* - Coordinate which tab is "controlling" vs "observing"
|
|
10
|
+
* - Send messages between app instances
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Register this instance
|
|
15
|
+
* const instances = await this.instances.register({ url: window.location.href });
|
|
16
|
+
*
|
|
17
|
+
* // Claim controller status (for automation)
|
|
18
|
+
* const claimed = await this.instances.claimController();
|
|
19
|
+
*
|
|
20
|
+
* // Listen for messages from other instances
|
|
21
|
+
* this.instances.onMessage((msg, sender) => {
|
|
22
|
+
* if (msg.type === 'YIELD_CONTROL') {
|
|
23
|
+
* // Another tab wants control
|
|
24
|
+
* }
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* // Broadcast to all other instances
|
|
28
|
+
* await this.instances.broadcast({ type: 'YIELD_CONTROL' });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
// Request timeout in ms
|
|
32
|
+
const REQUEST_TIMEOUT = 5000;
|
|
33
|
+
// Counter for unique request IDs
|
|
34
|
+
let requestIdCounter = 0;
|
|
35
|
+
// Pending requests waiting for response
|
|
36
|
+
const pendingRequests = new Map();
|
|
37
|
+
// Flag to track if listener is set up
|
|
38
|
+
let listenerInitialized = false;
|
|
39
|
+
// Global callbacks for instance messages (keyed by appId)
|
|
40
|
+
const messageCallbacks = new Map();
|
|
41
|
+
// Global callbacks for instance changes (keyed by appId)
|
|
42
|
+
const changeCallbacks = new Map();
|
|
43
|
+
/**
|
|
44
|
+
* Initialize the response listener (called once)
|
|
45
|
+
*/
|
|
46
|
+
function initializeListener() {
|
|
47
|
+
if (listenerInitialized)
|
|
48
|
+
return;
|
|
49
|
+
listenerInitialized = true;
|
|
50
|
+
window.addEventListener('message', (event) => {
|
|
51
|
+
const message = event.data;
|
|
52
|
+
// Handle app instance responses
|
|
53
|
+
if (message?.type === 'APP_INSTANCE_RESPONSE') {
|
|
54
|
+
const response = message;
|
|
55
|
+
const pending = pendingRequests.get(response.requestId);
|
|
56
|
+
if (!pending)
|
|
57
|
+
return;
|
|
58
|
+
// Clear timeout and remove from pending
|
|
59
|
+
clearTimeout(pending.timeout);
|
|
60
|
+
pendingRequests.delete(response.requestId);
|
|
61
|
+
if (response.success) {
|
|
62
|
+
pending.resolve(response);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
pending.reject(new Error(response.error || 'Unknown error'));
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Handle incoming instance messages from other tabs
|
|
70
|
+
if (message?.type === 'APP_INSTANCE_MESSAGE') {
|
|
71
|
+
const { appId, message: instanceMessage } = message.payload;
|
|
72
|
+
const callbacks = messageCallbacks.get(appId);
|
|
73
|
+
if (callbacks) {
|
|
74
|
+
for (const callback of callbacks) {
|
|
75
|
+
try {
|
|
76
|
+
callback(instanceMessage);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
console.error('[WeaveAppInstanceAPI] Error in message callback:', error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Handle instance change notifications
|
|
86
|
+
if (message?.type === 'APP_INSTANCE_CHANGE') {
|
|
87
|
+
const { appId, instances } = message.payload;
|
|
88
|
+
const callbacks = changeCallbacks.get(appId);
|
|
89
|
+
if (callbacks) {
|
|
90
|
+
for (const callback of callbacks) {
|
|
91
|
+
try {
|
|
92
|
+
callback(instances);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error('[WeaveAppInstanceAPI] Error in change callback:', error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Send a message to the background service via content script
|
|
105
|
+
*/
|
|
106
|
+
async function sendMessage(type, payload) {
|
|
107
|
+
// Initialize listener on first use
|
|
108
|
+
initializeListener();
|
|
109
|
+
const requestId = `app-${++requestIdCounter}-${Date.now()}`;
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
// Set up timeout
|
|
112
|
+
const timeout = setTimeout(() => {
|
|
113
|
+
pendingRequests.delete(requestId);
|
|
114
|
+
reject(new Error(`App instance request timed out: ${type}`));
|
|
115
|
+
}, REQUEST_TIMEOUT);
|
|
116
|
+
// Store pending request
|
|
117
|
+
pendingRequests.set(requestId, { resolve, reject, timeout });
|
|
118
|
+
// Send message to content script (which forwards to background)
|
|
119
|
+
window.parent.postMessage({
|
|
120
|
+
type,
|
|
121
|
+
requestId,
|
|
122
|
+
payload,
|
|
123
|
+
}, '*');
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* WeaveAppInstanceAPI class
|
|
128
|
+
*
|
|
129
|
+
* Provides methods for apps to discover and communicate with
|
|
130
|
+
* other instances of themselves across browser tabs.
|
|
131
|
+
*/
|
|
132
|
+
export class WeaveAppInstanceAPI {
|
|
133
|
+
constructor(appId) {
|
|
134
|
+
this._tabId = null;
|
|
135
|
+
this.appId = appId;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get this instance's tab ID (available after register)
|
|
139
|
+
*/
|
|
140
|
+
get tabId() {
|
|
141
|
+
return this._tabId;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Register this app instance
|
|
145
|
+
* Call this when your app starts to make it discoverable by other instances.
|
|
146
|
+
*
|
|
147
|
+
* @param metadata - Optional metadata to associate with this instance
|
|
148
|
+
* @returns All current instances of this app (including this one)
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* const instances = await this.instances.register({
|
|
152
|
+
* url: window.location.href,
|
|
153
|
+
* startedAt: Date.now()
|
|
154
|
+
* });
|
|
155
|
+
* console.log(`There are ${instances.length} instances of this app`);
|
|
156
|
+
*/
|
|
157
|
+
async register(metadata) {
|
|
158
|
+
const response = await sendMessage('APP_REGISTER_INSTANCE', {
|
|
159
|
+
appId: this.appId,
|
|
160
|
+
url: window.location.href,
|
|
161
|
+
metadata,
|
|
162
|
+
});
|
|
163
|
+
// Store our tab ID
|
|
164
|
+
if (response.tabId) {
|
|
165
|
+
this._tabId = response.tabId;
|
|
166
|
+
}
|
|
167
|
+
return response.instances || [];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Unregister this app instance
|
|
171
|
+
* Call this when your app is being destroyed.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* await this.instances.unregister();
|
|
175
|
+
*/
|
|
176
|
+
async unregister() {
|
|
177
|
+
await sendMessage('APP_UNREGISTER_INSTANCE', {
|
|
178
|
+
appId: this.appId,
|
|
179
|
+
});
|
|
180
|
+
this._tabId = null;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get all instances of this app
|
|
184
|
+
*
|
|
185
|
+
* @returns All current instances
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* const instances = await this.instances.getInstances();
|
|
189
|
+
* const otherInstances = instances.filter(i => i.tabId !== this.instances.tabId);
|
|
190
|
+
*/
|
|
191
|
+
async getInstances() {
|
|
192
|
+
const response = await sendMessage('APP_GET_INSTANCES', {
|
|
193
|
+
appId: this.appId,
|
|
194
|
+
});
|
|
195
|
+
return response.instances || [];
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Claim controller status for this instance
|
|
199
|
+
* Only one instance can be controller at a time.
|
|
200
|
+
*
|
|
201
|
+
* @param force - If true, forcibly take control from current controller
|
|
202
|
+
* @returns true if successfully claimed, false if another instance is controller
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* const claimed = await this.instances.claimController();
|
|
206
|
+
* if (claimed) {
|
|
207
|
+
* // This instance is now the controller
|
|
208
|
+
* this.startAutomation();
|
|
209
|
+
* } else {
|
|
210
|
+
* // Another instance is already controlling
|
|
211
|
+
* this.showObserverUI();
|
|
212
|
+
* }
|
|
213
|
+
*/
|
|
214
|
+
async claimController(force = false) {
|
|
215
|
+
const response = await sendMessage('APP_CLAIM_CONTROLLER', {
|
|
216
|
+
appId: this.appId,
|
|
217
|
+
force,
|
|
218
|
+
});
|
|
219
|
+
return response.claimed ?? false;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Release controller status
|
|
223
|
+
* Call this when you want to give up control.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* await this.instances.releaseController();
|
|
227
|
+
*/
|
|
228
|
+
async releaseController() {
|
|
229
|
+
await sendMessage('APP_RELEASE_CONTROLLER', {
|
|
230
|
+
appId: this.appId,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Check if this instance is the controller
|
|
235
|
+
*
|
|
236
|
+
* @returns true if this instance is the controller
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* if (await this.instances.isController()) {
|
|
240
|
+
* // We're in control
|
|
241
|
+
* }
|
|
242
|
+
*/
|
|
243
|
+
async isController() {
|
|
244
|
+
const response = await sendMessage('APP_IS_CONTROLLER', {
|
|
245
|
+
appId: this.appId,
|
|
246
|
+
});
|
|
247
|
+
return response.isController ?? false;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Get the current controller instance
|
|
251
|
+
*
|
|
252
|
+
* @returns The controller instance, or null if none
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* const controller = await this.instances.getController();
|
|
256
|
+
* if (controller) {
|
|
257
|
+
* console.log(`Controller is in tab ${controller.tabId}`);
|
|
258
|
+
* }
|
|
259
|
+
*/
|
|
260
|
+
async getController() {
|
|
261
|
+
const response = await sendMessage('APP_GET_CONTROLLER', {
|
|
262
|
+
appId: this.appId,
|
|
263
|
+
});
|
|
264
|
+
return response.controller ?? null;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Broadcast a message to all other instances
|
|
268
|
+
*
|
|
269
|
+
* @param message - Message to send (type and optional data)
|
|
270
|
+
* @returns Array of tab IDs that received the message
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* await this.instances.broadcast({
|
|
274
|
+
* type: 'YIELD_CONTROL',
|
|
275
|
+
* data: { reason: 'User requested control' }
|
|
276
|
+
* });
|
|
277
|
+
*/
|
|
278
|
+
async broadcast(message) {
|
|
279
|
+
const response = await sendMessage('APP_BROADCAST', {
|
|
280
|
+
appId: this.appId,
|
|
281
|
+
instanceMessage: message,
|
|
282
|
+
});
|
|
283
|
+
return response.recipientTabIds || [];
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Send a message to the controller instance
|
|
287
|
+
*
|
|
288
|
+
* @param message - Message to send
|
|
289
|
+
* @returns Tab ID of the controller, or null if no controller
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* await this.instances.sendToController({
|
|
293
|
+
* type: 'REQUEST_STATUS',
|
|
294
|
+
* });
|
|
295
|
+
*/
|
|
296
|
+
async sendToController(message) {
|
|
297
|
+
const response = await sendMessage('APP_SEND_TO_CONTROLLER', {
|
|
298
|
+
appId: this.appId,
|
|
299
|
+
instanceMessage: message,
|
|
300
|
+
});
|
|
301
|
+
return response.controllerTabId ?? null;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Send a message to a specific tab
|
|
305
|
+
*
|
|
306
|
+
* @param targetTabId - Tab ID to send to
|
|
307
|
+
* @param message - Message to send
|
|
308
|
+
* @returns true if sent successfully
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* await this.instances.sendToTab(123, {
|
|
312
|
+
* type: 'PING',
|
|
313
|
+
* });
|
|
314
|
+
*/
|
|
315
|
+
async sendToTab(targetTabId, message) {
|
|
316
|
+
const response = await sendMessage('APP_SEND_TO_TAB', {
|
|
317
|
+
appId: this.appId,
|
|
318
|
+
targetTabId,
|
|
319
|
+
instanceMessage: message,
|
|
320
|
+
});
|
|
321
|
+
return response.sent ?? false;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Listen for messages from other instances
|
|
325
|
+
*
|
|
326
|
+
* @param callback - Function to call when a message is received
|
|
327
|
+
* @returns Unsubscribe function
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* const unsubscribe = this.instances.onMessage((msg) => {
|
|
331
|
+
* if (msg.type === 'YIELD_CONTROL') {
|
|
332
|
+
* this.releaseController();
|
|
333
|
+
* this.showObserverUI();
|
|
334
|
+
* }
|
|
335
|
+
* });
|
|
336
|
+
*
|
|
337
|
+
* // Later, to stop listening:
|
|
338
|
+
* unsubscribe();
|
|
339
|
+
*/
|
|
340
|
+
onMessage(callback) {
|
|
341
|
+
// Initialize listener
|
|
342
|
+
initializeListener();
|
|
343
|
+
// Get or create callback set for this app
|
|
344
|
+
let callbacks = messageCallbacks.get(this.appId);
|
|
345
|
+
if (!callbacks) {
|
|
346
|
+
callbacks = new Set();
|
|
347
|
+
messageCallbacks.set(this.appId, callbacks);
|
|
348
|
+
}
|
|
349
|
+
callbacks.add(callback);
|
|
350
|
+
// Return unsubscribe function
|
|
351
|
+
return () => {
|
|
352
|
+
callbacks?.delete(callback);
|
|
353
|
+
if (callbacks?.size === 0) {
|
|
354
|
+
messageCallbacks.delete(this.appId);
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Listen for instance changes (new instances, removed instances)
|
|
360
|
+
*
|
|
361
|
+
* @param callback - Function to call when instances change
|
|
362
|
+
* @returns Unsubscribe function
|
|
363
|
+
*
|
|
364
|
+
* @example
|
|
365
|
+
* const unsubscribe = this.instances.onInstanceChange((instances) => {
|
|
366
|
+
* console.log(`Now ${instances.length} instances`);
|
|
367
|
+
* const controller = instances.find(i => i.isController);
|
|
368
|
+
* if (controller) {
|
|
369
|
+
* console.log(`Controller is tab ${controller.tabId}`);
|
|
370
|
+
* }
|
|
371
|
+
* });
|
|
372
|
+
*/
|
|
373
|
+
onInstanceChange(callback) {
|
|
374
|
+
// Initialize listener
|
|
375
|
+
initializeListener();
|
|
376
|
+
// Get or create callback set for this app
|
|
377
|
+
let callbacks = changeCallbacks.get(this.appId);
|
|
378
|
+
if (!callbacks) {
|
|
379
|
+
callbacks = new Set();
|
|
380
|
+
changeCallbacks.set(this.appId, callbacks);
|
|
381
|
+
}
|
|
382
|
+
callbacks.add(callback);
|
|
383
|
+
// Return unsubscribe function
|
|
384
|
+
return () => {
|
|
385
|
+
callbacks?.delete(callback);
|
|
386
|
+
if (callbacks?.size === 0) {
|
|
387
|
+
changeCallbacks.delete(this.appId);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// Export for global usage
|
|
393
|
+
if (typeof window !== 'undefined') {
|
|
394
|
+
window.WeaveAppInstanceAPI = WeaveAppInstanceAPI;
|
|
395
|
+
}
|
package/dist/WeaveBaseApp.d.ts
CHANGED
|
@@ -116,6 +116,8 @@ export declare abstract class WeaveBaseApp<TSettings = Record<string, any>, TSta
|
|
|
116
116
|
private _backgroundAPI;
|
|
117
117
|
/** Cron API for scheduled tasks */
|
|
118
118
|
private _cronAPI;
|
|
119
|
+
/** App Instance API for cross-tab communication */
|
|
120
|
+
private _instancesAPI;
|
|
119
121
|
/** Debounced persist function */
|
|
120
122
|
private _debouncedPersist;
|
|
121
123
|
/** Flag to track if state has been restored */
|
|
@@ -142,6 +144,33 @@ export declare abstract class WeaveBaseApp<TSettings = Record<string, any>, TSta
|
|
|
142
144
|
* Get the cron API for scheduled tasks
|
|
143
145
|
*/
|
|
144
146
|
protected get cron(): any;
|
|
147
|
+
/**
|
|
148
|
+
* Get the app instance API for cross-tab communication
|
|
149
|
+
*
|
|
150
|
+
* Use this to:
|
|
151
|
+
* - Discover other instances of your app across browser tabs
|
|
152
|
+
* - Claim/release controller status (for automation coordination)
|
|
153
|
+
* - Send messages between instances
|
|
154
|
+
* - Listen for instance changes
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* // Register this instance and check for others
|
|
158
|
+
* const instances = await this.instances.register();
|
|
159
|
+
* if (instances.length > 1) {
|
|
160
|
+
* // Other instances exist
|
|
161
|
+
* }
|
|
162
|
+
*
|
|
163
|
+
* // Claim controller status
|
|
164
|
+
* const claimed = await this.instances.claimController();
|
|
165
|
+
*
|
|
166
|
+
* // Listen for messages from other instances
|
|
167
|
+
* this.instances.onMessage((msg) => {
|
|
168
|
+
* if (msg.type === 'YIELD_CONTROL') {
|
|
169
|
+
* // Another tab wants control
|
|
170
|
+
* }
|
|
171
|
+
* });
|
|
172
|
+
*/
|
|
173
|
+
protected get instances(): any;
|
|
145
174
|
/**
|
|
146
175
|
* Register a cron job with a callback
|
|
147
176
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WeaveBaseApp.d.ts","sourceRoot":"","sources":["../src/WeaveBaseApp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAKH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,GAAG,CAAC;IACV,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAMhB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAsBD;;;;GAIG;AACH,8BAAsB,YAAY,CAChC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAC5B,SAAQ,WAAW;IACnB,mBAAmB;IACnB,SAAS,CAAC,OAAO,EAAE,YAAY,CAAC;IAEhC,2CAA2C;IAC3C,SAAS,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC;IAElC,gDAAgD;IAChD,SAAS,CAAC,KAAK,EAAE,MAAM,CAAgB;IAEvC,sCAAsC;IACtC,OAAO,CAAC,WAAW,CAAa;IAEhC,mEAAmE;IACnE,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC;IAExB,2CAA2C;IAC3C,OAAO,CAAC,cAAc,CAAa;IAEnC,mCAAmC;IACnC,OAAO,CAAC,QAAQ,CAAa;IAE7B,iCAAiC;IACjC,OAAO,CAAC,iBAAiB,CAA6B;IAEtD,+CAA+C;IAC/C,OAAO,CAAC,cAAc,CAAkB;IAExC,6BAA6B;IAC7B,OAAO,CAAC,QAAQ,CAAuB;IAEvC,oDAAoD;IACpD,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE5C,0BAA0B;IAC1B,SAAS,CAAC,WAAW,EAAE,GAAG,CAAQ;IAElC,6BAA6B;IAC7B,SAAS,CAAC,cAAc,EAAE,GAAG,CAAQ;IAErC;;;OAGG;gBACS,OAAO,EAAE,YAAY;
|
|
1
|
+
{"version":3,"file":"WeaveBaseApp.d.ts","sourceRoot":"","sources":["../src/WeaveBaseApp.ts"],"names":[],"mappings":"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"}
|