@ripplo/testing 0.4.8 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -18
- package/dist/actions.d.ts +2 -0
- package/dist/actions.js +1 -0
- package/dist/assert.d.ts +1 -1
- package/dist/{builder-BMjy83Iy.d.ts → builder-DiVz3t1D.d.ts} +10 -3
- package/dist/{chunk-V6LMXKGL.js → chunk-XO36IU66.js} +32 -38
- package/dist/compiler.d.ts +2 -2
- package/dist/elysia.d.ts +3 -3
- package/dist/elysia.js +16 -23
- package/dist/{engine-DMOkJdjd.d.ts → engine-DVbF4E5A.d.ts} +20 -6
- package/dist/express.d.ts +3 -3
- package/dist/express.js +16 -45
- package/dist/fastify.d.ts +3 -3
- package/dist/fastify.js +16 -24
- package/dist/hono.d.ts +3 -3
- package/dist/hono.js +17 -24
- package/dist/index.d.ts +32 -13
- package/dist/index.js +220 -86
- package/dist/koa.d.ts +3 -3
- package/dist/koa.js +17 -20
- package/dist/lockfile.d.ts +8 -2
- package/dist/lockfile.js +6 -1
- package/dist/nestjs.d.ts +3 -3
- package/dist/nestjs.js +15 -20
- package/dist/nextjs.d.ts +3 -3
- package/dist/nextjs.js +16 -23
- package/dist/{types-16SB7zjP.d.ts → types-BzZrl65Z.d.ts} +9 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,6 +77,17 @@ export const preconditions = { authLoggedIn, dataProject };
|
|
|
77
77
|
|
|
78
78
|
`precondition(name)` → `.description()` → `.requires()` (optional) → `.contract<T>()`. Each field in `T` must be a primitive — `string`, `number`, or `boolean` (run-scoped value).
|
|
79
79
|
|
|
80
|
+
#### Preconditions are create-only
|
|
81
|
+
|
|
82
|
+
Precondition setups must be **additive**: insert new rows, don't `update` or `delete` existing ones. Concurrent runs share a database, and a `WHERE` clause that looks scoped to the current run can match rows that belong to another run in flight. Even when writes are reliably scoped, mutating a row another precondition produced creates ordering dependencies that break under composition.
|
|
83
|
+
|
|
84
|
+
If a test needs a non-default state, the precondition that **creates** the underlying row should accept that state as input — don't seed a default and mutate it from a downstream precondition.
|
|
85
|
+
|
|
86
|
+
Allowed exceptions:
|
|
87
|
+
|
|
88
|
+
- `upsert` on a row whose primary key makes it a 1:1 settings record for the current run (e.g. a per-`(userId, resourceId)` view). Treat it as create-with-default.
|
|
89
|
+
- Teardown may delete — but only data the precondition itself created.
|
|
90
|
+
|
|
80
91
|
### Observers
|
|
81
92
|
|
|
82
93
|
```typescript
|
|
@@ -278,12 +289,21 @@ import { prisma } from "../lib/prisma.js";
|
|
|
278
289
|
export const engine = createEngine(ripplo, {
|
|
279
290
|
preconditions: {
|
|
280
291
|
authLoggedIn: {
|
|
281
|
-
setup
|
|
282
|
-
|
|
283
|
-
|
|
292
|
+
// setup receives a *batch* of items — one per concurrent run that requires this precondition.
|
|
293
|
+
// Issue one bulk write (createMany) and return one result per item, in input order.
|
|
294
|
+
setup: async (items) => {
|
|
295
|
+
const seeds = items.map(({ ctx }) => ({
|
|
296
|
+
id: ctx.uniqueId("user"),
|
|
297
|
+
email: ctx.uniqueEmail(),
|
|
298
|
+
}));
|
|
299
|
+
await prisma.user.createMany({ data: seeds });
|
|
300
|
+
// each item.ctx.setCookie(...) flows back to that run's browser
|
|
301
|
+
return seeds.map(({ id }) => ({ userId: id }));
|
|
284
302
|
},
|
|
285
|
-
teardown: async (
|
|
286
|
-
|
|
303
|
+
teardown: async (items) => {
|
|
304
|
+
await prisma.user.deleteMany({
|
|
305
|
+
where: { id: { in: items.map((it) => it.ctx.data.userId) } },
|
|
306
|
+
});
|
|
287
307
|
},
|
|
288
308
|
},
|
|
289
309
|
dataProject: notImplemented("awaiting prisma seed helper"), // stub for planning
|
|
@@ -302,15 +322,17 @@ export const engine = createEngine(ripplo, {
|
|
|
302
322
|
});
|
|
303
323
|
```
|
|
304
324
|
|
|
325
|
+
`setup` and `teardown` are **batched**: the runtime collects all concurrent runs that need a precondition within a 200ms window and calls the impl once with the full batch. Use `createMany` / `deleteMany` over per-item `create` / `delete` to keep DB load proportional to wall-clock time, not run count. The result array length must equal the input array length and order must be preserved (the engine zips by index back to runs).
|
|
326
|
+
|
|
305
327
|
### Setup context
|
|
306
328
|
|
|
307
|
-
`ctx`
|
|
329
|
+
`item.ctx` available inside each batched setup item:
|
|
308
330
|
|
|
309
331
|
- `ctx.runId` — unique 12-char id for this run
|
|
310
332
|
- `ctx.fixed<T extends string | number | boolean>(value: T)` — static test value (any primitive)
|
|
311
333
|
- `ctx.uniqueId(prefix)` — `ripplo-test-<prefix>-<runId>`
|
|
312
334
|
- `ctx.uniqueEmail()` — `ripplo-test-<runId>@test.ripplo.ai`
|
|
313
|
-
- `ctx.setCookie(name, value, options?)` —
|
|
335
|
+
- `ctx.setCookie(name, value, options?)` — applied to the run's browser context before the test starts
|
|
314
336
|
|
|
315
337
|
Helpers return plain primitives — interpolate, JSON.stringify, or pass through observer params directly. The return type is type-branded, so a hardcoded literal in a `setup` return fails at compile time.
|
|
316
338
|
|
|
@@ -447,10 +469,14 @@ const app = new Elysia().group("/ripplo", (g) => g.use(createElysiaHandler({ ena
|
|
|
447
469
|
For unsupported frameworks. The adapters above are thin wrappers over this API.
|
|
448
470
|
|
|
449
471
|
```ts
|
|
450
|
-
import {
|
|
472
|
+
import {
|
|
473
|
+
readAdapterWebhookSecret,
|
|
474
|
+
toBatchRunResults,
|
|
475
|
+
verifyWebhookSignature,
|
|
476
|
+
} from "@ripplo/testing";
|
|
451
477
|
import { engine } from "./test/engine.js";
|
|
452
478
|
|
|
453
|
-
const webhookSecret =
|
|
479
|
+
const webhookSecret = readAdapterWebhookSecret();
|
|
454
480
|
|
|
455
481
|
async function executePreconditions(req: Request): Promise<Response> {
|
|
456
482
|
const body = await req.text();
|
|
@@ -467,27 +493,31 @@ async function executePreconditions(req: Request): Promise<Response> {
|
|
|
467
493
|
return new Response(JSON.stringify({ error: "Invalid signature" }), { status: 401 });
|
|
468
494
|
}
|
|
469
495
|
|
|
470
|
-
|
|
496
|
+
// Request body is a batch: { batch: [{ runId, preconditions: [...names] }, ...] }
|
|
497
|
+
const { batch } = JSON.parse(body);
|
|
471
498
|
const appUrl = `${req.headers.get("x-forwarded-proto") ?? "http"}://${req.headers.get("host")}`;
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
result.cookies.forEach((c) =>
|
|
476
|
-
headers.append("Set-Cookie", buildSetCookieHeader(serializeCookie(c))),
|
|
499
|
+
const results = await engine.executePreconditions(
|
|
500
|
+
batch.map((b) => ({ runId: b.runId, names: b.preconditions })),
|
|
501
|
+
{ appUrl },
|
|
477
502
|
);
|
|
478
|
-
|
|
503
|
+
|
|
504
|
+
// Response body: { results: [{ runId, ok, cookies, data, executed } | { runId, ok: false, error }] }
|
|
505
|
+
return new Response(JSON.stringify({ results: toBatchRunResults(results) }), {
|
|
506
|
+
headers: { "content-type": "application/json" },
|
|
507
|
+
});
|
|
479
508
|
}
|
|
480
509
|
// teardown-preconditions and execute-observer follow the same verify-then-dispatch pattern.
|
|
510
|
+
// teardown's request shape is { batch: [{ runId, preconditions, data }, ...] }, response { results: [{ runId, ok, error? }] }.
|
|
481
511
|
```
|
|
482
512
|
|
|
483
|
-
You're responsible for: webhook verification (always before invoking the engine), routing the three endpoints,
|
|
513
|
+
You're responsible for: webhook verification (always before invoking the engine), routing the three endpoints, calling `toBatchRunResults` / `toTeardownResults` to shape the response body, and reading the raw body for signature verification before `JSON.parse`. Cookies travel inside the JSON response body — never as `Set-Cookie` headers; the runtime parses them out and applies them to the test browser context.
|
|
484
514
|
|
|
485
515
|
## Security & parallelism
|
|
486
516
|
|
|
487
517
|
- All requests signed via Standard Webhooks (HMAC-SHA256). Headers: `webhook-id`, `webhook-timestamp`, `webhook-signature`. **Always verify before executing.**
|
|
488
518
|
- `ENABLE_RIPPLO_TESTING` gates every adapter. Never expose in production.
|
|
489
519
|
- Use `ctx.uniqueId(prefix)` / `ctx.uniqueEmail()` so parallel runs don't collide.
|
|
490
|
-
- Return created entity IDs in the data contract; teardown deletes only
|
|
520
|
+
- Return created entity IDs in the data contract; teardown deletes only data this run created. Bulk operations are fine — and encouraged — as long as the `WHERE` is scoped to the batch's own ids (`deleteMany({ where: { id: { in: items.map((it) => it.ctx.data.userId) } } })`). Never write a `WHERE` that could match another run's rows.
|
|
491
521
|
|
|
492
522
|
## Lockfile
|
|
493
523
|
|
package/dist/actions.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ declare function navigate(url: string): UnlabeledStep<{
|
|
|
20
20
|
};
|
|
21
21
|
}>;
|
|
22
22
|
interface StepOptions {
|
|
23
|
+
readonly modifier?: "Alt" | "Control" | "Meta" | "Shift";
|
|
23
24
|
readonly uiOnly?: boolean;
|
|
24
25
|
}
|
|
25
26
|
declare function click(locator: AnyLocator, options?: StepOptions): UnlabeledStep<{
|
|
@@ -31,6 +32,7 @@ declare function click(locator: AnyLocator, options?: StepOptions): UnlabeledSte
|
|
|
31
32
|
role: string;
|
|
32
33
|
name?: string | undefined;
|
|
33
34
|
};
|
|
35
|
+
modifier: "Alt" | "Control" | "Meta" | "Shift" | undefined;
|
|
34
36
|
type: "click";
|
|
35
37
|
uiOnly: boolean | undefined;
|
|
36
38
|
}>;
|
package/dist/actions.js
CHANGED
package/dist/assert.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { O as ObserverHandle, a as ObserverInput, P as Primitive, b as ObserverBudgetTier } from './types-
|
|
1
|
+
import { O as ObserverHandle, a as ObserverInput, P as Primitive, b as ObserverBudgetTier } from './types-BzZrl65Z.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';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { P as Primitive, O as ObserverHandle, h as Precondition, m as PreconditionData, j as TestDefinition, f as ObserverDefinition, l as PreconditionDefinition, U as UnimplementedItems, e as ObserverContext, g as ObserverOutcome, S as SetupContext, k as TestValue, T as TeardownContext } from './types-
|
|
1
|
+
import { P as Primitive, O as ObserverHandle, h as Precondition, m as PreconditionData, j as TestDefinition, f as ObserverDefinition, l as PreconditionDefinition, U as UnimplementedItems, e as ObserverContext, g as ObserverOutcome, S as SetupContext, k as TestValue, T as TeardownContext } from './types-BzZrl65Z.js';
|
|
2
2
|
import { ObserverBudget } from '@ripplo/spec';
|
|
3
3
|
import { S as Step } from './step-De52hTLd.js';
|
|
4
4
|
|
|
@@ -90,9 +90,16 @@ interface TestNeedsCoverage {
|
|
|
90
90
|
type PreconditionSetupResult<TData extends Record<string, Primitive>> = {
|
|
91
91
|
readonly [K in keyof TData]: TestValue<TData[K]>;
|
|
92
92
|
};
|
|
93
|
+
interface PreconditionSetupItem<TDeps extends Record<string, Record<string, Primitive>>> {
|
|
94
|
+
readonly ctx: SetupContext;
|
|
95
|
+
readonly deps: TDeps;
|
|
96
|
+
}
|
|
97
|
+
interface PreconditionTeardownItem<TData extends Record<string, Primitive>> {
|
|
98
|
+
readonly ctx: TeardownContext<TData>;
|
|
99
|
+
}
|
|
93
100
|
interface PreconditionImpl<TData extends Record<string, Primitive>, TDeps extends Record<string, Record<string, Primitive>>> {
|
|
94
|
-
readonly setup: (
|
|
95
|
-
readonly teardown: (
|
|
101
|
+
readonly setup: (items: ReadonlyArray<PreconditionSetupItem<TDeps>>) => Promise<ReadonlyArray<PreconditionSetupResult<TData>>>;
|
|
102
|
+
readonly teardown: (items: ReadonlyArray<PreconditionTeardownItem<TData>>) => Promise<void>;
|
|
96
103
|
}
|
|
97
104
|
type ObserverImplFn<TInput extends Record<string, Primitive>> = (ctx: ObserverContext, params: TInput) => Promise<ObserverOutcome>;
|
|
98
105
|
interface TestOptions {
|
|
@@ -10,26 +10,28 @@ function readAdapterWebhookSecret() {
|
|
|
10
10
|
}
|
|
11
11
|
return value;
|
|
12
12
|
}
|
|
13
|
+
var primitiveSchema = z.union([z.string(), z.number(), z.boolean()]);
|
|
14
|
+
var dataSchema = z.record(z.string(), z.record(z.string(), primitiveSchema));
|
|
13
15
|
var batchRequestSchema = z.object({
|
|
14
|
-
|
|
16
|
+
batch: z.array(
|
|
17
|
+
z.object({
|
|
18
|
+
preconditions: z.array(z.string().min(1)),
|
|
19
|
+
runId: z.string().min(1)
|
|
20
|
+
})
|
|
21
|
+
).min(1)
|
|
15
22
|
});
|
|
16
23
|
var teardownRequestSchema = z.object({
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
batch: z.array(
|
|
25
|
+
z.object({
|
|
26
|
+
data: dataSchema,
|
|
27
|
+
preconditions: z.array(z.string().min(1)),
|
|
28
|
+
runId: z.string().min(1)
|
|
29
|
+
})
|
|
30
|
+
).min(1)
|
|
19
31
|
});
|
|
20
32
|
var observerRequestSchema = z.object({
|
|
21
33
|
observer: z.string().min(1).max(200),
|
|
22
|
-
params: z.record(z.string().max(200),
|
|
23
|
-
});
|
|
24
|
-
var observerOutcomeSchema = z.discriminatedUnion("kind", [
|
|
25
|
-
z.object({ kind: z.literal("pass") }),
|
|
26
|
-
z.object({ kind: z.literal("retry"), reason: z.string() }),
|
|
27
|
-
z.object({ kind: z.literal("fail"), reason: z.string() })
|
|
28
|
-
]);
|
|
29
|
-
var observerResponseSchema = z.object({
|
|
30
|
-
error: z.string().optional(),
|
|
31
|
-
outcome: observerOutcomeSchema.optional(),
|
|
32
|
-
success: z.boolean()
|
|
34
|
+
params: z.record(z.string().max(200), primitiveSchema)
|
|
33
35
|
});
|
|
34
36
|
function verifyWebhookSignature(payload, headers, secret) {
|
|
35
37
|
try {
|
|
@@ -50,7 +52,7 @@ function verifyWebhookSignature(payload, headers, secret) {
|
|
|
50
52
|
function serializeCookie(cookie) {
|
|
51
53
|
return {
|
|
52
54
|
domain: cookie.options?.domain,
|
|
53
|
-
expires: cookie.options?.expires
|
|
55
|
+
expires: cookie.options?.expires,
|
|
54
56
|
httpOnly: cookie.options?.httpOnly,
|
|
55
57
|
name: cookie.name,
|
|
56
58
|
path: cookie.options?.path,
|
|
@@ -59,28 +61,19 @@ function serializeCookie(cookie) {
|
|
|
59
61
|
value: cookie.value
|
|
60
62
|
};
|
|
61
63
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
if (cookie.secure === true) {
|
|
77
|
-
parts.push("Secure");
|
|
78
|
-
}
|
|
79
|
-
if (cookie.sameSite != null) {
|
|
80
|
-
const capitalized = cookie.sameSite.charAt(0).toUpperCase() + cookie.sameSite.slice(1);
|
|
81
|
-
parts.push(`SameSite=${capitalized}`);
|
|
82
|
-
}
|
|
83
|
-
return parts.join("; ");
|
|
64
|
+
function toBatchRunResults(results) {
|
|
65
|
+
return results.map(
|
|
66
|
+
(r) => r.success ? {
|
|
67
|
+
cookies: r.cookies.map((c) => serializeCookie(c)),
|
|
68
|
+
data: r.data,
|
|
69
|
+
executed: [...r.executed],
|
|
70
|
+
ok: true,
|
|
71
|
+
runId: r.runId
|
|
72
|
+
} : { error: r.error ?? "unknown error", ok: false, runId: r.runId }
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function toTeardownResults(results) {
|
|
76
|
+
return results.map((r) => ({ error: r.error, ok: r.success, runId: r.runId }));
|
|
84
77
|
}
|
|
85
78
|
|
|
86
79
|
export {
|
|
@@ -90,5 +83,6 @@ export {
|
|
|
90
83
|
observerRequestSchema,
|
|
91
84
|
verifyWebhookSignature,
|
|
92
85
|
serializeCookie,
|
|
93
|
-
|
|
86
|
+
toBatchRunResults,
|
|
87
|
+
toTeardownResults
|
|
94
88
|
};
|
package/dist/compiler.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Observer, Precondition, WorkflowSpec } from '@ripplo/spec';
|
|
2
|
-
import { d as RipploBuilder } from './builder-
|
|
3
|
-
import './types-
|
|
2
|
+
import { d as RipploBuilder } from './builder-DiVz3t1D.js';
|
|
3
|
+
import './types-BzZrl65Z.js';
|
|
4
4
|
import './step-De52hTLd.js';
|
|
5
5
|
|
|
6
6
|
interface CompiledFixture {
|
package/dist/elysia.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
|
-
import { R as RipploEngine } from './engine-
|
|
3
|
-
import './builder-
|
|
4
|
-
import './types-
|
|
2
|
+
import { R as RipploEngine } from './engine-DVbF4E5A.js';
|
|
3
|
+
import './builder-DiVz3t1D.js';
|
|
4
|
+
import './types-BzZrl65Z.js';
|
|
5
5
|
import './step-De52hTLd.js';
|
|
6
6
|
import '@ripplo/spec';
|
|
7
7
|
|
package/dist/elysia.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
batchRequestSchema,
|
|
3
|
-
buildSetCookieHeader,
|
|
4
3
|
observerRequestSchema,
|
|
5
4
|
readAdapterWebhookSecret,
|
|
6
|
-
serializeCookie,
|
|
7
5
|
teardownRequestSchema,
|
|
6
|
+
toBatchRunResults,
|
|
7
|
+
toTeardownResults,
|
|
8
8
|
verifyWebhookSignature
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XO36IU66.js";
|
|
10
10
|
import "./chunk-4MGIQFAJ.js";
|
|
11
11
|
|
|
12
12
|
// src/adapters/elysia.ts
|
|
@@ -27,29 +27,17 @@ function build({ enabled, engine }) {
|
|
|
27
27
|
}
|
|
28
28
|
const parsed = parseWith(gate.body, batchRequestSchema);
|
|
29
29
|
if (parsed == null) {
|
|
30
|
-
return jsonResponse({ error: "Invalid request body"
|
|
30
|
+
return jsonResponse({ error: "Invalid request body" }, 400);
|
|
31
31
|
}
|
|
32
32
|
const host = request.headers.get("host");
|
|
33
33
|
if (host == null || host.length === 0) {
|
|
34
|
-
return jsonResponse({ error: "Missing host header"
|
|
34
|
+
return jsonResponse({ error: "Missing host header" }, 400);
|
|
35
35
|
}
|
|
36
36
|
const proto = request.headers.get("x-forwarded-proto") ?? "http";
|
|
37
37
|
const appUrl = `${proto}://${host}`;
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
headers.append("Set-Cookie", buildSetCookieHeader(serializeCookie(cookie)));
|
|
42
|
-
});
|
|
43
|
-
return new Response(
|
|
44
|
-
JSON.stringify({
|
|
45
|
-
data: result.data,
|
|
46
|
-
error: result.error,
|
|
47
|
-
executed: result.executed,
|
|
48
|
-
runId: result.runId,
|
|
49
|
-
success: result.success
|
|
50
|
-
}),
|
|
51
|
-
{ headers, status: 200 }
|
|
52
|
-
);
|
|
38
|
+
const items = parsed.batch.map((b) => ({ names: b.preconditions, runId: b.runId }));
|
|
39
|
+
const results = await engine.executePreconditions(items, { appUrl });
|
|
40
|
+
return jsonResponse({ results: toBatchRunResults(results) }, 200);
|
|
53
41
|
}).put("/execute-observer", async ({ request }) => {
|
|
54
42
|
const gate = await verifyAndReadBody(request, webhookSecret);
|
|
55
43
|
if ("response" in gate) {
|
|
@@ -71,10 +59,15 @@ function build({ enabled, engine }) {
|
|
|
71
59
|
}
|
|
72
60
|
const parsed = parseWith(gate.body, teardownRequestSchema);
|
|
73
61
|
if (parsed == null) {
|
|
74
|
-
return jsonResponse({ error: "Invalid request body"
|
|
62
|
+
return jsonResponse({ error: "Invalid request body" }, 400);
|
|
75
63
|
}
|
|
76
|
-
|
|
77
|
-
|
|
64
|
+
const items = parsed.batch.map((b) => ({
|
|
65
|
+
data: b.data,
|
|
66
|
+
names: b.preconditions,
|
|
67
|
+
runId: b.runId
|
|
68
|
+
}));
|
|
69
|
+
const results = await engine.teardown(items);
|
|
70
|
+
return jsonResponse({ results: toTeardownResults(results) }, 200);
|
|
78
71
|
});
|
|
79
72
|
}
|
|
80
73
|
async function verifyAndReadBody(req, webhookSecret) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { c as PreconditionRegistry, a as ObserverRegistry, O as ObserverImplFn, P as PreconditionImpl, e as RipploInstance } from './builder-
|
|
2
|
-
import { P as Primitive, g as ObserverOutcome, C as CookieEntry, f as ObserverDefinition, l as PreconditionDefinition, U as UnimplementedItems, O as ObserverHandle, h as Precondition } from './types-
|
|
1
|
+
import { c as PreconditionRegistry, a as ObserverRegistry, O as ObserverImplFn, P as PreconditionImpl, e as RipploInstance } from './builder-DiVz3t1D.js';
|
|
2
|
+
import { P as Primitive, g as ObserverOutcome, C as CookieEntry, f as ObserverDefinition, l as PreconditionDefinition, U as UnimplementedItems, O as ObserverHandle, h as Precondition } from './types-BzZrl65Z.js';
|
|
3
3
|
|
|
4
|
-
interface
|
|
4
|
+
interface EngineRunResult {
|
|
5
5
|
readonly cookies: ReadonlyArray<CookieEntry>;
|
|
6
6
|
readonly data: Record<string, Record<string, Primitive>>;
|
|
7
7
|
readonly error: string | undefined;
|
|
@@ -12,6 +12,20 @@ interface EngineResult {
|
|
|
12
12
|
interface ExecuteBatchOptions {
|
|
13
13
|
readonly appUrl: string | undefined;
|
|
14
14
|
}
|
|
15
|
+
interface ExecuteBatchItem {
|
|
16
|
+
readonly names: ReadonlyArray<string>;
|
|
17
|
+
readonly runId: string;
|
|
18
|
+
}
|
|
19
|
+
interface TeardownBatchItem {
|
|
20
|
+
readonly data: Record<string, Record<string, Primitive>>;
|
|
21
|
+
readonly names: ReadonlyArray<string>;
|
|
22
|
+
readonly runId: string;
|
|
23
|
+
}
|
|
24
|
+
interface TeardownRunResult {
|
|
25
|
+
readonly error: string | undefined;
|
|
26
|
+
readonly runId: string;
|
|
27
|
+
readonly success: boolean;
|
|
28
|
+
}
|
|
15
29
|
interface ObserverExecutionResult {
|
|
16
30
|
readonly error: string | undefined;
|
|
17
31
|
readonly outcome: ObserverOutcome | undefined;
|
|
@@ -19,11 +33,11 @@ interface ObserverExecutionResult {
|
|
|
19
33
|
}
|
|
20
34
|
interface RipploEngine {
|
|
21
35
|
readonly executeObserver: (name: string, params: Record<string, Primitive>) => Promise<ObserverExecutionResult>;
|
|
22
|
-
readonly executePreconditions: (
|
|
36
|
+
readonly executePreconditions: (items: ReadonlyArray<ExecuteBatchItem>, options?: ExecuteBatchOptions) => Promise<ReadonlyArray<EngineRunResult>>;
|
|
23
37
|
readonly getObservers: () => ReadonlyArray<ObserverDefinition>;
|
|
24
38
|
readonly getPreconditions: () => ReadonlyArray<PreconditionDefinition>;
|
|
25
39
|
readonly getUnimplemented: () => UnimplementedItems;
|
|
26
|
-
readonly teardown: (
|
|
40
|
+
readonly teardown: (items: ReadonlyArray<TeardownBatchItem>) => Promise<ReadonlyArray<TeardownRunResult>>;
|
|
27
41
|
}
|
|
28
42
|
declare const NOT_IMPLEMENTED_BRAND: unique symbol;
|
|
29
43
|
interface NotImplemented {
|
|
@@ -43,4 +57,4 @@ interface EngineImpls<P extends PreconditionRegistry, O extends ObserverRegistry
|
|
|
43
57
|
}
|
|
44
58
|
declare function createEngine<P extends PreconditionRegistry, O extends ObserverRegistry>(ripplo: RipploInstance<P, O>, impls: EngineImpls<P, O>): RipploEngine;
|
|
45
59
|
|
|
46
|
-
export { type
|
|
60
|
+
export { type EngineRunResult as E, type NotImplemented as N, type ObserverImplFnFor as O, type PreconditionImplFor as P, type RipploEngine as R, type TeardownRunResult as T, type EngineImpls as a, type ExecuteBatchItem as b, type ExecuteBatchOptions as c, type TeardownBatchItem as d, createEngine as e, notImplemented as n };
|
package/dist/express.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import { R as RipploEngine } from './engine-
|
|
3
|
-
import './builder-
|
|
4
|
-
import './types-
|
|
2
|
+
import { R as RipploEngine } from './engine-DVbF4E5A.js';
|
|
3
|
+
import './builder-DiVz3t1D.js';
|
|
4
|
+
import './types-BzZrl65Z.js';
|
|
5
5
|
import './step-De52hTLd.js';
|
|
6
6
|
import '@ripplo/spec';
|
|
7
7
|
|
package/dist/express.js
CHANGED
|
@@ -2,10 +2,11 @@ import {
|
|
|
2
2
|
batchRequestSchema,
|
|
3
3
|
observerRequestSchema,
|
|
4
4
|
readAdapterWebhookSecret,
|
|
5
|
-
serializeCookie,
|
|
6
5
|
teardownRequestSchema,
|
|
6
|
+
toBatchRunResults,
|
|
7
|
+
toTeardownResults,
|
|
7
8
|
verifyWebhookSignature
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XO36IU66.js";
|
|
9
10
|
import "./chunk-4MGIQFAJ.js";
|
|
10
11
|
|
|
11
12
|
// src/adapters/express.ts
|
|
@@ -29,22 +30,13 @@ function createExpressHandler({ enabled, engine }) {
|
|
|
29
30
|
router.put("/execute-preconditions", (req, res) => {
|
|
30
31
|
const parsed = batchRequestSchema.safeParse(req.body);
|
|
31
32
|
if (!parsed.success) {
|
|
32
|
-
res.status(400).json({ error: "Invalid request body"
|
|
33
|
+
res.status(400).json({ error: "Invalid request body" });
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
36
|
const appUrl = `${req.protocol}://${req.get("host") ?? ""}`;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
res.cookie(s.name, s.value, buildExpressCookieOptions(s));
|
|
40
|
-
});
|
|
41
|
-
res.json({
|
|
42
|
-
data: result.data,
|
|
43
|
-
error: result.error,
|
|
44
|
-
executed: result.executed,
|
|
45
|
-
runId: result.runId,
|
|
46
|
-
success: result.success
|
|
47
|
-
});
|
|
37
|
+
const items = parsed.data.batch.map((b) => ({ names: b.preconditions, runId: b.runId }));
|
|
38
|
+
void engine.executePreconditions(items, { appUrl }).then((results) => {
|
|
39
|
+
res.json({ results: toBatchRunResults(results) });
|
|
48
40
|
});
|
|
49
41
|
});
|
|
50
42
|
router.put("/execute-observer", (req, res) => {
|
|
@@ -54,21 +46,22 @@ function createExpressHandler({ enabled, engine }) {
|
|
|
54
46
|
return;
|
|
55
47
|
}
|
|
56
48
|
void engine.executeObserver(parsed.data.observer, parsed.data.params).then((result) => {
|
|
57
|
-
res.json({
|
|
58
|
-
error: result.error,
|
|
59
|
-
outcome: result.outcome,
|
|
60
|
-
success: result.success
|
|
61
|
-
});
|
|
49
|
+
res.json({ error: result.error, outcome: result.outcome, success: result.success });
|
|
62
50
|
});
|
|
63
51
|
});
|
|
64
52
|
router.put("/teardown-preconditions", (req, res) => {
|
|
65
53
|
const parsed = teardownRequestSchema.safeParse(req.body);
|
|
66
54
|
if (!parsed.success) {
|
|
67
|
-
res.status(400).json({ error: "Invalid request body"
|
|
55
|
+
res.status(400).json({ error: "Invalid request body" });
|
|
68
56
|
return;
|
|
69
57
|
}
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
const items = parsed.data.batch.map((b) => ({
|
|
59
|
+
data: b.data,
|
|
60
|
+
names: b.preconditions,
|
|
61
|
+
runId: b.runId
|
|
62
|
+
}));
|
|
63
|
+
void engine.teardown(items).then((results) => {
|
|
64
|
+
res.json({ results: toTeardownResults(results) });
|
|
72
65
|
});
|
|
73
66
|
});
|
|
74
67
|
return router;
|
|
@@ -89,28 +82,6 @@ function asString(value) {
|
|
|
89
82
|
}
|
|
90
83
|
return void 0;
|
|
91
84
|
}
|
|
92
|
-
function buildExpressCookieOptions(s) {
|
|
93
|
-
const opts = {};
|
|
94
|
-
if (s.domain != null) {
|
|
95
|
-
opts.domain = s.domain;
|
|
96
|
-
}
|
|
97
|
-
if (s.expires != null) {
|
|
98
|
-
opts.expires = s.expires;
|
|
99
|
-
}
|
|
100
|
-
if (s.httpOnly != null) {
|
|
101
|
-
opts.httpOnly = s.httpOnly;
|
|
102
|
-
}
|
|
103
|
-
if (s.path != null) {
|
|
104
|
-
opts.path = s.path;
|
|
105
|
-
}
|
|
106
|
-
if (s.sameSite != null) {
|
|
107
|
-
opts.sameSite = s.sameSite;
|
|
108
|
-
}
|
|
109
|
-
if (s.secure != null) {
|
|
110
|
-
opts.secure = s.secure;
|
|
111
|
-
}
|
|
112
|
-
return opts;
|
|
113
|
-
}
|
|
114
85
|
export {
|
|
115
86
|
createExpressHandler
|
|
116
87
|
};
|
package/dist/fastify.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify';
|
|
2
|
-
import { R as RipploEngine } from './engine-
|
|
3
|
-
import './builder-
|
|
4
|
-
import './types-
|
|
2
|
+
import { R as RipploEngine } from './engine-DVbF4E5A.js';
|
|
3
|
+
import './builder-DiVz3t1D.js';
|
|
4
|
+
import './types-BzZrl65Z.js';
|
|
5
5
|
import './step-De52hTLd.js';
|
|
6
6
|
import '@ripplo/spec';
|
|
7
7
|
|
package/dist/fastify.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
batchRequestSchema,
|
|
3
|
-
buildSetCookieHeader,
|
|
4
3
|
observerRequestSchema,
|
|
5
4
|
readAdapterWebhookSecret,
|
|
6
|
-
serializeCookie,
|
|
7
5
|
teardownRequestSchema,
|
|
6
|
+
toBatchRunResults,
|
|
7
|
+
toTeardownResults,
|
|
8
8
|
verifyWebhookSignature
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XO36IU66.js";
|
|
10
10
|
import "./chunk-4MGIQFAJ.js";
|
|
11
11
|
|
|
12
12
|
// src/adapters/fastify.ts
|
|
@@ -34,21 +34,12 @@ function registerFastifyHandler({
|
|
|
34
34
|
fastify.put("/execute-preconditions", async (req, reply) => {
|
|
35
35
|
const parsed = batchRequestSchema.safeParse(req.body);
|
|
36
36
|
if (!parsed.success) {
|
|
37
|
-
return reply.code(400).send({ error: "Invalid request body"
|
|
37
|
+
return reply.code(400).send({ error: "Invalid request body" });
|
|
38
38
|
}
|
|
39
39
|
const appUrl = `${req.protocol}://${req.hostname}`;
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
reply.header("Set-Cookie", buildSetCookieHeader(s));
|
|
44
|
-
});
|
|
45
|
-
return reply.send({
|
|
46
|
-
data: result.data,
|
|
47
|
-
error: result.error,
|
|
48
|
-
executed: result.executed,
|
|
49
|
-
runId: result.runId,
|
|
50
|
-
success: result.success
|
|
51
|
-
});
|
|
40
|
+
const items = parsed.data.batch.map((b) => ({ names: b.preconditions, runId: b.runId }));
|
|
41
|
+
const results = await engine.executePreconditions(items, { appUrl });
|
|
42
|
+
return reply.send({ results: toBatchRunResults(results) });
|
|
52
43
|
});
|
|
53
44
|
fastify.put("/execute-observer", async (req, reply) => {
|
|
54
45
|
const parsed = observerRequestSchema.safeParse(req.body);
|
|
@@ -56,19 +47,20 @@ function registerFastifyHandler({
|
|
|
56
47
|
return reply.code(400).send({ error: "Invalid request body", success: false });
|
|
57
48
|
}
|
|
58
49
|
const result = await engine.executeObserver(parsed.data.observer, parsed.data.params);
|
|
59
|
-
return reply.send({
|
|
60
|
-
error: result.error,
|
|
61
|
-
outcome: result.outcome,
|
|
62
|
-
success: result.success
|
|
63
|
-
});
|
|
50
|
+
return reply.send({ error: result.error, outcome: result.outcome, success: result.success });
|
|
64
51
|
});
|
|
65
52
|
fastify.put("/teardown-preconditions", async (req, reply) => {
|
|
66
53
|
const parsed = teardownRequestSchema.safeParse(req.body);
|
|
67
54
|
if (!parsed.success) {
|
|
68
|
-
return reply.code(400).send({ error: "Invalid request body"
|
|
55
|
+
return reply.code(400).send({ error: "Invalid request body" });
|
|
69
56
|
}
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
const items = parsed.data.batch.map((b) => ({
|
|
58
|
+
data: b.data,
|
|
59
|
+
names: b.preconditions,
|
|
60
|
+
runId: b.runId
|
|
61
|
+
}));
|
|
62
|
+
const results = await engine.teardown(items);
|
|
63
|
+
return reply.send({ results: toTeardownResults(results) });
|
|
72
64
|
});
|
|
73
65
|
};
|
|
74
66
|
}
|
package/dist/hono.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
import { R as RipploEngine } from './engine-
|
|
3
|
-
import './builder-
|
|
4
|
-
import './types-
|
|
2
|
+
import { R as RipploEngine } from './engine-DVbF4E5A.js';
|
|
3
|
+
import './builder-DiVz3t1D.js';
|
|
4
|
+
import './types-BzZrl65Z.js';
|
|
5
5
|
import './step-De52hTLd.js';
|
|
6
6
|
import '@ripplo/spec';
|
|
7
7
|
|