@ax-kit/playwright 0.0.0-stage → 0.0.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/LICENSE +202 -0
- package/README.md +96 -2
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +160 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +160 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/init.global.iife.js +1 -0
- package/package.json +54 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright 2026 Utpal Sen
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,97 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@ax-kit/playwright`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Runner-side companion for driving `document.modelContext` in tests.
|
|
4
|
+
|
|
5
|
+
Pinned draft: WebMCP Draft Community Group Report, 15 October 2026, from
|
|
6
|
+
the W3C Web Machine Learning Community Group. The pin is exported as
|
|
7
|
+
`SPEC_VERSION` from `@ax-kit/core` and repeated in every release note.
|
|
8
|
+
|
|
9
|
+
Everything here is ours, not the draft's: the draft defines the
|
|
10
|
+
`document.modelContext` surface, while this fixture and companion are
|
|
11
|
+
conveniences reachable only through this entry so no spec-conformant core
|
|
12
|
+
path can observe them.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm add -D @ax-kit/playwright @playwright/test
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Requires `@playwright/test` 1.63.0 as a peer. The page-side bundle is
|
|
21
|
+
installed from this package's `dist`, so build before running tests.
|
|
22
|
+
|
|
23
|
+
## Use
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { expect, test } from "@ax-kit/playwright";
|
|
27
|
+
|
|
28
|
+
test("drives the offered surface", async ({ page }) => {
|
|
29
|
+
await page.goto("/");
|
|
30
|
+
const tools = await page.ax.getAvailableTools();
|
|
31
|
+
expect(tools.map((tool) => tool.name)).toContain("viewCart");
|
|
32
|
+
const cart = await page.ax.executeTool("viewCart", {});
|
|
33
|
+
expect(cart).toEqual({ items: [] });
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The overriding `page` fixture installs exactly one init script carrying the
|
|
38
|
+
built bundle before any test navigation, attaches the runner-realm companion
|
|
39
|
+
as `page.ax`, yields the page, and disposes the init-script handle on
|
|
40
|
+
teardown. No helper enters the page namespace besides the typed surface
|
|
41
|
+
itself, so page script cannot observe, shadow, or collide with the companion.
|
|
42
|
+
The fixture composes with project-dependency setup, `mergeTests`, and
|
|
43
|
+
worker-scoped setup.
|
|
44
|
+
|
|
45
|
+
## Operations
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
await page.ax.getAvailableTools({ fromOrigins?: string[] });
|
|
49
|
+
await page.ax.waitForTool(name, { timeout?: number });
|
|
50
|
+
await page.ax.expectTool(name, { timeout?: number });
|
|
51
|
+
await page.ax.executeTool<T>(name, args?, { signal?: AbortSignal });
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `getAvailableTools` lists the current tools sorted ascending by name in
|
|
55
|
+
code-unit order, with origin scoping and annotations preserved. The live
|
|
56
|
+
`window` field never crosses the boundary; use the returned names.
|
|
57
|
+
- `waitForTool` checks the current listing first and resolves at once on a
|
|
58
|
+
hit, otherwise waits on the in-page change hint with an in-page guard.
|
|
59
|
+
Every wake re-lists and matches by name, looping to the deadline on
|
|
60
|
+
unrelated registrations. The default timeout follows the runner's
|
|
61
|
+
assertion default of 5 seconds and a miss rejects with a `TimeoutError`
|
|
62
|
+
naming the tool.
|
|
63
|
+
- `expectTool` polls the same listing through the runner's retrying
|
|
64
|
+
primitive with the same matcher, forwarding the given timeout options.
|
|
65
|
+
A miss rejects with a `TimeoutError` naming the tool, matching
|
|
66
|
+
`waitForTool`. Listing failures other than a miss propagate unchanged.
|
|
67
|
+
- `executeTool` resolves the name to a fresh handle inside the page on
|
|
68
|
+
every call, passes arguments as serializable data only, and parses the
|
|
69
|
+
specified string result at the companion boundary. A result outside
|
|
70
|
+
JSON rejects with a typed `AxParseError` carrying the tool name and the
|
|
71
|
+
raw text. A passed `signal` abandons the runner-side wait without
|
|
72
|
+
unregistering the tool; availability and execution stay on distinct
|
|
73
|
+
lifetimes.
|
|
74
|
+
|
|
75
|
+
## Errors
|
|
76
|
+
|
|
77
|
+
Draft rejection families propagate unchanged: duplicate or malformed
|
|
78
|
+
names reject with `InvalidStateError`, non-object arguments with
|
|
79
|
+
`TypeError`, unknown tools at call time with `NotFoundError`, and the
|
|
80
|
+
remaining gates (inactive document, denied feature, untrustworthy origin
|
|
81
|
+
entries, opaque origin) with their specified names.
|
|
82
|
+
A missing installation fails fast with a typed `AxMissingSurfaceError`
|
|
83
|
+
instead of passing silently.
|
|
84
|
+
|
|
85
|
+
## Notes
|
|
86
|
+
|
|
87
|
+
- The companion lives in the test process. Only serializable data crosses
|
|
88
|
+
the evaluate boundary; handles never cross, names cross instead, and
|
|
89
|
+
every page function is self-contained with no closed-over state.
|
|
90
|
+
- The change signal is treated as a re-list hint with no ordering promise;
|
|
91
|
+
consumers re-list on every wake.
|
|
92
|
+
- Listings sort ascending by name in code-unit order and match by name,
|
|
93
|
+
never by position. Each frame evaluates its own bundle copy, so listings
|
|
94
|
+
are frame-local and cross-origin tools never leak into the top document.
|
|
95
|
+
- There is no sleep-based waiting anywhere: bounded waits ride the change
|
|
96
|
+
hint or the retrying assertion, and the DOM-shuffling gate harness that
|
|
97
|
+
proves it lives in `test/` only, never in the published entry.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@playwright/test"),t=require("node:fs"),n=require("node:url");var r=class extends Error{constructor(){super(`typed surface is missing`),this.name=`AxMissingSurfaceError`}};async function i(e){try{return await e}catch(e){throw e instanceof Error&&e.message.includes(`typed surface is missing`)?new r:e}}function a(e,t){return e<t?-1:+(e>t)}function o(e,t){return e.find(e=>e.name===t)}async function s(e,t){let n=await i(e.evaluate(e=>{let t=e,n=document.modelContext;if(n==null||typeof n.getTools!=`function`)throw Error(`typed surface is missing`);let r=t.fromOrigins===void 0||t.fromOrigins===null?void 0:{fromOrigins:t.fromOrigins};return n.getTools(r).then(e=>e.map(e=>({name:e.name,description:e.description,title:e.title,origin:e.origin,annotations:e.annotations})))},{fromOrigins:t??null})),r=[];for(let e of n)typeof e.name==`string`&&r.push({name:e.name,description:typeof e.description==`string`?e.description:``,title:typeof e.title==`string`?e.title:``,origin:typeof e.origin==`string`?e.origin:``,...c(e.annotations)?{annotations:e.annotations}:{}});return r.sort((e,t)=>a(e.name,t.name)),r}function c(e){return!(typeof e!=`object`||!e)}var l=class extends Error{toolName;raw;constructor(e,t,n){super(`unparsable result from tool "${e}"`),this.name=`AxParseError`,this.toolName=e,this.raw=t,this.cause=n}};function u(e,t){return t.aborted===!0?Promise.reject(t.reason):new Promise((n,r)=>{function i(){r(t.reason)}t.addEventListener(`abort`,i,{once:!0}),e.then(e=>{t.removeEventListener(`abort`,i),n(e)},e=>{t.removeEventListener(`abort`,i),r(e)})})}async function d(e,t){let n=t?.fromOrigins;if(n!==void 0){if(!Array.isArray(n))throw TypeError(`bad fromOrigins`);for(let e of n)if(typeof e!=`string`)throw TypeError(`bad fromOrigins`)}return s(e,n)}async function f(e,t,n,r){if(typeof t!=`string`)throw TypeError(`bad tool name`);let a=n===void 0?{}:n;if(typeof a!=`object`||!a)throw TypeError(`bad arguments`);let o=r?.signal;if(o!==void 0&&(typeof o.aborted!=`boolean`||typeof o.addEventListener!=`function`))throw TypeError(`bad signal`);let s=e.evaluate(e=>{let t=e,n=typeof t.name==`string`?t.name:``,r=document.modelContext;if(typeof r!=`object`||!r)throw Error(`typed surface is missing`);let i=r;if(typeof i.getTools!=`function`||typeof i.executeTool!=`function`)throw Error(`typed surface is missing`);return i.getTools().then(e=>{let r=e.find(e=>typeof e.name==`string`&&e.name===n);if(r===void 0)throw new DOMException(`unknown tool ${n}`,`NotFoundError`);return i.executeTool(r,t.input)}).then(e=>{if(typeof e!=`string`)throw new DOMException(`bad tool result`,`OperationError`);return e})},{name:t,input:a}),c=o===void 0?await i(s):await u(i(s),o);try{return JSON.parse(c)}catch(e){throw new l(t,c,e)}}function p(e){let t=e.timeout??5e3;if(typeof t!=`number`||Number.isNaN(t)||!Number.isFinite(t)||t<0)throw TypeError(`bad timeout`);return t}function m(e,t){let n=/* @__PURE__ */ Error(`timed out waiting for tool "${e}" (${t}ms)`);return n.name=`TimeoutError`,n}async function h(e,t,n){if(typeof t!=`string`)throw TypeError(`bad tool name`);let i=p(n??{}),a=Date.now()+i,c=o(await s(e),t);if(c!==void 0)return c;for(;;){let n=a-Date.now();if(n<=0)throw m(t,i);try{await e.evaluate(e=>{let t=e,n=typeof t.ms==`number`?t.ms:0,r=typeof t.name==`string`?t.name:``,i=document.modelContext;if(typeof i!=`object`||!i)throw Error(`typed surface is missing`);let a=i;if(typeof a.addEventListener!=`function`||typeof a.removeEventListener!=`function`)throw Error(`typed surface is missing`);let o=i.getTools;if(typeof o!=`function`)throw Error(`typed surface is missing`);let s=o;return new Promise((e,t)=>{let i=setTimeout(()=>{a.removeEventListener(`toolchange`,o),t(/* @__PURE__ */ Error(`wait guard expired`))},n);function o(){clearTimeout(i),e({found:!1})}a.addEventListener(`toolchange`,o,{once:!0}),s.call(a).then(t=>{t.some(e=>typeof e.name==`string`&&e.name===r)&&(clearTimeout(i),a.removeEventListener(`toolchange`,o),e({found:!0}))}).catch(()=>void 0)})},{ms:n,name:t})}catch(e){throw e instanceof Error&&e.message.includes(`typed surface is missing`)?new r:e instanceof Error&&e.message.includes(`wait guard expired`)?m(t,i):e}let c=o(await s(e),t);if(c!==void 0)return c}}async function g(t,n,i){if(typeof n!=`string`)throw TypeError(`bad tool name`);let a=p(i??{});await s(t);try{await e.expect.poll(async()=>o(await s(t),n)?.name??null,{timeout:a}).toBe(n)}catch(e){throw e instanceof r?e:e instanceof Error&&`matcherResult`in e?m(n,a):e}let c=o(await s(t),n);if(c===void 0)throw m(n,a);return c}var _=class{page;constructor(e){this.page=e}async isInstalled(){return this.page.evaluate(()=>{let e=document.modelContext;if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.getTools==`function`&&typeof t.registerTool==`function`&&typeof t.executeTool==`function`})}async waitForTool(e,t){return h(this.page,e,t)}async expectTool(e,t){return g(this.page,e,t)}async getAvailableTools(e){return d(this.page,e)}async executeTool(e,t,n){return f(this.page,e,t,n)}};const v=`init.global.iife.js`;function y(){let e=[new URL(`./${v}`,require("url").pathToFileURL(__filename).href),new URL(`../dist/${v}`,require("url").pathToFileURL(__filename).href)];for(let r of e){let e=(0,n.fileURLToPath)(r);if((0,t.existsSync)(e))return e}return(0,n.fileURLToPath)(e[0])}const b=e.test.extend({page:async({page:e},t)=>{let n=y(),r=await e.addInitScript({path:n});try{let n=e;n.ax=new _(e),await t(n)}finally{await r.dispose()}}});exports.AxCompanion=_,exports.AxMissingSurfaceError=r,exports.AxParseError=l,exports.compareToolNames=a,exports.executeTool=f,exports.expect=e.expect,exports.expectTool=g,exports.findToolByName=o,exports.getAvailableTools=d,exports.resolveInitScriptPath=y,exports.test=b,exports.waitForTool=h;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Page, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType, expect } from "@playwright/test";
|
|
2
|
+
//#region src/types.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Runner-side shapes for the typed page surface. These are ours, not the
|
|
5
|
+
* draft's: the draft defines `RegisteredTool` with a live `window` field
|
|
6
|
+
* that cannot cross the evaluate boundary, so the companion strips it and
|
|
7
|
+
* carries names instead. Nothing here is visible to a conformance run.
|
|
8
|
+
*/
|
|
9
|
+
interface AxToolAnnotations {
|
|
10
|
+
readonly readOnlyHint?: boolean | undefined;
|
|
11
|
+
readonly untrustedContentHint?: boolean | undefined;
|
|
12
|
+
readonly consequentialHint?: boolean | undefined;
|
|
13
|
+
readonly debugging?: boolean | undefined;
|
|
14
|
+
}
|
|
15
|
+
interface AxToolSummary {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly description: string;
|
|
18
|
+
readonly title: string;
|
|
19
|
+
readonly origin: string;
|
|
20
|
+
readonly annotations?: AxToolAnnotations | undefined;
|
|
21
|
+
}
|
|
22
|
+
interface AxWaitOptions {
|
|
23
|
+
readonly timeout?: number | undefined;
|
|
24
|
+
}
|
|
25
|
+
interface AxExpectOptions {
|
|
26
|
+
readonly timeout?: number | undefined;
|
|
27
|
+
}
|
|
28
|
+
interface AxListOptions {
|
|
29
|
+
readonly fromOrigins?: ReadonlyArray<string> | undefined;
|
|
30
|
+
}
|
|
31
|
+
interface AxExecuteOptions {
|
|
32
|
+
readonly signal?: AbortSignal | undefined;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/ax.d.ts
|
|
36
|
+
/**
|
|
37
|
+
* Runner-realm companion attached to the page by the fixture. It never
|
|
38
|
+
* enters the page namespace: all state lives in the test process and only
|
|
39
|
+
* serializable data crosses the evaluate boundary.
|
|
40
|
+
*/
|
|
41
|
+
export declare class AxCompanion {
|
|
42
|
+
readonly page: Page;
|
|
43
|
+
constructor(page: Page);
|
|
44
|
+
/**
|
|
45
|
+
* True where the page-side bundle installed the typed surface.
|
|
46
|
+
* Feature-detects rather than assuming installation succeeded.
|
|
47
|
+
*/
|
|
48
|
+
isInstalled(): Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve when the named tool is listed, via pre-flight plus an
|
|
51
|
+
* in-page change hint with a bounded guard. Matches by name in
|
|
52
|
+
* code-unit order; spurious wakes re-list until the deadline.
|
|
53
|
+
*/
|
|
54
|
+
waitForTool(name: string, options?: AxWaitOptions): Promise<AxToolSummary>;
|
|
55
|
+
/**
|
|
56
|
+
* Assertion flavor over the same listing and matcher, polling with
|
|
57
|
+
* the runner's retrying primitive and forwarded timeout options.
|
|
58
|
+
*/
|
|
59
|
+
expectTool(name: string, options?: AxExpectOptions): Promise<AxToolSummary>;
|
|
60
|
+
/**
|
|
61
|
+
* Available tools sorted ascending by name in code-unit order, with
|
|
62
|
+
* origin scoping preserved from the underlying surface.
|
|
63
|
+
*/
|
|
64
|
+
getAvailableTools(options?: AxListOptions): Promise<AxToolSummary[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Execute a named tool with serializable arguments. The name resolves
|
|
67
|
+
* to a fresh handle inside the page on every call; the string result
|
|
68
|
+
* parses at this boundary with a typed error on failure.
|
|
69
|
+
*/
|
|
70
|
+
executeTool<T = unknown>(name: string, args?: unknown, options?: AxExecuteOptions): Promise<T>;
|
|
71
|
+
}
|
|
72
|
+
/** A page with the runner-realm companion attached by the fixture. */
|
|
73
|
+
type AxPage = Page & {
|
|
74
|
+
ax: AxCompanion;
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/errors.d.ts
|
|
78
|
+
/**
|
|
79
|
+
* Typed runner-side errors. Ours, not the draft's: the draft's families
|
|
80
|
+
* travel inside the page, while these describe the runner boundary itself
|
|
81
|
+
* so a missing installation never passes silently as a generic error.
|
|
82
|
+
*/
|
|
83
|
+
export declare class AxMissingSurfaceError extends Error {
|
|
84
|
+
constructor();
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/execute.d.ts
|
|
88
|
+
/**
|
|
89
|
+
* Structured parse failure: the tool resolved to a string outside JSON,
|
|
90
|
+
* so call sites receive a typed error carrying the tool name and the raw
|
|
91
|
+
* text instead of a bare syntax error.
|
|
92
|
+
*/
|
|
93
|
+
export declare class AxParseError extends Error {
|
|
94
|
+
readonly toolName: string;
|
|
95
|
+
readonly raw: string;
|
|
96
|
+
constructor(toolName: string, raw: string, cause: unknown);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Available tools sorted ascending by name in code-unit order, with origin
|
|
100
|
+
* scoping preserved from the underlying surface.
|
|
101
|
+
*/
|
|
102
|
+
export declare function getAvailableTools(page: Page, options?: AxListOptions): Promise<AxToolSummary[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Execute a named tool with serializable arguments. The name is resolved
|
|
105
|
+
* to a fresh handle inside the page on every call and never cached across
|
|
106
|
+
* the boundary; the specified string result is parsed at this boundary.
|
|
107
|
+
* Draft rejection families propagate unchanged.
|
|
108
|
+
*/
|
|
109
|
+
export declare function executeTool<T = unknown>(page: Page, name: string, args?: unknown, options?: AxExecuteOptions): Promise<T>;
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/fixture.d.ts
|
|
112
|
+
type AxTestArgs = PlaywrightTestArgs & PlaywrightTestOptions & {
|
|
113
|
+
page: AxPage;
|
|
114
|
+
};
|
|
115
|
+
type AxWorkerArgs = PlaywrightWorkerArgs & PlaywrightWorkerOptions;
|
|
116
|
+
/**
|
|
117
|
+
* Overriding page fixture. It installs exactly one init script carrying the
|
|
118
|
+
* built page-side bundle before any test navigation, attaches the
|
|
119
|
+
* runner-realm companion, yields the page, and disposes the init-script
|
|
120
|
+
* handle on teardown so no registration outlives its test.
|
|
121
|
+
*/
|
|
122
|
+
export declare const test: TestType<AxTestArgs, AxWorkerArgs>;
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/match.d.ts
|
|
125
|
+
/**
|
|
126
|
+
* Code-unit name comparison, mirroring the draft's ascending sort. JS
|
|
127
|
+
* relational comparison is code-unit based, so `<`/`>` implement the rule
|
|
128
|
+
* without locale sensitivity: case and non-ASCII order by code point.
|
|
129
|
+
*/
|
|
130
|
+
export declare function compareToolNames(first: string, second: string): number;
|
|
131
|
+
/** Exact name lookup. Position is never identity. */
|
|
132
|
+
export declare function findToolByName(tools: ReadonlyArray<AxToolSummary>, name: string): AxToolSummary | undefined;
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/paths.d.ts
|
|
135
|
+
/**
|
|
136
|
+
* Absolute path of the built page-side bundle installed by the fixture.
|
|
137
|
+
* Resolved from the current module so the runner entry and the page bundle
|
|
138
|
+
* stay co-located after publish. During development the entry runs from
|
|
139
|
+
* source, so the sibling build output is accepted as well.
|
|
140
|
+
*/
|
|
141
|
+
export declare function resolveInitScriptPath(): string;
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/wait.d.ts
|
|
144
|
+
/**
|
|
145
|
+
* Event-driven wait: pre-flight listing first so an already-registered tool
|
|
146
|
+
* resolves without attaching a listener, otherwise a one-shot in-page
|
|
147
|
+
* change hint racing an in-page guard, re-listing on every wake and
|
|
148
|
+
* matching by name in code-unit order until the deadline.
|
|
149
|
+
*/
|
|
150
|
+
export declare function waitForTool(page: Page, name: string, options?: AxWaitOptions): Promise<AxToolSummary>;
|
|
151
|
+
/**
|
|
152
|
+
* Assertion flavor over the same listing and matcher, built on the
|
|
153
|
+
* runner's retrying poll with forwarded timeout options. A miss rejects
|
|
154
|
+
* with a `TimeoutError` naming the tool, matching `waitForTool`. Listing
|
|
155
|
+
* failures other than a miss propagate unchanged.
|
|
156
|
+
*/
|
|
157
|
+
export declare function expectTool(page: Page, name: string, options?: AxExpectOptions): Promise<AxToolSummary>;
|
|
158
|
+
//#endregion
|
|
159
|
+
export { type AxExecuteOptions, type AxExpectOptions, type AxListOptions, type AxPage, type AxToolAnnotations, type AxToolSummary, type AxWaitOptions, expect };
|
|
160
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/ax.ts","../src/errors.ts","../src/execute.ts","../src/fixture.ts","../src/match.ts","../src/paths.ts","../src/wait.ts"],"mappings":";;;;;;;;UAUiB;WACP;WACA;WACA;WACA;;UAGO;WACP;WACA;WACA;WACA;WACA,cAAc;;UAGP;WACP;;UAGO;WACP;;UAGO;WACP,cAAc;;UAGP;WACP,SAAS;;;;;;;;;qBCnBN;WACH,MAAM;EAEf,YAAY,MAAM;;;;;EAQlB,eAAqB;;;;;;EAqBrB,YACC,cACA,UAAU,gBACR,QAAQ;;;;;EAQX,WACC,cACA,UAAU,kBACR,QAAQ;;;;;EAQX,kBAAwB,UAAU,gBAAgB,QAAQ;;;;;;EAS1D,YAAkB,aACjB,cACA,gBACA,UAAU,mBACR,QAAQ;;;KAMA,SAAS;EACpB,IAAI;;;;;;;;;qBCrFQ,8BAA8B;EAC1C;;;;;;;;;qBCQY,qBAAqB;WACxB;WACA;EAET,YAAY,kBAAkB,aAAa;;;;;;wBAmCtB,kBACrB,MAAM,MACN,UAAU,gBACR,QAAQ;;;;;;;wBAqBW,YAAY,aACjC,MAAM,MACN,cACA,gBACA,UAAU,mBACR,QAAQ;;;KCtEC,aAAa,qBACxB;EAA0B,MAAM;;KACrB,eAAe,uBAAuB;;;;;;;qBAQrC,MAAM,SAAS,YAAY;;;;;;;;wBCfxB,iBAAiB,eAAe;;wBAWhC,eACf,OAAO,cAAc,gBACrB,eACE;;;;;;;;;wBCVa;;;;;;;;;wBC4BM,YACrB,MAAM,MACN,cACA,UAAU,gBACR,QAAQ;;;;;;;wBAsGW,WACrB,MAAM,MACN,cACA,UAAU,kBACR,QAAQ"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Page, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType, expect } from "@playwright/test";
|
|
2
|
+
//#region src/types.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Runner-side shapes for the typed page surface. These are ours, not the
|
|
5
|
+
* draft's: the draft defines `RegisteredTool` with a live `window` field
|
|
6
|
+
* that cannot cross the evaluate boundary, so the companion strips it and
|
|
7
|
+
* carries names instead. Nothing here is visible to a conformance run.
|
|
8
|
+
*/
|
|
9
|
+
interface AxToolAnnotations {
|
|
10
|
+
readonly readOnlyHint?: boolean | undefined;
|
|
11
|
+
readonly untrustedContentHint?: boolean | undefined;
|
|
12
|
+
readonly consequentialHint?: boolean | undefined;
|
|
13
|
+
readonly debugging?: boolean | undefined;
|
|
14
|
+
}
|
|
15
|
+
interface AxToolSummary {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly description: string;
|
|
18
|
+
readonly title: string;
|
|
19
|
+
readonly origin: string;
|
|
20
|
+
readonly annotations?: AxToolAnnotations | undefined;
|
|
21
|
+
}
|
|
22
|
+
interface AxWaitOptions {
|
|
23
|
+
readonly timeout?: number | undefined;
|
|
24
|
+
}
|
|
25
|
+
interface AxExpectOptions {
|
|
26
|
+
readonly timeout?: number | undefined;
|
|
27
|
+
}
|
|
28
|
+
interface AxListOptions {
|
|
29
|
+
readonly fromOrigins?: ReadonlyArray<string> | undefined;
|
|
30
|
+
}
|
|
31
|
+
interface AxExecuteOptions {
|
|
32
|
+
readonly signal?: AbortSignal | undefined;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/ax.d.ts
|
|
36
|
+
/**
|
|
37
|
+
* Runner-realm companion attached to the page by the fixture. It never
|
|
38
|
+
* enters the page namespace: all state lives in the test process and only
|
|
39
|
+
* serializable data crosses the evaluate boundary.
|
|
40
|
+
*/
|
|
41
|
+
export declare class AxCompanion {
|
|
42
|
+
readonly page: Page;
|
|
43
|
+
constructor(page: Page);
|
|
44
|
+
/**
|
|
45
|
+
* True where the page-side bundle installed the typed surface.
|
|
46
|
+
* Feature-detects rather than assuming installation succeeded.
|
|
47
|
+
*/
|
|
48
|
+
isInstalled(): Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve when the named tool is listed, via pre-flight plus an
|
|
51
|
+
* in-page change hint with a bounded guard. Matches by name in
|
|
52
|
+
* code-unit order; spurious wakes re-list until the deadline.
|
|
53
|
+
*/
|
|
54
|
+
waitForTool(name: string, options?: AxWaitOptions): Promise<AxToolSummary>;
|
|
55
|
+
/**
|
|
56
|
+
* Assertion flavor over the same listing and matcher, polling with
|
|
57
|
+
* the runner's retrying primitive and forwarded timeout options.
|
|
58
|
+
*/
|
|
59
|
+
expectTool(name: string, options?: AxExpectOptions): Promise<AxToolSummary>;
|
|
60
|
+
/**
|
|
61
|
+
* Available tools sorted ascending by name in code-unit order, with
|
|
62
|
+
* origin scoping preserved from the underlying surface.
|
|
63
|
+
*/
|
|
64
|
+
getAvailableTools(options?: AxListOptions): Promise<AxToolSummary[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Execute a named tool with serializable arguments. The name resolves
|
|
67
|
+
* to a fresh handle inside the page on every call; the string result
|
|
68
|
+
* parses at this boundary with a typed error on failure.
|
|
69
|
+
*/
|
|
70
|
+
executeTool<T = unknown>(name: string, args?: unknown, options?: AxExecuteOptions): Promise<T>;
|
|
71
|
+
}
|
|
72
|
+
/** A page with the runner-realm companion attached by the fixture. */
|
|
73
|
+
type AxPage = Page & {
|
|
74
|
+
ax: AxCompanion;
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/errors.d.ts
|
|
78
|
+
/**
|
|
79
|
+
* Typed runner-side errors. Ours, not the draft's: the draft's families
|
|
80
|
+
* travel inside the page, while these describe the runner boundary itself
|
|
81
|
+
* so a missing installation never passes silently as a generic error.
|
|
82
|
+
*/
|
|
83
|
+
export declare class AxMissingSurfaceError extends Error {
|
|
84
|
+
constructor();
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/execute.d.ts
|
|
88
|
+
/**
|
|
89
|
+
* Structured parse failure: the tool resolved to a string outside JSON,
|
|
90
|
+
* so call sites receive a typed error carrying the tool name and the raw
|
|
91
|
+
* text instead of a bare syntax error.
|
|
92
|
+
*/
|
|
93
|
+
export declare class AxParseError extends Error {
|
|
94
|
+
readonly toolName: string;
|
|
95
|
+
readonly raw: string;
|
|
96
|
+
constructor(toolName: string, raw: string, cause: unknown);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Available tools sorted ascending by name in code-unit order, with origin
|
|
100
|
+
* scoping preserved from the underlying surface.
|
|
101
|
+
*/
|
|
102
|
+
export declare function getAvailableTools(page: Page, options?: AxListOptions): Promise<AxToolSummary[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Execute a named tool with serializable arguments. The name is resolved
|
|
105
|
+
* to a fresh handle inside the page on every call and never cached across
|
|
106
|
+
* the boundary; the specified string result is parsed at this boundary.
|
|
107
|
+
* Draft rejection families propagate unchanged.
|
|
108
|
+
*/
|
|
109
|
+
export declare function executeTool<T = unknown>(page: Page, name: string, args?: unknown, options?: AxExecuteOptions): Promise<T>;
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/fixture.d.ts
|
|
112
|
+
type AxTestArgs = PlaywrightTestArgs & PlaywrightTestOptions & {
|
|
113
|
+
page: AxPage;
|
|
114
|
+
};
|
|
115
|
+
type AxWorkerArgs = PlaywrightWorkerArgs & PlaywrightWorkerOptions;
|
|
116
|
+
/**
|
|
117
|
+
* Overriding page fixture. It installs exactly one init script carrying the
|
|
118
|
+
* built page-side bundle before any test navigation, attaches the
|
|
119
|
+
* runner-realm companion, yields the page, and disposes the init-script
|
|
120
|
+
* handle on teardown so no registration outlives its test.
|
|
121
|
+
*/
|
|
122
|
+
export declare const test: TestType<AxTestArgs, AxWorkerArgs>;
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/match.d.ts
|
|
125
|
+
/**
|
|
126
|
+
* Code-unit name comparison, mirroring the draft's ascending sort. JS
|
|
127
|
+
* relational comparison is code-unit based, so `<`/`>` implement the rule
|
|
128
|
+
* without locale sensitivity: case and non-ASCII order by code point.
|
|
129
|
+
*/
|
|
130
|
+
export declare function compareToolNames(first: string, second: string): number;
|
|
131
|
+
/** Exact name lookup. Position is never identity. */
|
|
132
|
+
export declare function findToolByName(tools: ReadonlyArray<AxToolSummary>, name: string): AxToolSummary | undefined;
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/paths.d.ts
|
|
135
|
+
/**
|
|
136
|
+
* Absolute path of the built page-side bundle installed by the fixture.
|
|
137
|
+
* Resolved from the current module so the runner entry and the page bundle
|
|
138
|
+
* stay co-located after publish. During development the entry runs from
|
|
139
|
+
* source, so the sibling build output is accepted as well.
|
|
140
|
+
*/
|
|
141
|
+
export declare function resolveInitScriptPath(): string;
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/wait.d.ts
|
|
144
|
+
/**
|
|
145
|
+
* Event-driven wait: pre-flight listing first so an already-registered tool
|
|
146
|
+
* resolves without attaching a listener, otherwise a one-shot in-page
|
|
147
|
+
* change hint racing an in-page guard, re-listing on every wake and
|
|
148
|
+
* matching by name in code-unit order until the deadline.
|
|
149
|
+
*/
|
|
150
|
+
export declare function waitForTool(page: Page, name: string, options?: AxWaitOptions): Promise<AxToolSummary>;
|
|
151
|
+
/**
|
|
152
|
+
* Assertion flavor over the same listing and matcher, built on the
|
|
153
|
+
* runner's retrying poll with forwarded timeout options. A miss rejects
|
|
154
|
+
* with a `TimeoutError` naming the tool, matching `waitForTool`. Listing
|
|
155
|
+
* failures other than a miss propagate unchanged.
|
|
156
|
+
*/
|
|
157
|
+
export declare function expectTool(page: Page, name: string, options?: AxExpectOptions): Promise<AxToolSummary>;
|
|
158
|
+
//#endregion
|
|
159
|
+
export { type AxExecuteOptions, type AxExpectOptions, type AxListOptions, type AxPage, type AxToolAnnotations, type AxToolSummary, type AxWaitOptions, expect };
|
|
160
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/ax.ts","../src/errors.ts","../src/execute.ts","../src/fixture.ts","../src/match.ts","../src/paths.ts","../src/wait.ts"],"mappings":";;;;;;;;UAUiB;WACP;WACA;WACA;WACA;;UAGO;WACP;WACA;WACA;WACA;WACA,cAAc;;UAGP;WACP;;UAGO;WACP;;UAGO;WACP,cAAc;;UAGP;WACP,SAAS;;;;;;;;;qBCnBN;WACH,MAAM;EAEf,YAAY,MAAM;;;;;EAQlB,eAAqB;;;;;;EAqBrB,YACC,cACA,UAAU,gBACR,QAAQ;;;;;EAQX,WACC,cACA,UAAU,kBACR,QAAQ;;;;;EAQX,kBAAwB,UAAU,gBAAgB,QAAQ;;;;;;EAS1D,YAAkB,aACjB,cACA,gBACA,UAAU,mBACR,QAAQ;;;KAMA,SAAS;EACpB,IAAI;;;;;;;;;qBCrFQ,8BAA8B;EAC1C;;;;;;;;;qBCQY,qBAAqB;WACxB;WACA;EAET,YAAY,kBAAkB,aAAa;;;;;;wBAmCtB,kBACrB,MAAM,MACN,UAAU,gBACR,QAAQ;;;;;;;wBAqBW,YAAY,aACjC,MAAM,MACN,cACA,gBACA,UAAU,mBACR,QAAQ;;;KCtEC,aAAa,qBACxB;EAA0B,MAAM;;KACrB,eAAe,uBAAuB;;;;;;;qBAQrC,MAAM,SAAS,YAAY;;;;;;;;wBCfxB,iBAAiB,eAAe;;wBAWhC,eACf,OAAO,cAAc,gBACrB,eACE;;;;;;;;;wBCVa;;;;;;;;;wBC4BM,YACrB,MAAM,MACN,cACA,UAAU,gBACR,QAAQ;;;;;;;wBAsGW,WACrB,MAAM,MACN,cACA,UAAU,kBACR,QAAQ"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{expect as e,expect as t,test as n}from"@playwright/test";import{existsSync as r}from"node:fs";import{fileURLToPath as i}from"node:url";var a=class extends Error{constructor(){super(`typed surface is missing`),this.name=`AxMissingSurfaceError`}};async function o(e){try{return await e}catch(e){throw e instanceof Error&&e.message.includes(`typed surface is missing`)?new a:e}}function s(e,t){return e<t?-1:+(e>t)}function c(e,t){return e.find(e=>e.name===t)}async function l(e,t){let n=await o(e.evaluate(e=>{let t=e,n=document.modelContext;if(n==null||typeof n.getTools!=`function`)throw Error(`typed surface is missing`);let r=t.fromOrigins===void 0||t.fromOrigins===null?void 0:{fromOrigins:t.fromOrigins};return n.getTools(r).then(e=>e.map(e=>({name:e.name,description:e.description,title:e.title,origin:e.origin,annotations:e.annotations})))},{fromOrigins:t??null})),r=[];for(let e of n)typeof e.name==`string`&&r.push({name:e.name,description:typeof e.description==`string`?e.description:``,title:typeof e.title==`string`?e.title:``,origin:typeof e.origin==`string`?e.origin:``,...u(e.annotations)?{annotations:e.annotations}:{}});return r.sort((e,t)=>s(e.name,t.name)),r}function u(e){return!(typeof e!=`object`||!e)}var d=class extends Error{toolName;raw;constructor(e,t,n){super(`unparsable result from tool "${e}"`),this.name=`AxParseError`,this.toolName=e,this.raw=t,this.cause=n}};function f(e,t){return t.aborted===!0?Promise.reject(t.reason):new Promise((n,r)=>{function i(){r(t.reason)}t.addEventListener(`abort`,i,{once:!0}),e.then(e=>{t.removeEventListener(`abort`,i),n(e)},e=>{t.removeEventListener(`abort`,i),r(e)})})}async function p(e,t){let n=t?.fromOrigins;if(n!==void 0){if(!Array.isArray(n))throw TypeError(`bad fromOrigins`);for(let e of n)if(typeof e!=`string`)throw TypeError(`bad fromOrigins`)}return l(e,n)}async function m(e,t,n,r){if(typeof t!=`string`)throw TypeError(`bad tool name`);let i=n===void 0?{}:n;if(typeof i!=`object`||!i)throw TypeError(`bad arguments`);let a=r?.signal;if(a!==void 0&&(typeof a.aborted!=`boolean`||typeof a.addEventListener!=`function`))throw TypeError(`bad signal`);let s=e.evaluate(e=>{let t=e,n=typeof t.name==`string`?t.name:``,r=document.modelContext;if(typeof r!=`object`||!r)throw Error(`typed surface is missing`);let i=r;if(typeof i.getTools!=`function`||typeof i.executeTool!=`function`)throw Error(`typed surface is missing`);return i.getTools().then(e=>{let r=e.find(e=>typeof e.name==`string`&&e.name===n);if(r===void 0)throw new DOMException(`unknown tool ${n}`,`NotFoundError`);return i.executeTool(r,t.input)}).then(e=>{if(typeof e!=`string`)throw new DOMException(`bad tool result`,`OperationError`);return e})},{name:t,input:i}),c=a===void 0?await o(s):await f(o(s),a);try{return JSON.parse(c)}catch(e){throw new d(t,c,e)}}function h(e){let t=e.timeout??5e3;if(typeof t!=`number`||Number.isNaN(t)||!Number.isFinite(t)||t<0)throw TypeError(`bad timeout`);return t}function g(e,t){let n=/* @__PURE__ */ Error(`timed out waiting for tool "${e}" (${t}ms)`);return n.name=`TimeoutError`,n}async function _(e,t,n){if(typeof t!=`string`)throw TypeError(`bad tool name`);let r=h(n??{}),i=Date.now()+r,o=c(await l(e),t);if(o!==void 0)return o;for(;;){let n=i-Date.now();if(n<=0)throw g(t,r);try{await e.evaluate(e=>{let t=e,n=typeof t.ms==`number`?t.ms:0,r=typeof t.name==`string`?t.name:``,i=document.modelContext;if(typeof i!=`object`||!i)throw Error(`typed surface is missing`);let a=i;if(typeof a.addEventListener!=`function`||typeof a.removeEventListener!=`function`)throw Error(`typed surface is missing`);let o=i.getTools;if(typeof o!=`function`)throw Error(`typed surface is missing`);let s=o;return new Promise((e,t)=>{let i=setTimeout(()=>{a.removeEventListener(`toolchange`,o),t(/* @__PURE__ */ Error(`wait guard expired`))},n);function o(){clearTimeout(i),e({found:!1})}a.addEventListener(`toolchange`,o,{once:!0}),s.call(a).then(t=>{t.some(e=>typeof e.name==`string`&&e.name===r)&&(clearTimeout(i),a.removeEventListener(`toolchange`,o),e({found:!0}))}).catch(()=>void 0)})},{ms:n,name:t})}catch(e){throw e instanceof Error&&e.message.includes(`typed surface is missing`)?new a:e instanceof Error&&e.message.includes(`wait guard expired`)?g(t,r):e}let o=c(await l(e),t);if(o!==void 0)return o}}async function v(e,n,r){if(typeof n!=`string`)throw TypeError(`bad tool name`);let i=h(r??{});await l(e);try{await t.poll(async()=>c(await l(e),n)?.name??null,{timeout:i}).toBe(n)}catch(e){throw e instanceof a?e:e instanceof Error&&`matcherResult`in e?g(n,i):e}let o=c(await l(e),n);if(o===void 0)throw g(n,i);return o}var y=class{page;constructor(e){this.page=e}async isInstalled(){return this.page.evaluate(()=>{let e=document.modelContext;if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.getTools==`function`&&typeof t.registerTool==`function`&&typeof t.executeTool==`function`})}async waitForTool(e,t){return _(this.page,e,t)}async expectTool(e,t){return v(this.page,e,t)}async getAvailableTools(e){return p(this.page,e)}async executeTool(e,t,n){return m(this.page,e,t,n)}};const b=`init.global.iife.js`;function x(){let e=[new URL(`./${b}`,import.meta.url),new URL(`../dist/${b}`,import.meta.url)];for(let t of e){let e=i(t);if(r(e))return e}return i(e[0])}const S=n.extend({page:async({page:e},t)=>{let n=x(),r=await e.addInitScript({path:n});try{let n=e;n.ax=new y(e),await t(n)}finally{await r.dispose()}}});export{y as AxCompanion,a as AxMissingSurfaceError,d as AxParseError,s as compareToolNames,m as executeTool,e as expect,v as expectTool,c as findToolByName,p as getAvailableTools,x as resolveInitScriptPath,S as test,_ as waitForTool};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["holder","holder","holder","expect","holder","base","expect$1","test$1","AxMissingSurfaceError","withSurfaceError","task","error","compareToolNames","first","second","findToolByName","tools","tool","name","listToolSummaries","records","page","payload","arg","surface","origins","fromOrigins","summaries","record","isAnnotations","value","AxParseError","toolName","raw","cause","raceWithSignal","signal","onAbort","reject","resolve","getAvailableTools","options","origin","executeTool","input","args","wanted","match","text","readTimeout","timeout","timeoutError","waitForTool","deadline","preflight","remaining","ms","target","getTools","list","timer","onChange","current","expectTool","AxCompanion","candidate","INIT_SCRIPT_FILENAME","resolveInitScriptPath","candidates","path","fileURLToPath","existsSync","test","initPath","handle","axPage","use","expect"],"sources":["../src/errors.ts","../src/match.ts","../src/listing.ts","../src/execute.ts","../src/wait.ts","../src/ax.ts","../src/paths.ts","../src/fixture.ts"],"sourcesContent":["// Copyright 2026 Utpal Sen\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Typed runner-side errors. Ours, not the draft's: the draft's families\n * travel inside the page, while these describe the runner boundary itself\n * so a missing installation never passes silently as a generic error.\n */\nexport class AxMissingSurfaceError extends Error {\n\tconstructor() {\n\t\tsuper(\"typed surface is missing\");\n\t\tthis.name = \"AxMissingSurfaceError\";\n\t}\n}\n\n/**\n * Map a surface-absence failure crossing the evaluate boundary to the\n * typed error. In-page throws arrive as generic errors carrying our\n * message; callers observe the type, never the string.\n */\nexport async function withSurfaceError<T>(task: Promise<T>): Promise<T> {\n\ttry {\n\t\treturn await task;\n\t} catch (error) {\n\t\tif (\n\t\t\terror instanceof Error &&\n\t\t\terror.message.includes(\"typed surface is missing\")\n\t\t) {\n\t\t\tthrow new AxMissingSurfaceError();\n\t\t}\n\t\tthrow error;\n\t}\n}\n","// Copyright 2026 Utpal Sen\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { AxToolSummary } from \"./types.js\";\n\n/**\n * Code-unit name comparison, mirroring the draft's ascending sort. JS\n * relational comparison is code-unit based, so `<`/`>` implement the rule\n * without locale sensitivity: case and non-ASCII order by code point.\n */\nexport function compareToolNames(first: string, second: string): number {\n\tif (first < second) {\n\t\treturn -1;\n\t}\n\tif (first > second) {\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\n/** Exact name lookup. Position is never identity. */\nexport function findToolByName(\n\ttools: ReadonlyArray<AxToolSummary>,\n\tname: string,\n): AxToolSummary | undefined {\n\treturn tools.find((tool) => tool.name === name);\n}\n","// Copyright 2026 Utpal Sen\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Page } from \"@playwright/test\";\nimport { withSurfaceError } from \"./errors.js\";\nimport { compareToolNames } from \"./match.js\";\nimport type { AxToolSummary } from \"./types.js\";\n\ninterface PageToolRecord {\n\treadonly name: unknown;\n\treadonly description: unknown;\n\treadonly title: unknown;\n\treadonly origin: unknown;\n\treadonly annotations: unknown;\n}\n\n/**\n * Current listing mapped to serializable summaries. The live `window`\n * field never crosses the boundary; names cross instead. Sorted ascending\n * by name in code-unit order.\n */\nexport async function listToolSummaries(\n\tpage: Page,\n\tfromOrigins?: ReadonlyArray<string> | undefined,\n): Promise<AxToolSummary[]> {\n\tconst records = await withSurfaceError(\n\t\tpage.evaluate(\n\t\t\t(arg: unknown): Promise<PageToolRecord[]> => {\n\t\t\t\tconst payload = arg as { fromOrigins: unknown };\n\t\t\t\tconst holder = document as unknown as Record<string, unknown>;\n\t\t\t\tconst surface = holder.modelContext as unknown as {\n\t\t\t\t\tgetTools?: (\n\t\t\t\t\t\toptions?: unknown,\n\t\t\t\t\t) => Promise<Array<Record<string, unknown>>>;\n\t\t\t\t} | null;\n\t\t\t\tif (\n\t\t\t\t\tsurface === null ||\n\t\t\t\t\tsurface === undefined ||\n\t\t\t\t\ttypeof surface.getTools !== \"function\"\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\"typed surface is missing\");\n\t\t\t\t}\n\t\t\t\tconst origins =\n\t\t\t\t\tpayload.fromOrigins === undefined || payload.fromOrigins === null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: { fromOrigins: payload.fromOrigins };\n\t\t\t\treturn surface.getTools(origins).then((tools) =>\n\t\t\t\t\ttools.map(\n\t\t\t\t\t\t(tool): PageToolRecord => ({\n\t\t\t\t\t\t\tname: tool.name,\n\t\t\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\t\t\ttitle: tool.title,\n\t\t\t\t\t\t\torigin: tool.origin,\n\t\t\t\t\t\t\tannotations: tool.annotations,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t},\n\t\t\t{ fromOrigins: fromOrigins ?? null },\n\t\t),\n\t);\n\tconst summaries: AxToolSummary[] = [];\n\tfor (const record of records) {\n\t\tif (typeof record.name !== \"string\") {\n\t\t\tcontinue;\n\t\t}\n\t\tsummaries.push({\n\t\t\tname: record.name,\n\t\t\tdescription:\n\t\t\t\ttypeof record.description === \"string\" ? record.description : \"\",\n\t\t\ttitle: typeof record.title === \"string\" ? record.title : \"\",\n\t\t\torigin: typeof record.origin === \"string\" ? record.origin : \"\",\n\t\t\t...(isAnnotations(record.annotations)\n\t\t\t\t? { annotations: record.annotations }\n\t\t\t\t: {}),\n\t\t});\n\t}\n\tsummaries.sort((first, second) => compareToolNames(first.name, second.name));\n\treturn summaries;\n}\n\nfunction isAnnotations(value: unknown): value is AxToolSummary[\"annotations\"] {\n\tif (typeof value !== \"object\" || value === null) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n","// Copyright 2026 Utpal Sen\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Page } from \"@playwright/test\";\nimport { withSurfaceError } from \"./errors.js\";\nimport { listToolSummaries } from \"./listing.js\";\nimport type {\n\tAxExecuteOptions,\n\tAxListOptions,\n\tAxToolSummary,\n} from \"./types.js\";\n\n/**\n * Structured parse failure: the tool resolved to a string outside JSON,\n * so call sites receive a typed error carrying the tool name and the raw\n * text instead of a bare syntax error.\n */\nexport class AxParseError extends Error {\n\treadonly toolName: string;\n\treadonly raw: string;\n\n\tconstructor(toolName: string, raw: string, cause: unknown) {\n\t\tsuper(`unparsable result from tool \"${toolName}\"`);\n\t\tthis.name = \"AxParseError\";\n\t\tthis.toolName = toolName;\n\t\tthis.raw = raw;\n\t\tthis.cause = cause;\n\t}\n}\n\nfunction raceWithSignal<T>(task: Promise<T>, signal: AbortSignal): Promise<T> {\n\tif (signal.aborted === true) {\n\t\treturn Promise.reject(signal.reason);\n\t}\n\treturn new Promise<T>((resolve, reject) => {\n\t\tfunction onAbort(): void {\n\t\t\treject(signal.reason);\n\t\t}\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\ttask.then(\n\t\t\t(value) => {\n\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\n/**\n * Available tools sorted ascending by name in code-unit order, with origin\n * scoping preserved from the underlying surface.\n */\nexport async function getAvailableTools(\n\tpage: Page,\n\toptions?: AxListOptions,\n): Promise<AxToolSummary[]> {\n\tconst fromOrigins = options?.fromOrigins;\n\tif (fromOrigins !== undefined) {\n\t\tif (!Array.isArray(fromOrigins)) {\n\t\t\tthrow new TypeError(\"bad fromOrigins\");\n\t\t}\n\t\tfor (const origin of fromOrigins) {\n\t\t\tif (typeof origin !== \"string\") {\n\t\t\t\tthrow new TypeError(\"bad fromOrigins\");\n\t\t\t}\n\t\t}\n\t}\n\treturn listToolSummaries(page, fromOrigins);\n}\n\n/**\n * Execute a named tool with serializable arguments. The name is resolved\n * to a fresh handle inside the page on every call and never cached across\n * the boundary; the specified string result is parsed at this boundary.\n * Draft rejection families propagate unchanged.\n */\nexport async function executeTool<T = unknown>(\n\tpage: Page,\n\tname: string,\n\targs?: unknown,\n\toptions?: AxExecuteOptions,\n): Promise<T> {\n\tif (typeof name !== \"string\") {\n\t\tthrow new TypeError(\"bad tool name\");\n\t}\n\tconst input: unknown = args === undefined ? {} : args;\n\tif (typeof input !== \"object\" || input === null) {\n\t\tthrow new TypeError(\"bad arguments\");\n\t}\n\tconst signal = options?.signal;\n\tif (signal !== undefined) {\n\t\tif (\n\t\t\ttypeof signal.aborted !== \"boolean\" ||\n\t\t\ttypeof signal.addEventListener !== \"function\"\n\t\t) {\n\t\t\tthrow new TypeError(\"bad signal\");\n\t\t}\n\t}\n\tconst task: Promise<string> = page.evaluate(\n\t\t(arg: unknown): Promise<string> => {\n\t\t\tconst payload = arg as { name: unknown; input: unknown };\n\t\t\tconst wanted = typeof payload.name === \"string\" ? payload.name : \"\";\n\t\t\tconst holder = document as unknown as Record<string, unknown>;\n\t\t\tconst raw = holder.modelContext as unknown;\n\t\t\tif (typeof raw !== \"object\" || raw === null) {\n\t\t\t\tthrow new Error(\"typed surface is missing\");\n\t\t\t}\n\t\t\tconst surface = raw as {\n\t\t\t\tgetTools: () => Promise<Array<Record<string, unknown>>>;\n\t\t\t\texecuteTool: (tool: unknown, input: unknown) => Promise<string>;\n\t\t\t};\n\t\t\tif (\n\t\t\t\ttypeof surface.getTools !== \"function\" ||\n\t\t\t\ttypeof surface.executeTool !== \"function\"\n\t\t\t) {\n\t\t\t\tthrow new Error(\"typed surface is missing\");\n\t\t\t}\n\t\t\treturn surface\n\t\t\t\t.getTools()\n\t\t\t\t.then((tools) => {\n\t\t\t\t\tconst match = tools.find(\n\t\t\t\t\t\t(tool) => typeof tool.name === \"string\" && tool.name === wanted,\n\t\t\t\t\t);\n\t\t\t\t\tif (match === undefined) {\n\t\t\t\t\t\tthrow new DOMException(`unknown tool ${wanted}`, \"NotFoundError\");\n\t\t\t\t\t}\n\t\t\t\t\treturn surface.executeTool(match, payload.input);\n\t\t\t\t})\n\t\t\t\t.then((text) => {\n\t\t\t\t\tif (typeof text !== \"string\") {\n\t\t\t\t\t\tthrow new DOMException(\"bad tool result\", \"OperationError\");\n\t\t\t\t\t}\n\t\t\t\t\treturn text;\n\t\t\t\t});\n\t\t},\n\t\t{ name, input },\n\t);\n\tconst text =\n\t\tsignal === undefined\n\t\t\t? await withSurfaceError(task)\n\t\t\t: await raceWithSignal(withSurfaceError(task), signal);\n\ttry {\n\t\treturn JSON.parse(text) as T;\n\t} catch (cause: unknown) {\n\t\tthrow new AxParseError(name, text, cause);\n\t}\n}\n","// Copyright 2026 Utpal Sen\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Page } from \"@playwright/test\";\nimport { expect } from \"@playwright/test\";\nimport { AxMissingSurfaceError } from \"./errors.js\";\nimport { listToolSummaries } from \"./listing.js\";\nimport { findToolByName } from \"./match.js\";\nimport type { AxExpectOptions, AxToolSummary, AxWaitOptions } from \"./types.js\";\n\n// Matches the runner's documented assertion default of 5 seconds. No public\n// API exposes the configured expect timeout, so per-call override and the\n// runner's own configuration (honored by expectTool's poll) compose instead.\nconst DEFAULT_TIMEOUT_MS = 5_000;\n\nfunction readTimeout(options: AxWaitOptions | AxExpectOptions): number {\n\tconst timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;\n\tif (\n\t\ttypeof timeout !== \"number\" ||\n\t\tNumber.isNaN(timeout) ||\n\t\t!Number.isFinite(timeout) ||\n\t\ttimeout < 0\n\t) {\n\t\tthrow new TypeError(\"bad timeout\");\n\t}\n\treturn timeout;\n}\n\nfunction timeoutError(name: string, timeout: number): Error {\n\tconst error = new Error(\n\t\t`timed out waiting for tool \"${name}\" (${timeout}ms)`,\n\t);\n\terror.name = \"TimeoutError\";\n\treturn error;\n}\n\n/**\n * Event-driven wait: pre-flight listing first so an already-registered tool\n * resolves without attaching a listener, otherwise a one-shot in-page\n * change hint racing an in-page guard, re-listing on every wake and\n * matching by name in code-unit order until the deadline.\n */\nexport async function waitForTool(\n\tpage: Page,\n\tname: string,\n\toptions?: AxWaitOptions,\n): Promise<AxToolSummary> {\n\tif (typeof name !== \"string\") {\n\t\tthrow new TypeError(\"bad tool name\");\n\t}\n\tconst timeout = readTimeout(options ?? {});\n\tconst deadline = Date.now() + timeout;\n\tconst preflight = findToolByName(await listToolSummaries(page), name);\n\tif (preflight !== undefined) {\n\t\treturn preflight;\n\t}\n\tfor (;;) {\n\t\tconst remaining = deadline - Date.now();\n\t\tif (remaining <= 0) {\n\t\t\tthrow timeoutError(name, timeout);\n\t\t}\n\t\t// Attach, re-check, then wait in one evaluate so a registration\n\t\t// landing between the pre-flight and this attach is still seen.\n\t\ttry {\n\t\t\tawait page.evaluate(\n\t\t\t\t(arg: unknown): Promise<{ found: boolean }> => {\n\t\t\t\t\tconst payload = arg as { ms: unknown; name: unknown };\n\t\t\t\t\tconst ms = typeof payload.ms === \"number\" ? payload.ms : 0;\n\t\t\t\t\tconst wanted = typeof payload.name === \"string\" ? payload.name : \"\";\n\t\t\t\t\tconst holder = document as unknown as Record<string, unknown>;\n\t\t\t\t\tconst raw = holder.modelContext as unknown;\n\t\t\t\t\tif (typeof raw !== \"object\" || raw === null) {\n\t\t\t\t\t\tthrow new Error(\"typed surface is missing\");\n\t\t\t\t\t}\n\t\t\t\t\tconst target = raw as EventTarget;\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof target.addEventListener !== \"function\" ||\n\t\t\t\t\t\ttypeof target.removeEventListener !== \"function\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new Error(\"typed surface is missing\");\n\t\t\t\t\t}\n\t\t\t\t\tconst getTools = (raw as Record<string, unknown>).getTools;\n\t\t\t\t\tif (typeof getTools !== \"function\") {\n\t\t\t\t\t\tthrow new Error(\"typed surface is missing\");\n\t\t\t\t\t}\n\t\t\t\t\tconst list = getTools as () => Promise<\n\t\t\t\t\t\tArray<Record<string, unknown>>\n\t\t\t\t\t>;\n\t\t\t\t\treturn new Promise<{ found: boolean }>((resolve, reject) => {\n\t\t\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\t\t\ttarget.removeEventListener(\"toolchange\", onChange);\n\t\t\t\t\t\t\treject(new Error(\"wait guard expired\"));\n\t\t\t\t\t\t}, ms);\n\t\t\t\t\t\tfunction onChange(): void {\n\t\t\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\t\t\tresolve({ found: false });\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttarget.addEventListener(\"toolchange\", onChange, {\n\t\t\t\t\t\t\tonce: true,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tvoid list\n\t\t\t\t\t\t\t.call(target)\n\t\t\t\t\t\t\t.then((tools) => {\n\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\ttools.some(\n\t\t\t\t\t\t\t\t\t\t(tool) =>\n\t\t\t\t\t\t\t\t\t\t\ttypeof tool.name === \"string\" &&\n\t\t\t\t\t\t\t\t\t\t\t(tool.name as string) === wanted,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\t\t\t\t\ttarget.removeEventListener(\"toolchange\", onChange);\n\t\t\t\t\t\t\t\t\tresolve({ found: true });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\t{ ms: remaining, name },\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (\n\t\t\t\terror instanceof Error &&\n\t\t\t\terror.message.includes(\"typed surface is missing\")\n\t\t\t) {\n\t\t\t\tthrow new AxMissingSurfaceError();\n\t\t\t}\n\t\t\tif (\n\t\t\t\terror instanceof Error &&\n\t\t\t\terror.message.includes(\"wait guard expired\")\n\t\t\t) {\n\t\t\t\tthrow timeoutError(name, timeout);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tconst current = findToolByName(await listToolSummaries(page), name);\n\t\tif (current !== undefined) {\n\t\t\treturn current;\n\t\t}\n\t}\n}\n\n/**\n * Assertion flavor over the same listing and matcher, built on the\n * runner's retrying poll with forwarded timeout options. A miss rejects\n * with a `TimeoutError` naming the tool, matching `waitForTool`. Listing\n * failures other than a miss propagate unchanged.\n */\nexport async function expectTool(\n\tpage: Page,\n\tname: string,\n\toptions?: AxExpectOptions,\n): Promise<AxToolSummary> {\n\tif (typeof name !== \"string\") {\n\t\tthrow new TypeError(\"bad tool name\");\n\t}\n\tconst timeout = readTimeout(options ?? {});\n\tawait listToolSummaries(page);\n\ttry {\n\t\tawait expect\n\t\t\t.poll(\n\t\t\t\tasync (): Promise<string | null> => {\n\t\t\t\t\tconst tools = await listToolSummaries(page);\n\t\t\t\t\treturn findToolByName(tools, name)?.name ?? null;\n\t\t\t\t},\n\t\t\t\t{ timeout },\n\t\t\t)\n\t\t\t.toBe(name);\n\t} catch (error) {\n\t\tif (error instanceof AxMissingSurfaceError) {\n\t\t\tthrow error;\n\t\t}\n\t\tif (error instanceof Error && \"matcherResult\" in error) {\n\t\t\tthrow timeoutError(name, timeout);\n\t\t}\n\t\tthrow error;\n\t}\n\tconst current = findToolByName(await listToolSummaries(page), name);\n\tif (current === undefined) {\n\t\tthrow timeoutError(name, timeout);\n\t}\n\treturn current;\n}\n","// Copyright 2026 Utpal Sen\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Page } from \"@playwright/test\";\nimport { executeTool, getAvailableTools } from \"./execute.js\";\nimport type {\n\tAxExecuteOptions,\n\tAxExpectOptions,\n\tAxListOptions,\n\tAxToolSummary,\n\tAxWaitOptions,\n} from \"./types.js\";\nimport { expectTool, waitForTool } from \"./wait.js\";\n\n/**\n * Runner-realm companion attached to the page by the fixture. It never\n * enters the page namespace: all state lives in the test process and only\n * serializable data crosses the evaluate boundary.\n */\nexport class AxCompanion {\n\treadonly page: Page;\n\n\tconstructor(page: Page) {\n\t\tthis.page = page;\n\t}\n\n\t/**\n\t * True where the page-side bundle installed the typed surface.\n\t * Feature-detects rather than assuming installation succeeded.\n\t */\n\tasync isInstalled(): Promise<boolean> {\n\t\treturn this.page.evaluate((): boolean => {\n\t\t\tconst holder = document as unknown as Record<string, unknown>;\n\t\t\tconst candidate = holder.modelContext;\n\t\t\tif (typeof candidate !== \"object\" || candidate === null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tconst surface = candidate as Record<string, unknown>;\n\t\t\treturn (\n\t\t\t\ttypeof surface.getTools === \"function\" &&\n\t\t\t\ttypeof surface.registerTool === \"function\" &&\n\t\t\t\ttypeof surface.executeTool === \"function\"\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Resolve when the named tool is listed, via pre-flight plus an\n\t * in-page change hint with a bounded guard. Matches by name in\n\t * code-unit order; spurious wakes re-list until the deadline.\n\t */\n\tasync waitForTool(\n\t\tname: string,\n\t\toptions?: AxWaitOptions,\n\t): Promise<AxToolSummary> {\n\t\treturn waitForTool(this.page, name, options);\n\t}\n\n\t/**\n\t * Assertion flavor over the same listing and matcher, polling with\n\t * the runner's retrying primitive and forwarded timeout options.\n\t */\n\tasync expectTool(\n\t\tname: string,\n\t\toptions?: AxExpectOptions,\n\t): Promise<AxToolSummary> {\n\t\treturn expectTool(this.page, name, options);\n\t}\n\n\t/**\n\t * Available tools sorted ascending by name in code-unit order, with\n\t * origin scoping preserved from the underlying surface.\n\t */\n\tasync getAvailableTools(options?: AxListOptions): Promise<AxToolSummary[]> {\n\t\treturn getAvailableTools(this.page, options);\n\t}\n\n\t/**\n\t * Execute a named tool with serializable arguments. The name resolves\n\t * to a fresh handle inside the page on every call; the string result\n\t * parses at this boundary with a typed error on failure.\n\t */\n\tasync executeTool<T = unknown>(\n\t\tname: string,\n\t\targs?: unknown,\n\t\toptions?: AxExecuteOptions,\n\t): Promise<T> {\n\t\treturn executeTool<T>(this.page, name, args, options);\n\t}\n}\n\n/** A page with the runner-realm companion attached by the fixture. */\nexport type AxPage = Page & {\n\tax: AxCompanion;\n};\n","// Copyright 2026 Utpal Sen\n// SPDX-License-Identifier: Apache-2.0\n\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\n\nconst INIT_SCRIPT_FILENAME = \"init.global.iife.js\";\n\n/**\n * Absolute path of the built page-side bundle installed by the fixture.\n * Resolved from the current module so the runner entry and the page bundle\n * stay co-located after publish. During development the entry runs from\n * source, so the sibling build output is accepted as well.\n */\nexport function resolveInitScriptPath(): string {\n\tconst candidates = [\n\t\tnew URL(`./${INIT_SCRIPT_FILENAME}`, import.meta.url),\n\t\tnew URL(`../dist/${INIT_SCRIPT_FILENAME}`, import.meta.url),\n\t];\n\tfor (const candidate of candidates) {\n\t\tconst path = fileURLToPath(candidate);\n\t\tif (existsSync(path)) {\n\t\t\treturn path;\n\t\t}\n\t}\n\treturn fileURLToPath(candidates[0]);\n}\n","// Copyright 2026 Utpal Sen\n// SPDX-License-Identifier: Apache-2.0\n\nimport type {\n\tPlaywrightTestArgs,\n\tPlaywrightTestOptions,\n\tPlaywrightWorkerArgs,\n\tPlaywrightWorkerOptions,\n\tTestType,\n} from \"@playwright/test\";\nimport { test as base, expect } from \"@playwright/test\";\nimport type { AxPage } from \"./ax.js\";\nimport { AxCompanion } from \"./ax.js\";\nimport { resolveInitScriptPath } from \"./paths.js\";\n\nexport type AxTestArgs = PlaywrightTestArgs &\n\tPlaywrightTestOptions & { page: AxPage };\nexport type AxWorkerArgs = PlaywrightWorkerArgs & PlaywrightWorkerOptions;\n\n/**\n * Overriding page fixture. It installs exactly one init script carrying the\n * built page-side bundle before any test navigation, attaches the\n * runner-realm companion, yields the page, and disposes the init-script\n * handle on teardown so no registration outlives its test.\n */\nexport const test: TestType<AxTestArgs, AxWorkerArgs> = base.extend<{\n\tpage: AxPage;\n}>({\n\tpage: async ({ page }, use) => {\n\t\tconst initPath = resolveInitScriptPath();\n\t\tconst handle = await page.addInitScript({ path: initPath });\n\t\ttry {\n\t\t\tconst axPage = page as AxPage;\n\t\t\taxPage.ax = new AxCompanion(page);\n\t\t\tawait use(axPage);\n\t\t} finally {\n\t\t\tawait handle.dispose();\n\t\t}\n\t},\n});\n\nexport { expect };\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(){function e(e){e.dispatchEvent(new Event(`toolchange`))}function t(e){let t=e?.toolName===void 0?``:String(e.toolName);return[{...e,cancelable:!1},t]}var n=class extends Event{toolName;constructor(e,n){let[r,i]=t(n);super(e,r),this.toolName=i}},r=class extends Event{toolName;constructor(e,n){let[r,i]=t(n);super(e,r),this.toolName=i}};function i(e,t){e.dispatchEvent(new n(`toolactivated`,{toolName:t}))}function a(e,t){e.dispatchEvent(new r(`toolcancel`,{toolName:t}))}function o(e){return new DOMException(e,`InvalidStateError`)}function s(e){return new DOMException(e,`SecurityError`)}function c(e){return new DOMException(e,`NotAllowedError`)}function l(e){return new DOMException(e,`NotSupportedError`)}function u(e){return new DOMException(e,`NotFoundError`)}function d(e){return new DOMException(e,`DataError`)}function f(e){return new DOMException(e,`OperationError`)}function p(e){return new DOMException(e,`AbortError`)}function m(e){return e.defaultView!==null}function h(e){try{let t=e.permissionsPolicy;if(t!==void 0&&typeof t.allowsFeature==`function`){let e=t.features;if(typeof e==`function`)try{let n=e.call(t);if(Array.isArray(n)&&!n.includes(`tools`))return!0}catch{}return t.allowsFeature(`tools`)!==!1}}catch{return!1}return!0}function g(e){if(e.protocol===`https:`||e.protocol===`wss:`||e.protocol===`file:`)return!0;let t=e.hostname.toLowerCase();return!!(t===`localhost`||t===`[::1]`||t===`::1`||_(t))}function _(e){let t=e.split(`.`);return t.length!==4||t[0]!==`127`?!1:t.slice(1).every(e=>/^\d{1,3}$/.test(e)&&Number(e)<=255)}function v(e,t){if(!Array.isArray(e))throw TypeError(`${t}: bad origin list`);let n=[];for(let r of e){if(typeof r!=`string`)throw TypeError(`${t}: bad origin list`);let e;try{e=new URL(r)}catch{throw s(`${t}: unparseable origin`)}if(!g(e))throw s(`${t}: untrustworthy origin`);if(e.origin===`null`)throw s(`${t}: opaque origin`);n.push(e.origin)}return n}function y(e){try{console.warn(e)}catch{}}function b(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.aborted==`boolean`&&typeof t.addEventListener==`function`&&`reason`in t}let x=/* @__PURE__ */ new Set([`__proto__`,`constructor`,`prototype`]),S=/* @__PURE__ */ new Set([`object`,`array`,`string`,`number`,`integer`,`boolean`,`null`]),C=/* @__PURE__ */ new Set([`type`,`properties`,`required`,`items`,`enum`,`additionalProperties`,`description`,`title`]);function w(e){if(typeof e!=`object`||!e||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function T(e,t){for(let t of Object.keys(e))if(x.has(t))throw TypeError(`forbidden key: ${t}`);if(t.keys+=Object.keys(e).length,t.keys>500)throw TypeError(`too many keys`)}function E(e,t,n,r){if(e===null)return;let i=typeof e;if(i!==`string`&&i!==`boolean`){if(i===`number`){if(!Number.isFinite(e))throw TypeError(`non-finite number`);return}if(Array.isArray(e)||w(e)){if(t>10)throw TypeError(`too deep`);if(r.has(e))throw TypeError(`circular schema`);r.add(e);try{if(Array.isArray(e))for(let i of e)E(i,t+1,n,r);else{T(e,n);for(let i of Object.values(e))E(i,t+1,n,r)}}finally{r.delete(e)}return}throw TypeError(`non-JSON value`)}}function D(e,t,n,r){if(!w(e))throw TypeError(`schema not an object`);if(t>10)throw TypeError(`too deep`);if(r.has(e))throw TypeError(`circular schema`);r.add(e);try{T(e,n);let i=e.type;if(i!==void 0&&(typeof i!=`string`||!S.has(i)))throw TypeError(`unsupported type`);let a=e.properties;if(a!==void 0){if(!w(a))throw TypeError(`bad properties`);T(a,n);for(let e of Object.values(a))D(e,t+1,n,r)}let o=e.required;if(o!==void 0&&(!Array.isArray(o)||o.some(e=>typeof e!=`string`)))throw TypeError(`bad required`);let s=e.items;s!==void 0&&D(s,t+1,n,r);let c=e.enum;if(c!==void 0){if(!Array.isArray(c))throw TypeError(`bad enum`);for(let e of c)E(e,t+1,n,r)}let l=e.additionalProperties;if(l!==void 0&&typeof l!=`boolean`)throw TypeError(`bad additionalProperties`);for(let t of[`description`,`title`]){let n=e[t];if(n!==void 0&&typeof n!=`string`)throw TypeError(`bad ${t}`)}for(let[i,a]of Object.entries(e))C.has(i)||E(a,t+1,n,r)}finally{r.delete(e)}}function O(e){if(e===void 0)return;D(e,0,{keys:0},/* @__PURE__ */ new Set);let t=JSON.stringify(e);if(typeof t!=`string`)throw TypeError(`unserializable schema`);if(t.length>65536)throw TypeError(`schema too large`);return t}function k(e){if(e!==void 0)return JSON.parse(e)}function A(e,t){if(Object.is(e,t))return!0;if(typeof e!=`object`||typeof t!=`object`||e===null||t===null)return!1;if(Array.isArray(e)||Array.isArray(t))return!Array.isArray(e)||!Array.isArray(t)||e.length!==t.length?!1:e.every((e,n)=>A(e,t[n]));let n=Object.keys(e);return n.length===Object.keys(t).length&&n.every(n=>Object.hasOwn(t,n)&&A(Reflect.get(e,n),Reflect.get(t,n)))}function ee(e,t,n){switch(t){case`object`:if(!w(e))throw TypeError(`${n} not an object`);break;case`array`:if(!Array.isArray(e))throw TypeError(`${n} not an array`);break;case`string`:if(typeof e!=`string`)throw TypeError(`${n} not a string`);break;case`number`:if(typeof e!=`number`||!Number.isFinite(e))throw TypeError(`${n} not a number`);break;case`integer`:if(typeof e!=`number`||!Number.isInteger(e))throw TypeError(`${n} not an integer`);break;case`boolean`:if(typeof e!=`boolean`)throw TypeError(`${n} not a boolean`);break;case`null`:if(e!==null)throw TypeError(`${n} not null`);break;default:throw TypeError(`${n} unsupported type`)}}function j(e,t,n){let r=t.type;typeof r==`string`&&ee(e,r,n);let i=t.enum;if(Array.isArray(i)&&!i.some(t=>A(e,t)))throw TypeError(`${n} disallowed value`);if(Array.isArray(e)){let r=t.items;w(r)&&e.forEach((e,t)=>{j(e,r,`${n}[${t}]`)});return}if(!w(e))return;let a=t.required;if(Array.isArray(a)){for(let t of a)if(typeof t==`string`&&!Object.hasOwn(e,t))throw TypeError(`${n} missing required ${t}`)}let o=t.properties,s=w(o)?o:void 0;if(s!==void 0)for(let[t,r]of Object.entries(s))Object.hasOwn(e,t)&&w(r)&&j(Reflect.get(e,t),r,`${n}.${t}`);if(t.additionalProperties===!1){let t=new Set(Object.keys(s??{}));for(let r of Object.keys(e))if(!t.has(r))throw TypeError(`${n} unexpected ${r}`)}}function te(e,t){if(typeof e!=`object`||!e)throw TypeError(`bad arguments`);if(t===void 0)return;let n=k(t);if(!w(n))throw TypeError(`bad stored schema`);j(e,n,`arguments`)}function M(e){if(typeof e==`symbol`)throw TypeError(`symbol title`);return String(e).replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,`�`)}let N=/* @__PURE__ */ new WeakMap;function ne(e){return N.get(e)}function P(e){let t=N.get(e);return t===void 0&&(t={document:e,context:new oe(e),tools:/* @__PURE__ */ new Map},N.set(e,t)),t}let re=/^[A-Za-z0-9_.-]+$/;function F(e){if(!m(e))throw o(`inactive document`);if(!h(e))throw c(`tools not allowed`)}function I(e){let t=e??{};if(typeof t!=`object`||!t)throw TypeError(`bad options`);return t}function L(e){let t=e.signal;if(t!==void 0){if(!b(t))throw TypeError(`bad signal`);if(t.aborted===!0)throw t.reason;return t}}let R=/* @__PURE__ */ new Map,z=1;function B(e,t){R.delete(e),t.controller.abort();let n=N.get(t.targetDocument);n!==void 0&&a(n.context,t.toolName)}function ie(e){for(let[t,n]of R){if(n.callerDocument!==e&&n.targetDocument!==e)continue;let r=n.callerDocument===e,i=n.targetDocument===e;i&&!r?(n.controller.abort(),n.complete(null,p(`target document unloaded`))):r&&!i?B(t,n):R.delete(t)}}function V(e){let t=e;for(;;){let e;try{e=t.defaultView?.frameElement??null}catch{return t}if(e===null)return t;let n;try{n=e.ownerDocument}catch{return t}if(n===t)return t;t=n}}function H(e,t){if(t.has(e))throw o(`duplicate tool ${e}`);if(e.length>128||!re.test(e))throw o(`bad tool name`);return e}function ae(e){if(e===void 0)return null;if(typeof e!=`object`||!e||Array.isArray(e))throw TypeError(`bad annotations`);let t=e,n=e=>{let n=t[e];if(n===void 0)return!1;if(typeof n!=`boolean`)throw TypeError(`annotations not booleans`);return n};return{readOnlyHint:n(`readOnlyHint`),untrustedContentHint:n(`untrustedContentHint`),consequentialHint:n(`consequentialHint`),debugging:n(`debugging`)}}function U(e){return W(e,null)}function W(e,t){let n;try{n=String(e.location.origin??`null`)}catch{n=`null`}if(n!==`null`&&n!==``)return n;if(t!==null)return t;try{let t=e.defaultView?.frameElement??null;if(t!==null)return U(t.ownerDocument)}catch{}return`null`}function G(e,t){return e===`null`||t===`null`?!1:e===t}function K(e,t,n){return e!==`null`&&e===n||t.some(e=>e===n)}function q(e){let t=[],n=/* @__PURE__ */ new Set,r=(e,i)=>{if(n.has(e))return;n.add(e);let a=W(e,i);t.push({document:e,origin:a});let o;try{o=e.querySelectorAll(`iframe`)}catch{return}for(let e of o){let t=null;try{t=e.contentDocument}catch{continue}t!==null&&r(t,a)}};return r(e,null),t}function J(e){let t=[];for(let{document:n,origin:r}of q(e)){if(!h(n))continue;let e=N.get(n);if(e===void 0)continue;let i=n.defaultView;i!==null&&t.push({document:n,origin:r,state:e,window:i})}return t}function Y(e,t,n){if(!m(e))throw o(`inactive document`);H(t,n.tools)}var oe=class extends EventTarget{#e;#t=null;#n=null;#r=null;constructor(e){super(),this.#e=e}get ontoolchange(){return this.#t}set ontoolchange(e){this.#t=$(this,`toolchange`,()=>this.#t,e)}get ontoolactivated(){return this.#n}set ontoolactivated(e){this.#n=$(this,`toolactivated`,()=>this.#n,e)}get ontoolcancel(){return this.#r}set ontoolcancel(e){this.#r=$(this,`toolcancel`,()=>this.#r,e)}async registerTool(e,t){let n=this.#e;F(n);let r=e;if(typeof r!=`object`||!r)throw TypeError(`bad tool`);let i=r,a=i.name;if(a===void 0)throw TypeError(`name required`);let s=P(n),c=H(String(a),s.tools),l=i.description;if(l===void 0)throw TypeError(`description required`);let u=String(l);if(u===``)throw o(`empty description`);let d=O(i.inputSchema);Y(n,c,s);let f=i.title,p=f===void 0?null:M(f),m=i.execute;if(typeof m!=`function`)throw TypeError(`bad execute`);let h=ae(i.annotations),g=I(t),_=L(g),y=g.exposedTo,b=y===void 0?[]:v(y,`exposedTo`),x;if(_!==void 0){let e=()=>{s.tools.get(c)===S&&ce(n,c)};_.addEventListener(`abort`,e,{once:!0}),x=()=>{_.removeEventListener(`abort`,e)}}try{Y(n,c,s)}catch(e){throw x?.(),e}let S={name:c,title:p,description:u,schemaJson:d,execute:m,annotations:h,exposedOrigins:b,cleanup:x};s.tools.set(c,S),Z(n,b)}async getTools(e){let t=this.#e;F(t);let n=I(e).fromOrigins;return X(t,n===void 0?[]:v(n,`fromOrigins`))}async executeTool(e,t,n){let r=this.#e;F(r);let i=e;if(typeof i!=`object`||!i)throw TypeError(`bad tool`);let a=i,s=a.origin,c=s===void 0?``:String(s),d;try{d=new URL(c)}catch{throw l(`bad tool origin`)}if(d.origin===`null`)throw l(`opaque tool origin`);let p=t===void 0?{}:t;if(typeof p!=`object`||!p)throw TypeError(`bad arguments`);let h=JSON.stringify(p);if(typeof h!=`string`)throw TypeError(`arguments unserializable`);let g=L(I(n)),_;try{let e=a.window;if(typeof e!=`object`||!e)throw TypeError(`bad tool target`);_=e.document}catch(e){throw e instanceof TypeError?e:u(`tool not exposed`)}if(typeof _!=`object`||!_)throw TypeError(`bad tool target`);if(!m(_))throw o(`detached tool target`);if(V(r)!==V(_))throw o(`foreign tool target`);try{F(_)}catch{throw u(`target not eligible`)}let v=a.name===void 0?`undefined`:String(a.name),y=U(_);if(y!==d.origin)throw u(`tool origin mismatch`);let b=N.get(_)?.tools.get(v);if(b===void 0)throw u(`unknown tool`);let x=U(r);if(!K(y,b.exposedOrigins,x))throw u(`tool not exposed`);let S=z++,C={arguments:h,name:v,target:_};return new Promise((e,t)=>{let n=new AbortController,i,a={callerDocument:r,targetDocument:_,toolName:v,controller:n,complete:(n,r)=>{R.has(S)&&(R.delete(S),i!==void 0&&g!==void 0&&g.removeEventListener(`abort`,i),n===null?t(r??f(`tool did not complete`)):e(n))}};R.set(S,a),g!==void 0&&(i=()=>{t(g.reason);let e=R.get(S);e!==void 0&&B(S,e)},g.addEventListener(`abort`,i,{once:!0})),se(C,n.signal,a.complete).catch(()=>{a.complete(null,f(`tool did not complete`))})})}};async function se(e,t,n){let r,a;try{if(r=JSON.parse(e.arguments),typeof r!=`object`||!r)throw d(`invalid input`);let t=N.get(e.target)?.tools.get(e.name);if(t===void 0)throw u(`tool not found`);a=t;try{te(r,a.schemaJson)}catch{throw d(`invalid input`)}}catch(e){e instanceof DOMException?n(null,e):n(null,d(`invalid input`));return}let o=N.get(e.target);o!==void 0&&i(o.context,e.name);let s;try{s=await a.execute(r,{signal:t})}catch(e){y(`tool failed: ${e instanceof Error?e.message:`unknown reason`}`),n(null,f(`tool rejected`));return}let c;try{let e=JSON.stringify(s);c=typeof e==`string`?e:void 0}catch{c=void 0}if(c===void 0){n(null,f(`unserializable result`));return}n(c,null)}function ce(e,t){let n=N.get(e);if(n===void 0)return!1;let r=n.tools.get(t);return r!==void 0&&(n.tools.delete(t),r.cleanup?.(),m(e)&&Z(e,r.exposedOrigins),!0)}function X(e,t){let n=U(e),r=[];for(let{document:i,origin:a,state:o,window:s}of J(V(e)))if(i===e||G(a,n)||t.includes(a))for(let t of o.tools.values())(i===e||K(a,t.exposedOrigins,n))&&r.push({name:t.name,title:t.title??``,description:t.description,...t.schemaJson===void 0?{}:{inputSchema:k(t.schemaJson)},window:s,origin:a,...t.annotations===null?{}:{annotations:{...t.annotations}}});return r.sort((e,t)=>e.name<t.name?-1:+(e.name>t.name)),r}function Z(e,t){let n=U(e);for(let{document:r,origin:i,state:a}of J(V(e)))(r===e||K(n,t,i))&&le(a.context)}function le(t){if(typeof MessageChannel==`function`){let n=new MessageChannel;n.port1.onmessage=()=>{n.port1.close(),n.port2.close(),e(t)},n.port2.postMessage(void 0)}else queueMicrotask(()=>e(t))}let Q=/* @__PURE__ */ new WeakMap;function $(e,t,n,r){let i=typeof r==`function`?r:null,a=Q.get(e),o=a?.get(t);return o===void 0&&(o=e=>{let t=n();t!==null&&t(e)},a===void 0&&(a=/* @__PURE__ */ new Map,Q.set(e,a)),a.set(t,o),e.addEventListener(t,o)),i}function ue(e){let t=ne(e)?.context,n=e.modelContext;if(n!==void 0&&n===t)return t;if(!fe(e)||!h(e))return;let r=P(e);return Object.defineProperty(e,"modelContext",{value:r.context,writable:!1,configurable:!1,enumerable:!0}),de(e),r.context}function de(e){let t=e.defaultView;t!==null&&t.addEventListener(`pagehide`,t=>{typeof PageTransitionEvent<`u`&&t instanceof PageTransitionEvent&&t.persisted||ie(e)})}function fe(e){let t=e.defaultView,n=t===null?void 0:t.isSecureContext;if(typeof n==`boolean`)return n;try{let t=new URL(e.location.href);return t.protocol===`about:`||g(t)}catch{return!1}}ue(document)})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ax-kit/playwright",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Runner-side companion for driving document.modelContext in tests",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/open-ax/ax-kit.git",
|
|
9
|
+
"directory": "packages/playwright"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/open-ax/ax-kit",
|
|
12
|
+
"bugs": "https://github.com/open-ax/ax-kit/issues",
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=24.19.0 <25"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "./dist/index.cjs",
|
|
18
|
+
"module": "./dist/index.mjs",
|
|
19
|
+
"types": "./dist/index.d.mts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./dist/index.d.mts",
|
|
24
|
+
"default": "./dist/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./dist/index.d.cts",
|
|
28
|
+
"default": "./dist/index.cjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"sideEffects": false,
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@playwright/test": "^1.63.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@playwright/test": "1.63.0",
|
|
42
|
+
"@types/node": "^24.19.0",
|
|
43
|
+
"playwright": "1.63.0",
|
|
44
|
+
"tsdown": "0.23.1",
|
|
45
|
+
"typescript": "6.0.3",
|
|
46
|
+
"@ax-kit/core": "0.1.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsdown",
|
|
50
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
51
|
+
"test": "playwright test --project=chromium",
|
|
52
|
+
"test:conformance": "playwright test --project=conformance-chromium --project=conformance-firefox --project=conformance-webkit",
|
|
53
|
+
"size": "echo \"no size gate for runner adapter\"",
|
|
54
|
+
"publint": "publint --strict",
|
|
55
|
+
"attw": "attw --pack . --profile node16"
|
|
56
|
+
}
|
|
6
57
|
}
|