@nextclaw/x-harness 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +231 -6
- package/README.md +17 -3
- package/SOURCE.md +9 -0
- package/cli/main.js +232 -188
- package/dist/api.d.ts +56 -2
- package/dist/api.js +178 -75
- package/dist/boundary.js +1 -0
- package/dist/browser-common.d.ts +11 -0
- package/dist/browser-common.js +34 -18
- package/dist/browser-dom-capabilities.d.ts +9 -0
- package/dist/browser-dom-capabilities.js +131 -0
- package/dist/browser-dom-dependencies.d.ts +16 -0
- package/dist/browser-dom-dependencies.js +1 -0
- package/dist/browser-dom-editing.d.ts +166 -0
- package/dist/browser-dom-editing.js +418 -0
- package/dist/browser-dom-navigation.d.ts +18 -0
- package/dist/browser-dom-navigation.js +181 -0
- package/dist/browser-dom-network.d.ts +1 -0
- package/dist/browser-dom-network.js +128 -0
- package/dist/browser-dom-submission.d.ts +7 -0
- package/dist/browser-dom-submission.js +101 -0
- package/dist/browser-dom.d.ts +14 -2
- package/dist/browser-dom.js +120 -824
- package/dist/browser-native.d.ts +14 -1
- package/dist/browser-native.js +180 -143
- package/dist/browser.js +6 -2
- package/dist/creator-native.d.ts +3 -0
- package/dist/creator-native.js +113 -0
- package/dist/creator.js +1 -1
- package/dist/diagnostics.d.ts +11 -0
- package/dist/diagnostics.js +10 -0
- package/dist/growth-batch.d.ts +8 -0
- package/dist/growth-batch.js +190 -0
- package/dist/growth-constants.d.ts +5 -0
- package/dist/growth-constants.js +13 -0
- package/dist/growth-dependencies.d.ts +33 -0
- package/dist/growth-dependencies.js +1 -0
- package/dist/growth-execution.d.ts +17 -0
- package/dist/growth-execution.js +346 -0
- package/dist/growth-parallel.d.ts +8 -0
- package/dist/growth-parallel.js +332 -0
- package/dist/growth-plan.d.ts +9 -0
- package/dist/growth-plan.js +229 -0
- package/dist/growth-policy.d.ts +18 -0
- package/dist/growth-policy.js +208 -0
- package/dist/growth-publishing.d.ts +10 -0
- package/dist/growth-publishing.js +266 -0
- package/dist/growth-reading.d.ts +8 -0
- package/dist/growth-reading.js +157 -0
- package/dist/growth-recovery.d.ts +9 -0
- package/dist/growth-recovery.js +187 -0
- package/dist/growth-state.d.ts +16 -0
- package/dist/growth-state.js +220 -0
- package/dist/growth-tasks.d.ts +20 -0
- package/dist/growth-tasks.js +164 -0
- package/dist/growth.d.ts +26 -1
- package/dist/growth.js +211 -1908
- package/dist/platform-capabilities.d.ts +8 -0
- package/dist/platform-capabilities.js +29 -0
- package/dist/platform-collections.d.ts +14 -0
- package/dist/platform-collections.js +250 -0
- package/dist/platform-dependencies.d.ts +43 -0
- package/dist/platform-dependencies.js +1 -0
- package/dist/platform-diagnostics.d.ts +14 -0
- package/dist/platform-diagnostics.js +63 -0
- package/dist/platform-observation.d.ts +16 -0
- package/dist/platform-observation.js +96 -0
- package/dist/platform-rules.d.ts +17 -0
- package/dist/platform-rules.js +41 -0
- package/dist/platform-targets.d.ts +14 -0
- package/dist/platform-targets.js +154 -0
- package/dist/platform-writing.d.ts +16 -0
- package/dist/platform-writing.js +202 -0
- package/dist/platform.d.ts +23 -2
- package/dist/platform.js +125 -678
- package/dist/readers-actions.d.ts +2 -0
- package/dist/readers-actions.js +66 -0
- package/dist/readers-ax-host.d.ts +12 -0
- package/dist/readers-ax-host.js +134 -0
- package/dist/readers-ax-parser.d.ts +1 -0
- package/dist/readers-ax-parser.js +69 -0
- package/dist/readers-ax-program.d.ts +1 -0
- package/dist/readers-ax-program.js +5 -0
- package/dist/readers-ax-tree.d.ts +19 -0
- package/dist/readers-ax-tree.js +111 -0
- package/dist/readers-ax-views.d.ts +87 -0
- package/dist/readers-ax-views.js +247 -0
- package/dist/readers-cli.d.ts +6 -0
- package/dist/readers-cli.js +79 -0
- package/dist/readers-composer.d.ts +2 -0
- package/dist/readers-composer.js +131 -0
- package/dist/readers-navigation.d.ts +2 -0
- package/dist/readers-navigation.js +66 -0
- package/dist/readers-posts.d.ts +2 -0
- package/dist/readers-posts.js +141 -0
- package/dist/readers-profile.d.ts +3 -0
- package/dist/readers-profile.js +157 -0
- package/dist/readers.d.ts +9 -20
- package/dist/readers.js +9 -1045
- package/dist/results.js +1 -1
- package/dist/round-state.js +20 -3
- package/dist/store.d.ts +22 -4
- package/dist/store.js +173 -151
- package/dist/types.d.ts +10 -0
- package/dist/types.js +1 -0
- package/generated/observation.mjs +3 -2
- package/package.json +23 -9
- package/skill/SKILL.md +17 -36
- package/skill/references/api.md +7 -34
- package/skill/references/reads.md +23 -0
- package/skill/references/recovery.md +25 -0
- package/skill/references/setup.md +23 -0
- package/skill/references/writes.md +51 -0
package/dist/api.js
CHANGED
|
@@ -1,19 +1,80 @@
|
|
|
1
1
|
import { createGrowthHarness } from "./growth.js";
|
|
2
2
|
import { readResult, decisions, diagnostic, stableReason } from "./results.js";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
export class BusinessHarness {
|
|
4
|
+
#store;
|
|
5
|
+
#core;
|
|
6
|
+
#readCache;
|
|
7
|
+
constructor(dependencies) {
|
|
8
|
+
this.verifyFollow = this.verifyFollow.bind(this);
|
|
9
|
+
this.followBatch = this.followBatch.bind(this);
|
|
10
|
+
this.verifyPublishedPost = this.verifyPublishedPost.bind(this);
|
|
11
|
+
this.verifySubmission = this.verifySubmission.bind(this);
|
|
12
|
+
this.followOne = this.followOne.bind(this);
|
|
13
|
+
this.replyToPost = this.replyToPost.bind(this);
|
|
14
|
+
this.publishPost = this.publishPost.bind(this);
|
|
15
|
+
this.runGrowthRound = this.runGrowthRound.bind(this);
|
|
16
|
+
this.inspectRound = this.inspectRound.bind(this);
|
|
17
|
+
this.inspectAccount = this.inspectAccount.bind(this);
|
|
18
|
+
this.readFeedback = this.readFeedback.bind(this);
|
|
19
|
+
this.readPerson = this.readPerson.bind(this);
|
|
20
|
+
this.readPost = this.readPost.bind(this);
|
|
21
|
+
this.searchSources = this.searchSources.bind(this);
|
|
22
|
+
this.readBookmarks = this.readBookmarks.bind(this);
|
|
23
|
+
this.readCreator = this.readCreator.bind(this);
|
|
24
|
+
const { store } = dependencies;
|
|
25
|
+
this.#store = store;
|
|
26
|
+
this.#core = createGrowthHarness(dependencies);
|
|
27
|
+
this.#readCache = new Map();
|
|
28
|
+
}
|
|
29
|
+
async invoke(operation, args) {
|
|
9
30
|
try {
|
|
10
|
-
|
|
31
|
+
const raw = await this.#core[operation](args);
|
|
32
|
+
// Legacy core decisions can contain a human error without a diagnostic proof.
|
|
33
|
+
// Expose a bounded recovery route; consumers never need implementation source.
|
|
34
|
+
const errors = [
|
|
35
|
+
raw.reason,
|
|
36
|
+
...(raw.decision ?? []).flatMap((d) => [
|
|
37
|
+
d.reason,
|
|
38
|
+
d.error,
|
|
39
|
+
]),
|
|
40
|
+
].filter((reason) => typeof reason === "string" && stableReason(reason) !== reason);
|
|
41
|
+
if (errors.length) {
|
|
42
|
+
const evidenceRef = await this.#store.recordProof({
|
|
43
|
+
operation,
|
|
44
|
+
error: errors[0],
|
|
45
|
+
...(errors.length > 1 ? { errors } : {}),
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
...raw,
|
|
49
|
+
...(raw.reason ? { reason: stableReason(raw.reason) } : {}),
|
|
50
|
+
evidenceRef,
|
|
51
|
+
...(raw.decision
|
|
52
|
+
? {
|
|
53
|
+
decision: raw.decision.map((d) => ({
|
|
54
|
+
...d,
|
|
55
|
+
reason: stableReason(d.reason),
|
|
56
|
+
})),
|
|
57
|
+
}
|
|
58
|
+
: {}),
|
|
59
|
+
diagnostic: {
|
|
60
|
+
operation,
|
|
61
|
+
category: "boundary",
|
|
62
|
+
code: "harness-boundary-error",
|
|
63
|
+
next: "inspect-local-proof",
|
|
64
|
+
evidenceRef,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return raw;
|
|
11
69
|
}
|
|
12
70
|
catch (error) {
|
|
13
71
|
const message = error instanceof Error ? error.message : String(error);
|
|
14
72
|
let evidenceRef;
|
|
15
73
|
try {
|
|
16
|
-
evidenceRef = await store.recordProof({
|
|
74
|
+
evidenceRef = await this.#store.recordProof({
|
|
75
|
+
operation,
|
|
76
|
+
error: message,
|
|
77
|
+
});
|
|
17
78
|
}
|
|
18
79
|
catch {
|
|
19
80
|
/* Evidence failure is explicit below. */
|
|
@@ -29,10 +90,19 @@ export function createBusinessHarness(dependencies) {
|
|
|
29
90
|
...(evidenceRef
|
|
30
91
|
? { evidenceRef }
|
|
31
92
|
: { missing: ["local-proof-unavailable"] }),
|
|
93
|
+
diagnostic: {
|
|
94
|
+
operation,
|
|
95
|
+
category: "boundary",
|
|
96
|
+
code: evidenceRef
|
|
97
|
+
? "harness-boundary-error"
|
|
98
|
+
: "evidence-store-unavailable",
|
|
99
|
+
next: evidenceRef ? "inspect-local-proof" : "restore-proof-store",
|
|
100
|
+
...(evidenceRef ? { evidenceRef } : {}),
|
|
101
|
+
},
|
|
32
102
|
};
|
|
33
103
|
}
|
|
34
104
|
}
|
|
35
|
-
async
|
|
105
|
+
async read(operation, args = {}, mode = "receipt") {
|
|
36
106
|
if (!["receipt", "decision", "delta"].includes(mode))
|
|
37
107
|
return {
|
|
38
108
|
status: "needsDecision",
|
|
@@ -40,7 +110,7 @@ export function createBusinessHarness(dependencies) {
|
|
|
40
110
|
reason: "read-mode-invalid",
|
|
41
111
|
};
|
|
42
112
|
// Always collect fresh evidence once. Projection, not a second browser call, chooses output.
|
|
43
|
-
const raw = await invoke(operation, { ...args, mode: "decision" });
|
|
113
|
+
const raw = await this.invoke(operation, { ...args, mode: "decision" });
|
|
44
114
|
const output = readResult(operation, raw, mode);
|
|
45
115
|
if (mode === "receipt")
|
|
46
116
|
return output;
|
|
@@ -55,11 +125,11 @@ export function createBusinessHarness(dependencies) {
|
|
|
55
125
|
continue;
|
|
56
126
|
}
|
|
57
127
|
const key = JSON.stringify([operation, id]), value = JSON.stringify(entry);
|
|
58
|
-
if (readCache.get(key) === value)
|
|
128
|
+
if (this.#readCache.get(key) === value)
|
|
59
129
|
unchangedIds.push(id);
|
|
60
130
|
else
|
|
61
131
|
changed.push(entry);
|
|
62
|
-
readCache.set(key, value);
|
|
132
|
+
this.#readCache.set(key, value);
|
|
63
133
|
}
|
|
64
134
|
// Creator metadata changes independently from the post collection.
|
|
65
135
|
if (operation === "readCreator") {
|
|
@@ -68,9 +138,9 @@ export function createBusinessHarness(dependencies) {
|
|
|
68
138
|
continue;
|
|
69
139
|
const key = JSON.stringify([operation, name, args.handle]);
|
|
70
140
|
const value = JSON.stringify(output[name], (k, v) => k === "observedAt" ? undefined : v);
|
|
71
|
-
if (mode === "delta" && readCache.get(key) === value)
|
|
141
|
+
if (mode === "delta" && this.#readCache.get(key) === value)
|
|
72
142
|
delete output[name];
|
|
73
|
-
readCache.set(key, value);
|
|
143
|
+
this.#readCache.set(key, value);
|
|
74
144
|
}
|
|
75
145
|
}
|
|
76
146
|
// A decision has delivered complete evidence and seeds the next delta.
|
|
@@ -87,8 +157,8 @@ export function createBusinessHarness(dependencies) {
|
|
|
87
157
|
output.unchangedIds = unchangedIds;
|
|
88
158
|
return output;
|
|
89
159
|
}
|
|
90
|
-
async
|
|
91
|
-
const raw = await invoke(operation, args);
|
|
160
|
+
async compactRound(operation, args) {
|
|
161
|
+
const raw = await this.invoke(operation, args);
|
|
92
162
|
const out = {
|
|
93
163
|
status: raw.status,
|
|
94
164
|
taskId: args.taskId,
|
|
@@ -112,6 +182,9 @@ export function createBusinessHarness(dependencies) {
|
|
|
112
182
|
out.decision = issues;
|
|
113
183
|
if (raw.evidenceRef)
|
|
114
184
|
out.evidenceRef = raw.evidenceRef;
|
|
185
|
+
const d = diagnostic(raw.diagnostic);
|
|
186
|
+
if (d)
|
|
187
|
+
out.diagnostic = d;
|
|
115
188
|
if (Array.isArray(raw.settlementErrors))
|
|
116
189
|
out.settlementErrors = raw.settlementErrors
|
|
117
190
|
.map(stableReason)
|
|
@@ -132,11 +205,11 @@ export function createBusinessHarness(dependencies) {
|
|
|
132
205
|
};
|
|
133
206
|
return out;
|
|
134
207
|
}
|
|
135
|
-
async
|
|
136
|
-
const raw = await invoke(operation, args);
|
|
208
|
+
async single(operation, args, selector) {
|
|
209
|
+
const raw = await this.invoke(operation, args);
|
|
137
210
|
let snapshot;
|
|
138
211
|
try {
|
|
139
|
-
snapshot = await store.load();
|
|
212
|
+
snapshot = await this.#store.load();
|
|
140
213
|
}
|
|
141
214
|
catch {
|
|
142
215
|
return {
|
|
@@ -197,60 +270,90 @@ export function createBusinessHarness(dependencies) {
|
|
|
197
270
|
out.diagnostic = d;
|
|
198
271
|
return out;
|
|
199
272
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
273
|
+
readCreator(args) {
|
|
274
|
+
return this.read("readCreator", args, args.mode);
|
|
275
|
+
}
|
|
276
|
+
readBookmarks(args) {
|
|
277
|
+
return this.read("readBookmarks", args, args.mode);
|
|
278
|
+
}
|
|
279
|
+
searchSources(args) {
|
|
280
|
+
return this.read("searchSources", args, args.mode);
|
|
281
|
+
}
|
|
282
|
+
readPost({ target, mode, }) {
|
|
283
|
+
return this.read("readPost", target, mode);
|
|
284
|
+
}
|
|
285
|
+
readPerson({ target, mode, }) {
|
|
286
|
+
return this.read("readPerson", target, mode);
|
|
287
|
+
}
|
|
288
|
+
readFeedback(args) {
|
|
289
|
+
return this.read("readFeedback", args, args.mode);
|
|
290
|
+
}
|
|
291
|
+
inspectAccount(args = {}) {
|
|
292
|
+
return this.read("inspectAccount", args, args.mode);
|
|
293
|
+
}
|
|
294
|
+
inspectRound(args) {
|
|
295
|
+
return this.compactRound("inspectRound", args);
|
|
296
|
+
}
|
|
297
|
+
runGrowthRound(args) {
|
|
298
|
+
return this.compactRound("runGrowthRound", args);
|
|
299
|
+
}
|
|
300
|
+
publishPost(args) {
|
|
301
|
+
return this.single("publishPost", args, (a) => a.kind === "original" && a.draftPath === args.draftPath);
|
|
302
|
+
}
|
|
303
|
+
replyToPost(args) {
|
|
304
|
+
return this.single("replyToPost", args, (a) => a.kind === "reply" && a.postId === args.postId && a.text === args.text);
|
|
305
|
+
}
|
|
306
|
+
followOne(args) {
|
|
307
|
+
return this.single("followOne", args, (a) => a.kind === "follow" &&
|
|
308
|
+
a.handle?.toLowerCase() === args.handle.toLowerCase());
|
|
309
|
+
}
|
|
310
|
+
verifySubmission(args) {
|
|
311
|
+
return this.single("verifySubmission", args, (a) => a.id === args.actionId);
|
|
312
|
+
}
|
|
313
|
+
verifyPublishedPost(args) {
|
|
314
|
+
return this.single("verifyPublishedPost", args, (a) => a.kind === "original" && a.draftPath === args.draftPath);
|
|
315
|
+
}
|
|
316
|
+
async followBatch(args) {
|
|
317
|
+
const raw = await this.invoke("followBatch", args);
|
|
318
|
+
const output = {
|
|
319
|
+
status: raw.status ?? (raw.mayEnd ? "verified" : "needsDecision"),
|
|
320
|
+
taskId: args.taskId,
|
|
321
|
+
planned: raw.planned ?? args.handles.length,
|
|
322
|
+
attempted: raw.attempted ?? 0,
|
|
323
|
+
verified: raw.verifiedThisCall ?? 0,
|
|
324
|
+
unknown: raw.unknownInBatch ?? 0,
|
|
325
|
+
remaining: raw.remaining ?? args.handles.length,
|
|
326
|
+
mayEnd: raw.mayEnd === true,
|
|
327
|
+
};
|
|
328
|
+
if (raw.batchId)
|
|
329
|
+
output.batchId = raw.batchId;
|
|
330
|
+
if (raw.evidenceRef)
|
|
331
|
+
output.evidenceRef = raw.evidenceRef;
|
|
332
|
+
const d = diagnostic(raw.diagnostic);
|
|
333
|
+
if (d)
|
|
334
|
+
output.diagnostic = d;
|
|
335
|
+
const issues = decisions(raw.decision);
|
|
336
|
+
if (raw.reason)
|
|
337
|
+
issues.push({ reason: raw.reason, status: "needsDecision" });
|
|
338
|
+
if (raw.missing)
|
|
339
|
+
issues.push({ missing: raw.missing });
|
|
340
|
+
if (issues.length)
|
|
341
|
+
output.decision = issues;
|
|
342
|
+
return output;
|
|
343
|
+
}
|
|
344
|
+
async verifyFollow(args) {
|
|
345
|
+
const raw = await this.invoke("verifyFollow", {
|
|
346
|
+
...args,
|
|
347
|
+
mode: args.mode ?? "receipt",
|
|
348
|
+
});
|
|
349
|
+
const out = readResult("verifyFollow", raw, "receipt");
|
|
350
|
+
out.handle = args.handle;
|
|
351
|
+
out.verified = raw.verified === true;
|
|
352
|
+
if (args.mode === "decision" && raw.observation)
|
|
353
|
+
out.person = readResult("readPerson", raw.observation, "decision").person;
|
|
354
|
+
return out;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
export function createBusinessHarness(...args) {
|
|
358
|
+
return new BusinessHarness(...args);
|
|
256
359
|
}
|
package/dist/boundary.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/browser-common.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
export declare const plain: (label: any) => any;
|
|
2
2
|
export declare const valueOf: (label: any) => any;
|
|
3
3
|
export declare const missingMethods: (object: any, names: any) => any;
|
|
4
|
+
/** One instance owns one browser session and its concurrency boundary. */
|
|
5
|
+
export declare class SerializedBrowser {
|
|
6
|
+
#private;
|
|
7
|
+
collectCreator?: (args: unknown) => Promise<any>;
|
|
8
|
+
constructor(port: any);
|
|
9
|
+
private run;
|
|
10
|
+
observe(query: any): Promise<any>;
|
|
11
|
+
act(command: any): Promise<any>;
|
|
12
|
+
screenshot(): Promise<any>;
|
|
13
|
+
private collect;
|
|
14
|
+
}
|
|
4
15
|
export declare function serialized(port: any): any;
|
package/dist/browser-common.js
CHANGED
|
@@ -1,29 +1,45 @@
|
|
|
1
1
|
export const plain = (label) => label.replace(/^(?:按钮|link|文本输入区域|文本输入区|文本区域|文本栏) /, "");
|
|
2
2
|
export const valueOf = (label) => label.match(/, Value: ([\s\S]*)$/)?.[1] ?? null;
|
|
3
3
|
export const missingMethods = (object, names) => names.filter((name) => typeof object?.[name] !== "function");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
/** One instance owns one browser session and its concurrency boundary. */
|
|
5
|
+
export class SerializedBrowser {
|
|
6
|
+
#busy = false;
|
|
7
|
+
#port;
|
|
8
|
+
collectCreator;
|
|
9
|
+
constructor(port) {
|
|
10
|
+
this.#port = port;
|
|
11
|
+
Object.assign(this, port);
|
|
12
|
+
this.observe = SerializedBrowser.prototype.observe.bind(this);
|
|
13
|
+
this.act = SerializedBrowser.prototype.act.bind(this);
|
|
14
|
+
this.screenshot = SerializedBrowser.prototype.screenshot.bind(this);
|
|
15
|
+
if (port.collectCreator)
|
|
16
|
+
this.collectCreator = this.collect.bind(this);
|
|
17
|
+
}
|
|
18
|
+
async run(fn) {
|
|
19
|
+
if (this.#busy)
|
|
8
20
|
throw new Error("浏览器harness操作进行中,禁止并发");
|
|
9
|
-
busy = true;
|
|
21
|
+
this.#busy = true;
|
|
10
22
|
try {
|
|
11
23
|
return await fn();
|
|
12
24
|
}
|
|
13
25
|
finally {
|
|
14
|
-
busy = false;
|
|
26
|
+
this.#busy = false;
|
|
15
27
|
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
}
|
|
29
|
+
observe(query) {
|
|
30
|
+
return this.run(() => this.#port.observe(query));
|
|
31
|
+
}
|
|
32
|
+
act(command) {
|
|
33
|
+
return this.run(() => this.#port.act(command));
|
|
34
|
+
}
|
|
35
|
+
screenshot() {
|
|
36
|
+
return this.run(() => this.#port.screenshot());
|
|
37
|
+
}
|
|
38
|
+
collect(args) {
|
|
39
|
+
return this.run(() => this.#port.collectCreator(args));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function serialized(port) {
|
|
43
|
+
return new SerializedBrowser(port);
|
|
28
44
|
}
|
|
29
45
|
// AX indices never cross this port. Only semantic commands reach the platform layer.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DOMDependencies } from "./browser-dom-dependencies.js";
|
|
2
|
+
export declare class DOMCapabilities {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(dependencies: Pick<DOMDependencies, "tab" | "nativeSubmit">);
|
|
5
|
+
capabilities(operation: any): any;
|
|
6
|
+
private creatorCapabilities;
|
|
7
|
+
private collectionCapabilities;
|
|
8
|
+
}
|
|
9
|
+
export declare function createDOMCapabilities(...args: ConstructorParameters<typeof DOMCapabilities>): DOMCapabilities;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { missingMethods } from "./browser-common.js";
|
|
2
|
+
export class DOMCapabilities {
|
|
3
|
+
#tab;
|
|
4
|
+
#nativeSubmit;
|
|
5
|
+
constructor(dependencies) {
|
|
6
|
+
this.capabilities = this.capabilities.bind(this);
|
|
7
|
+
const { tab, nativeSubmit } = dependencies;
|
|
8
|
+
this.#tab = tab;
|
|
9
|
+
this.#nativeSubmit = nativeSubmit;
|
|
10
|
+
}
|
|
11
|
+
capabilities(operation) {
|
|
12
|
+
const base = missingMethods(this.#tab?.playwright, ["evaluate"]);
|
|
13
|
+
if (operation === "readCreator") {
|
|
14
|
+
return this.creatorCapabilities(base, operation);
|
|
15
|
+
}
|
|
16
|
+
if (["readBookmarks", "searchSources", "readFeedback"].includes(operation)) {
|
|
17
|
+
return this.collectionCapabilities(base, operation);
|
|
18
|
+
}
|
|
19
|
+
if (operation !== "read")
|
|
20
|
+
base.push(...missingMethods(this.#tab?.playwright, ["getByRole"]), ...missingMethods(this.#tab, ["goto"]));
|
|
21
|
+
if (operation !== "read" &&
|
|
22
|
+
typeof this.#tab?.playwright?.getByRole === "function") {
|
|
23
|
+
base.push(...missingMethods(this.#tab.playwright.getByRole("button"), [
|
|
24
|
+
"count",
|
|
25
|
+
"click",
|
|
26
|
+
]));
|
|
27
|
+
if (operation !== "submitFollow")
|
|
28
|
+
base.push(...missingMethods(this.#tab.playwright.getByRole("textbox"), [
|
|
29
|
+
"count",
|
|
30
|
+
"fill",
|
|
31
|
+
]));
|
|
32
|
+
if (["submitPost", "submitReply"].includes(operation)) {
|
|
33
|
+
base.push(...missingMethods(this.#tab.playwright, [
|
|
34
|
+
"locator",
|
|
35
|
+
"waitForLoadState",
|
|
36
|
+
]));
|
|
37
|
+
base.push(...missingMethods(this.#tab.playwright.getByRole("button"), [
|
|
38
|
+
"and",
|
|
39
|
+
"waitFor",
|
|
40
|
+
]));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (operation === "refreshPerson" || operation === "refreshPosts") {
|
|
44
|
+
base.push(...missingMethods(this.#tab, ["reload"]));
|
|
45
|
+
base.push(...missingMethods(this.#tab?.playwright, ["waitForLoadState"]));
|
|
46
|
+
if (typeof this.#tab?.playwright?.getByRole === "function")
|
|
47
|
+
base.push(...missingMethods(this.#tab.playwright.getByRole("button"), [
|
|
48
|
+
"waitFor",
|
|
49
|
+
]));
|
|
50
|
+
}
|
|
51
|
+
if (operation === "openOwnPostMenu") {
|
|
52
|
+
base.push(...missingMethods(this.#tab?.playwright, ["locator"]));
|
|
53
|
+
if (typeof this.#tab?.playwright?.getByRole === "function")
|
|
54
|
+
base.push(...missingMethods(this.#tab.playwright.getByRole("article"), [
|
|
55
|
+
"filter",
|
|
56
|
+
"getByRole",
|
|
57
|
+
]));
|
|
58
|
+
}
|
|
59
|
+
if (operation === "submitEditedPost") {
|
|
60
|
+
base.push(...missingMethods(this.#tab?.playwright, ["waitForLoadState"]));
|
|
61
|
+
if (typeof this.#tab?.playwright?.getByRole === "function")
|
|
62
|
+
base.push(...missingMethods(this.#tab.playwright.getByRole("button"), [
|
|
63
|
+
"press",
|
|
64
|
+
]));
|
|
65
|
+
}
|
|
66
|
+
if (operation === "attachAssets")
|
|
67
|
+
base.push(...missingMethods(this.#tab?.playwright, [
|
|
68
|
+
"waitForEvent",
|
|
69
|
+
"getByLabel",
|
|
70
|
+
"getByPlaceholder",
|
|
71
|
+
]));
|
|
72
|
+
if (this.#nativeSubmit && ["submitPost", "submitReply"].includes(operation))
|
|
73
|
+
base.push(...missingMethods(this.#tab, ["getAXState", "click"]));
|
|
74
|
+
return base;
|
|
75
|
+
}
|
|
76
|
+
creatorCapabilities(base, operation) {
|
|
77
|
+
base.push(...missingMethods(this.#tab, [
|
|
78
|
+
"url",
|
|
79
|
+
"goto",
|
|
80
|
+
"reload",
|
|
81
|
+
"getAXState",
|
|
82
|
+
"scroll",
|
|
83
|
+
]));
|
|
84
|
+
base.push(...missingMethods(this.#tab.playwright, ["locator"]));
|
|
85
|
+
if (typeof this.#tab.playwright?.locator === "function") {
|
|
86
|
+
const article = this.#tab.playwright.locator("article");
|
|
87
|
+
base.push(...missingMethods(article, ["first", "getByRole"]));
|
|
88
|
+
if (typeof article.first === "function")
|
|
89
|
+
base.push(...missingMethods(article.first(), ["waitFor"]));
|
|
90
|
+
if (typeof article.getByRole === "function")
|
|
91
|
+
base.push(...missingMethods(article.getByRole("button", { name: "显示更多", exact: true }), ["all"]));
|
|
92
|
+
}
|
|
93
|
+
return base;
|
|
94
|
+
}
|
|
95
|
+
collectionCapabilities(base, operation) {
|
|
96
|
+
base.push(...missingMethods(this.#tab, ["goto"]));
|
|
97
|
+
base.push(...missingMethods(this.#tab?.playwright, ["getByRole", "waitForURL"]));
|
|
98
|
+
if (typeof this.#tab?.playwright?.getByRole === "function")
|
|
99
|
+
base.push(...missingMethods(this.#tab.playwright.getByRole("article"), [
|
|
100
|
+
"first",
|
|
101
|
+
"waitFor",
|
|
102
|
+
]));
|
|
103
|
+
if (operation !== "readFeedback") {
|
|
104
|
+
base.push(...missingMethods(this.#tab?.playwright, [
|
|
105
|
+
"getByRole",
|
|
106
|
+
"getByText",
|
|
107
|
+
"locator",
|
|
108
|
+
"waitForURL",
|
|
109
|
+
]));
|
|
110
|
+
if (typeof this.#tab?.playwright?.getByRole === "function")
|
|
111
|
+
base.push(...missingMethods(this.#tab.playwright.getByRole("main"), [
|
|
112
|
+
"count",
|
|
113
|
+
"click",
|
|
114
|
+
"first",
|
|
115
|
+
"waitFor",
|
|
116
|
+
]));
|
|
117
|
+
if (typeof this.#tab?.playwright?.locator === "function")
|
|
118
|
+
base.push(...missingMethods(this.#tab.playwright.locator("a"), [
|
|
119
|
+
"count",
|
|
120
|
+
"filter",
|
|
121
|
+
"press",
|
|
122
|
+
]));
|
|
123
|
+
if (typeof this.#tab?.playwright?.getByText === "function")
|
|
124
|
+
base.push(...missingMethods(this.#tab.playwright.getByText(""), ["waitFor"]));
|
|
125
|
+
}
|
|
126
|
+
return base;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export function createDOMCapabilities(...args) {
|
|
130
|
+
return new DOMCapabilities(...args);
|
|
131
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface DOMSession {
|
|
2
|
+
current?: any;
|
|
3
|
+
replyBinding?: any;
|
|
4
|
+
refreshNotice?: any;
|
|
5
|
+
editBinding?: any;
|
|
6
|
+
}
|
|
7
|
+
export interface DOMDependencies {
|
|
8
|
+
tab: any;
|
|
9
|
+
nativeSubmit: boolean;
|
|
10
|
+
readDOM: any;
|
|
11
|
+
session: DOMSession;
|
|
12
|
+
submitAX: any;
|
|
13
|
+
observe: (query: any) => Promise<any>;
|
|
14
|
+
locator: (role: any, name?: any) => any;
|
|
15
|
+
unique: (loc: any) => Promise<any>;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|