@edgelens/playwright-plugin 0.0.0-stage → 0.2.2

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EdgeLens
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,144 @@
1
- # Temporary Holding Version
1
+ # @edgelens/playwright-plugin
2
+
3
+ Playwright integration for [EdgeLens](https://edgelens.ai) — write a
4
+ Playwright test that gets a real edge AI camera as a fixture, with
5
+ auto-allocate, auto-release, and project presets for each
6
+ (deviceType, os) combination.
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ npm install -D @edgelens/playwright-plugin @edgelens/core @playwright/test
12
+ ```
13
+
14
+ Requires Node 18+.
15
+
16
+ ## Quickstart
17
+
18
+ ```ts
19
+ // e2e/camera.spec.ts
20
+ import { readFile } from "node:fs/promises";
21
+ import { test, expect } from "@edgelens/playwright-plugin";
22
+
23
+ test("camera displays the test pattern", async ({ device, page }) => {
24
+ await device.display.show("https://example.com/test-pattern.png");
25
+ await device.display.waitStable();
26
+
27
+ // device.host is reachable from your test process — the platform
28
+ // proxies through to the camera-LAN device.
29
+ await page.goto(`http://${device.host}:8000/preview`);
30
+ await expect(page).toHaveScreenshot();
31
+ });
32
+
33
+ test("inference latency", async ({ device }) => {
34
+ // deploy() uploads local file data.
35
+ const blob = new Blob([await readFile("./models/face.cvimodel")]);
36
+ await device.deploy({ model: { name: "face.cvimodel", blob } });
37
+ await device.inference.start();
38
+ const stats = await device.inference.run(30);
39
+ expect(stats.fps.avg).toBeGreaterThan(15);
40
+ expect(stats.latency_ms.p95).toBeLessThan(100);
41
+ });
42
+ ```
43
+
44
+ `device` (and `session`, `client`) are **worker-scoped** by default —
45
+ one camera per worker process, shared across every test in that
46
+ worker. The right tradeoff for read-only test suites where sessions
47
+ take 30s+ to provision.
48
+
49
+ For tests that mutate device state and want isolation, use
50
+ `workerSession` directly or override in `test.use()` — see
51
+ `Test scopes` below.
52
+
53
+ ## Configure
54
+
55
+ In `playwright.config.ts`:
56
+
57
+ ```ts
58
+ import { defineConfig } from "@playwright/test";
59
+ import { edgelensProjects } from "@edgelens/playwright-plugin";
60
+
61
+ export default defineConfig({
62
+ projects: [
63
+ ...edgelensProjects({
64
+ apiKey: process.env.EDGELENS_API_KEY,
65
+ // matrix: [{ deviceType: "recamera-2002", os: "stock-os" }],
66
+ }),
67
+ ],
68
+ });
69
+ ```
70
+
71
+ `edgelensProjects()` emits one Playwright project per
72
+ `(deviceType, os)` combination — by default both `stock-os` and
73
+ `recamagined`. Filter with the standard Playwright `--project=...`
74
+ flag.
75
+
76
+ Or, no projects — just `use: { edgelensOptions: ... }` directly:
77
+
78
+ ```ts
79
+ import { defineConfig } from "@playwright/test";
80
+
81
+ export default defineConfig({
82
+ use: {
83
+ edgelensOptions: {
84
+ apiKey: process.env.EDGELENS_API_KEY,
85
+ session: { deviceType: "recamera-2002", os: "stock-os", timeoutS: 1800 },
86
+ },
87
+ },
88
+ });
89
+ ```
90
+
91
+ ## Fixtures
92
+
93
+ | Fixture | Scope | What |
94
+ |---|---|---|
95
+ | `client` | test | `EdgeLensClient` from `@edgelens/core`, defaults to the worker-scoped one |
96
+ | `session` | test | `Session`, defaults to the worker-scoped one |
97
+ | `device` | test | `Device` (the test surface), defaults to the worker-scoped one |
98
+ | `workerClient` | worker | One `EdgeLensClient` per worker process |
99
+ | `workerSession` | worker | One ready `Session` per worker, auto-released at worker teardown |
100
+ | `workerDevice` | worker | The `Device` from the worker-scoped session |
101
+ | `edgelensOptions` | worker | The options passed to the underlying client + lifecycle helpers (override in `use`) |
102
+
103
+ ## Test scopes
104
+
105
+ **Worker-scoped (default — `device`):** one camera per worker, reused
106
+ across every test in that worker. Wall-clock-cheap, but tests share
107
+ device state — order matters and a stuck device may take other tests
108
+ down with it.
109
+
110
+ **Test-scoped:** define a custom fixture if you want a fresh device
111
+ every test:
112
+
113
+ ```ts
114
+ import { test as base, allocateSession, releaseSession } from "@edgelens/playwright-plugin";
115
+
116
+ const test = base.extend<{ freshDevice: import("@edgelens/core").Device }>({
117
+ freshDevice: async ({ workerClient, edgelensOptions }, use) => {
118
+ const session = await allocateSession(workerClient, edgelensOptions);
119
+ try {
120
+ await use(session.device);
121
+ } finally {
122
+ await releaseSession(session);
123
+ }
124
+ },
125
+ });
126
+ ```
127
+
128
+ ## Errors
129
+
130
+ The fixtures don't swallow errors during setup — Playwright reports
131
+ fixture failures as the cause of the test failure. Teardown errors
132
+ (failed release) are swallowed by default so they don't mask the
133
+ original test failure; pass an `onError` callback to `releaseSession`
134
+ to log them.
135
+
136
+ ## Develop
137
+
138
+ ```sh
139
+ npm install
140
+ npm run typecheck
141
+ npm test
142
+ npm run build
143
+ ```
2
144
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
package/dist/index.cjs ADDED
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DEFAULTS: () => DEFAULTS,
24
+ allocateSession: () => allocateSession,
25
+ buildClient: () => buildClient,
26
+ edgelensProjects: () => edgelensProjects,
27
+ expect: () => import_test2.expect,
28
+ releaseSession: () => releaseSession,
29
+ test: () => test
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/fixtures.ts
34
+ var import_test = require("@playwright/test");
35
+
36
+ // src/lifecycle.ts
37
+ var import_core = require("@edgelens/core");
38
+ var DEFAULTS = {
39
+ waitReadyTimeoutMs: 5 * 6e4,
40
+ waitReadyPollMs: 2e3
41
+ };
42
+ function buildClient(opts) {
43
+ return new import_core.EdgeLensClient({
44
+ ...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
45
+ ...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {},
46
+ ...opts.client ?? {}
47
+ });
48
+ }
49
+ async function allocateSession(client, opts = {}, onReleaseError = (err) => console.warn("edgelens: session release failed after waitReady error:", err)) {
50
+ const session = await client.createSession(opts.session ?? {});
51
+ try {
52
+ await session.waitReady(
53
+ opts.waitReadyTimeoutMs ?? DEFAULTS.waitReadyTimeoutMs,
54
+ opts.waitReadyPollMs ?? DEFAULTS.waitReadyPollMs
55
+ );
56
+ } catch (err) {
57
+ await releaseSession(session, onReleaseError);
58
+ throw err;
59
+ }
60
+ return session;
61
+ }
62
+ async function releaseSession(session, onError) {
63
+ if (!session) return;
64
+ try {
65
+ await session.release();
66
+ } catch (e) {
67
+ if (onError) onError(e);
68
+ }
69
+ }
70
+
71
+ // src/fixtures.ts
72
+ var import_test2 = require("@playwright/test");
73
+ var test = import_test.test.extend({
74
+ // -------- worker-scoped: client / session / device -----------------------
75
+ // The default fixture options. Override in playwright.config.ts via:
76
+ // use: { edgelensOptions: { session: { os: "recamagined" } } }
77
+ edgelensOptions: [
78
+ { waitReadyTimeoutMs: DEFAULTS.waitReadyTimeoutMs, waitReadyPollMs: DEFAULTS.waitReadyPollMs },
79
+ { option: true, scope: "worker" }
80
+ ],
81
+ workerClient: [
82
+ async ({ edgelensOptions }, use) => {
83
+ const client = buildClient(edgelensOptions);
84
+ await use(client);
85
+ },
86
+ { scope: "worker" }
87
+ ],
88
+ workerSession: [
89
+ async ({ workerClient, edgelensOptions }, use) => {
90
+ const session = await allocateSession(workerClient, edgelensOptions);
91
+ try {
92
+ await use(session);
93
+ } finally {
94
+ await releaseSession(session);
95
+ }
96
+ },
97
+ { scope: "worker", timeout: 0 }
98
+ ],
99
+ workerDevice: [
100
+ async ({ workerSession }, use) => {
101
+ await use(workerSession.device);
102
+ },
103
+ { scope: "worker" }
104
+ ],
105
+ // -------- test-scoped: passthrough to worker-scoped by default ----------
106
+ client: async ({ workerClient }, use) => {
107
+ await use(workerClient);
108
+ },
109
+ session: async ({ workerSession }, use) => {
110
+ await use(workerSession);
111
+ },
112
+ device: async ({ workerSession }, use) => {
113
+ await use(workerSession.device);
114
+ }
115
+ });
116
+
117
+ // src/projects.ts
118
+ var DEFAULT_MATRIX = [
119
+ { deviceType: "recamera-2002", os: "stock-os" },
120
+ { deviceType: "recamera-2002", os: "recamagined" }
121
+ ];
122
+ function edgelensProjects(opts = {}) {
123
+ const matrix = opts.matrix ?? DEFAULT_MATRIX;
124
+ return matrix.map(({ deviceType, os, sessionTimeoutS }) => {
125
+ const session = {
126
+ deviceType,
127
+ os,
128
+ ...sessionTimeoutS !== void 0 ? { timeoutS: sessionTimeoutS } : {}
129
+ };
130
+ return {
131
+ name: `${deviceType}/${os}`,
132
+ use: {
133
+ edgelensOptions: {
134
+ ...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
135
+ ...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {},
136
+ session
137
+ }
138
+ }
139
+ };
140
+ });
141
+ }
142
+ // Annotate the CommonJS export names for ESM import in node:
143
+ 0 && (module.exports = {
144
+ DEFAULTS,
145
+ allocateSession,
146
+ buildClient,
147
+ edgelensProjects,
148
+ expect,
149
+ releaseSession,
150
+ test
151
+ });
152
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/fixtures.ts","../src/lifecycle.ts","../src/projects.ts"],"sourcesContent":["export { test, expect } from \"./fixtures.js\";\nexport type { EdgelensTestFixtures, EdgelensWorkerFixtures } from \"./fixtures.js\";\nexport {\n allocateSession,\n releaseSession,\n buildClient,\n DEFAULTS,\n} from \"./lifecycle.js\";\nexport type { EdgelensFixtureOptions } from \"./lifecycle.js\";\nexport { edgelensProjects } from \"./projects.js\";\nexport type { EdgelensProject, EdgelensProjectsOptions } from \"./projects.js\";\n","/**\n * Playwright fixtures. The standard import:\n *\n * import { test, expect } from \"@edgelens/playwright-plugin\";\n *\n * test(\"display playback\", async ({ device }) => {\n * await device.display.play(\"https://example.com/video.mp4\");\n * // ... assertions\n * });\n *\n * Two scopes:\n *\n * - **worker-scoped** (`workerDevice`, `workerSession`, `workerClient`)\n * — one camera per worker process, reused across every test in that\n * worker. The right default when sessions take 30+s to provision and\n * tests are mostly read-only.\n *\n * - **test-scoped** (`device`, `session`, `client`) — fresh camera per\n * test. Picks the worker-scoped one up by default but can be\n * overridden via `test.use({ session: ... })`.\n */\n\nimport { test as base } from \"@playwright/test\";\nimport { EdgeLensClient } from \"@edgelens/core\";\nimport type { Device, Session } from \"@edgelens/core\";\n\nimport {\n DEFAULTS,\n allocateSession,\n buildClient,\n releaseSession,\n type EdgelensFixtureOptions,\n} from \"./lifecycle.js\";\n\nexport interface EdgelensWorkerFixtures {\n edgelensOptions: EdgelensFixtureOptions;\n workerClient: EdgeLensClient;\n workerSession: Session;\n workerDevice: Device;\n}\n\nexport interface EdgelensTestFixtures {\n /** Fresh per-test client. Defaults to the worker-scoped one. */\n client: EdgeLensClient;\n /** Fresh per-test session. Defaults to the worker-scoped one. */\n session: Session;\n /** Fresh per-test device. Defaults to the worker-scoped one. */\n device: Device;\n}\n\nexport const test = base.extend<EdgelensTestFixtures, EdgelensWorkerFixtures>({\n // -------- worker-scoped: client / session / device -----------------------\n\n // The default fixture options. Override in playwright.config.ts via:\n // use: { edgelensOptions: { session: { os: \"recamagined\" } } }\n edgelensOptions: [\n { waitReadyTimeoutMs: DEFAULTS.waitReadyTimeoutMs, waitReadyPollMs: DEFAULTS.waitReadyPollMs },\n { option: true, scope: \"worker\" },\n ],\n\n workerClient: [\n async ({ edgelensOptions }, use) => {\n const client = buildClient(edgelensOptions);\n await use(client);\n },\n { scope: \"worker\" },\n ],\n\n workerSession: [\n async ({ workerClient, edgelensOptions }, use) => {\n const session = await allocateSession(workerClient, edgelensOptions);\n try {\n await use(session);\n } finally {\n await releaseSession(session);\n }\n },\n { scope: \"worker\", timeout: 0 },\n ],\n\n workerDevice: [\n async ({ workerSession }, use) => {\n await use(workerSession.device);\n },\n { scope: \"worker\" },\n ],\n\n // -------- test-scoped: passthrough to worker-scoped by default ----------\n\n client: async ({ workerClient }, use) => {\n await use(workerClient);\n },\n\n session: async ({ workerSession }, use) => {\n await use(workerSession);\n },\n\n device: async ({ workerSession }, use) => {\n await use(workerSession.device);\n },\n});\n\nexport { expect } from \"@playwright/test\";\n","/**\n * Fixture lifecycle helpers — the actual session-allocation +\n * release logic. Kept separate from `fixtures.ts` so it's easy to\n * unit-test without Playwright's runner.\n */\n\nimport { EdgeLensClient } from \"@edgelens/core\";\nimport type {\n CreateSessionOptions,\n EdgeLensClientOptions,\n Session,\n} from \"@edgelens/core\";\n\nexport interface EdgelensFixtureOptions {\n /** API key. Falls back to `EDGELENS_API_KEY` env var. */\n apiKey?: string;\n /** Override the platform API base URL. */\n baseUrl?: string;\n /** Per-test session params. Same shape as `client.createSession()`. */\n session?: CreateSessionOptions;\n /** How long to wait for `status=ready`. Default 5 minutes. */\n waitReadyTimeoutMs?: number;\n /** Test poll interval while waiting for ready. Default 2s. */\n waitReadyPollMs?: number;\n /** Extra options passed through to the underlying HTTP client. */\n client?: Pick<EdgeLensClientOptions, \"timeoutMs\" | \"retryPolicy\" | \"userAgent\" | \"fetch\">;\n}\n\nexport const DEFAULTS = {\n waitReadyTimeoutMs: 5 * 60_000,\n waitReadyPollMs: 2_000,\n} as const;\n\nexport function buildClient(opts: EdgelensFixtureOptions): EdgeLensClient {\n return new EdgeLensClient({\n ...(opts.apiKey !== undefined ? { apiKey: opts.apiKey } : {}),\n ...(opts.baseUrl !== undefined ? { baseUrl: opts.baseUrl } : {}),\n ...(opts.client ?? {}),\n });\n}\n\n/**\n * Allocate a session and wait for it to be ready.\n *\n * Thrown errors propagate to the test (Playwright will fail the\n * fixture setup); the caller is responsible for invoking\n * `releaseSession()` on the result in a `finally` to avoid leaking\n * a billable device.\n *\n * If `waitReady` rejects, the caller never receives the session, so\n * it is released here before the original error is rethrown. A\n * failed release goes to `onReleaseError` (default `console.warn`)\n * and never replaces the original error.\n */\nexport async function allocateSession(\n client: EdgeLensClient,\n opts: EdgelensFixtureOptions = {},\n onReleaseError: (err: unknown) => void = (err) =>\n console.warn(\"edgelens: session release failed after waitReady error:\", err),\n): Promise<Session> {\n const session = await client.createSession(opts.session ?? {});\n try {\n await session.waitReady(\n opts.waitReadyTimeoutMs ?? DEFAULTS.waitReadyTimeoutMs,\n opts.waitReadyPollMs ?? DEFAULTS.waitReadyPollMs,\n );\n } catch (err) {\n await releaseSession(session, onReleaseError);\n throw err;\n }\n return session;\n}\n\n/**\n * Release a session, swallowing any errors that fire during\n * teardown (a failing release shouldn't mask the actual test\n * failure that preceded it).\n */\nexport async function releaseSession(\n session: Session | undefined,\n onError?: (err: unknown) => void,\n): Promise<void> {\n if (!session) return;\n try {\n await session.release();\n } catch (e) {\n if (onError) onError(e);\n }\n}\n","/**\n * Project presets for common (deviceType, os) combinations.\n *\n * Use in `playwright.config.ts`:\n *\n * import { defineConfig } from \"@playwright/test\";\n * import { edgelensProjects } from \"@edgelens/playwright-plugin\";\n *\n * export default defineConfig({\n * projects: [\n * ...edgelensProjects({ apiKey: process.env.EDGELENS_API_KEY }),\n * ],\n * });\n *\n * Each project sets `edgelensOptions.session` to the matching combo.\n * Test files filter via `test.use({ ... })` or Playwright's project\n * tag mechanism (`test.describe.configure({ tag: [\"@stock-os\"] })`).\n */\n\nimport type { CreateSessionOptions, DeviceType, OSType } from \"@edgelens/core\";\n\nimport type { EdgelensFixtureOptions } from \"./lifecycle.js\";\n\nexport interface EdgelensProject {\n name: string;\n use: {\n edgelensOptions: EdgelensFixtureOptions;\n };\n}\n\nexport interface EdgelensMatrixEntry {\n deviceType: DeviceType;\n os: OSType;\n sessionTimeoutS?: number;\n}\n\nexport interface EdgelensProjectsOptions {\n apiKey?: string;\n baseUrl?: string;\n /** Which (deviceType, os) combos to expose. Defaults to all supported. */\n matrix?: EdgelensMatrixEntry[];\n}\n\nconst DEFAULT_MATRIX: EdgelensMatrixEntry[] = [\n { deviceType: \"recamera-2002\", os: \"stock-os\" },\n { deviceType: \"recamera-2002\", os: \"recamagined\" },\n];\n\nexport function edgelensProjects(\n opts: EdgelensProjectsOptions = {},\n): EdgelensProject[] {\n const matrix = opts.matrix ?? DEFAULT_MATRIX;\n return matrix.map(({ deviceType, os, sessionTimeoutS }) => {\n const session: CreateSessionOptions = {\n deviceType,\n os,\n ...(sessionTimeoutS !== undefined ? { timeoutS: sessionTimeoutS } : {}),\n };\n return {\n name: `${deviceType}/${os}`,\n use: {\n edgelensOptions: {\n ...(opts.apiKey !== undefined ? { apiKey: opts.apiKey } : {}),\n ...(opts.baseUrl !== undefined ? { baseUrl: opts.baseUrl } : {}),\n session,\n },\n },\n };\n });\n}\n"],"mappings":"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","names":["import_test","base"]}
@@ -0,0 +1,105 @@
1
+ import * as playwright_test from 'playwright/test';
2
+ import { CreateSessionOptions, EdgeLensClientOptions, EdgeLensClient, Session, Device, DeviceType, OSType } from '@edgelens/core';
3
+ export { expect } from '@playwright/test';
4
+
5
+ /**
6
+ * Fixture lifecycle helpers — the actual session-allocation +
7
+ * release logic. Kept separate from `fixtures.ts` so it's easy to
8
+ * unit-test without Playwright's runner.
9
+ */
10
+
11
+ interface EdgelensFixtureOptions {
12
+ /** API key. Falls back to `EDGELENS_API_KEY` env var. */
13
+ apiKey?: string;
14
+ /** Override the platform API base URL. */
15
+ baseUrl?: string;
16
+ /** Per-test session params. Same shape as `client.createSession()`. */
17
+ session?: CreateSessionOptions;
18
+ /** How long to wait for `status=ready`. Default 5 minutes. */
19
+ waitReadyTimeoutMs?: number;
20
+ /** Test poll interval while waiting for ready. Default 2s. */
21
+ waitReadyPollMs?: number;
22
+ /** Extra options passed through to the underlying HTTP client. */
23
+ client?: Pick<EdgeLensClientOptions, "timeoutMs" | "retryPolicy" | "userAgent" | "fetch">;
24
+ }
25
+ declare const DEFAULTS: {
26
+ readonly waitReadyTimeoutMs: number;
27
+ readonly waitReadyPollMs: 2000;
28
+ };
29
+ declare function buildClient(opts: EdgelensFixtureOptions): EdgeLensClient;
30
+ /**
31
+ * Allocate a session and wait for it to be ready.
32
+ *
33
+ * Thrown errors propagate to the test (Playwright will fail the
34
+ * fixture setup); the caller is responsible for invoking
35
+ * `releaseSession()` on the result in a `finally` to avoid leaking
36
+ * a billable device.
37
+ *
38
+ * If `waitReady` rejects, the caller never receives the session, so
39
+ * it is released here before the original error is rethrown. A
40
+ * failed release goes to `onReleaseError` (default `console.warn`)
41
+ * and never replaces the original error.
42
+ */
43
+ declare function allocateSession(client: EdgeLensClient, opts?: EdgelensFixtureOptions, onReleaseError?: (err: unknown) => void): Promise<Session>;
44
+ /**
45
+ * Release a session, swallowing any errors that fire during
46
+ * teardown (a failing release shouldn't mask the actual test
47
+ * failure that preceded it).
48
+ */
49
+ declare function releaseSession(session: Session | undefined, onError?: (err: unknown) => void): Promise<void>;
50
+
51
+ interface EdgelensWorkerFixtures {
52
+ edgelensOptions: EdgelensFixtureOptions;
53
+ workerClient: EdgeLensClient;
54
+ workerSession: Session;
55
+ workerDevice: Device;
56
+ }
57
+ interface EdgelensTestFixtures {
58
+ /** Fresh per-test client. Defaults to the worker-scoped one. */
59
+ client: EdgeLensClient;
60
+ /** Fresh per-test session. Defaults to the worker-scoped one. */
61
+ session: Session;
62
+ /** Fresh per-test device. Defaults to the worker-scoped one. */
63
+ device: Device;
64
+ }
65
+ declare const test: playwright_test.TestType<playwright_test.PlaywrightTestArgs & playwright_test.PlaywrightTestOptions & EdgelensTestFixtures, playwright_test.PlaywrightWorkerArgs & playwright_test.PlaywrightWorkerOptions & EdgelensWorkerFixtures>;
66
+
67
+ /**
68
+ * Project presets for common (deviceType, os) combinations.
69
+ *
70
+ * Use in `playwright.config.ts`:
71
+ *
72
+ * import { defineConfig } from "@playwright/test";
73
+ * import { edgelensProjects } from "@edgelens/playwright-plugin";
74
+ *
75
+ * export default defineConfig({
76
+ * projects: [
77
+ * ...edgelensProjects({ apiKey: process.env.EDGELENS_API_KEY }),
78
+ * ],
79
+ * });
80
+ *
81
+ * Each project sets `edgelensOptions.session` to the matching combo.
82
+ * Test files filter via `test.use({ ... })` or Playwright's project
83
+ * tag mechanism (`test.describe.configure({ tag: ["@stock-os"] })`).
84
+ */
85
+
86
+ interface EdgelensProject {
87
+ name: string;
88
+ use: {
89
+ edgelensOptions: EdgelensFixtureOptions;
90
+ };
91
+ }
92
+ interface EdgelensMatrixEntry {
93
+ deviceType: DeviceType;
94
+ os: OSType;
95
+ sessionTimeoutS?: number;
96
+ }
97
+ interface EdgelensProjectsOptions {
98
+ apiKey?: string;
99
+ baseUrl?: string;
100
+ /** Which (deviceType, os) combos to expose. Defaults to all supported. */
101
+ matrix?: EdgelensMatrixEntry[];
102
+ }
103
+ declare function edgelensProjects(opts?: EdgelensProjectsOptions): EdgelensProject[];
104
+
105
+ export { DEFAULTS, type EdgelensFixtureOptions, type EdgelensProject, type EdgelensProjectsOptions, type EdgelensTestFixtures, type EdgelensWorkerFixtures, allocateSession, buildClient, edgelensProjects, releaseSession, test };
@@ -0,0 +1,105 @@
1
+ import * as playwright_test from 'playwright/test';
2
+ import { CreateSessionOptions, EdgeLensClientOptions, EdgeLensClient, Session, Device, DeviceType, OSType } from '@edgelens/core';
3
+ export { expect } from '@playwright/test';
4
+
5
+ /**
6
+ * Fixture lifecycle helpers — the actual session-allocation +
7
+ * release logic. Kept separate from `fixtures.ts` so it's easy to
8
+ * unit-test without Playwright's runner.
9
+ */
10
+
11
+ interface EdgelensFixtureOptions {
12
+ /** API key. Falls back to `EDGELENS_API_KEY` env var. */
13
+ apiKey?: string;
14
+ /** Override the platform API base URL. */
15
+ baseUrl?: string;
16
+ /** Per-test session params. Same shape as `client.createSession()`. */
17
+ session?: CreateSessionOptions;
18
+ /** How long to wait for `status=ready`. Default 5 minutes. */
19
+ waitReadyTimeoutMs?: number;
20
+ /** Test poll interval while waiting for ready. Default 2s. */
21
+ waitReadyPollMs?: number;
22
+ /** Extra options passed through to the underlying HTTP client. */
23
+ client?: Pick<EdgeLensClientOptions, "timeoutMs" | "retryPolicy" | "userAgent" | "fetch">;
24
+ }
25
+ declare const DEFAULTS: {
26
+ readonly waitReadyTimeoutMs: number;
27
+ readonly waitReadyPollMs: 2000;
28
+ };
29
+ declare function buildClient(opts: EdgelensFixtureOptions): EdgeLensClient;
30
+ /**
31
+ * Allocate a session and wait for it to be ready.
32
+ *
33
+ * Thrown errors propagate to the test (Playwright will fail the
34
+ * fixture setup); the caller is responsible for invoking
35
+ * `releaseSession()` on the result in a `finally` to avoid leaking
36
+ * a billable device.
37
+ *
38
+ * If `waitReady` rejects, the caller never receives the session, so
39
+ * it is released here before the original error is rethrown. A
40
+ * failed release goes to `onReleaseError` (default `console.warn`)
41
+ * and never replaces the original error.
42
+ */
43
+ declare function allocateSession(client: EdgeLensClient, opts?: EdgelensFixtureOptions, onReleaseError?: (err: unknown) => void): Promise<Session>;
44
+ /**
45
+ * Release a session, swallowing any errors that fire during
46
+ * teardown (a failing release shouldn't mask the actual test
47
+ * failure that preceded it).
48
+ */
49
+ declare function releaseSession(session: Session | undefined, onError?: (err: unknown) => void): Promise<void>;
50
+
51
+ interface EdgelensWorkerFixtures {
52
+ edgelensOptions: EdgelensFixtureOptions;
53
+ workerClient: EdgeLensClient;
54
+ workerSession: Session;
55
+ workerDevice: Device;
56
+ }
57
+ interface EdgelensTestFixtures {
58
+ /** Fresh per-test client. Defaults to the worker-scoped one. */
59
+ client: EdgeLensClient;
60
+ /** Fresh per-test session. Defaults to the worker-scoped one. */
61
+ session: Session;
62
+ /** Fresh per-test device. Defaults to the worker-scoped one. */
63
+ device: Device;
64
+ }
65
+ declare const test: playwright_test.TestType<playwright_test.PlaywrightTestArgs & playwright_test.PlaywrightTestOptions & EdgelensTestFixtures, playwright_test.PlaywrightWorkerArgs & playwright_test.PlaywrightWorkerOptions & EdgelensWorkerFixtures>;
66
+
67
+ /**
68
+ * Project presets for common (deviceType, os) combinations.
69
+ *
70
+ * Use in `playwright.config.ts`:
71
+ *
72
+ * import { defineConfig } from "@playwright/test";
73
+ * import { edgelensProjects } from "@edgelens/playwright-plugin";
74
+ *
75
+ * export default defineConfig({
76
+ * projects: [
77
+ * ...edgelensProjects({ apiKey: process.env.EDGELENS_API_KEY }),
78
+ * ],
79
+ * });
80
+ *
81
+ * Each project sets `edgelensOptions.session` to the matching combo.
82
+ * Test files filter via `test.use({ ... })` or Playwright's project
83
+ * tag mechanism (`test.describe.configure({ tag: ["@stock-os"] })`).
84
+ */
85
+
86
+ interface EdgelensProject {
87
+ name: string;
88
+ use: {
89
+ edgelensOptions: EdgelensFixtureOptions;
90
+ };
91
+ }
92
+ interface EdgelensMatrixEntry {
93
+ deviceType: DeviceType;
94
+ os: OSType;
95
+ sessionTimeoutS?: number;
96
+ }
97
+ interface EdgelensProjectsOptions {
98
+ apiKey?: string;
99
+ baseUrl?: string;
100
+ /** Which (deviceType, os) combos to expose. Defaults to all supported. */
101
+ matrix?: EdgelensMatrixEntry[];
102
+ }
103
+ declare function edgelensProjects(opts?: EdgelensProjectsOptions): EdgelensProject[];
104
+
105
+ export { DEFAULTS, type EdgelensFixtureOptions, type EdgelensProject, type EdgelensProjectsOptions, type EdgelensTestFixtures, type EdgelensWorkerFixtures, allocateSession, buildClient, edgelensProjects, releaseSession, test };
package/dist/index.js ADDED
@@ -0,0 +1,119 @@
1
+ // src/fixtures.ts
2
+ import { test as base } from "@playwright/test";
3
+
4
+ // src/lifecycle.ts
5
+ import { EdgeLensClient } from "@edgelens/core";
6
+ var DEFAULTS = {
7
+ waitReadyTimeoutMs: 5 * 6e4,
8
+ waitReadyPollMs: 2e3
9
+ };
10
+ function buildClient(opts) {
11
+ return new EdgeLensClient({
12
+ ...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
13
+ ...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {},
14
+ ...opts.client ?? {}
15
+ });
16
+ }
17
+ async function allocateSession(client, opts = {}, onReleaseError = (err) => console.warn("edgelens: session release failed after waitReady error:", err)) {
18
+ const session = await client.createSession(opts.session ?? {});
19
+ try {
20
+ await session.waitReady(
21
+ opts.waitReadyTimeoutMs ?? DEFAULTS.waitReadyTimeoutMs,
22
+ opts.waitReadyPollMs ?? DEFAULTS.waitReadyPollMs
23
+ );
24
+ } catch (err) {
25
+ await releaseSession(session, onReleaseError);
26
+ throw err;
27
+ }
28
+ return session;
29
+ }
30
+ async function releaseSession(session, onError) {
31
+ if (!session) return;
32
+ try {
33
+ await session.release();
34
+ } catch (e) {
35
+ if (onError) onError(e);
36
+ }
37
+ }
38
+
39
+ // src/fixtures.ts
40
+ import { expect } from "@playwright/test";
41
+ var test = base.extend({
42
+ // -------- worker-scoped: client / session / device -----------------------
43
+ // The default fixture options. Override in playwright.config.ts via:
44
+ // use: { edgelensOptions: { session: { os: "recamagined" } } }
45
+ edgelensOptions: [
46
+ { waitReadyTimeoutMs: DEFAULTS.waitReadyTimeoutMs, waitReadyPollMs: DEFAULTS.waitReadyPollMs },
47
+ { option: true, scope: "worker" }
48
+ ],
49
+ workerClient: [
50
+ async ({ edgelensOptions }, use) => {
51
+ const client = buildClient(edgelensOptions);
52
+ await use(client);
53
+ },
54
+ { scope: "worker" }
55
+ ],
56
+ workerSession: [
57
+ async ({ workerClient, edgelensOptions }, use) => {
58
+ const session = await allocateSession(workerClient, edgelensOptions);
59
+ try {
60
+ await use(session);
61
+ } finally {
62
+ await releaseSession(session);
63
+ }
64
+ },
65
+ { scope: "worker", timeout: 0 }
66
+ ],
67
+ workerDevice: [
68
+ async ({ workerSession }, use) => {
69
+ await use(workerSession.device);
70
+ },
71
+ { scope: "worker" }
72
+ ],
73
+ // -------- test-scoped: passthrough to worker-scoped by default ----------
74
+ client: async ({ workerClient }, use) => {
75
+ await use(workerClient);
76
+ },
77
+ session: async ({ workerSession }, use) => {
78
+ await use(workerSession);
79
+ },
80
+ device: async ({ workerSession }, use) => {
81
+ await use(workerSession.device);
82
+ }
83
+ });
84
+
85
+ // src/projects.ts
86
+ var DEFAULT_MATRIX = [
87
+ { deviceType: "recamera-2002", os: "stock-os" },
88
+ { deviceType: "recamera-2002", os: "recamagined" }
89
+ ];
90
+ function edgelensProjects(opts = {}) {
91
+ const matrix = opts.matrix ?? DEFAULT_MATRIX;
92
+ return matrix.map(({ deviceType, os, sessionTimeoutS }) => {
93
+ const session = {
94
+ deviceType,
95
+ os,
96
+ ...sessionTimeoutS !== void 0 ? { timeoutS: sessionTimeoutS } : {}
97
+ };
98
+ return {
99
+ name: `${deviceType}/${os}`,
100
+ use: {
101
+ edgelensOptions: {
102
+ ...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
103
+ ...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {},
104
+ session
105
+ }
106
+ }
107
+ };
108
+ });
109
+ }
110
+ export {
111
+ DEFAULTS,
112
+ allocateSession,
113
+ buildClient,
114
+ edgelensProjects,
115
+ expect,
116
+ releaseSession,
117
+ test
118
+ };
119
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/fixtures.ts","../src/lifecycle.ts","../src/projects.ts"],"sourcesContent":["/**\n * Playwright fixtures. The standard import:\n *\n * import { test, expect } from \"@edgelens/playwright-plugin\";\n *\n * test(\"display playback\", async ({ device }) => {\n * await device.display.play(\"https://example.com/video.mp4\");\n * // ... assertions\n * });\n *\n * Two scopes:\n *\n * - **worker-scoped** (`workerDevice`, `workerSession`, `workerClient`)\n * — one camera per worker process, reused across every test in that\n * worker. The right default when sessions take 30+s to provision and\n * tests are mostly read-only.\n *\n * - **test-scoped** (`device`, `session`, `client`) — fresh camera per\n * test. Picks the worker-scoped one up by default but can be\n * overridden via `test.use({ session: ... })`.\n */\n\nimport { test as base } from \"@playwright/test\";\nimport { EdgeLensClient } from \"@edgelens/core\";\nimport type { Device, Session } from \"@edgelens/core\";\n\nimport {\n DEFAULTS,\n allocateSession,\n buildClient,\n releaseSession,\n type EdgelensFixtureOptions,\n} from \"./lifecycle.js\";\n\nexport interface EdgelensWorkerFixtures {\n edgelensOptions: EdgelensFixtureOptions;\n workerClient: EdgeLensClient;\n workerSession: Session;\n workerDevice: Device;\n}\n\nexport interface EdgelensTestFixtures {\n /** Fresh per-test client. Defaults to the worker-scoped one. */\n client: EdgeLensClient;\n /** Fresh per-test session. Defaults to the worker-scoped one. */\n session: Session;\n /** Fresh per-test device. Defaults to the worker-scoped one. */\n device: Device;\n}\n\nexport const test = base.extend<EdgelensTestFixtures, EdgelensWorkerFixtures>({\n // -------- worker-scoped: client / session / device -----------------------\n\n // The default fixture options. Override in playwright.config.ts via:\n // use: { edgelensOptions: { session: { os: \"recamagined\" } } }\n edgelensOptions: [\n { waitReadyTimeoutMs: DEFAULTS.waitReadyTimeoutMs, waitReadyPollMs: DEFAULTS.waitReadyPollMs },\n { option: true, scope: \"worker\" },\n ],\n\n workerClient: [\n async ({ edgelensOptions }, use) => {\n const client = buildClient(edgelensOptions);\n await use(client);\n },\n { scope: \"worker\" },\n ],\n\n workerSession: [\n async ({ workerClient, edgelensOptions }, use) => {\n const session = await allocateSession(workerClient, edgelensOptions);\n try {\n await use(session);\n } finally {\n await releaseSession(session);\n }\n },\n { scope: \"worker\", timeout: 0 },\n ],\n\n workerDevice: [\n async ({ workerSession }, use) => {\n await use(workerSession.device);\n },\n { scope: \"worker\" },\n ],\n\n // -------- test-scoped: passthrough to worker-scoped by default ----------\n\n client: async ({ workerClient }, use) => {\n await use(workerClient);\n },\n\n session: async ({ workerSession }, use) => {\n await use(workerSession);\n },\n\n device: async ({ workerSession }, use) => {\n await use(workerSession.device);\n },\n});\n\nexport { expect } from \"@playwright/test\";\n","/**\n * Fixture lifecycle helpers — the actual session-allocation +\n * release logic. Kept separate from `fixtures.ts` so it's easy to\n * unit-test without Playwright's runner.\n */\n\nimport { EdgeLensClient } from \"@edgelens/core\";\nimport type {\n CreateSessionOptions,\n EdgeLensClientOptions,\n Session,\n} from \"@edgelens/core\";\n\nexport interface EdgelensFixtureOptions {\n /** API key. Falls back to `EDGELENS_API_KEY` env var. */\n apiKey?: string;\n /** Override the platform API base URL. */\n baseUrl?: string;\n /** Per-test session params. Same shape as `client.createSession()`. */\n session?: CreateSessionOptions;\n /** How long to wait for `status=ready`. Default 5 minutes. */\n waitReadyTimeoutMs?: number;\n /** Test poll interval while waiting for ready. Default 2s. */\n waitReadyPollMs?: number;\n /** Extra options passed through to the underlying HTTP client. */\n client?: Pick<EdgeLensClientOptions, \"timeoutMs\" | \"retryPolicy\" | \"userAgent\" | \"fetch\">;\n}\n\nexport const DEFAULTS = {\n waitReadyTimeoutMs: 5 * 60_000,\n waitReadyPollMs: 2_000,\n} as const;\n\nexport function buildClient(opts: EdgelensFixtureOptions): EdgeLensClient {\n return new EdgeLensClient({\n ...(opts.apiKey !== undefined ? { apiKey: opts.apiKey } : {}),\n ...(opts.baseUrl !== undefined ? { baseUrl: opts.baseUrl } : {}),\n ...(opts.client ?? {}),\n });\n}\n\n/**\n * Allocate a session and wait for it to be ready.\n *\n * Thrown errors propagate to the test (Playwright will fail the\n * fixture setup); the caller is responsible for invoking\n * `releaseSession()` on the result in a `finally` to avoid leaking\n * a billable device.\n *\n * If `waitReady` rejects, the caller never receives the session, so\n * it is released here before the original error is rethrown. A\n * failed release goes to `onReleaseError` (default `console.warn`)\n * and never replaces the original error.\n */\nexport async function allocateSession(\n client: EdgeLensClient,\n opts: EdgelensFixtureOptions = {},\n onReleaseError: (err: unknown) => void = (err) =>\n console.warn(\"edgelens: session release failed after waitReady error:\", err),\n): Promise<Session> {\n const session = await client.createSession(opts.session ?? {});\n try {\n await session.waitReady(\n opts.waitReadyTimeoutMs ?? DEFAULTS.waitReadyTimeoutMs,\n opts.waitReadyPollMs ?? DEFAULTS.waitReadyPollMs,\n );\n } catch (err) {\n await releaseSession(session, onReleaseError);\n throw err;\n }\n return session;\n}\n\n/**\n * Release a session, swallowing any errors that fire during\n * teardown (a failing release shouldn't mask the actual test\n * failure that preceded it).\n */\nexport async function releaseSession(\n session: Session | undefined,\n onError?: (err: unknown) => void,\n): Promise<void> {\n if (!session) return;\n try {\n await session.release();\n } catch (e) {\n if (onError) onError(e);\n }\n}\n","/**\n * Project presets for common (deviceType, os) combinations.\n *\n * Use in `playwright.config.ts`:\n *\n * import { defineConfig } from \"@playwright/test\";\n * import { edgelensProjects } from \"@edgelens/playwright-plugin\";\n *\n * export default defineConfig({\n * projects: [\n * ...edgelensProjects({ apiKey: process.env.EDGELENS_API_KEY }),\n * ],\n * });\n *\n * Each project sets `edgelensOptions.session` to the matching combo.\n * Test files filter via `test.use({ ... })` or Playwright's project\n * tag mechanism (`test.describe.configure({ tag: [\"@stock-os\"] })`).\n */\n\nimport type { CreateSessionOptions, DeviceType, OSType } from \"@edgelens/core\";\n\nimport type { EdgelensFixtureOptions } from \"./lifecycle.js\";\n\nexport interface EdgelensProject {\n name: string;\n use: {\n edgelensOptions: EdgelensFixtureOptions;\n };\n}\n\nexport interface EdgelensMatrixEntry {\n deviceType: DeviceType;\n os: OSType;\n sessionTimeoutS?: number;\n}\n\nexport interface EdgelensProjectsOptions {\n apiKey?: string;\n baseUrl?: string;\n /** Which (deviceType, os) combos to expose. Defaults to all supported. */\n matrix?: EdgelensMatrixEntry[];\n}\n\nconst DEFAULT_MATRIX: EdgelensMatrixEntry[] = [\n { deviceType: \"recamera-2002\", os: \"stock-os\" },\n { deviceType: \"recamera-2002\", os: \"recamagined\" },\n];\n\nexport function edgelensProjects(\n opts: EdgelensProjectsOptions = {},\n): EdgelensProject[] {\n const matrix = opts.matrix ?? DEFAULT_MATRIX;\n return matrix.map(({ deviceType, os, sessionTimeoutS }) => {\n const session: CreateSessionOptions = {\n deviceType,\n os,\n ...(sessionTimeoutS !== undefined ? { timeoutS: sessionTimeoutS } : {}),\n };\n return {\n name: `${deviceType}/${os}`,\n use: {\n edgelensOptions: {\n ...(opts.apiKey !== undefined ? { apiKey: opts.apiKey } : {}),\n ...(opts.baseUrl !== undefined ? { baseUrl: opts.baseUrl } : {}),\n session,\n },\n },\n };\n });\n}\n"],"mappings":";AAsBA,SAAS,QAAQ,YAAY;;;AChB7B,SAAS,sBAAsB;AAsBxB,IAAM,WAAW;AAAA,EACtB,oBAAoB,IAAI;AAAA,EACxB,iBAAiB;AACnB;AAEO,SAAS,YAAY,MAA8C;AACxE,SAAO,IAAI,eAAe;AAAA,IACxB,GAAI,KAAK,WAAW,SAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,IAC3D,GAAI,KAAK,YAAY,SAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,IAC9D,GAAI,KAAK,UAAU,CAAC;AAAA,EACtB,CAAC;AACH;AAeA,eAAsB,gBACpB,QACA,OAA+B,CAAC,GAChC,iBAAyC,CAAC,QACxC,QAAQ,KAAK,2DAA2D,GAAG,GAC3D;AAClB,QAAM,UAAU,MAAM,OAAO,cAAc,KAAK,WAAW,CAAC,CAAC;AAC7D,MAAI;AACF,UAAM,QAAQ;AAAA,MACZ,KAAK,sBAAsB,SAAS;AAAA,MACpC,KAAK,mBAAmB,SAAS;AAAA,IACnC;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,eAAe,SAAS,cAAc;AAC5C,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAOA,eAAsB,eACpB,SACA,SACe;AACf,MAAI,CAAC,QAAS;AACd,MAAI;AACF,UAAM,QAAQ,QAAQ;AAAA,EACxB,SAAS,GAAG;AACV,QAAI,QAAS,SAAQ,CAAC;AAAA,EACxB;AACF;;;ADcA,SAAS,cAAc;AApDhB,IAAM,OAAO,KAAK,OAAqD;AAAA;AAAA;AAAA;AAAA,EAK5E,iBAAiB;AAAA,IACf,EAAE,oBAAoB,SAAS,oBAAoB,iBAAiB,SAAS,gBAAgB;AAAA,IAC7F,EAAE,QAAQ,MAAM,OAAO,SAAS;AAAA,EAClC;AAAA,EAEA,cAAc;AAAA,IACZ,OAAO,EAAE,gBAAgB,GAAG,QAAQ;AAClC,YAAM,SAAS,YAAY,eAAe;AAC1C,YAAM,IAAI,MAAM;AAAA,IAClB;AAAA,IACA,EAAE,OAAO,SAAS;AAAA,EACpB;AAAA,EAEA,eAAe;AAAA,IACb,OAAO,EAAE,cAAc,gBAAgB,GAAG,QAAQ;AAChD,YAAM,UAAU,MAAM,gBAAgB,cAAc,eAAe;AACnE,UAAI;AACF,cAAM,IAAI,OAAO;AAAA,MACnB,UAAE;AACA,cAAM,eAAe,OAAO;AAAA,MAC9B;AAAA,IACF;AAAA,IACA,EAAE,OAAO,UAAU,SAAS,EAAE;AAAA,EAChC;AAAA,EAEA,cAAc;AAAA,IACZ,OAAO,EAAE,cAAc,GAAG,QAAQ;AAChC,YAAM,IAAI,cAAc,MAAM;AAAA,IAChC;AAAA,IACA,EAAE,OAAO,SAAS;AAAA,EACpB;AAAA;AAAA,EAIA,QAAQ,OAAO,EAAE,aAAa,GAAG,QAAQ;AACvC,UAAM,IAAI,YAAY;AAAA,EACxB;AAAA,EAEA,SAAS,OAAO,EAAE,cAAc,GAAG,QAAQ;AACzC,UAAM,IAAI,aAAa;AAAA,EACzB;AAAA,EAEA,QAAQ,OAAO,EAAE,cAAc,GAAG,QAAQ;AACxC,UAAM,IAAI,cAAc,MAAM;AAAA,EAChC;AACF,CAAC;;;AEzDD,IAAM,iBAAwC;AAAA,EAC5C,EAAE,YAAY,iBAAiB,IAAI,WAAW;AAAA,EAC9C,EAAE,YAAY,iBAAiB,IAAI,cAAc;AACnD;AAEO,SAAS,iBACd,OAAgC,CAAC,GACd;AACnB,QAAM,SAAS,KAAK,UAAU;AAC9B,SAAO,OAAO,IAAI,CAAC,EAAE,YAAY,IAAI,gBAAgB,MAAM;AACzD,UAAM,UAAgC;AAAA,MACpC;AAAA,MACA;AAAA,MACA,GAAI,oBAAoB,SAAY,EAAE,UAAU,gBAAgB,IAAI,CAAC;AAAA,IACvE;AACA,WAAO;AAAA,MACL,MAAM,GAAG,UAAU,IAAI,EAAE;AAAA,MACzB,KAAK;AAAA,QACH,iBAAiB;AAAA,UACf,GAAI,KAAK,WAAW,SAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,UAC3D,GAAI,KAAK,YAAY,SAAY,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,UAC9D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,68 @@
1
1
  {
2
2
  "name": "@edgelens/playwright-plugin",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.2",
4
+ "description": "Playwright integration for EdgeLens — per-test camera sessions, auto-release, project presets.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "require": {
17
+ "types": "./dist/index.d.cts",
18
+ "default": "./dist/index.cjs"
19
+ }
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
30
+ "typecheck": "tsc --noEmit",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest"
33
+ },
34
+ "peerDependencies": {
35
+ "@edgelens/core": "^0.2.2",
36
+ "@playwright/test": ">=1.50.0"
37
+ },
38
+ "devDependencies": {
39
+ "@playwright/test": "^1.55.0",
40
+ "@types/node": "^20.19.0",
41
+ "tsup": "^8.5.0",
42
+ "typescript": "^5.6.0",
43
+ "vitest": "^2.1.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=18"
47
+ },
48
+ "keywords": [
49
+ "edgelens",
50
+ "playwright",
51
+ "playwright-plugin",
52
+ "edge-ai",
53
+ "testing",
54
+ "device-farm"
55
+ ],
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/edgelens-ai/edgelens-sdk.git",
59
+ "directory": "integrations/playwright"
60
+ },
61
+ "homepage": "https://edgelens.ai",
62
+ "bugs": {
63
+ "url": "https://github.com/edgelens-ai/edgelens-sdk/issues"
64
+ },
65
+ "publishConfig": {
66
+ "access": "public"
67
+ }
68
+ }