@flrande/bak-extension 0.3.0 → 0.3.1
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/.bak-e2e-build-stamp +1 -1
- package/dist/background.global.js +108 -29
- package/package.json +2 -2
- package/src/workspace.ts +118 -38
|
@@ -1 +1 @@
|
|
|
1
|
-
2026-03-
|
|
1
|
+
2026-03-09T04:26:20.181Z
|
|
@@ -43,12 +43,7 @@
|
|
|
43
43
|
this.browser = browser;
|
|
44
44
|
}
|
|
45
45
|
async getWorkspaceInfo(workspaceId = DEFAULT_WORKSPACE_ID) {
|
|
46
|
-
|
|
47
|
-
if (!state) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
const repaired = await this.ensureWorkspace({ workspaceId, focus: false, initialUrl: DEFAULT_WORKSPACE_URL });
|
|
51
|
-
return repaired.workspace;
|
|
46
|
+
return this.inspectWorkspace(workspaceId);
|
|
52
47
|
}
|
|
53
48
|
async ensureWorkspace(options = {}) {
|
|
54
49
|
const workspaceId = this.normalizeWorkspaceId(options.workspaceId);
|
|
@@ -57,7 +52,7 @@
|
|
|
57
52
|
const persisted = await this.storage.load();
|
|
58
53
|
const created = !persisted;
|
|
59
54
|
let state = this.normalizeState(persisted, workspaceId);
|
|
60
|
-
let window = state.windowId !== null ? await this.
|
|
55
|
+
let window = state.windowId !== null ? await this.waitForWindow(state.windowId) : null;
|
|
61
56
|
let tabs = [];
|
|
62
57
|
if (!window) {
|
|
63
58
|
const createdWindow = await this.browser.createWindow({
|
|
@@ -138,6 +133,12 @@
|
|
|
138
133
|
if (activeTab && !tabs.some((tab) => tab.id === activeTab.id)) {
|
|
139
134
|
tabs = [...tabs, activeTab];
|
|
140
135
|
}
|
|
136
|
+
if (tabs.length === 0 && state.primaryTabId !== null) {
|
|
137
|
+
const primaryTab = await this.waitForTrackedTab(state.primaryTabId, state.windowId);
|
|
138
|
+
if (primaryTab) {
|
|
139
|
+
tabs = [primaryTab];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
141
142
|
state.tabIds = [...new Set(tabs.map((tab) => tab.id))];
|
|
142
143
|
if (options.focus === true && state.activeTabId !== null) {
|
|
143
144
|
await this.browser.updateTab(state.activeTabId, { active: true });
|
|
@@ -162,18 +163,43 @@
|
|
|
162
163
|
focus: false,
|
|
163
164
|
initialUrl: options.url ?? DEFAULT_WORKSPACE_URL
|
|
164
165
|
});
|
|
165
|
-
|
|
166
|
+
let state = { ...ensured.workspace };
|
|
166
167
|
const active = options.active === true;
|
|
167
168
|
const desiredUrl = options.url ?? DEFAULT_WORKSPACE_URL;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
169
|
+
let reusablePrimaryTab = await this.resolveReusablePrimaryTab(
|
|
170
|
+
state,
|
|
171
|
+
ensured.created || ensured.repairActions.includes("recreated-window") || ensured.repairActions.includes("created-primary-tab")
|
|
172
|
+
);
|
|
173
|
+
let createdTab;
|
|
174
|
+
try {
|
|
175
|
+
createdTab = reusablePrimaryTab ? await this.browser.updateTab(reusablePrimaryTab.id, {
|
|
176
|
+
url: desiredUrl,
|
|
177
|
+
active
|
|
178
|
+
}) : await this.createWorkspaceTab({
|
|
179
|
+
windowId: state.windowId,
|
|
180
|
+
url: desiredUrl,
|
|
181
|
+
active
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (!this.isMissingWindowError(error)) {
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
const repaired = await this.ensureWorkspace({
|
|
188
|
+
workspaceId: options.workspaceId,
|
|
189
|
+
focus: false,
|
|
190
|
+
initialUrl: desiredUrl
|
|
191
|
+
});
|
|
192
|
+
state = { ...repaired.workspace };
|
|
193
|
+
reusablePrimaryTab = await this.resolveReusablePrimaryTab(state, true);
|
|
194
|
+
createdTab = reusablePrimaryTab ? await this.browser.updateTab(reusablePrimaryTab.id, {
|
|
195
|
+
url: desiredUrl,
|
|
196
|
+
active
|
|
197
|
+
}) : await this.createWorkspaceTab({
|
|
198
|
+
windowId: state.windowId,
|
|
199
|
+
url: desiredUrl,
|
|
200
|
+
active
|
|
201
|
+
});
|
|
202
|
+
}
|
|
177
203
|
const nextTabIds = [.../* @__PURE__ */ new Set([...state.tabIds, createdTab.id])];
|
|
178
204
|
const groupId = await this.browser.groupTabs([createdTab.id], state.groupId ?? void 0);
|
|
179
205
|
await this.browser.updateGroup(groupId, {
|
|
@@ -207,17 +233,30 @@
|
|
|
207
233
|
};
|
|
208
234
|
}
|
|
209
235
|
async listTabs(workspaceId = DEFAULT_WORKSPACE_ID) {
|
|
210
|
-
const ensured = await this.
|
|
236
|
+
const ensured = await this.inspectWorkspace(workspaceId);
|
|
237
|
+
if (!ensured) {
|
|
238
|
+
throw new Error(`Workspace ${workspaceId} does not exist`);
|
|
239
|
+
}
|
|
211
240
|
return {
|
|
212
|
-
workspace: ensured
|
|
213
|
-
tabs: ensured.
|
|
241
|
+
workspace: ensured,
|
|
242
|
+
tabs: ensured.tabs
|
|
214
243
|
};
|
|
215
244
|
}
|
|
216
245
|
async getActiveTab(workspaceId = DEFAULT_WORKSPACE_ID) {
|
|
217
|
-
const ensured = await this.
|
|
246
|
+
const ensured = await this.inspectWorkspace(workspaceId);
|
|
247
|
+
if (!ensured) {
|
|
248
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
249
|
+
return {
|
|
250
|
+
workspace: {
|
|
251
|
+
...this.normalizeState(null, normalizedWorkspaceId),
|
|
252
|
+
tabs: []
|
|
253
|
+
},
|
|
254
|
+
tab: null
|
|
255
|
+
};
|
|
256
|
+
}
|
|
218
257
|
return {
|
|
219
|
-
workspace: ensured
|
|
220
|
-
tab: ensured.
|
|
258
|
+
workspace: ensured,
|
|
259
|
+
tab: ensured.tabs.find((tab) => tab.id === ensured.activeTabId) ?? null
|
|
221
260
|
};
|
|
222
261
|
}
|
|
223
262
|
async setActiveTab(tabId, workspaceId = DEFAULT_WORKSPACE_ID) {
|
|
@@ -446,12 +485,6 @@
|
|
|
446
485
|
const deadline = Date.now() + 1500;
|
|
447
486
|
let lastError2 = null;
|
|
448
487
|
while (Date.now() < deadline) {
|
|
449
|
-
const window = await this.browser.getWindow(options.windowId);
|
|
450
|
-
if (!window) {
|
|
451
|
-
lastError2 = new Error(`No window with id: ${options.windowId}.`);
|
|
452
|
-
await this.delay(50);
|
|
453
|
-
continue;
|
|
454
|
-
}
|
|
455
488
|
try {
|
|
456
489
|
return await this.browser.createTab({
|
|
457
490
|
windowId: options.windowId,
|
|
@@ -468,6 +501,52 @@
|
|
|
468
501
|
}
|
|
469
502
|
throw lastError2 ?? new Error(`No window with id: ${options.windowId}.`);
|
|
470
503
|
}
|
|
504
|
+
async inspectWorkspace(workspaceId = DEFAULT_WORKSPACE_ID) {
|
|
505
|
+
const state = await this.loadWorkspaceRecord(workspaceId);
|
|
506
|
+
if (!state) {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
let tabs = await this.readTrackedTabs(state.tabIds, state.windowId);
|
|
510
|
+
const activeTab = state.activeTabId !== null ? await this.waitForTrackedTab(state.activeTabId, state.windowId, 300) : null;
|
|
511
|
+
if (activeTab && !tabs.some((tab) => tab.id === activeTab.id)) {
|
|
512
|
+
tabs = [...tabs, activeTab];
|
|
513
|
+
}
|
|
514
|
+
if (tabs.length === 0 && state.primaryTabId !== null) {
|
|
515
|
+
const primaryTab = await this.waitForTrackedTab(state.primaryTabId, state.windowId, 300);
|
|
516
|
+
if (primaryTab) {
|
|
517
|
+
tabs = [primaryTab];
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
...state,
|
|
522
|
+
tabIds: [...new Set(state.tabIds.concat(tabs.map((tab) => tab.id)))],
|
|
523
|
+
tabs
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
async resolveReusablePrimaryTab(workspace, allowReuse) {
|
|
527
|
+
if (!allowReuse || workspace.windowId === null) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
if (workspace.primaryTabId !== null) {
|
|
531
|
+
const trackedPrimary = workspace.tabs.find((tab) => tab.id === workspace.primaryTabId) ?? await this.waitForTrackedTab(workspace.primaryTabId, workspace.windowId);
|
|
532
|
+
if (trackedPrimary) {
|
|
533
|
+
return trackedPrimary;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
const windowTabs = await this.waitForWindowTabs(workspace.windowId, 750);
|
|
537
|
+
return windowTabs.length === 1 ? windowTabs[0] : null;
|
|
538
|
+
}
|
|
539
|
+
async waitForWindow(windowId, timeoutMs = 750) {
|
|
540
|
+
const deadline = Date.now() + timeoutMs;
|
|
541
|
+
while (Date.now() < deadline) {
|
|
542
|
+
const window = await this.browser.getWindow(windowId);
|
|
543
|
+
if (window) {
|
|
544
|
+
return window;
|
|
545
|
+
}
|
|
546
|
+
await this.delay(50);
|
|
547
|
+
}
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
471
550
|
async waitForTrackedTab(tabId, windowId, timeoutMs = 1e3) {
|
|
472
551
|
const deadline = Date.now() + timeoutMs;
|
|
473
552
|
while (Date.now() < deadline) {
|
package/package.json
CHANGED
package/src/workspace.ts
CHANGED
|
@@ -108,12 +108,7 @@ export class WorkspaceManager {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
async getWorkspaceInfo(workspaceId = DEFAULT_WORKSPACE_ID): Promise<WorkspaceInfo | null> {
|
|
111
|
-
|
|
112
|
-
if (!state) {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
const repaired = await this.ensureWorkspace({ workspaceId, focus: false, initialUrl: DEFAULT_WORKSPACE_URL });
|
|
116
|
-
return repaired.workspace;
|
|
111
|
+
return this.inspectWorkspace(workspaceId);
|
|
117
112
|
}
|
|
118
113
|
|
|
119
114
|
async ensureWorkspace(options: WorkspaceEnsureOptions = {}): Promise<WorkspaceEnsureResult> {
|
|
@@ -124,7 +119,7 @@ export class WorkspaceManager {
|
|
|
124
119
|
const created = !persisted;
|
|
125
120
|
let state = this.normalizeState(persisted, workspaceId);
|
|
126
121
|
|
|
127
|
-
let window = state.windowId !== null ? await this.
|
|
122
|
+
let window = state.windowId !== null ? await this.waitForWindow(state.windowId) : null;
|
|
128
123
|
let tabs: WorkspaceTab[] = [];
|
|
129
124
|
if (!window) {
|
|
130
125
|
const createdWindow = await this.browser.createWindow({
|
|
@@ -212,6 +207,12 @@ export class WorkspaceManager {
|
|
|
212
207
|
if (activeTab && !tabs.some((tab) => tab.id === activeTab.id)) {
|
|
213
208
|
tabs = [...tabs, activeTab];
|
|
214
209
|
}
|
|
210
|
+
if (tabs.length === 0 && state.primaryTabId !== null) {
|
|
211
|
+
const primaryTab = await this.waitForTrackedTab(state.primaryTabId, state.windowId);
|
|
212
|
+
if (primaryTab) {
|
|
213
|
+
tabs = [primaryTab];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
215
216
|
state.tabIds = [...new Set(tabs.map((tab) => tab.id))];
|
|
216
217
|
|
|
217
218
|
if (options.focus === true && state.activeTabId !== null) {
|
|
@@ -240,26 +241,48 @@ export class WorkspaceManager {
|
|
|
240
241
|
focus: false,
|
|
241
242
|
initialUrl: options.url ?? DEFAULT_WORKSPACE_URL
|
|
242
243
|
});
|
|
243
|
-
|
|
244
|
+
let state = { ...ensured.workspace };
|
|
244
245
|
const active = options.active === true;
|
|
245
246
|
const desiredUrl = options.url ?? DEFAULT_WORKSPACE_URL;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
247
|
+
let reusablePrimaryTab = await this.resolveReusablePrimaryTab(
|
|
248
|
+
state,
|
|
249
|
+
ensured.created || ensured.repairActions.includes('recreated-window') || ensured.repairActions.includes('created-primary-tab')
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
let createdTab: WorkspaceTab;
|
|
253
|
+
try {
|
|
254
|
+
createdTab = reusablePrimaryTab
|
|
255
|
+
? await this.browser.updateTab(reusablePrimaryTab.id, {
|
|
256
|
+
url: desiredUrl,
|
|
257
|
+
active
|
|
258
|
+
})
|
|
259
|
+
: await this.createWorkspaceTab({
|
|
260
|
+
windowId: state.windowId,
|
|
261
|
+
url: desiredUrl,
|
|
262
|
+
active
|
|
263
|
+
});
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (!this.isMissingWindowError(error)) {
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
const repaired = await this.ensureWorkspace({
|
|
269
|
+
workspaceId: options.workspaceId,
|
|
270
|
+
focus: false,
|
|
271
|
+
initialUrl: desiredUrl
|
|
272
|
+
});
|
|
273
|
+
state = { ...repaired.workspace };
|
|
274
|
+
reusablePrimaryTab = await this.resolveReusablePrimaryTab(state, true);
|
|
275
|
+
createdTab = reusablePrimaryTab
|
|
276
|
+
? await this.browser.updateTab(reusablePrimaryTab.id, {
|
|
277
|
+
url: desiredUrl,
|
|
278
|
+
active
|
|
279
|
+
})
|
|
280
|
+
: await this.createWorkspaceTab({
|
|
281
|
+
windowId: state.windowId,
|
|
282
|
+
url: desiredUrl,
|
|
283
|
+
active
|
|
284
|
+
});
|
|
285
|
+
}
|
|
263
286
|
const nextTabIds = [...new Set([...state.tabIds, createdTab.id])];
|
|
264
287
|
const groupId = await this.browser.groupTabs([createdTab.id], state.groupId ?? undefined);
|
|
265
288
|
await this.browser.updateGroup(groupId, {
|
|
@@ -296,18 +319,31 @@ export class WorkspaceManager {
|
|
|
296
319
|
}
|
|
297
320
|
|
|
298
321
|
async listTabs(workspaceId = DEFAULT_WORKSPACE_ID): Promise<{ workspace: WorkspaceInfo; tabs: WorkspaceTab[] }> {
|
|
299
|
-
const ensured = await this.
|
|
322
|
+
const ensured = await this.inspectWorkspace(workspaceId);
|
|
323
|
+
if (!ensured) {
|
|
324
|
+
throw new Error(`Workspace ${workspaceId} does not exist`);
|
|
325
|
+
}
|
|
300
326
|
return {
|
|
301
|
-
workspace: ensured
|
|
302
|
-
tabs: ensured.
|
|
327
|
+
workspace: ensured,
|
|
328
|
+
tabs: ensured.tabs
|
|
303
329
|
};
|
|
304
330
|
}
|
|
305
331
|
|
|
306
332
|
async getActiveTab(workspaceId = DEFAULT_WORKSPACE_ID): Promise<{ workspace: WorkspaceInfo; tab: WorkspaceTab | null }> {
|
|
307
|
-
const ensured = await this.
|
|
333
|
+
const ensured = await this.inspectWorkspace(workspaceId);
|
|
334
|
+
if (!ensured) {
|
|
335
|
+
const normalizedWorkspaceId = this.normalizeWorkspaceId(workspaceId);
|
|
336
|
+
return {
|
|
337
|
+
workspace: {
|
|
338
|
+
...this.normalizeState(null, normalizedWorkspaceId),
|
|
339
|
+
tabs: []
|
|
340
|
+
},
|
|
341
|
+
tab: null
|
|
342
|
+
};
|
|
343
|
+
}
|
|
308
344
|
return {
|
|
309
|
-
workspace: ensured
|
|
310
|
-
tab: ensured.
|
|
345
|
+
workspace: ensured,
|
|
346
|
+
tab: ensured.tabs.find((tab) => tab.id === ensured.activeTabId) ?? null
|
|
311
347
|
};
|
|
312
348
|
}
|
|
313
349
|
|
|
@@ -566,13 +602,6 @@ export class WorkspaceManager {
|
|
|
566
602
|
let lastError: Error | null = null;
|
|
567
603
|
|
|
568
604
|
while (Date.now() < deadline) {
|
|
569
|
-
const window = await this.browser.getWindow(options.windowId);
|
|
570
|
-
if (!window) {
|
|
571
|
-
lastError = new Error(`No window with id: ${options.windowId}.`);
|
|
572
|
-
await this.delay(50);
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
605
|
try {
|
|
577
606
|
return await this.browser.createTab({
|
|
578
607
|
windowId: options.windowId,
|
|
@@ -591,6 +620,57 @@ export class WorkspaceManager {
|
|
|
591
620
|
throw lastError ?? new Error(`No window with id: ${options.windowId}.`);
|
|
592
621
|
}
|
|
593
622
|
|
|
623
|
+
private async inspectWorkspace(workspaceId = DEFAULT_WORKSPACE_ID): Promise<WorkspaceInfo | null> {
|
|
624
|
+
const state = await this.loadWorkspaceRecord(workspaceId);
|
|
625
|
+
if (!state) {
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
let tabs = await this.readTrackedTabs(state.tabIds, state.windowId);
|
|
630
|
+
const activeTab = state.activeTabId !== null ? await this.waitForTrackedTab(state.activeTabId, state.windowId, 300) : null;
|
|
631
|
+
if (activeTab && !tabs.some((tab) => tab.id === activeTab.id)) {
|
|
632
|
+
tabs = [...tabs, activeTab];
|
|
633
|
+
}
|
|
634
|
+
if (tabs.length === 0 && state.primaryTabId !== null) {
|
|
635
|
+
const primaryTab = await this.waitForTrackedTab(state.primaryTabId, state.windowId, 300);
|
|
636
|
+
if (primaryTab) {
|
|
637
|
+
tabs = [primaryTab];
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return {
|
|
642
|
+
...state,
|
|
643
|
+
tabIds: [...new Set(state.tabIds.concat(tabs.map((tab) => tab.id)))],
|
|
644
|
+
tabs
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private async resolveReusablePrimaryTab(workspace: WorkspaceInfo, allowReuse: boolean): Promise<WorkspaceTab | null> {
|
|
649
|
+
if (!allowReuse || workspace.windowId === null) {
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
if (workspace.primaryTabId !== null) {
|
|
653
|
+
const trackedPrimary = workspace.tabs.find((tab) => tab.id === workspace.primaryTabId) ?? (await this.waitForTrackedTab(workspace.primaryTabId, workspace.windowId));
|
|
654
|
+
if (trackedPrimary) {
|
|
655
|
+
return trackedPrimary;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
const windowTabs = await this.waitForWindowTabs(workspace.windowId, 750);
|
|
659
|
+
return windowTabs.length === 1 ? windowTabs[0]! : null;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
private async waitForWindow(windowId: number, timeoutMs = 750): Promise<WorkspaceWindow | null> {
|
|
663
|
+
const deadline = Date.now() + timeoutMs;
|
|
664
|
+
while (Date.now() < deadline) {
|
|
665
|
+
const window = await this.browser.getWindow(windowId);
|
|
666
|
+
if (window) {
|
|
667
|
+
return window;
|
|
668
|
+
}
|
|
669
|
+
await this.delay(50);
|
|
670
|
+
}
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
|
|
594
674
|
private async waitForTrackedTab(tabId: number, windowId: number | null, timeoutMs = 1_000): Promise<WorkspaceTab | null> {
|
|
595
675
|
const deadline = Date.now() + timeoutMs;
|
|
596
676
|
while (Date.now() < deadline) {
|