@ripplo/testing 0.3.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +152 -337
- package/dist/actions.d.ts +20 -0
- package/dist/assert.d.ts +7 -2
- package/dist/assert.js +8 -1
- package/dist/{builder-CNJmzJVe.d.ts → builder-0oT23S0W.d.ts} +61 -4
- package/dist/{chunk-5AGV4KQA.js → chunk-GWSEDWEF.js} +1 -1
- package/dist/{chunk-D7DRM7AG.js → chunk-SBZJDJP4.js} +0 -13
- package/dist/{chunk-TO3T2D2Y.js → chunk-UFHSNW4E.js} +10 -0
- package/dist/compiler.d.ts +2 -4
- package/dist/compiler.js +1 -1
- package/dist/control.d.ts +11 -0
- package/dist/elysia.d.ts +3 -4
- package/dist/elysia.js +3 -2
- package/dist/{engine-U84UJ0sj.d.ts → engine-BdKDGBYw.d.ts} +2 -3
- package/dist/express.d.ts +3 -4
- package/dist/express.js +3 -6
- package/dist/fastify.d.ts +3 -4
- package/dist/fastify.js +3 -5
- package/dist/hono.d.ts +3 -4
- package/dist/hono.js +3 -2
- package/dist/index.d.ts +4 -5
- package/dist/index.js +5 -17
- package/dist/koa.d.ts +3 -4
- package/dist/koa.js +3 -7
- package/dist/locators.d.ts +10 -0
- package/dist/lockfile.d.ts +2 -2
- package/dist/nestjs.d.ts +3 -4
- package/dist/nestjs.js +3 -2
- package/dist/nextjs.d.ts +3 -4
- package/dist/nextjs.js +3 -2
- package/dist/{types-Do4o4Y_c.d.ts → types-B7YljrTz.d.ts} +1 -20
- package/package.json +1 -1
package/dist/actions.d.ts
CHANGED
|
@@ -191,6 +191,11 @@ interface HandleDialogOptions {
|
|
|
191
191
|
readonly promptText: string | undefined;
|
|
192
192
|
readonly uiOnly?: boolean;
|
|
193
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Respond to a native browser dialog (`alert`, `confirm`, `prompt`). Set
|
|
196
|
+
* `uiOnly: true` only when the dialog has no backend side effect — if it
|
|
197
|
+
* triggers a mutation, wire an observer and keep `uiOnly` off.
|
|
198
|
+
*/
|
|
194
199
|
declare function handleDialog({ action, promptText, uiOnly }: HandleDialogOptions): UnlabeledStep<{
|
|
195
200
|
action: "accept" | "dismiss";
|
|
196
201
|
promptText: string | undefined;
|
|
@@ -202,6 +207,12 @@ interface ClipboardOptions {
|
|
|
202
207
|
readonly target: Variable<string> | undefined;
|
|
203
208
|
readonly value: StringOrVariable | undefined;
|
|
204
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Read from or write to the browser clipboard. Use when a flow depends on
|
|
212
|
+
* copy/paste (share links, invite tokens) — pair `write` to seed the
|
|
213
|
+
* clipboard before a paste action, or `read` into a `variable()` to assert
|
|
214
|
+
* what the app copied.
|
|
215
|
+
*/
|
|
205
216
|
declare function clipboard({ action, target, value }: ClipboardOptions): UnlabeledStep<{
|
|
206
217
|
action: "read" | "write";
|
|
207
218
|
type: "clipboard";
|
|
@@ -212,6 +223,10 @@ interface SetPermissionOptions {
|
|
|
212
223
|
readonly permission: string;
|
|
213
224
|
readonly state: "granted" | "prompt";
|
|
214
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Pre-answer a browser permission prompt (geolocation, camera, notifications,
|
|
228
|
+
* clipboard-read, etc.) so the test doesn't stall on the native dialog.
|
|
229
|
+
*/
|
|
215
230
|
declare function setPermission({ permission, state }: SetPermissionOptions): UnlabeledStep<{
|
|
216
231
|
permission: string;
|
|
217
232
|
state: "granted" | "prompt";
|
|
@@ -221,6 +236,11 @@ interface SetViewportOptions {
|
|
|
221
236
|
readonly height: number;
|
|
222
237
|
readonly width: number;
|
|
223
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Resize the browser viewport mid-test. Use to cover responsive breakpoints
|
|
241
|
+
* (mobile nav, collapsed sidebars) within the same flow rather than
|
|
242
|
+
* duplicating the test per size.
|
|
243
|
+
*/
|
|
224
244
|
declare function setViewport({ height, width }: SetViewportOptions): UnlabeledStep<{
|
|
225
245
|
height: number;
|
|
226
246
|
type: "setViewport";
|
package/dist/assert.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { O as ObserverHandle, a as ObserverInput, b as ObserverBudgetTier } from './types-
|
|
1
|
+
import { O as ObserverHandle, a as ObserverInput, b as ObserverBudgetTier } from './types-B7YljrTz.js';
|
|
2
2
|
import { Variable, StaticStringRef, VariableRef } from './control.js';
|
|
3
3
|
import { U as UnlabeledStep } from './step-De52hTLd.js';
|
|
4
4
|
import { CheckLocator, AnyLocator } from './locators.js';
|
|
5
|
-
import 'zod';
|
|
6
5
|
import '@ripplo/spec';
|
|
7
6
|
|
|
8
7
|
type StringOrVariable = string | Variable<string>;
|
|
@@ -67,6 +66,12 @@ declare const assert: {
|
|
|
67
66
|
operator: "equals";
|
|
68
67
|
type: "assertAttribute";
|
|
69
68
|
}>;
|
|
69
|
+
/**
|
|
70
|
+
* Verify a backend side effect by polling an observer. The server-side
|
|
71
|
+
* observer impl returns `ctx.retry(reason)` for transient states (keep
|
|
72
|
+
* polling) and `ctx.fail(reason)` only for invariant violations (stop
|
|
73
|
+
* early). The observer's `.budget()` governs poll interval and timeout.
|
|
74
|
+
*/
|
|
70
75
|
backend<THandle extends ObserverHandle>(observer: THandle, params: ObserverInput<THandle> & Record<string, string>): UnlabeledStep<{
|
|
71
76
|
budget: ObserverBudgetTier;
|
|
72
77
|
observer: string;
|
package/dist/assert.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readObserverBudget,
|
|
3
3
|
readObserverName
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SBZJDJP4.js";
|
|
5
5
|
import {
|
|
6
6
|
toSpecLocator,
|
|
7
7
|
toStringValueRef
|
|
@@ -37,6 +37,13 @@ var assert = {
|
|
|
37
37
|
type: "assertAttribute"
|
|
38
38
|
});
|
|
39
39
|
},
|
|
40
|
+
// eslint-disable-next-line no-comments/disallowComments
|
|
41
|
+
/**
|
|
42
|
+
* Verify a backend side effect by polling an observer. The server-side
|
|
43
|
+
* observer impl returns `ctx.retry(reason)` for transient states (keep
|
|
44
|
+
* polling) and `ctx.fail(reason)` only for invariant violations (stop
|
|
45
|
+
* early). The observer's `.budget()` governs poll interval and timeout.
|
|
46
|
+
*/
|
|
40
47
|
backend(observer, params) {
|
|
41
48
|
return createStep({
|
|
42
49
|
budget: readObserverBudget(observer),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { O as ObserverHandle, P as Precondition,
|
|
1
|
+
import { O as ObserverHandle, P as Precondition, k as PreconditionData, i as TestDefinition, f as ObserverDefinition, j as PreconditionDefinition, U as UnimplementedItems, e as ObserverContext, g as ObserverOutcome, S as SetupContext, l as TestValue, T as TeardownContext } from './types-B7YljrTz.js';
|
|
2
2
|
import { ObserverBudget } from '@ripplo/spec';
|
|
3
3
|
import { S as Step } from './step-De52hTLd.js';
|
|
4
4
|
|
|
@@ -6,6 +6,12 @@ interface ObserverNeedsDescription {
|
|
|
6
6
|
readonly description: (text: string) => ObserverNeedsBudget;
|
|
7
7
|
}
|
|
8
8
|
interface ObserverNeedsBudget {
|
|
9
|
+
/**
|
|
10
|
+
* Polling envelope: interval + total timeout the runner uses when calling
|
|
11
|
+
* `assert.backend()`. Pick by where the side effect lands — fast for
|
|
12
|
+
* same-request DB reads, slow for job-queue settle, async for
|
|
13
|
+
* webhook/email delivery. Too tight = flaky; too loose = slow suite.
|
|
14
|
+
*/
|
|
9
15
|
readonly budget: (tier: ObserverBudget) => ObserverNeedsInput;
|
|
10
16
|
}
|
|
11
17
|
interface ObserverNeedsInput {
|
|
@@ -37,6 +43,13 @@ interface TestNeedsName {
|
|
|
37
43
|
}
|
|
38
44
|
interface TestNeedsRequires {
|
|
39
45
|
readonly description: (text: string) => TestNeedsRequires;
|
|
46
|
+
/**
|
|
47
|
+
* Declare the preconditions this test needs. The object keys become the
|
|
48
|
+
* destructured parameter in `.startsAt` / `.steps`, and each precondition's
|
|
49
|
+
* return data flows through as a typed proxy — interpolate proxy fields
|
|
50
|
+
* directly (e.g. `` `/workspaces/${workspace.id}` ``); never hand-write
|
|
51
|
+
* `{{namespace.key}}` template strings (lint blocks this).
|
|
52
|
+
*/
|
|
40
53
|
readonly requires: <TReqs extends PreconditionRecord>(reqs: TReqs) => TestNeedsOutcome<ResolveDeps<TReqs>>;
|
|
41
54
|
}
|
|
42
55
|
interface TestNeedsOutcome<TVars extends Record<string, Record<string, string>>> {
|
|
@@ -44,13 +57,33 @@ interface TestNeedsOutcome<TVars extends Record<string, Record<string, string>>>
|
|
|
44
57
|
}
|
|
45
58
|
|
|
46
59
|
interface TestNeedsStartsAt<TVars extends Record<string, Record<string, string>>> {
|
|
60
|
+
/**
|
|
61
|
+
* Skip implementation — compiles as a planning stub and is reported in
|
|
62
|
+
* `getUnimplemented()`. The test appears in the lockfile but is not run.
|
|
63
|
+
*/
|
|
47
64
|
readonly notImplemented: () => TestDefinition;
|
|
65
|
+
/**
|
|
66
|
+
* Starting URL for the test, derived from precondition data. The function
|
|
67
|
+
* receives the `.requires()` object destructured as typed proxies; it runs
|
|
68
|
+
* at compile time, not at test-author time.
|
|
69
|
+
*/
|
|
48
70
|
readonly startsAt: (fn: (vars: TVars) => string) => TestNeedsSteps<TVars>;
|
|
49
71
|
}
|
|
50
72
|
interface TestNeedsSteps<TVars extends Record<string, Record<string, string>>> {
|
|
73
|
+
/**
|
|
74
|
+
* Declare the ordered step list. Receives destructured precondition data as
|
|
75
|
+
* typed proxies (same as `.startsAt`). Every step must be labeled with
|
|
76
|
+
* `.as("…")` — lint enforces it.
|
|
77
|
+
*/
|
|
51
78
|
readonly steps: (fn: (vars: TVars) => ReadonlyArray<Step>) => TestNeedsCoverage;
|
|
52
79
|
}
|
|
53
80
|
interface TestNeedsCoverage {
|
|
81
|
+
/**
|
|
82
|
+
* Finalize the test and declare which mutation/AST branches it covers.
|
|
83
|
+
* Coverage IDs feed LLM-driven test scoping on diffs so only relevant tests
|
|
84
|
+
* run for a change. `.coverage()` (or `.notImplemented()`) is the only way
|
|
85
|
+
* to terminate the chain into a runnable `TestDefinition`.
|
|
86
|
+
*/
|
|
54
87
|
readonly coverage: (...ids: ReadonlyArray<CoverageStatementId>) => TestDefinition;
|
|
55
88
|
}
|
|
56
89
|
|
|
@@ -73,20 +106,44 @@ interface RipploRegistries<P extends PreconditionRegistry, O extends ObserverReg
|
|
|
73
106
|
readonly tests: ReadonlyArray<TestDefinition>;
|
|
74
107
|
}
|
|
75
108
|
interface RipploInstance<P extends PreconditionRegistry = PreconditionRegistry, O extends ObserverRegistry = ObserverRegistry> {
|
|
76
|
-
readonly config: DslConfig;
|
|
77
109
|
readonly observers: O;
|
|
78
110
|
readonly preconditions: P;
|
|
79
111
|
readonly tests: ReadonlyArray<TestDefinition>;
|
|
80
|
-
readonly getConfig: () => DslConfig;
|
|
81
112
|
readonly getObservers: () => ReadonlyArray<ObserverDefinition>;
|
|
82
113
|
readonly getPreconditions: () => ReadonlyArray<PreconditionDefinition>;
|
|
83
114
|
readonly getTests: () => ReadonlyArray<TestDefinition>;
|
|
84
115
|
readonly getUnimplemented: () => UnimplementedItems;
|
|
85
116
|
}
|
|
86
117
|
type RipploBuilder = RipploInstance;
|
|
118
|
+
/**
|
|
119
|
+
* Declare a precondition — a named, reusable piece of test data setup. Returns
|
|
120
|
+
* a pure handle (no side effects at definition time); the setup/teardown impl
|
|
121
|
+
* is wired once in `createEngine`. Depend on other preconditions via
|
|
122
|
+
* `.requires()` so the DAG can resolve independents in parallel, giving every
|
|
123
|
+
* test a clean, isolated slate.
|
|
124
|
+
*/
|
|
87
125
|
declare function precondition(name: string): PreconditionNeedsSetup;
|
|
126
|
+
/**
|
|
127
|
+
* Declare a backend assertion — checked from tests via `assert.backend()`.
|
|
128
|
+
* The impl runs on your server (where DB/queue access lives) and is polled
|
|
129
|
+
* until pass/fail; `.budget()` picks the polling envelope. Use this instead
|
|
130
|
+
* of sleeps when verifying async side effects (jobs, webhooks, emails, DB rows).
|
|
131
|
+
*/
|
|
88
132
|
declare function observer(name: string): ObserverNeedsDescription;
|
|
133
|
+
/**
|
|
134
|
+
* Start a test definition. `id` is a stable kebab-case key persisted in the
|
|
135
|
+
* lockfile and used for run history — changing it renames the test and loses
|
|
136
|
+
* history. Set `options.uiOnly` only for genuinely read-only flows (no
|
|
137
|
+
* mutations); do not use it to silence observer lint on flows that mutate
|
|
138
|
+
* state — wire the observer instead.
|
|
139
|
+
*/
|
|
89
140
|
declare function test(id: string, options?: TestOptions): TestNeedsName;
|
|
90
|
-
|
|
141
|
+
/**
|
|
142
|
+
* The single registration point for the DSL graph. Collects the three
|
|
143
|
+
* registries into an instance consumed both by `ripplo compile` (to write
|
|
144
|
+
* `.ripplo/ripplo.lock`) and by `createEngine` on the server, where
|
|
145
|
+
* TypeScript exhaustiveness-checks that every handle has exactly one impl.
|
|
146
|
+
*/
|
|
147
|
+
declare function createRipplo<P extends PreconditionRegistry, O extends ObserverRegistry>(registries: RipploRegistries<P, O>): RipploInstance<P, O>;
|
|
91
148
|
|
|
92
149
|
export { type CoverageRegistry as C, type ObserverImplFn as O, type PreconditionImpl as P, type ResolveDeps as R, type ObserverRegistry as a, type PreconditionRecord as b, type PreconditionRegistry as c, type RipploBuilder as d, type RipploInstance as e, type RipploRegistries as f, createRipplo as g, observer as o, precondition as p, test as t };
|
|
@@ -25,7 +25,7 @@ function compile(ripplo) {
|
|
|
25
25
|
};
|
|
26
26
|
});
|
|
27
27
|
const tests = testDefs.map((def) => compileTest(def, preconditionDefs));
|
|
28
|
-
return {
|
|
28
|
+
return { observers, preconditions, tests };
|
|
29
29
|
}
|
|
30
30
|
function validateUniqueIds(defs) {
|
|
31
31
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// src/types.ts
|
|
2
|
-
import { z } from "zod";
|
|
3
2
|
var DEFAULT_WATCH_PATHS = [
|
|
4
3
|
"**/src/**",
|
|
5
4
|
"**/app/**",
|
|
@@ -60,17 +59,6 @@ var DEFAULT_IGNORE_PATHS = [
|
|
|
60
59
|
"**/prisma/migrations/**",
|
|
61
60
|
"**/scripts/**"
|
|
62
61
|
];
|
|
63
|
-
var dslConfigSchema = z.object({
|
|
64
|
-
appUrl: z.string(),
|
|
65
|
-
engineUrl: z.string(),
|
|
66
|
-
ignorePaths: z.array(z.string()).optional(),
|
|
67
|
-
projectId: z.string(),
|
|
68
|
-
watchPaths: z.array(z.string()).optional(),
|
|
69
|
-
webhookSecret: z.string()
|
|
70
|
-
});
|
|
71
|
-
var userDslConfigSchema = dslConfigSchema.extend({
|
|
72
|
-
webhookSecret: z.string().optional()
|
|
73
|
-
});
|
|
74
62
|
function readTestValue(value) {
|
|
75
63
|
return value.value;
|
|
76
64
|
}
|
|
@@ -109,7 +97,6 @@ function makeObserverHandle({
|
|
|
109
97
|
export {
|
|
110
98
|
DEFAULT_WATCH_PATHS,
|
|
111
99
|
DEFAULT_IGNORE_PATHS,
|
|
112
|
-
userDslConfigSchema,
|
|
113
100
|
readTestValue,
|
|
114
101
|
createTestValue,
|
|
115
102
|
readPreconditionName,
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
// src/adapters/shared.ts
|
|
2
2
|
import { Webhook, WebhookVerificationError } from "standardwebhooks";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
function readAdapterWebhookSecret() {
|
|
5
|
+
const value = process.env.RIPPLO_WEBHOOK_SECRET;
|
|
6
|
+
if (value == null || value.length === 0) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
"ripplo: RIPPLO_WEBHOOK_SECRET is not set. The adapter needs it to verify precondition calls. Run `ripplo init` to generate one, or unset `enabled` to disable the adapter."
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
4
13
|
var batchRequestSchema = z.object({
|
|
5
14
|
preconditions: z.array(z.string().min(1))
|
|
6
15
|
});
|
|
@@ -75,6 +84,7 @@ function buildSetCookieHeader(cookie) {
|
|
|
75
84
|
}
|
|
76
85
|
|
|
77
86
|
export {
|
|
87
|
+
readAdapterWebhookSecret,
|
|
78
88
|
batchRequestSchema,
|
|
79
89
|
teardownRequestSchema,
|
|
80
90
|
observerRequestSchema,
|
package/dist/compiler.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { Observer, Precondition, WorkflowSpec } from '@ripplo/spec';
|
|
2
|
-
import { d as RipploBuilder } from './builder-
|
|
3
|
-
import
|
|
2
|
+
import { d as RipploBuilder } from './builder-0oT23S0W.js';
|
|
3
|
+
import './types-B7YljrTz.js';
|
|
4
4
|
import './step-De52hTLd.js';
|
|
5
|
-
import 'zod';
|
|
6
5
|
|
|
7
6
|
interface CompileResult {
|
|
8
|
-
readonly config: DslConfig;
|
|
9
7
|
readonly observers: Record<string, Observer>;
|
|
10
8
|
readonly preconditions: Record<string, Precondition>;
|
|
11
9
|
readonly tests: ReadonlyArray<CompiledTest>;
|
package/dist/compiler.js
CHANGED
package/dist/control.d.ts
CHANGED
|
@@ -6,6 +6,12 @@ declare const VARIABLE_INTERNAL: unique symbol;
|
|
|
6
6
|
interface Variable<_TName extends string> {
|
|
7
7
|
readonly [VARIABLE_INTERNAL]: _TName;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Declare a runtime placeholder, bound later by `extract()` and consumed by
|
|
11
|
+
* actions/assertions. Use for values produced mid-test (e.g. an ID rendered
|
|
12
|
+
* into the DOM). Precondition data is already typed and injected — reach for
|
|
13
|
+
* `variable` only when the value isn't known until the test runs.
|
|
14
|
+
*/
|
|
9
15
|
declare function variable<TName extends string>(name: TName): Variable<TName>;
|
|
10
16
|
declare function readVariable(v: Variable<string>): string;
|
|
11
17
|
declare function isVariable(v: unknown): v is Variable<string>;
|
|
@@ -18,6 +24,11 @@ interface VariableRef {
|
|
|
18
24
|
readonly type: "variable";
|
|
19
25
|
}
|
|
20
26
|
declare function toStringValueRef(value: string | Variable<string>): StaticStringRef | VariableRef;
|
|
27
|
+
/**
|
|
28
|
+
* Capture the text of the matched element into `target` (a `variable()`).
|
|
29
|
+
* Pairs by name — downstream steps referencing the same `variable` see the
|
|
30
|
+
* extracted value.
|
|
31
|
+
*/
|
|
21
32
|
declare function extract(locator: AnyLocator, target: Variable<string>): UnlabeledStep<{
|
|
22
33
|
locator: {
|
|
23
34
|
by: "testId";
|
package/dist/elysia.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
|
-
import { R as RipploEngine } from './engine-
|
|
3
|
-
import './builder-
|
|
4
|
-
import './types-
|
|
5
|
-
import 'zod';
|
|
2
|
+
import { R as RipploEngine } from './engine-BdKDGBYw.js';
|
|
3
|
+
import './builder-0oT23S0W.js';
|
|
4
|
+
import './types-B7YljrTz.js';
|
|
6
5
|
import './step-De52hTLd.js';
|
|
7
6
|
import '@ripplo/spec';
|
|
8
7
|
|
package/dist/elysia.js
CHANGED
|
@@ -2,10 +2,11 @@ import {
|
|
|
2
2
|
batchRequestSchema,
|
|
3
3
|
buildSetCookieHeader,
|
|
4
4
|
observerRequestSchema,
|
|
5
|
+
readAdapterWebhookSecret,
|
|
5
6
|
serializeCookie,
|
|
6
7
|
teardownRequestSchema,
|
|
7
8
|
verifyWebhookSignature
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UFHSNW4E.js";
|
|
9
10
|
import "./chunk-4MGIQFAJ.js";
|
|
10
11
|
|
|
11
12
|
// src/adapters/elysia.ts
|
|
@@ -18,7 +19,7 @@ function build({ enabled, engine }) {
|
|
|
18
19
|
if (!enabled) {
|
|
19
20
|
return app.put("/execute-preconditions", () => notFoundResponse()).put("/execute-observer", () => notFoundResponse()).put("/teardown-preconditions", () => notFoundResponse());
|
|
20
21
|
}
|
|
21
|
-
const webhookSecret =
|
|
22
|
+
const webhookSecret = readAdapterWebhookSecret();
|
|
22
23
|
return app.put("/execute-preconditions", async ({ request }) => {
|
|
23
24
|
const gate = await verifyAndReadBody(request, webhookSecret);
|
|
24
25
|
if ("response" in gate) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as PreconditionRegistry, a as ObserverRegistry, O as ObserverImplFn, P as PreconditionImpl, e as RipploInstance } from './builder-
|
|
2
|
-
import {
|
|
1
|
+
import { c as PreconditionRegistry, a as ObserverRegistry, O as ObserverImplFn, P as PreconditionImpl, e as RipploInstance } from './builder-0oT23S0W.js';
|
|
2
|
+
import { g as ObserverOutcome, C as CookieEntry, f as ObserverDefinition, j as PreconditionDefinition, U as UnimplementedItems, O as ObserverHandle, P as Precondition } from './types-B7YljrTz.js';
|
|
3
3
|
|
|
4
4
|
interface EngineResult {
|
|
5
5
|
readonly cookies: ReadonlyArray<CookieEntry>;
|
|
@@ -20,7 +20,6 @@ interface ObserverExecutionResult {
|
|
|
20
20
|
interface RipploEngine {
|
|
21
21
|
readonly executeObserver: (name: string, params: Record<string, string>) => Promise<ObserverExecutionResult>;
|
|
22
22
|
readonly executePreconditions: (names: ReadonlyArray<string>, options?: ExecuteBatchOptions) => Promise<EngineResult>;
|
|
23
|
-
readonly getConfig: () => DslConfig;
|
|
24
23
|
readonly getObservers: () => ReadonlyArray<ObserverDefinition>;
|
|
25
24
|
readonly getPreconditions: () => ReadonlyArray<PreconditionDefinition>;
|
|
26
25
|
readonly getUnimplemented: () => UnimplementedItems;
|
package/dist/express.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import { R as RipploEngine } from './engine-
|
|
3
|
-
import './builder-
|
|
4
|
-
import './types-
|
|
5
|
-
import 'zod';
|
|
2
|
+
import { R as RipploEngine } from './engine-BdKDGBYw.js';
|
|
3
|
+
import './builder-0oT23S0W.js';
|
|
4
|
+
import './types-B7YljrTz.js';
|
|
6
5
|
import './step-De52hTLd.js';
|
|
7
6
|
import '@ripplo/spec';
|
|
8
7
|
|
package/dist/express.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
batchRequestSchema,
|
|
3
3
|
observerRequestSchema,
|
|
4
|
+
readAdapterWebhookSecret,
|
|
4
5
|
serializeCookie,
|
|
5
6
|
teardownRequestSchema,
|
|
6
7
|
verifyWebhookSignature
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-UFHSNW4E.js";
|
|
8
9
|
import "./chunk-4MGIQFAJ.js";
|
|
9
10
|
|
|
10
11
|
// src/adapters/express.ts
|
|
@@ -14,13 +15,9 @@ function createExpressHandler({ enabled, engine }) {
|
|
|
14
15
|
if (!enabled) {
|
|
15
16
|
return router;
|
|
16
17
|
}
|
|
17
|
-
const webhookSecret =
|
|
18
|
+
const webhookSecret = readAdapterWebhookSecret();
|
|
18
19
|
router.use(json());
|
|
19
20
|
router.use((req, res, next) => {
|
|
20
|
-
if (webhookSecret.length === 0) {
|
|
21
|
-
res.status(403).json({ error: "Webhook secret not configured" });
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
21
|
const payload = JSON.stringify(req.body);
|
|
25
22
|
const headers = extractWebhookHeaders(req);
|
|
26
23
|
if (!verifyWebhookSignature(payload, headers, webhookSecret)) {
|
package/dist/fastify.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify';
|
|
2
|
-
import { R as RipploEngine } from './engine-
|
|
3
|
-
import './builder-
|
|
4
|
-
import './types-
|
|
5
|
-
import 'zod';
|
|
2
|
+
import { R as RipploEngine } from './engine-BdKDGBYw.js';
|
|
3
|
+
import './builder-0oT23S0W.js';
|
|
4
|
+
import './types-B7YljrTz.js';
|
|
6
5
|
import './step-De52hTLd.js';
|
|
7
6
|
import '@ripplo/spec';
|
|
8
7
|
|
package/dist/fastify.js
CHANGED
|
@@ -2,10 +2,11 @@ import {
|
|
|
2
2
|
batchRequestSchema,
|
|
3
3
|
buildSetCookieHeader,
|
|
4
4
|
observerRequestSchema,
|
|
5
|
+
readAdapterWebhookSecret,
|
|
5
6
|
serializeCookie,
|
|
6
7
|
teardownRequestSchema,
|
|
7
8
|
verifyWebhookSignature
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UFHSNW4E.js";
|
|
9
10
|
import "./chunk-4MGIQFAJ.js";
|
|
10
11
|
|
|
11
12
|
// src/adapters/fastify.ts
|
|
@@ -17,12 +18,9 @@ function registerFastifyHandler({
|
|
|
17
18
|
return async () => {
|
|
18
19
|
};
|
|
19
20
|
}
|
|
20
|
-
const webhookSecret =
|
|
21
|
+
const webhookSecret = readAdapterWebhookSecret();
|
|
21
22
|
return async (fastify) => {
|
|
22
23
|
fastify.addHook("preHandler", async (req, reply) => {
|
|
23
|
-
if (webhookSecret.length === 0) {
|
|
24
|
-
return reply.code(403).send({ error: "Webhook secret not configured" });
|
|
25
|
-
}
|
|
26
24
|
const payload = JSON.stringify(req.body);
|
|
27
25
|
const headers = {
|
|
28
26
|
"webhook-id": extractHeader(req, "webhook-id"),
|
package/dist/hono.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
import { R as RipploEngine } from './engine-
|
|
3
|
-
import './builder-
|
|
4
|
-
import './types-
|
|
5
|
-
import 'zod';
|
|
2
|
+
import { R as RipploEngine } from './engine-BdKDGBYw.js';
|
|
3
|
+
import './builder-0oT23S0W.js';
|
|
4
|
+
import './types-B7YljrTz.js';
|
|
6
5
|
import './step-De52hTLd.js';
|
|
7
6
|
import '@ripplo/spec';
|
|
8
7
|
|
package/dist/hono.js
CHANGED
|
@@ -2,10 +2,11 @@ import {
|
|
|
2
2
|
batchRequestSchema,
|
|
3
3
|
buildSetCookieHeader,
|
|
4
4
|
observerRequestSchema,
|
|
5
|
+
readAdapterWebhookSecret,
|
|
5
6
|
serializeCookie,
|
|
6
7
|
teardownRequestSchema,
|
|
7
8
|
verifyWebhookSignature
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UFHSNW4E.js";
|
|
9
10
|
import "./chunk-4MGIQFAJ.js";
|
|
10
11
|
|
|
11
12
|
// src/adapters/hono.ts
|
|
@@ -15,7 +16,7 @@ function createHonoHandler({ enabled, engine }) {
|
|
|
15
16
|
if (!enabled) {
|
|
16
17
|
return app;
|
|
17
18
|
}
|
|
18
|
-
const webhookSecret =
|
|
19
|
+
const webhookSecret = readAdapterWebhookSecret();
|
|
19
20
|
app.use("*", createWebhookMiddleware(webhookSecret));
|
|
20
21
|
app.put("/execute-preconditions", async (c) => {
|
|
21
22
|
const body = tryParseJson(c.get("rawBody"));
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
export { C as CoverageRegistry, O as ObserverImplFn, a as ObserverRegistry, P as PreconditionImpl, b as PreconditionRecord, c as PreconditionRegistry, R as ResolveDeps, d as RipploBuilder, e as RipploInstance, f as RipploRegistries, g as createRipplo, o as observer, p as precondition, t as test } from './builder-
|
|
1
|
+
export { C as CoverageRegistry, O as ObserverImplFn, a as ObserverRegistry, P as PreconditionImpl, b as PreconditionRecord, c as PreconditionRegistry, R as ResolveDeps, d as RipploBuilder, e as RipploInstance, f as RipploRegistries, g as createRipplo, o as observer, p as precondition, t as test } from './builder-0oT23S0W.js';
|
|
2
2
|
import { CompileResult } from './compiler.js';
|
|
3
3
|
export { CompiledTest, compile } from './compiler.js';
|
|
4
|
-
export { E as EngineImpls, a as EngineResult, b as ExecuteBatchOptions, N as NotImplemented, O as ObserverImplFnFor, P as PreconditionImplFor, R as RipploEngine, c as createEngine, n as notImplemented } from './engine-
|
|
5
|
-
import { C as CookieEntry } from './types-
|
|
6
|
-
export { c as CookieOptions, D as DEFAULT_IGNORE_PATHS, d as DEFAULT_WATCH_PATHS, e as
|
|
4
|
+
export { E as EngineImpls, a as EngineResult, b as ExecuteBatchOptions, N as NotImplemented, O as ObserverImplFnFor, P as PreconditionImplFor, R as RipploEngine, c as createEngine, n as notImplemented } from './engine-BdKDGBYw.js';
|
|
5
|
+
import { C as CookieEntry } from './types-B7YljrTz.js';
|
|
6
|
+
export { c as CookieOptions, D as DEFAULT_IGNORE_PATHS, d as DEFAULT_WATCH_PATHS, e as ObserverContext, f as ObserverDefinition, O as ObserverHandle, a as ObserverInput, g as ObserverOutcome, P as Precondition, h as PreconditionDeps, S as SetupContext, T as TeardownContext, i as TestDefinition } from './types-B7YljrTz.js';
|
|
7
7
|
export { D as DslNodeInput } from './step-De52hTLd.js';
|
|
8
8
|
import '@ripplo/spec';
|
|
9
|
-
import 'zod';
|
|
10
9
|
|
|
11
10
|
interface LintDiagnostic {
|
|
12
11
|
readonly message: string;
|
package/dist/index.js
CHANGED
|
@@ -10,18 +10,17 @@ import {
|
|
|
10
10
|
readPreconditionDependsOn,
|
|
11
11
|
readPreconditionDescription,
|
|
12
12
|
readPreconditionName,
|
|
13
|
-
readTestValue
|
|
14
|
-
|
|
15
|
-
} from "./chunk-D7DRM7AG.js";
|
|
13
|
+
readTestValue
|
|
14
|
+
} from "./chunk-SBZJDJP4.js";
|
|
16
15
|
import {
|
|
17
16
|
compile
|
|
18
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-GWSEDWEF.js";
|
|
19
18
|
import "./chunk-MGATMMCZ.js";
|
|
20
19
|
import {
|
|
21
20
|
buildSetCookieHeader,
|
|
22
21
|
serializeCookie,
|
|
23
22
|
verifyWebhookSignature
|
|
24
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-UFHSNW4E.js";
|
|
25
24
|
import "./chunk-4MGIQFAJ.js";
|
|
26
25
|
|
|
27
26
|
// src/observer.ts
|
|
@@ -63,26 +62,16 @@ function test(id, options) {
|
|
|
63
62
|
validateTestId(id);
|
|
64
63
|
return buildTestName(id, options?.uiOnly);
|
|
65
64
|
}
|
|
66
|
-
function createRipplo(
|
|
67
|
-
const parsed = userDslConfigSchema.parse(rawConfig);
|
|
68
|
-
const webhookSecret = parsed.webhookSecret ?? process.env["RIPPLO_WEBHOOK_SECRET"] ?? "";
|
|
69
|
-
if (webhookSecret.length === 0) {
|
|
70
|
-
throw new Error(
|
|
71
|
-
"RIPPLO_WEBHOOK_SECRET is required. Set it in .ripplo/.env or pass webhookSecret to createRipplo()."
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
const config = { ...parsed, webhookSecret };
|
|
65
|
+
function createRipplo(registries) {
|
|
75
66
|
const { observers, preconditions, tests } = registries;
|
|
76
67
|
validateUniqueNames(preconditions, observers, tests);
|
|
77
68
|
const preconditionDefs = Object.values(preconditions).map((p) => stubPreconditionDef(p));
|
|
78
69
|
const observerDefs = Object.values(observers).map((o) => stubObserverDef(o));
|
|
79
70
|
const testDefs = [...tests];
|
|
80
71
|
return {
|
|
81
|
-
config,
|
|
82
72
|
observers,
|
|
83
73
|
preconditions,
|
|
84
74
|
tests: testDefs,
|
|
85
|
-
getConfig: () => config,
|
|
86
75
|
getObservers: () => observerDefs,
|
|
87
76
|
getPreconditions: () => preconditionDefs,
|
|
88
77
|
getTests: () => testDefs,
|
|
@@ -777,7 +766,6 @@ function createEngine(ripplo, impls) {
|
|
|
777
766
|
return {
|
|
778
767
|
executeObserver: (name, params) => executeObserver(observersByName, name, params),
|
|
779
768
|
executePreconditions: (names, options) => executePreconditions({ defsByName: preconditionsByName, names, options }),
|
|
780
|
-
getConfig: () => ripplo.getConfig(),
|
|
781
769
|
getObservers: () => observerDefs,
|
|
782
770
|
getPreconditions: () => preconditionDefs,
|
|
783
771
|
getUnimplemented: () => ({
|
package/dist/koa.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Middleware } from 'koa';
|
|
2
|
-
import { R as RipploEngine } from './engine-
|
|
3
|
-
import './builder-
|
|
4
|
-
import './types-
|
|
5
|
-
import 'zod';
|
|
2
|
+
import { R as RipploEngine } from './engine-BdKDGBYw.js';
|
|
3
|
+
import './builder-0oT23S0W.js';
|
|
4
|
+
import './types-B7YljrTz.js';
|
|
6
5
|
import './step-De52hTLd.js';
|
|
7
6
|
import '@ripplo/spec';
|
|
8
7
|
|
package/dist/koa.js
CHANGED
|
@@ -2,10 +2,11 @@ import {
|
|
|
2
2
|
batchRequestSchema,
|
|
3
3
|
buildSetCookieHeader,
|
|
4
4
|
observerRequestSchema,
|
|
5
|
+
readAdapterWebhookSecret,
|
|
5
6
|
serializeCookie,
|
|
6
7
|
teardownRequestSchema,
|
|
7
8
|
verifyWebhookSignature
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UFHSNW4E.js";
|
|
9
10
|
import "./chunk-4MGIQFAJ.js";
|
|
10
11
|
|
|
11
12
|
// src/adapters/koa.ts
|
|
@@ -15,7 +16,7 @@ function createKoaHandler({ enabled, engine }) {
|
|
|
15
16
|
await next();
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
|
-
const webhookSecret =
|
|
19
|
+
const webhookSecret = readAdapterWebhookSecret();
|
|
19
20
|
return async (ctx, next) => {
|
|
20
21
|
if (ctx.method !== "PUT") {
|
|
21
22
|
await next();
|
|
@@ -26,11 +27,6 @@ function createKoaHandler({ enabled, engine }) {
|
|
|
26
27
|
await next();
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
|
-
if (webhookSecret.length === 0) {
|
|
30
|
-
ctx.status = 403;
|
|
31
|
-
ctx.body = { error: "Webhook secret not configured" };
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
30
|
const body = await readBody(ctx);
|
|
35
31
|
const headers = {
|
|
36
32
|
"webhook-id": headerString(ctx.get("webhook-id")),
|
package/dist/locators.d.ts
CHANGED
|
@@ -24,7 +24,17 @@ type AnyLocator = Locator<"role" | "testId">;
|
|
|
24
24
|
type InputLocator = Locator<"testId"> | RoleLocator<"combobox" | "searchbox" | "spinbutton" | "textbox">;
|
|
25
25
|
type SelectLocator = Locator<"testId"> | RoleLocator<"combobox" | "listbox">;
|
|
26
26
|
type CheckLocator = Locator<"testId"> | RoleLocator<"checkbox" | "switch">;
|
|
27
|
+
/**
|
|
28
|
+
* Preferred locator: targets an element by its ARIA role and accessible name.
|
|
29
|
+
* Survives DOM churn and doubles as an accessibility check — if a role query
|
|
30
|
+
* can't find your element, real users likely can't either.
|
|
31
|
+
*/
|
|
27
32
|
declare function role<TRole extends AriaRole>(ariaRole: TRole, name?: string): RoleLocator<TRole>;
|
|
33
|
+
/**
|
|
34
|
+
* Escape hatch for elements without a semantic role (decorative wrappers,
|
|
35
|
+
* non-interactive panels). Prefer `role()` when the element has one; a
|
|
36
|
+
* test-id locator has no accessibility signal.
|
|
37
|
+
*/
|
|
28
38
|
declare function testId(id: string): Locator<"testId">;
|
|
29
39
|
|
|
30
40
|
export { type AnyLocator, type AriaRole, type CheckLocator, type InputLocator, type Locator, type LocatorSpec, type RoleLocator, type SelectLocator, readLocator, role, testId };
|
package/dist/lockfile.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Codec } from '@ripplo/spec';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { CompileResult } from './compiler.js';
|
|
4
|
-
import './builder-
|
|
5
|
-
import './types-
|
|
4
|
+
import './builder-0oT23S0W.js';
|
|
5
|
+
import './types-B7YljrTz.js';
|
|
6
6
|
import './step-De52hTLd.js';
|
|
7
7
|
|
|
8
8
|
declare const LOCKFILE_RELATIVE_PATH = ".ripplo/ripplo.lock";
|