@almadar/verify 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,362 @@
1
+ import { Browser, BrowserContext, Page } from 'playwright';
2
+ export { buildGuardPayloads, buildReplayPaths, buildStateGraph, collectReachableStates, extractPayloadFieldRef, walkStatePairs } from '@almadar/core';
3
+
4
+ /**
5
+ * Browser launch with shared defaults.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ interface LaunchOptions {
11
+ headless?: boolean;
12
+ viewport?: {
13
+ width: number;
14
+ height: number;
15
+ };
16
+ timeout?: number;
17
+ }
18
+ /**
19
+ * Launch a Chromium browser with shared defaults.
20
+ * Returns both the browser and a context with the configured viewport.
21
+ */
22
+ declare function launchBrowser(options?: LaunchOptions): Promise<{
23
+ browser: Browser;
24
+ context: BrowserContext;
25
+ }>;
26
+
27
+ /**
28
+ * Shared types for verification tools.
29
+ *
30
+ * @packageDocumentation
31
+ */
32
+ /** Result of a single verification check */
33
+ interface VerifyResult {
34
+ name: string;
35
+ status: 'pass' | 'error' | 'warning';
36
+ errors: string[];
37
+ warnings: string[];
38
+ screenshotPath: string | null;
39
+ durationMs: number;
40
+ runtimeState?: RuntimeState;
41
+ }
42
+ /** Runtime state snapshot read from window.__orbitalVerification */
43
+ interface RuntimeState {
44
+ traits: Record<string, {
45
+ currentState: string;
46
+ context: unknown;
47
+ }>;
48
+ entities: Record<string, unknown[]>;
49
+ events: string[];
50
+ guards: Record<string, boolean>;
51
+ }
52
+ /** A single console message captured from the browser */
53
+ interface ConsoleEntry {
54
+ type: 'error' | 'warning' | 'info';
55
+ text: string;
56
+ timestamp: number;
57
+ }
58
+ /** Full report of a verification run */
59
+ interface VerifyReport {
60
+ timestamp: string;
61
+ url: string;
62
+ mode: 'playground' | 'app';
63
+ summary: {
64
+ total: number;
65
+ pass: number;
66
+ error: number;
67
+ warning: number;
68
+ };
69
+ results: VerifyResult[];
70
+ }
71
+
72
+ /**
73
+ * Console message collector for Playwright pages.
74
+ *
75
+ * Collects console errors and warnings from a Playwright page,
76
+ * filtering out noise (favicon, DevTools, net::ERR_, etc.).
77
+ *
78
+ * @packageDocumentation
79
+ */
80
+
81
+ /**
82
+ * Collects and categorizes console messages from a Playwright page.
83
+ *
84
+ * Usage:
85
+ * ```ts
86
+ * const collector = new ConsoleCollector(page);
87
+ * // ... navigate and interact ...
88
+ * const { errors, warnings } = collector.getResults();
89
+ * collector.dispose();
90
+ * ```
91
+ */
92
+ declare class ConsoleCollector {
93
+ private entries;
94
+ private uncaughtErrors;
95
+ private page;
96
+ private consoleHandler;
97
+ private errorHandler;
98
+ constructor(page: Page);
99
+ /** Get all collected entries */
100
+ getEntries(): ConsoleEntry[];
101
+ /** Get categorized results */
102
+ getResults(): {
103
+ errors: string[];
104
+ warnings: string[];
105
+ uncaughtErrors: string[];
106
+ };
107
+ /** Clear collected entries (useful between navigation steps) */
108
+ clear(): void;
109
+ /** Remove event listeners from the page */
110
+ dispose(): void;
111
+ }
112
+
113
+ /**
114
+ * Screenshot capture utilities.
115
+ *
116
+ * @packageDocumentation
117
+ */
118
+
119
+ /**
120
+ * Capture a screenshot of a specific element or the full page.
121
+ *
122
+ * @param page - Playwright page
123
+ * @param outputPath - File path for the screenshot
124
+ * @param selector - Optional CSS selector to screenshot a specific element
125
+ * @returns The actual path where the screenshot was saved, or null on failure
126
+ */
127
+ declare function takeScreenshot(page: Page, outputPath: string, selector?: string): Promise<string | null>;
128
+ /** Sanitize a name for use as a filename */
129
+ declare function safeFileName(name: string): string;
130
+
131
+ /**
132
+ * Navigation utilities with retry and wait helpers.
133
+ *
134
+ * @packageDocumentation
135
+ */
136
+
137
+ /**
138
+ * Navigate to a URL with retry on failure.
139
+ *
140
+ * @param page - Playwright page
141
+ * @param url - URL to navigate to
142
+ * @param options - Navigation options
143
+ */
144
+ declare function navigateWithRetry(page: Page, url: string, options?: {
145
+ waitUntil?: 'domcontentloaded' | 'load' | 'networkidle';
146
+ timeout?: number;
147
+ retries?: number;
148
+ }): Promise<void>;
149
+ /**
150
+ * Wait for the runtime to load and render content.
151
+ * The OrbitalPreview shows "Loading runtime..." then renders actual content.
152
+ *
153
+ * @param page - Playwright page
154
+ * @param waitMs - Time to wait for runtime to settle (default: 3000ms)
155
+ */
156
+ declare function waitForRuntime(page: Page, waitMs?: number): Promise<void>;
157
+
158
+ /**
159
+ * State bridge: read window.__orbitalVerification from a Playwright page.
160
+ *
161
+ * The OrbitalVerificationAPI is exposed by @almadar/ui's verificationRegistry.ts
162
+ * on window.__orbitalVerification. This module provides typed access to it.
163
+ *
164
+ * @packageDocumentation
165
+ */
166
+
167
+ /** Shape of window.__orbitalVerification (subset used by verify tools) */
168
+ interface OrbitalVerificationSnapshot {
169
+ checks: Array<{
170
+ id: string;
171
+ label: string;
172
+ status: 'pass' | 'fail' | 'pending' | 'warn';
173
+ details?: string;
174
+ }>;
175
+ transitions: Array<{
176
+ id: string;
177
+ traitName: string;
178
+ from: string;
179
+ to: string;
180
+ event: string;
181
+ effects: Array<{
182
+ type: string;
183
+ status: string;
184
+ error?: string;
185
+ }>;
186
+ }>;
187
+ bridge: {
188
+ connected: boolean;
189
+ } | null;
190
+ summary: {
191
+ totalChecks: number;
192
+ passed: number;
193
+ failed: number;
194
+ warnings: number;
195
+ pending: number;
196
+ };
197
+ }
198
+ /**
199
+ * Read the verification snapshot from the browser.
200
+ * Returns null if the API is not available (runtime not loaded).
201
+ */
202
+ declare function readVerificationSnapshot(page: Page): Promise<OrbitalVerificationSnapshot | null>;
203
+ /**
204
+ * Read trait states from the verification API.
205
+ * Returns a map of trait name -> current state name.
206
+ */
207
+ declare function readTraitStates(page: Page): Promise<Record<string, string>>;
208
+ /**
209
+ * Read the event log from the verification API.
210
+ */
211
+ declare function readEventLog(page: Page): Promise<Array<{
212
+ event: string;
213
+ listenerCount?: number;
214
+ }>>;
215
+ /**
216
+ * Build a RuntimeState from the verification snapshot.
217
+ * Combines trait states, entity data, events, and guard results.
218
+ */
219
+ declare function readRuntimeState(page: Page): Promise<RuntimeState | null>;
220
+
221
+ /**
222
+ * Entity data inspector.
223
+ *
224
+ * Extract entity data visibility from the DOM to verify that
225
+ * mock data actually reached the rendered components.
226
+ *
227
+ * @packageDocumentation
228
+ */
229
+
230
+ /** Result of an entity data inspection */
231
+ interface EntityInspection {
232
+ /** Whether any entity data content was found in the DOM */
233
+ hasContent: boolean;
234
+ /** Number of entity-related DOM elements found */
235
+ elementCount: number;
236
+ /** Whether an "empty state" pattern is visible */
237
+ showsEmptyState: boolean;
238
+ /** Text content of the preview area (truncated) */
239
+ previewText: string;
240
+ }
241
+ /**
242
+ * Inspect the preview area for entity data content.
243
+ * Checks for empty states, entity cards, table rows, etc.
244
+ *
245
+ * @param page - Playwright page
246
+ * @param previewSelector - CSS selector for the preview area
247
+ */
248
+ declare function inspectEntityData(page: Page, previewSelector?: string): Promise<EntityInspection>;
249
+
250
+ /**
251
+ * DOM state extraction and inspection.
252
+ *
253
+ * Check for common error patterns in the rendered DOM:
254
+ * "Unknown pattern", error boundaries, empty content, etc.
255
+ *
256
+ * @packageDocumentation
257
+ */
258
+
259
+ /** DOM inspection result */
260
+ interface DOMInspection {
261
+ /** Unknown pattern errors found in the DOM */
262
+ unknownPatterns: string[];
263
+ /** Whether an error boundary fallback is visible */
264
+ hasErrorBoundary: boolean;
265
+ /** Whether "Objects are not valid as a React child" appears */
266
+ hasObjectRenderError: boolean;
267
+ /** Whether the preview area is completely empty */
268
+ isEmpty: boolean;
269
+ /** Whether a preview error banner is visible */
270
+ hasPreviewError: boolean;
271
+ /** The preview error text, if any */
272
+ previewErrorText: string;
273
+ }
274
+ /**
275
+ * Inspect the DOM for common rendering errors.
276
+ *
277
+ * @param page - Playwright page
278
+ * @param previewSelector - CSS selector for the preview area
279
+ */
280
+ declare function inspectDOM(page: Page, previewSelector?: string): Promise<DOMInspection>;
281
+
282
+ /**
283
+ * Console message categorization and reporting.
284
+ *
285
+ * @packageDocumentation
286
+ */
287
+
288
+ /** Categorized console report */
289
+ interface ConsoleReport {
290
+ errorCount: number;
291
+ warningCount: number;
292
+ infoCount: number;
293
+ errors: string[];
294
+ warnings: string[];
295
+ /** Unique error patterns (deduplicated by first 80 chars) */
296
+ uniqueErrors: string[];
297
+ }
298
+ /**
299
+ * Build a console report from collected entries.
300
+ */
301
+ declare function buildConsoleReport(entries: ConsoleEntry[]): ConsoleReport;
302
+
303
+ /**
304
+ * JSON report generation.
305
+ *
306
+ * @packageDocumentation
307
+ */
308
+
309
+ /**
310
+ * Build a JSON report from verification results.
311
+ */
312
+ declare function buildJsonReport(url: string, mode: 'playground' | 'app', results: VerifyResult[]): VerifyReport;
313
+ /**
314
+ * Write a JSON report to disk.
315
+ */
316
+ declare function writeJsonReport(report: VerifyReport, outputPath: string): void;
317
+
318
+ /**
319
+ * Markdown report generation.
320
+ *
321
+ * @packageDocumentation
322
+ */
323
+
324
+ /**
325
+ * Generate a markdown report from verification results.
326
+ */
327
+ declare function buildMarkdownReport(report: VerifyReport): string;
328
+ /**
329
+ * Write a markdown report to disk.
330
+ */
331
+ declare function writeMarkdownReport(report: VerifyReport, outputPath: string): void;
332
+
333
+ /**
334
+ * Noise filters for console messages.
335
+ *
336
+ * Shared across orbital-verify, runtime-verify, and playground-test
337
+ * to exclude browser/webpack/DevTools noise from error reports.
338
+ *
339
+ * @packageDocumentation
340
+ */
341
+ /** Check if a console error message is noise (should be ignored) */
342
+ declare function isNoiseError(text: string): boolean;
343
+ /** Check if a console warning message is noise (should be ignored) */
344
+ declare function isNoiseWarning(text: string): boolean;
345
+
346
+ /**
347
+ * Retry utility with exponential backoff.
348
+ *
349
+ * @packageDocumentation
350
+ */
351
+ /**
352
+ * Retry an async operation with exponential backoff.
353
+ *
354
+ * @param fn - Async function to retry
355
+ * @param maxRetries - Maximum number of retries (default: 3)
356
+ * @param baseDelayMs - Base delay in ms before first retry (default: 500)
357
+ * @returns Result of the successful call
358
+ * @throws Last error if all retries exhausted
359
+ */
360
+ declare function retry<T>(fn: () => Promise<T>, maxRetries?: number, baseDelayMs?: number): Promise<T>;
361
+
362
+ export { ConsoleCollector, type ConsoleEntry, type ConsoleReport, type DOMInspection, type EntityInspection, type LaunchOptions, type OrbitalVerificationSnapshot, type RuntimeState, type VerifyReport, type VerifyResult, buildConsoleReport, buildJsonReport, buildMarkdownReport, inspectDOM, inspectEntityData, isNoiseError, isNoiseWarning, launchBrowser, navigateWithRetry, readEventLog, readRuntimeState, readTraitStates, readVerificationSnapshot, retry, safeFileName, takeScreenshot, waitForRuntime, writeJsonReport, writeMarkdownReport };
package/dist/index.js ADDED
@@ -0,0 +1,349 @@
1
+ import { chromium } from 'playwright';
2
+ import { mkdirSync, writeFileSync } from 'fs';
3
+ import { dirname } from 'path';
4
+ export { buildGuardPayloads, buildReplayPaths, buildStateGraph, collectReachableStates, extractPayloadFieldRef, walkStatePairs } from '@almadar/core';
5
+
6
+ // src/browser/launch.ts
7
+ var DEFAULTS = {
8
+ headless: true,
9
+ viewport: { width: 1440, height: 900 },
10
+ timeout: 3e4
11
+ };
12
+ async function launchBrowser(options) {
13
+ const opts = { ...DEFAULTS, ...options };
14
+ const browser = await chromium.launch({
15
+ headless: opts.headless
16
+ });
17
+ const context = await browser.newContext({
18
+ viewport: opts.viewport
19
+ });
20
+ context.setDefaultTimeout(opts.timeout);
21
+ return { browser, context };
22
+ }
23
+
24
+ // src/util/filter.ts
25
+ var ERROR_NOISE_PATTERNS = [
26
+ "favicon.ico",
27
+ "net::ERR_",
28
+ "the server responded with a status of 404",
29
+ "Failed to load resource",
30
+ "ERR_CONNECTION_REFUSED"
31
+ ];
32
+ var WARNING_NOISE_PATTERNS = [
33
+ "DevTools",
34
+ "React DevTools",
35
+ "Download the React DevTools",
36
+ "Warning: Each child in a list should have a unique"
37
+ ];
38
+ function isNoiseError(text) {
39
+ return ERROR_NOISE_PATTERNS.some((pattern) => text.includes(pattern));
40
+ }
41
+ function isNoiseWarning(text) {
42
+ return WARNING_NOISE_PATTERNS.some((pattern) => text.includes(pattern));
43
+ }
44
+
45
+ // src/browser/console.ts
46
+ var ConsoleCollector = class {
47
+ entries = [];
48
+ uncaughtErrors = [];
49
+ page;
50
+ consoleHandler;
51
+ errorHandler;
52
+ constructor(page) {
53
+ this.page = page;
54
+ this.consoleHandler = (msg) => {
55
+ const text = msg.text();
56
+ const timestamp = Date.now();
57
+ if (msg.type() === "error") {
58
+ if (isNoiseError(text)) return;
59
+ this.entries.push({ type: "error", text, timestamp });
60
+ } else if (msg.type() === "warning") {
61
+ if (isNoiseWarning(text)) return;
62
+ this.entries.push({ type: "warning", text, timestamp });
63
+ } else {
64
+ this.entries.push({ type: "info", text, timestamp });
65
+ }
66
+ };
67
+ this.errorHandler = (err) => {
68
+ this.uncaughtErrors.push(`Uncaught: ${err.message}`);
69
+ this.entries.push({ type: "error", text: `Uncaught: ${err.message}`, timestamp: Date.now() });
70
+ };
71
+ page.on("console", this.consoleHandler);
72
+ page.on("pageerror", this.errorHandler);
73
+ }
74
+ /** Get all collected entries */
75
+ getEntries() {
76
+ return [...this.entries];
77
+ }
78
+ /** Get categorized results */
79
+ getResults() {
80
+ return {
81
+ errors: this.entries.filter((e) => e.type === "error").map((e) => e.text),
82
+ warnings: this.entries.filter((e) => e.type === "warning").map((e) => e.text),
83
+ uncaughtErrors: [...this.uncaughtErrors]
84
+ };
85
+ }
86
+ /** Clear collected entries (useful between navigation steps) */
87
+ clear() {
88
+ this.entries = [];
89
+ this.uncaughtErrors = [];
90
+ }
91
+ /** Remove event listeners from the page */
92
+ dispose() {
93
+ this.page.removeListener("console", this.consoleHandler);
94
+ this.page.removeListener("pageerror", this.errorHandler);
95
+ }
96
+ };
97
+ async function takeScreenshot(page, outputPath, selector) {
98
+ try {
99
+ mkdirSync(dirname(outputPath), { recursive: true });
100
+ if (selector) {
101
+ const element = await page.$(selector);
102
+ if (element) {
103
+ await element.screenshot({ path: outputPath });
104
+ return outputPath;
105
+ }
106
+ }
107
+ await page.screenshot({ path: outputPath, fullPage: false });
108
+ return outputPath;
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+ function safeFileName(name) {
114
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_");
115
+ }
116
+
117
+ // src/util/retry.ts
118
+ async function retry(fn, maxRetries = 3, baseDelayMs = 500) {
119
+ let lastError;
120
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
121
+ try {
122
+ return await fn();
123
+ } catch (err) {
124
+ lastError = err;
125
+ if (attempt < maxRetries) {
126
+ const delay = baseDelayMs * Math.pow(2, attempt);
127
+ await new Promise((resolve) => setTimeout(resolve, delay));
128
+ }
129
+ }
130
+ }
131
+ throw lastError;
132
+ }
133
+
134
+ // src/browser/navigate.ts
135
+ async function navigateWithRetry(page, url, options) {
136
+ const { waitUntil = "domcontentloaded", timeout = 15e3, retries = 2 } = options ?? {};
137
+ await retry(
138
+ async () => {
139
+ const resp = await page.goto(url, { waitUntil, timeout });
140
+ if (!resp || resp.status() >= 500) {
141
+ throw new Error(`Navigation to ${url} failed with status ${resp?.status()}`);
142
+ }
143
+ },
144
+ retries,
145
+ 1e3
146
+ );
147
+ }
148
+ async function waitForRuntime(page, waitMs = 3e3) {
149
+ await page.waitForTimeout(waitMs);
150
+ }
151
+
152
+ // src/runtime/state-bridge.ts
153
+ async function readVerificationSnapshot(page) {
154
+ return page.evaluate(() => {
155
+ const api = window.__orbitalVerification;
156
+ return api?.getSnapshot?.() ?? null;
157
+ });
158
+ }
159
+ async function readTraitStates(page) {
160
+ return page.evaluate(() => {
161
+ const api = window.__orbitalVerification;
162
+ if (!api?.getTraitState) return {};
163
+ const snapshotApi = api;
164
+ const snapshot = snapshotApi.getSnapshot?.();
165
+ if (!snapshot) return {};
166
+ const result = {};
167
+ const traitNames = new Set(snapshot.transitions.map((t) => t.traitName));
168
+ for (const name of traitNames) {
169
+ const state = api.getTraitState(name);
170
+ if (state) result[name] = state.currentState;
171
+ }
172
+ return result;
173
+ });
174
+ }
175
+ async function readEventLog(page) {
176
+ return page.evaluate(() => {
177
+ const api = window.__orbitalVerification;
178
+ return api?.eventLog ?? [];
179
+ });
180
+ }
181
+ async function readRuntimeState(page) {
182
+ const snapshot = await readVerificationSnapshot(page);
183
+ if (!snapshot) return null;
184
+ const traitStates = await readTraitStates(page);
185
+ return {
186
+ traits: Object.fromEntries(
187
+ Object.entries(traitStates).map(([name, currentState]) => [
188
+ name,
189
+ { currentState, context: {} }
190
+ ])
191
+ ),
192
+ entities: {},
193
+ // Entity data requires FetchedDataContext inspection
194
+ events: snapshot.transitions.map((t) => t.event),
195
+ guards: Object.fromEntries(
196
+ snapshot.checks.filter((c) => c.id.startsWith("guard-")).map((c) => [c.id, c.status === "pass"])
197
+ )
198
+ };
199
+ }
200
+
201
+ // src/runtime/entity-inspector.ts
202
+ async function inspectEntityData(page, previewSelector = '[class*="livePreviewBox"], [class*="opPreviewBox"]') {
203
+ return page.evaluate((selector) => {
204
+ const preview = document.querySelector(selector);
205
+ if (!preview) {
206
+ return { hasContent: false, elementCount: 0, showsEmptyState: false, previewText: "" };
207
+ }
208
+ const text = (preview.textContent ?? "").trim();
209
+ const entityElements = preview.querySelectorAll(
210
+ '[data-pattern], [data-entity], table tbody tr, [class*="card"], [class*="Card"]'
211
+ );
212
+ const emptyStateTexts = ["empty", "no data", "no items", "nothing to show", "no results"];
213
+ const showsEmptyState = emptyStateTexts.some(
214
+ (t) => text.toLowerCase().includes(t)
215
+ );
216
+ return {
217
+ hasContent: text.length > 0 && entityElements.length > 0,
218
+ elementCount: entityElements.length,
219
+ showsEmptyState,
220
+ previewText: text.substring(0, 500)
221
+ };
222
+ }, previewSelector);
223
+ }
224
+
225
+ // src/analysis/dom-inspector.ts
226
+ async function inspectDOM(page, previewSelector = '[class*="livePreviewBox"], [class*="opPreviewBox"]') {
227
+ return page.evaluate((selector) => {
228
+ const result = {
229
+ unknownPatterns: [],
230
+ hasErrorBoundary: false,
231
+ hasObjectRenderError: false,
232
+ isEmpty: true,
233
+ hasPreviewError: false,
234
+ previewErrorText: ""
235
+ };
236
+ const previewError = document.querySelector('[class*="previewError"]');
237
+ if (previewError) {
238
+ result.hasPreviewError = true;
239
+ result.previewErrorText = (previewError.textContent ?? "").trim();
240
+ }
241
+ const preview = document.querySelector(selector);
242
+ if (!preview) return result;
243
+ const text = (preview.textContent ?? "").trim();
244
+ result.isEmpty = text.length === 0;
245
+ const unknownMatch = text.match(/Unknown pattern:\s*\S+/g);
246
+ if (unknownMatch) {
247
+ result.unknownPatterns = unknownMatch;
248
+ }
249
+ if (text.includes("Objects are not valid as a React child")) {
250
+ result.hasObjectRenderError = true;
251
+ }
252
+ const errorBoundary = preview.querySelector('[class*="error-boundary"], [class*="ErrorBoundary"]');
253
+ if (errorBoundary) {
254
+ result.hasErrorBoundary = true;
255
+ }
256
+ return result;
257
+ }, previewSelector);
258
+ }
259
+
260
+ // src/analysis/console-report.ts
261
+ function buildConsoleReport(entries) {
262
+ const errors = entries.filter((e) => e.type === "error").map((e) => e.text);
263
+ const warnings = entries.filter((e) => e.type === "warning").map((e) => e.text);
264
+ const infoCount = entries.filter((e) => e.type === "info").length;
265
+ const seen = /* @__PURE__ */ new Set();
266
+ const uniqueErrors = [];
267
+ for (const err of errors) {
268
+ const key = err.substring(0, 80);
269
+ if (!seen.has(key)) {
270
+ seen.add(key);
271
+ uniqueErrors.push(err);
272
+ }
273
+ }
274
+ return {
275
+ errorCount: errors.length,
276
+ warningCount: warnings.length,
277
+ infoCount,
278
+ errors,
279
+ warnings,
280
+ uniqueErrors
281
+ };
282
+ }
283
+ function buildJsonReport(url, mode, results) {
284
+ const pass = results.filter((r) => r.status === "pass").length;
285
+ const error = results.filter((r) => r.status === "error").length;
286
+ const warning = results.filter((r) => r.status === "warning").length;
287
+ return {
288
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
289
+ url,
290
+ mode,
291
+ summary: { total: results.length, pass, error, warning },
292
+ results
293
+ };
294
+ }
295
+ function writeJsonReport(report, outputPath) {
296
+ mkdirSync(dirname(outputPath), { recursive: true });
297
+ writeFileSync(outputPath, JSON.stringify(report, null, 2));
298
+ }
299
+ function buildMarkdownReport(report) {
300
+ const lines = [];
301
+ lines.push(`# Verification Report`);
302
+ lines.push("");
303
+ lines.push(`- **Date**: ${report.timestamp}`);
304
+ lines.push(`- **URL**: ${report.url}`);
305
+ lines.push(`- **Mode**: ${report.mode}`);
306
+ lines.push("");
307
+ lines.push(`## Summary`);
308
+ lines.push("");
309
+ lines.push(`| Status | Count |`);
310
+ lines.push(`|--------|-------|`);
311
+ lines.push(`| PASS | ${report.summary.pass} |`);
312
+ lines.push(`| WARN | ${report.summary.warning} |`);
313
+ lines.push(`| ERROR | ${report.summary.error} |`);
314
+ lines.push(`| **Total** | **${report.summary.total}** |`);
315
+ lines.push("");
316
+ const errors = report.results.filter((r) => r.status === "error");
317
+ if (errors.length > 0) {
318
+ lines.push(`## Errors (${errors.length})`);
319
+ lines.push("");
320
+ for (const item of errors) {
321
+ lines.push(`### ${item.name}`);
322
+ for (const e of item.errors) {
323
+ lines.push(`- ${e.substring(0, 200)}`);
324
+ }
325
+ lines.push("");
326
+ }
327
+ }
328
+ const warnings = report.results.filter((r) => r.status === "warning");
329
+ if (warnings.length > 0) {
330
+ lines.push(`## Warnings (${warnings.length})`);
331
+ lines.push("");
332
+ for (const item of warnings) {
333
+ lines.push(`### ${item.name}`);
334
+ for (const w of item.warnings) {
335
+ lines.push(`- ${w.substring(0, 200)}`);
336
+ }
337
+ lines.push("");
338
+ }
339
+ }
340
+ return lines.join("\n");
341
+ }
342
+ function writeMarkdownReport(report, outputPath) {
343
+ mkdirSync(dirname(outputPath), { recursive: true });
344
+ writeFileSync(outputPath, buildMarkdownReport(report));
345
+ }
346
+
347
+ export { ConsoleCollector, buildConsoleReport, buildJsonReport, buildMarkdownReport, inspectDOM, inspectEntityData, isNoiseError, isNoiseWarning, launchBrowser, navigateWithRetry, readEventLog, readRuntimeState, readTraitStates, readVerificationSnapshot, retry, safeFileName, takeScreenshot, waitForRuntime, writeJsonReport, writeMarkdownReport };
348
+ //# sourceMappingURL=index.js.map
349
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/browser/launch.ts","../src/util/filter.ts","../src/browser/console.ts","../src/browser/screenshot.ts","../src/util/retry.ts","../src/browser/navigate.ts","../src/runtime/state-bridge.ts","../src/runtime/entity-inspector.ts","../src/analysis/dom-inspector.ts","../src/analysis/console-report.ts","../src/report/json-report.ts","../src/report/markdown-report.ts"],"names":["mkdirSync","dirname","writeFileSync"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Browser launch with shared defaults.\n *\n * @packageDocumentation\n */\n\nimport { chromium, type Browser, type BrowserContext } from 'playwright';\n\nexport interface LaunchOptions {\n headless?: boolean;\n viewport?: { width: number; height: number };\n timeout?: number;\n}\n\nconst DEFAULTS: Required<LaunchOptions> = {\n headless: true,\n viewport: { width: 1440, height: 900 },\n timeout: 30000,\n};\n\n/**\n * Launch a Chromium browser with shared defaults.\n * Returns both the browser and a context with the configured viewport.\n */\nexport async function launchBrowser(\n options?: LaunchOptions\n): Promise<{ browser: Browser; context: BrowserContext }> {\n const opts = { ...DEFAULTS, ...options };\n\n const browser = await chromium.launch({\n headless: opts.headless,\n });\n\n const context = await browser.newContext({\n viewport: opts.viewport,\n });\n\n context.setDefaultTimeout(opts.timeout);\n\n return { browser, context };\n}\n","/**\n * Noise filters for console messages.\n *\n * Shared across orbital-verify, runtime-verify, and playground-test\n * to exclude browser/webpack/DevTools noise from error reports.\n *\n * @packageDocumentation\n */\n\n/** Patterns that should be ignored from console errors */\nconst ERROR_NOISE_PATTERNS = [\n 'favicon.ico',\n 'net::ERR_',\n 'the server responded with a status of 404',\n 'Failed to load resource',\n 'ERR_CONNECTION_REFUSED',\n] as const;\n\n/** Patterns that should be ignored from console warnings */\nconst WARNING_NOISE_PATTERNS = [\n 'DevTools',\n 'React DevTools',\n 'Download the React DevTools',\n 'Warning: Each child in a list should have a unique',\n] as const;\n\n/** Check if a console error message is noise (should be ignored) */\nexport function isNoiseError(text: string): boolean {\n return ERROR_NOISE_PATTERNS.some((pattern) => text.includes(pattern));\n}\n\n/** Check if a console warning message is noise (should be ignored) */\nexport function isNoiseWarning(text: string): boolean {\n return WARNING_NOISE_PATTERNS.some((pattern) => text.includes(pattern));\n}\n","/**\n * Console message collector for Playwright pages.\n *\n * Collects console errors and warnings from a Playwright page,\n * filtering out noise (favicon, DevTools, net::ERR_, etc.).\n *\n * @packageDocumentation\n */\n\nimport type { Page, ConsoleMessage } from 'playwright';\nimport type { ConsoleEntry } from '../util/types.js';\nimport { isNoiseError, isNoiseWarning } from '../util/filter.js';\n\n/**\n * Collects and categorizes console messages from a Playwright page.\n *\n * Usage:\n * ```ts\n * const collector = new ConsoleCollector(page);\n * // ... navigate and interact ...\n * const { errors, warnings } = collector.getResults();\n * collector.dispose();\n * ```\n */\nexport class ConsoleCollector {\n private entries: ConsoleEntry[] = [];\n private uncaughtErrors: string[] = [];\n private page: Page;\n private consoleHandler: (msg: ConsoleMessage) => void;\n private errorHandler: (err: Error) => void;\n\n constructor(page: Page) {\n this.page = page;\n\n this.consoleHandler = (msg: ConsoleMessage) => {\n const text = msg.text();\n const timestamp = Date.now();\n\n if (msg.type() === 'error') {\n if (isNoiseError(text)) return;\n this.entries.push({ type: 'error', text, timestamp });\n } else if (msg.type() === 'warning') {\n if (isNoiseWarning(text)) return;\n this.entries.push({ type: 'warning', text, timestamp });\n } else {\n this.entries.push({ type: 'info', text, timestamp });\n }\n };\n\n this.errorHandler = (err: Error) => {\n this.uncaughtErrors.push(`Uncaught: ${err.message}`);\n this.entries.push({ type: 'error', text: `Uncaught: ${err.message}`, timestamp: Date.now() });\n };\n\n page.on('console', this.consoleHandler);\n page.on('pageerror', this.errorHandler);\n }\n\n /** Get all collected entries */\n getEntries(): ConsoleEntry[] {\n return [...this.entries];\n }\n\n /** Get categorized results */\n getResults(): { errors: string[]; warnings: string[]; uncaughtErrors: string[] } {\n return {\n errors: this.entries.filter((e) => e.type === 'error').map((e) => e.text),\n warnings: this.entries.filter((e) => e.type === 'warning').map((e) => e.text),\n uncaughtErrors: [...this.uncaughtErrors],\n };\n }\n\n /** Clear collected entries (useful between navigation steps) */\n clear(): void {\n this.entries = [];\n this.uncaughtErrors = [];\n }\n\n /** Remove event listeners from the page */\n dispose(): void {\n this.page.removeListener('console', this.consoleHandler);\n this.page.removeListener('pageerror', this.errorHandler);\n }\n}\n","/**\n * Screenshot capture utilities.\n *\n * @packageDocumentation\n */\n\nimport { mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { Page, ElementHandle } from 'playwright';\n\n/**\n * Capture a screenshot of a specific element or the full page.\n *\n * @param page - Playwright page\n * @param outputPath - File path for the screenshot\n * @param selector - Optional CSS selector to screenshot a specific element\n * @returns The actual path where the screenshot was saved, or null on failure\n */\nexport async function takeScreenshot(\n page: Page,\n outputPath: string,\n selector?: string\n): Promise<string | null> {\n try {\n mkdirSync(dirname(outputPath), { recursive: true });\n\n if (selector) {\n const element: ElementHandle | null = await page.$(selector);\n if (element) {\n await element.screenshot({ path: outputPath });\n return outputPath;\n }\n }\n\n // Fallback to page screenshot\n await page.screenshot({ path: outputPath, fullPage: false });\n return outputPath;\n } catch {\n return null;\n }\n}\n\n/** Sanitize a name for use as a filename */\nexport function safeFileName(name: string): string {\n return name.replace(/[^a-zA-Z0-9_-]/g, '_');\n}\n","/**\n * Retry utility with exponential backoff.\n *\n * @packageDocumentation\n */\n\n/**\n * Retry an async operation with exponential backoff.\n *\n * @param fn - Async function to retry\n * @param maxRetries - Maximum number of retries (default: 3)\n * @param baseDelayMs - Base delay in ms before first retry (default: 500)\n * @returns Result of the successful call\n * @throws Last error if all retries exhausted\n */\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxRetries = 3,\n baseDelayMs = 500\n): Promise<T> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err;\n if (attempt < maxRetries) {\n const delay = baseDelayMs * Math.pow(2, attempt);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n throw lastError;\n}\n","/**\n * Navigation utilities with retry and wait helpers.\n *\n * @packageDocumentation\n */\n\nimport type { Page } from 'playwright';\nimport { retry } from '../util/retry.js';\n\n/**\n * Navigate to a URL with retry on failure.\n *\n * @param page - Playwright page\n * @param url - URL to navigate to\n * @param options - Navigation options\n */\nexport async function navigateWithRetry(\n page: Page,\n url: string,\n options?: {\n waitUntil?: 'domcontentloaded' | 'load' | 'networkidle';\n timeout?: number;\n retries?: number;\n }\n): Promise<void> {\n const { waitUntil = 'domcontentloaded', timeout = 15000, retries = 2 } = options ?? {};\n\n await retry(\n async () => {\n const resp = await page.goto(url, { waitUntil, timeout });\n if (!resp || resp.status() >= 500) {\n throw new Error(`Navigation to ${url} failed with status ${resp?.status()}`);\n }\n },\n retries,\n 1000\n );\n}\n\n/**\n * Wait for the runtime to load and render content.\n * The OrbitalPreview shows \"Loading runtime...\" then renders actual content.\n *\n * @param page - Playwright page\n * @param waitMs - Time to wait for runtime to settle (default: 3000ms)\n */\nexport async function waitForRuntime(page: Page, waitMs = 3000): Promise<void> {\n await page.waitForTimeout(waitMs);\n}\n","/**\n * State bridge: read window.__orbitalVerification from a Playwright page.\n *\n * The OrbitalVerificationAPI is exposed by @almadar/ui's verificationRegistry.ts\n * on window.__orbitalVerification. This module provides typed access to it.\n *\n * @packageDocumentation\n */\n\nimport type { Page } from 'playwright';\nimport type { RuntimeState } from '../util/types.js';\n\n/** Shape of window.__orbitalVerification (subset used by verify tools) */\nexport interface OrbitalVerificationSnapshot {\n checks: Array<{\n id: string;\n label: string;\n status: 'pass' | 'fail' | 'pending' | 'warn';\n details?: string;\n }>;\n transitions: Array<{\n id: string;\n traitName: string;\n from: string;\n to: string;\n event: string;\n effects: Array<{ type: string; status: string; error?: string }>;\n }>;\n bridge: { connected: boolean } | null;\n summary: {\n totalChecks: number;\n passed: number;\n failed: number;\n warnings: number;\n pending: number;\n };\n}\n\n/**\n * Read the verification snapshot from the browser.\n * Returns null if the API is not available (runtime not loaded).\n */\nexport async function readVerificationSnapshot(\n page: Page\n): Promise<OrbitalVerificationSnapshot | null> {\n return page.evaluate(() => {\n const api = (window as unknown as Record<string, unknown>).__orbitalVerification as\n | { getSnapshot?: () => OrbitalVerificationSnapshot }\n | undefined;\n return api?.getSnapshot?.() ?? null;\n });\n}\n\n/**\n * Read trait states from the verification API.\n * Returns a map of trait name -> current state name.\n */\nexport async function readTraitStates(\n page: Page\n): Promise<Record<string, string>> {\n return page.evaluate(() => {\n const api = (window as unknown as Record<string, unknown>).__orbitalVerification as\n | { getTraitState?: (name: string) => { currentState: string } | null }\n | undefined;\n if (!api?.getTraitState) return {};\n\n // We can't enumerate trait names from the API directly, so\n // use the snapshot's transitions to discover trait names.\n const snapshotApi = api as unknown as { getSnapshot?: () => OrbitalVerificationSnapshot };\n const snapshot = snapshotApi.getSnapshot?.();\n if (!snapshot) return {};\n\n const result: Record<string, string> = {};\n const traitNames = new Set(snapshot.transitions.map((t) => t.traitName));\n for (const name of traitNames) {\n const state = api.getTraitState(name);\n if (state) result[name] = state.currentState;\n }\n return result;\n });\n}\n\n/**\n * Read the event log from the verification API.\n */\nexport async function readEventLog(\n page: Page\n): Promise<Array<{ event: string; listenerCount?: number }>> {\n return page.evaluate(() => {\n const api = (window as unknown as Record<string, unknown>).__orbitalVerification as\n | { eventLog?: Array<{ event: string; listenerCount?: number }> }\n | undefined;\n return api?.eventLog ?? [];\n });\n}\n\n/**\n * Build a RuntimeState from the verification snapshot.\n * Combines trait states, entity data, events, and guard results.\n */\nexport async function readRuntimeState(page: Page): Promise<RuntimeState | null> {\n const snapshot = await readVerificationSnapshot(page);\n if (!snapshot) return null;\n\n const traitStates = await readTraitStates(page);\n\n return {\n traits: Object.fromEntries(\n Object.entries(traitStates).map(([name, currentState]) => [\n name,\n { currentState, context: {} },\n ])\n ),\n entities: {}, // Entity data requires FetchedDataContext inspection\n events: snapshot.transitions.map((t) => t.event),\n guards: Object.fromEntries(\n snapshot.checks\n .filter((c) => c.id.startsWith('guard-'))\n .map((c) => [c.id, c.status === 'pass'])\n ),\n };\n}\n","/**\n * Entity data inspector.\n *\n * Extract entity data visibility from the DOM to verify that\n * mock data actually reached the rendered components.\n *\n * @packageDocumentation\n */\n\nimport type { Page } from 'playwright';\n\n/** Result of an entity data inspection */\nexport interface EntityInspection {\n /** Whether any entity data content was found in the DOM */\n hasContent: boolean;\n /** Number of entity-related DOM elements found */\n elementCount: number;\n /** Whether an \"empty state\" pattern is visible */\n showsEmptyState: boolean;\n /** Text content of the preview area (truncated) */\n previewText: string;\n}\n\n/**\n * Inspect the preview area for entity data content.\n * Checks for empty states, entity cards, table rows, etc.\n *\n * @param page - Playwright page\n * @param previewSelector - CSS selector for the preview area\n */\nexport async function inspectEntityData(\n page: Page,\n previewSelector = '[class*=\"livePreviewBox\"], [class*=\"opPreviewBox\"]'\n): Promise<EntityInspection> {\n return page.evaluate((selector) => {\n const preview = document.querySelector(selector);\n if (!preview) {\n return { hasContent: false, elementCount: 0, showsEmptyState: false, previewText: '' };\n }\n\n const text = (preview.textContent ?? '').trim();\n\n // Count entity-related elements\n const entityElements = preview.querySelectorAll(\n '[data-pattern], [data-entity], table tbody tr, [class*=\"card\"], [class*=\"Card\"]'\n );\n\n // Check for empty state indicators\n const emptyStateTexts = ['empty', 'no data', 'no items', 'nothing to show', 'no results'];\n const showsEmptyState = emptyStateTexts.some((t) =>\n text.toLowerCase().includes(t)\n );\n\n return {\n hasContent: text.length > 0 && entityElements.length > 0,\n elementCount: entityElements.length,\n showsEmptyState,\n previewText: text.substring(0, 500),\n };\n }, previewSelector);\n}\n","/**\n * DOM state extraction and inspection.\n *\n * Check for common error patterns in the rendered DOM:\n * \"Unknown pattern\", error boundaries, empty content, etc.\n *\n * @packageDocumentation\n */\n\nimport type { Page } from 'playwright';\n\n/** DOM inspection result */\nexport interface DOMInspection {\n /** Unknown pattern errors found in the DOM */\n unknownPatterns: string[];\n /** Whether an error boundary fallback is visible */\n hasErrorBoundary: boolean;\n /** Whether \"Objects are not valid as a React child\" appears */\n hasObjectRenderError: boolean;\n /** Whether the preview area is completely empty */\n isEmpty: boolean;\n /** Whether a preview error banner is visible */\n hasPreviewError: boolean;\n /** The preview error text, if any */\n previewErrorText: string;\n}\n\n/**\n * Inspect the DOM for common rendering errors.\n *\n * @param page - Playwright page\n * @param previewSelector - CSS selector for the preview area\n */\nexport async function inspectDOM(\n page: Page,\n previewSelector = '[class*=\"livePreviewBox\"], [class*=\"opPreviewBox\"]'\n): Promise<DOMInspection> {\n return page.evaluate((selector: string) => {\n const result: DOMInspection = {\n unknownPatterns: [],\n hasErrorBoundary: false,\n hasObjectRenderError: false,\n isEmpty: true,\n hasPreviewError: false,\n previewErrorText: '',\n };\n\n // Check for preview error banner\n const previewError = document.querySelector('[class*=\"previewError\"]');\n if (previewError) {\n result.hasPreviewError = true;\n result.previewErrorText = (previewError.textContent ?? '').trim();\n }\n\n // Check preview area content\n const preview = document.querySelector(selector);\n if (!preview) return result;\n\n const text = (preview.textContent ?? '').trim();\n result.isEmpty = text.length === 0;\n\n // Check for \"Unknown pattern: xxx\"\n const unknownMatch = text.match(/Unknown pattern:\\s*\\S+/g);\n if (unknownMatch) {\n result.unknownPatterns = unknownMatch;\n }\n\n // Check for React render error\n if (text.includes('Objects are not valid as a React child')) {\n result.hasObjectRenderError = true;\n }\n\n // Check for error boundary\n const errorBoundary = preview.querySelector('[class*=\"error-boundary\"], [class*=\"ErrorBoundary\"]');\n if (errorBoundary) {\n result.hasErrorBoundary = true;\n }\n\n return result;\n }, previewSelector);\n}\n","/**\n * Console message categorization and reporting.\n *\n * @packageDocumentation\n */\n\nimport type { ConsoleEntry } from '../util/types.js';\n\n/** Categorized console report */\nexport interface ConsoleReport {\n errorCount: number;\n warningCount: number;\n infoCount: number;\n errors: string[];\n warnings: string[];\n /** Unique error patterns (deduplicated by first 80 chars) */\n uniqueErrors: string[];\n}\n\n/**\n * Build a console report from collected entries.\n */\nexport function buildConsoleReport(entries: ConsoleEntry[]): ConsoleReport {\n const errors = entries.filter((e) => e.type === 'error').map((e) => e.text);\n const warnings = entries.filter((e) => e.type === 'warning').map((e) => e.text);\n const infoCount = entries.filter((e) => e.type === 'info').length;\n\n // Deduplicate errors by first 80 characters\n const seen = new Set<string>();\n const uniqueErrors: string[] = [];\n for (const err of errors) {\n const key = err.substring(0, 80);\n if (!seen.has(key)) {\n seen.add(key);\n uniqueErrors.push(err);\n }\n }\n\n return {\n errorCount: errors.length,\n warningCount: warnings.length,\n infoCount,\n errors,\n warnings,\n uniqueErrors,\n };\n}\n","/**\n * JSON report generation.\n *\n * @packageDocumentation\n */\n\nimport { writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { VerifyReport, VerifyResult } from '../util/types.js';\n\n/**\n * Build a JSON report from verification results.\n */\nexport function buildJsonReport(\n url: string,\n mode: 'playground' | 'app',\n results: VerifyResult[]\n): VerifyReport {\n const pass = results.filter((r) => r.status === 'pass').length;\n const error = results.filter((r) => r.status === 'error').length;\n const warning = results.filter((r) => r.status === 'warning').length;\n\n return {\n timestamp: new Date().toISOString(),\n url,\n mode,\n summary: { total: results.length, pass, error, warning },\n results,\n };\n}\n\n/**\n * Write a JSON report to disk.\n */\nexport function writeJsonReport(report: VerifyReport, outputPath: string): void {\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, JSON.stringify(report, null, 2));\n}\n","/**\n * Markdown report generation.\n *\n * @packageDocumentation\n */\n\nimport { writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { VerifyReport } from '../util/types.js';\n\n/**\n * Generate a markdown report from verification results.\n */\nexport function buildMarkdownReport(report: VerifyReport): string {\n const lines: string[] = [];\n\n lines.push(`# Verification Report`);\n lines.push('');\n lines.push(`- **Date**: ${report.timestamp}`);\n lines.push(`- **URL**: ${report.url}`);\n lines.push(`- **Mode**: ${report.mode}`);\n lines.push('');\n\n lines.push(`## Summary`);\n lines.push('');\n lines.push(`| Status | Count |`);\n lines.push(`|--------|-------|`);\n lines.push(`| PASS | ${report.summary.pass} |`);\n lines.push(`| WARN | ${report.summary.warning} |`);\n lines.push(`| ERROR | ${report.summary.error} |`);\n lines.push(`| **Total** | **${report.summary.total}** |`);\n lines.push('');\n\n // Error details\n const errors = report.results.filter((r) => r.status === 'error');\n if (errors.length > 0) {\n lines.push(`## Errors (${errors.length})`);\n lines.push('');\n for (const item of errors) {\n lines.push(`### ${item.name}`);\n for (const e of item.errors) {\n lines.push(`- ${e.substring(0, 200)}`);\n }\n lines.push('');\n }\n }\n\n // Warning details\n const warnings = report.results.filter((r) => r.status === 'warning');\n if (warnings.length > 0) {\n lines.push(`## Warnings (${warnings.length})`);\n lines.push('');\n for (const item of warnings) {\n lines.push(`### ${item.name}`);\n for (const w of item.warnings) {\n lines.push(`- ${w.substring(0, 200)}`);\n }\n lines.push('');\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Write a markdown report to disk.\n */\nexport function writeMarkdownReport(report: VerifyReport, outputPath: string): void {\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, buildMarkdownReport(report));\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@almadar/verify",
3
+ "version": "1.0.0",
4
+ "description": "Shared Playwright verification utilities for Almadar runtime and compiled projects",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "dependencies": {
22
+ "@almadar/core": ">=2.2.0"
23
+ },
24
+ "peerDependencies": {
25
+ "playwright": ">=1.40.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^22.0.0",
29
+ "playwright": "1.50.1",
30
+ "tsup": "^8.0.0",
31
+ "typescript": "^5.4.0",
32
+ "eslint": "10.0.0",
33
+ "@typescript-eslint/parser": "8.56.0",
34
+ "@almadar/eslint-plugin": ">=2.3.0"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/almadar-io/almadar-verify.git"
39
+ },
40
+ "license": "MIT",
41
+ "keywords": [
42
+ "almadar",
43
+ "verify",
44
+ "playwright",
45
+ "testing"
46
+ ],
47
+ "homepage": "https://github.com/almadar-io/almadar-verify#readme",
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "build:watch": "tsup --watch",
51
+ "typecheck": "tsc --noEmit"
52
+ }
53
+ }