@flrande/browserctl 0.5.0 → 0.6.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/dist/client.d.ts +34 -0
- package/dist/client.js +138 -0
- package/dist/commandRegistry.d.ts +16 -0
- package/dist/commandRegistry.js +21 -0
- package/dist/help.d.ts +4 -0
- package/dist/help.js +24 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +23 -0
- package/dist/runCli.d.ts +5 -0
- package/dist/runCli.js +170 -0
- package/package.json +32 -57
- package/INSTALL-CN.md +0 -92
- package/INSTALL.md +0 -92
- package/LICENSE +0 -21
- package/README-CN.md +0 -69
- package/README.md +0 -69
- package/apps/browserctl/src/commands/a11y-snapshot.ts +0 -20
- package/apps/browserctl/src/commands/act.test.ts +0 -71
- package/apps/browserctl/src/commands/act.ts +0 -64
- package/apps/browserctl/src/commands/command-wrappers.test.ts +0 -688
- package/apps/browserctl/src/commands/common.test.ts +0 -87
- package/apps/browserctl/src/commands/common.ts +0 -191
- package/apps/browserctl/src/commands/console-list.test.ts +0 -102
- package/apps/browserctl/src/commands/console-list.ts +0 -108
- package/apps/browserctl/src/commands/cookie-clear.ts +0 -18
- package/apps/browserctl/src/commands/cookie-get.ts +0 -18
- package/apps/browserctl/src/commands/cookie-set.ts +0 -22
- package/apps/browserctl/src/commands/dialog-arm.ts +0 -20
- package/apps/browserctl/src/commands/dom-query-all.ts +0 -18
- package/apps/browserctl/src/commands/dom-query.ts +0 -18
- package/apps/browserctl/src/commands/download-trigger.ts +0 -22
- package/apps/browserctl/src/commands/download-wait.test.ts +0 -67
- package/apps/browserctl/src/commands/download-wait.ts +0 -27
- package/apps/browserctl/src/commands/element-screenshot.ts +0 -20
- package/apps/browserctl/src/commands/frame-list.ts +0 -16
- package/apps/browserctl/src/commands/frame-snapshot.ts +0 -18
- package/apps/browserctl/src/commands/har-export.test.ts +0 -112
- package/apps/browserctl/src/commands/har-export.ts +0 -120
- package/apps/browserctl/src/commands/memory-delete.ts +0 -20
- package/apps/browserctl/src/commands/memory-inspect.ts +0 -20
- package/apps/browserctl/src/commands/memory-list.ts +0 -90
- package/apps/browserctl/src/commands/memory-mode-set.ts +0 -29
- package/apps/browserctl/src/commands/memory-purge.ts +0 -16
- package/apps/browserctl/src/commands/memory-resolve.ts +0 -56
- package/apps/browserctl/src/commands/memory-status.ts +0 -16
- package/apps/browserctl/src/commands/memory-ttl-set.ts +0 -28
- package/apps/browserctl/src/commands/memory-upsert.ts +0 -142
- package/apps/browserctl/src/commands/network-list.test.ts +0 -110
- package/apps/browserctl/src/commands/network-list.ts +0 -112
- package/apps/browserctl/src/commands/network-wait-for.test.ts +0 -90
- package/apps/browserctl/src/commands/network-wait-for.ts +0 -100
- package/apps/browserctl/src/commands/profile-list.ts +0 -16
- package/apps/browserctl/src/commands/profile-use.ts +0 -18
- package/apps/browserctl/src/commands/response-body.ts +0 -24
- package/apps/browserctl/src/commands/screenshot.ts +0 -16
- package/apps/browserctl/src/commands/session-drop.test.ts +0 -36
- package/apps/browserctl/src/commands/session-drop.ts +0 -16
- package/apps/browserctl/src/commands/session-list.test.ts +0 -81
- package/apps/browserctl/src/commands/session-list.ts +0 -70
- package/apps/browserctl/src/commands/snapshot.ts +0 -16
- package/apps/browserctl/src/commands/status.ts +0 -10
- package/apps/browserctl/src/commands/storage-get.ts +0 -20
- package/apps/browserctl/src/commands/storage-set.ts +0 -22
- package/apps/browserctl/src/commands/tab-close.ts +0 -20
- package/apps/browserctl/src/commands/tab-focus.ts +0 -20
- package/apps/browserctl/src/commands/tab-open.ts +0 -19
- package/apps/browserctl/src/commands/tabs.ts +0 -13
- package/apps/browserctl/src/commands/trace-get.test.ts +0 -61
- package/apps/browserctl/src/commands/trace-get.ts +0 -62
- package/apps/browserctl/src/commands/upload-arm.ts +0 -26
- package/apps/browserctl/src/commands/wait-element.test.ts +0 -80
- package/apps/browserctl/src/commands/wait-element.ts +0 -76
- package/apps/browserctl/src/commands/wait-text.test.ts +0 -110
- package/apps/browserctl/src/commands/wait-text.ts +0 -93
- package/apps/browserctl/src/commands/wait-url.test.ts +0 -80
- package/apps/browserctl/src/commands/wait-url.ts +0 -76
- package/apps/browserctl/src/daemon-client.test.ts +0 -253
- package/apps/browserctl/src/daemon-client.ts +0 -632
- package/apps/browserctl/src/e2e.test.ts +0 -103
- package/apps/browserctl/src/main.dispatch.test.ts +0 -461
- package/apps/browserctl/src/main.test.ts +0 -334
- package/apps/browserctl/src/main.ts +0 -957
- package/apps/browserctl/src/smoke.e2e.test.ts +0 -97
- package/apps/browserctl/src/test-port.ts +0 -26
- package/apps/browserd/src/bootstrap.ts +0 -432
- package/apps/browserd/src/chrome-relay-extension-bridge.test.ts +0 -250
- package/apps/browserd/src/chrome-relay-extension-bridge.ts +0 -506
- package/apps/browserd/src/container.ts +0 -3088
- package/apps/browserd/src/main.test.ts +0 -1436
- package/apps/browserd/src/main.ts +0 -7
- package/apps/browserd/src/test-port.ts +0 -26
- package/apps/browserd/src/tool-matrix.test.ts +0 -887
- package/bin/browserctl.cjs +0 -21
- package/bin/browserd.cjs +0 -21
- package/extensions/chrome-relay/README-CN.md +0 -39
- package/extensions/chrome-relay/README.md +0 -39
- package/extensions/chrome-relay/background.js +0 -1687
- package/extensions/chrome-relay/manifest.json +0 -15
- package/extensions/chrome-relay/popup.html +0 -369
- package/extensions/chrome-relay/popup.js +0 -972
- package/packages/core/src/bootstrap.test.ts +0 -10
- package/packages/core/src/driver-registry.test.ts +0 -45
- package/packages/core/src/driver-registry.ts +0 -22
- package/packages/core/src/driver.ts +0 -47
- package/packages/core/src/index.ts +0 -6
- package/packages/core/src/navigation-memory.test.ts +0 -259
- package/packages/core/src/navigation-memory.ts +0 -360
- package/packages/core/src/ref-cache.test.ts +0 -61
- package/packages/core/src/ref-cache.ts +0 -28
- package/packages/core/src/session-store.test.ts +0 -82
- package/packages/core/src/session-store.ts +0 -138
- package/packages/core/src/types.ts +0 -9
- package/packages/driver-chrome-relay/src/chrome-relay-driver.test.ts +0 -744
- package/packages/driver-chrome-relay/src/chrome-relay-driver.ts +0 -2429
- package/packages/driver-chrome-relay/src/chrome-relay-extension-runtime.test.ts +0 -264
- package/packages/driver-chrome-relay/src/chrome-relay-extension-runtime.ts +0 -521
- package/packages/driver-chrome-relay/src/index.ts +0 -26
- package/packages/driver-managed/src/index.ts +0 -22
- package/packages/driver-managed/src/managed-driver.test.ts +0 -183
- package/packages/driver-managed/src/managed-driver.ts +0 -341
- package/packages/driver-managed/src/managed-local-driver.test.ts +0 -608
- package/packages/driver-managed/src/managed-local-driver.ts +0 -2243
- package/packages/driver-remote-cdp/src/index.ts +0 -19
- package/packages/driver-remote-cdp/src/remote-cdp-driver.test.ts +0 -727
- package/packages/driver-remote-cdp/src/remote-cdp-driver.ts +0 -2264
- package/packages/protocol/src/envelope.test.ts +0 -25
- package/packages/protocol/src/envelope.ts +0 -31
- package/packages/protocol/src/errors.test.ts +0 -17
- package/packages/protocol/src/errors.ts +0 -11
- package/packages/protocol/src/index.ts +0 -3
- package/packages/protocol/src/tools.ts +0 -3
- package/packages/transport-mcp-stdio/src/index.ts +0 -3
- package/packages/transport-mcp-stdio/src/sdk-server.ts +0 -139
- package/packages/transport-mcp-stdio/src/server.test.ts +0 -281
- package/packages/transport-mcp-stdio/src/server.ts +0 -183
- package/packages/transport-mcp-stdio/src/tool-map.ts +0 -84
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
describe("workspace bootstrap", () => {
|
|
4
|
-
it("loads test runner", () => {
|
|
5
|
-
const state = expect.getState();
|
|
6
|
-
|
|
7
|
-
expect(state.currentTestName).toContain("loads test runner");
|
|
8
|
-
expect(state.testPath).toContain("bootstrap.test.ts");
|
|
9
|
-
});
|
|
10
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import type { BrowserDriver } from "./driver";
|
|
4
|
-
import { DriverRegistry } from "./driver-registry";
|
|
5
|
-
|
|
6
|
-
function createDriver(): BrowserDriver {
|
|
7
|
-
return {
|
|
8
|
-
status: async () => ({ kind: "test" }),
|
|
9
|
-
listProfiles: async () => [],
|
|
10
|
-
listTabs: async () => [],
|
|
11
|
-
openTab: async () => "target:1",
|
|
12
|
-
focusTab: async () => {},
|
|
13
|
-
closeTab: async () => {},
|
|
14
|
-
snapshot: async () => ({ html: "" }),
|
|
15
|
-
act: async () => ({ ok: true }),
|
|
16
|
-
armUpload: async () => {},
|
|
17
|
-
armDialog: async () => {},
|
|
18
|
-
waitDownload: async () => ({ path: "download.bin" }),
|
|
19
|
-
triggerDownload: async () => {}
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("DriverRegistry", () => {
|
|
24
|
-
it("returns a registered driver", () => {
|
|
25
|
-
const registry = new DriverRegistry();
|
|
26
|
-
const driver = createDriver();
|
|
27
|
-
|
|
28
|
-
registry.register("managed", driver);
|
|
29
|
-
|
|
30
|
-
expect(registry.get("managed")).toBe(driver);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("throws when registering a duplicate driver name", () => {
|
|
34
|
-
const registry = new DriverRegistry();
|
|
35
|
-
registry.register("managed", createDriver());
|
|
36
|
-
|
|
37
|
-
expect(() => registry.register("managed", createDriver())).toThrowError("Driver already registered: managed");
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("throws when the requested driver is not registered", () => {
|
|
41
|
-
const registry = new DriverRegistry();
|
|
42
|
-
|
|
43
|
-
expect(() => registry.get("missing")).toThrowError("Unknown driver: missing");
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { BrowserDriver } from "./driver";
|
|
2
|
-
|
|
3
|
-
export class DriverRegistry {
|
|
4
|
-
readonly #drivers = new Map<string, BrowserDriver>();
|
|
5
|
-
|
|
6
|
-
register(name: string, driver: BrowserDriver): void {
|
|
7
|
-
if (this.#drivers.has(name)) {
|
|
8
|
-
throw new Error(`Driver already registered: ${name}`);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
this.#drivers.set(name, driver);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
get(name: string): BrowserDriver {
|
|
15
|
-
const driver = this.#drivers.get(name);
|
|
16
|
-
if (!driver) {
|
|
17
|
-
throw new Error(`Unknown driver: ${name}`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return driver;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { ProfileId, TargetId } from "./types";
|
|
2
|
-
|
|
3
|
-
export type DriverScalar = string | number | boolean | null;
|
|
4
|
-
export type DriverValue = DriverScalar | DriverObject | DriverValue[];
|
|
5
|
-
export type DriverObject = { [key: string]: DriverValue };
|
|
6
|
-
|
|
7
|
-
export type DriverStatus = DriverObject;
|
|
8
|
-
export type DriverSnapshot = DriverObject;
|
|
9
|
-
export type DriverActionPayload = DriverObject;
|
|
10
|
-
export type DriverActionResult = DriverObject;
|
|
11
|
-
export type DriverDownloadResult = DriverObject;
|
|
12
|
-
export type DriverScreenshot = DriverObject;
|
|
13
|
-
|
|
14
|
-
export type DriverAction<TPayload extends DriverObject = DriverActionPayload> = {
|
|
15
|
-
type: string;
|
|
16
|
-
payload?: TPayload;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export type BrowserDriverScreenshot<
|
|
20
|
-
TScreenshot extends DriverObject = DriverScreenshot
|
|
21
|
-
> = {
|
|
22
|
-
screenshot(targetId: TargetId, profile?: ProfileId): Promise<TScreenshot>;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export interface BrowserDriver<
|
|
26
|
-
TStatus extends DriverObject = DriverStatus,
|
|
27
|
-
TSnapshot extends DriverObject = DriverSnapshot,
|
|
28
|
-
TAction extends DriverAction = DriverAction,
|
|
29
|
-
TActionResult extends DriverObject = DriverActionResult,
|
|
30
|
-
TDownload extends DriverObject = DriverDownloadResult
|
|
31
|
-
> {
|
|
32
|
-
status(): Promise<TStatus>;
|
|
33
|
-
listProfiles(): Promise<ProfileId[]>;
|
|
34
|
-
listTabs(profile?: ProfileId): Promise<TargetId[]>;
|
|
35
|
-
|
|
36
|
-
openTab(url: string, profile?: ProfileId): Promise<TargetId>;
|
|
37
|
-
focusTab(targetId: TargetId, profile?: ProfileId): Promise<void>;
|
|
38
|
-
closeTab(targetId: TargetId, profile?: ProfileId): Promise<void>;
|
|
39
|
-
|
|
40
|
-
snapshot(targetId: TargetId, profile?: ProfileId): Promise<TSnapshot>;
|
|
41
|
-
act(action: TAction, targetId: TargetId, profile?: ProfileId): Promise<TActionResult>;
|
|
42
|
-
|
|
43
|
-
armUpload(targetId: TargetId, files: string[], profile?: ProfileId): Promise<void>;
|
|
44
|
-
armDialog(targetId: TargetId, profile?: ProfileId): Promise<void>;
|
|
45
|
-
waitDownload(targetId: TargetId, profile?: ProfileId, path?: string): Promise<TDownload>;
|
|
46
|
-
triggerDownload(targetId: TargetId, profile?: ProfileId): Promise<void>;
|
|
47
|
-
}
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
|
|
7
|
-
import { createNavigationMemoryStore } from "./navigation-memory";
|
|
8
|
-
|
|
9
|
-
describe("navigation memory contracts", () => {
|
|
10
|
-
it("scopes by domain+profile", async () => {
|
|
11
|
-
const store = createNavigationMemoryStore({
|
|
12
|
-
path: ":memory:",
|
|
13
|
-
now: () => 1_000
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
await store.upsert({
|
|
17
|
-
domain: "forum.example",
|
|
18
|
-
profileId: "chrome-relay",
|
|
19
|
-
intentKey: "open_profile",
|
|
20
|
-
signals: [{ kind: "urlPattern", value: "/u/*" }]
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const hit = await store.resolve({
|
|
24
|
-
domain: "forum.example",
|
|
25
|
-
profileId: "chrome-relay",
|
|
26
|
-
intentKey: "open_profile"
|
|
27
|
-
});
|
|
28
|
-
const missByProfile = await store.resolve({
|
|
29
|
-
domain: "forum.example",
|
|
30
|
-
profileId: "managed-local",
|
|
31
|
-
intentKey: "open_profile"
|
|
32
|
-
});
|
|
33
|
-
const missByDomain = await store.resolve({
|
|
34
|
-
domain: "news.example",
|
|
35
|
-
profileId: "chrome-relay",
|
|
36
|
-
intentKey: "open_profile"
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
expect(hit.hit).toBe(true);
|
|
40
|
-
expect(hit.entry?.signals).toEqual([{ kind: "urlPattern", value: "/u/*" }]);
|
|
41
|
-
expect(missByProfile.hit).toBe(false);
|
|
42
|
-
expect(missByDomain.hit).toBe(false);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("implements the core store API", async () => {
|
|
46
|
-
let now = 10_000;
|
|
47
|
-
const store = createNavigationMemoryStore({
|
|
48
|
-
path: ":memory:",
|
|
49
|
-
now: () => now,
|
|
50
|
-
ttlDays: 1
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const upserted = await store.upsert({
|
|
54
|
-
domain: "news.example",
|
|
55
|
-
profileId: "chrome-relay",
|
|
56
|
-
intentKey: "hot",
|
|
57
|
-
signals: [{ kind: "selector", value: "#hot-list" }],
|
|
58
|
-
confidence: 0.7
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
expect(upserted.created).toBe(true);
|
|
62
|
-
expect((await store.list())).toHaveLength(1);
|
|
63
|
-
expect(await store.inspect(upserted.entry.id)).toMatchObject({
|
|
64
|
-
id: upserted.entry.id,
|
|
65
|
-
domain: "news.example",
|
|
66
|
-
profileId: "chrome-relay",
|
|
67
|
-
intentKey: "hot"
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
await store.setMode("ask");
|
|
71
|
-
await store.setTtlDays(0);
|
|
72
|
-
expect(store.status()).toMatchObject({
|
|
73
|
-
mode: "ask",
|
|
74
|
-
ttlDays: 0,
|
|
75
|
-
totalEntries: 0
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
now += 1;
|
|
79
|
-
expect(await store.purgeExpired()).toBe(1);
|
|
80
|
-
expect(await store.delete(upserted.entry.id)).toBe(false);
|
|
81
|
-
expect(await store.list()).toHaveLength(0);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("keeps task1 store in-memory even when path is set", async () => {
|
|
85
|
-
const dirPath = await mkdtemp(join(tmpdir(), "navigation-memory-task1-"));
|
|
86
|
-
const filePath = join(dirPath, "navigation-memory.json");
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const store = createNavigationMemoryStore({
|
|
90
|
-
path: filePath,
|
|
91
|
-
now: () => 1_000
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
await store.upsert({
|
|
95
|
-
domain: "forum.example",
|
|
96
|
-
profileId: "chrome-relay",
|
|
97
|
-
intentKey: "open_profile",
|
|
98
|
-
signals: [{ kind: "urlPattern", value: "/u/*" }]
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
expect(existsSync(filePath)).toBe(false);
|
|
102
|
-
} finally {
|
|
103
|
-
await rm(dirPath, { recursive: true, force: true });
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("does not revive expired entries when ttl days changes", async () => {
|
|
108
|
-
const dayMs = 24 * 60 * 60 * 1000;
|
|
109
|
-
let now = 5_000;
|
|
110
|
-
const store = createNavigationMemoryStore({
|
|
111
|
-
path: ":memory:",
|
|
112
|
-
now: () => now,
|
|
113
|
-
ttlDays: 1
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
const upserted = await store.upsert({
|
|
117
|
-
domain: "forum.example",
|
|
118
|
-
profileId: "chrome-relay",
|
|
119
|
-
intentKey: "open_profile",
|
|
120
|
-
signals: [{ kind: "selector", value: "#profile-link" }]
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
now += dayMs + 1;
|
|
124
|
-
|
|
125
|
-
const status = await store.setTtlDays(30);
|
|
126
|
-
expect(status.totalEntries).toBe(0);
|
|
127
|
-
expect(await store.inspect(upserted.entry.id)).toBeUndefined();
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("supports extracted setMode and setTtlDays calls", async () => {
|
|
131
|
-
const store = createNavigationMemoryStore({
|
|
132
|
-
path: ":memory:"
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
const { setMode, setTtlDays } = store;
|
|
136
|
-
|
|
137
|
-
const modeStatus = await setMode("ask");
|
|
138
|
-
expect(modeStatus.mode).toBe("ask");
|
|
139
|
-
|
|
140
|
-
const ttlStatus = await setTtlDays(7);
|
|
141
|
-
expect(ttlStatus.ttlDays).toBe(7);
|
|
142
|
-
|
|
143
|
-
expect(store.status()).toMatchObject({
|
|
144
|
-
mode: "ask",
|
|
145
|
-
ttlDays: 7
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("renews expireAt when resolve hits", async () => {
|
|
150
|
-
let now = 10_000;
|
|
151
|
-
const dayMs = 24 * 60 * 60 * 1000;
|
|
152
|
-
const store = createNavigationMemoryStore({
|
|
153
|
-
path: ":memory:",
|
|
154
|
-
now: () => now,
|
|
155
|
-
ttlDays: 30
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
const created = await store.upsert({
|
|
159
|
-
domain: "news.example",
|
|
160
|
-
profileId: "chrome-relay",
|
|
161
|
-
intentKey: "hot",
|
|
162
|
-
signals: []
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
const before = await store.inspect(created.entry.id);
|
|
166
|
-
now += 60_000;
|
|
167
|
-
|
|
168
|
-
const first = await store.resolve({
|
|
169
|
-
domain: "news.example",
|
|
170
|
-
profileId: "chrome-relay",
|
|
171
|
-
intentKey: "hot"
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
expect(first.hit).toBe(true);
|
|
175
|
-
expect(first.entry).toBeDefined();
|
|
176
|
-
expect(first.entry!.expireAt).not.toBe(before!.expireAt);
|
|
177
|
-
expect(first.entry!.expireAt).toBe(new Date(now + 30 * dayMs).toISOString());
|
|
178
|
-
|
|
179
|
-
const inspected = await store.inspect(first.entry!.id);
|
|
180
|
-
expect(inspected!.hitCount).toBe(1);
|
|
181
|
-
expect(inspected!.expireAt).toBe(first.entry!.expireAt);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("rejects write when mode=ask and no explicit approval", async () => {
|
|
185
|
-
const store = createNavigationMemoryStore({ path: ":memory:", mode: "ask" });
|
|
186
|
-
|
|
187
|
-
await expect(
|
|
188
|
-
store.upsert({
|
|
189
|
-
domain: "forum.example",
|
|
190
|
-
profileId: "chrome-relay",
|
|
191
|
-
intentKey: "open_profile",
|
|
192
|
-
signals: [],
|
|
193
|
-
confirmed: false
|
|
194
|
-
})
|
|
195
|
-
).rejects.toThrow(/confirmation/i);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("returns miss and does not mutate entry when mode=off on resolve", async () => {
|
|
199
|
-
let now = 10_000;
|
|
200
|
-
const store = createNavigationMemoryStore({
|
|
201
|
-
path: ":memory:",
|
|
202
|
-
now: () => now,
|
|
203
|
-
ttlDays: 30
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
const created = await store.upsert({
|
|
207
|
-
domain: "forum.example",
|
|
208
|
-
profileId: "chrome-relay",
|
|
209
|
-
intentKey: "open_profile",
|
|
210
|
-
signals: [{ kind: "selector", value: "#profile-link" }]
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
const before = await store.inspect(created.entry.id);
|
|
214
|
-
await store.setMode("off");
|
|
215
|
-
now += 5_000;
|
|
216
|
-
|
|
217
|
-
const resolved = await store.resolve({
|
|
218
|
-
domain: "forum.example",
|
|
219
|
-
profileId: "chrome-relay",
|
|
220
|
-
intentKey: "open_profile"
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
expect(resolved.hit).toBe(false);
|
|
224
|
-
expect(resolved.entry).toBeUndefined();
|
|
225
|
-
|
|
226
|
-
const after = await store.inspect(created.entry.id);
|
|
227
|
-
expect(after).toBeDefined();
|
|
228
|
-
expect(after!.hitCount).toBe(before!.hitCount);
|
|
229
|
-
expect(after!.lastHitAt).toBe(before!.lastHitAt);
|
|
230
|
-
expect(after!.expireAt).toBe(before!.expireAt);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it("does not delete expired entry when mode=off resolve is called", async () => {
|
|
234
|
-
const store = createNavigationMemoryStore({
|
|
235
|
-
path: ":memory:",
|
|
236
|
-
now: () => 10_000,
|
|
237
|
-
ttlDays: 0
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
const created = await store.upsert({
|
|
241
|
-
domain: "forum.example",
|
|
242
|
-
profileId: "chrome-relay",
|
|
243
|
-
intentKey: "open_profile",
|
|
244
|
-
signals: [{ kind: "selector", value: "#profile-link" }]
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
await store.setMode("off");
|
|
248
|
-
|
|
249
|
-
const resolved = await store.resolve({
|
|
250
|
-
domain: "forum.example",
|
|
251
|
-
profileId: "chrome-relay",
|
|
252
|
-
intentKey: "open_profile"
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
expect(resolved.hit).toBe(false);
|
|
256
|
-
expect(resolved.entry).toBeUndefined();
|
|
257
|
-
expect(await store.delete(created.entry.id)).toBe(true);
|
|
258
|
-
});
|
|
259
|
-
});
|