@ripplo/testing 0.3.4 → 0.3.6
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 +80 -1
- package/dist/actions.js +3 -2
- package/dist/assert.js +7 -6
- package/dist/chunk-4MGIQFAJ.js +16 -0
- package/dist/{chunk-55HIO5LW.js → chunk-XBYG5NBY.js} +22 -22
- package/dist/compiler.js +1 -0
- package/dist/control.js +3 -2
- package/dist/elysia.d.ts +79 -0
- package/dist/elysia.js +120 -0
- package/dist/{engine-Bo5nkg02.d.ts → engine-CZ4F_Csu.d.ts} +3 -3
- package/dist/express.d.ts +2 -2
- package/dist/express.js +1 -0
- package/dist/fastify.d.ts +2 -2
- package/dist/fastify.js +1 -0
- package/dist/hono.d.ts +20 -0
- package/dist/hono.js +95 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/koa.d.ts +15 -0
- package/dist/koa.js +142 -0
- package/dist/locators.js +1 -0
- package/dist/lockfile.js +2 -0
- package/dist/nestjs.d.ts +18 -0
- package/dist/nestjs.js +143 -0
- package/dist/nextjs.d.ts +2 -2
- package/dist/nextjs.js +1 -0
- package/package.json +52 -4
- package/dist/{chunk-P67G7RA7.js → chunk-YJ4KB56W.js} +3 -3
package/README.md
CHANGED
|
@@ -518,9 +518,88 @@ export const PUT = createNextHandler({
|
|
|
518
518
|
|
|
519
519
|
The handler dispatches on the last URL segment (`execute-preconditions`, `execute-observer`, `teardown-preconditions`) and returns 404 for anything else. It depends only on the Web `Request` / `Response` types, so it runs on both the Node and Edge runtimes — no `next` import required.
|
|
520
520
|
|
|
521
|
+
### Hono
|
|
522
|
+
|
|
523
|
+
```ts
|
|
524
|
+
import { Hono } from "hono";
|
|
525
|
+
import { createHonoHandler } from "@ripplo/testing/hono";
|
|
526
|
+
import { engine } from "./test/engine.js";
|
|
527
|
+
|
|
528
|
+
const app = new Hono();
|
|
529
|
+
app.route(
|
|
530
|
+
"/ripplo",
|
|
531
|
+
createHonoHandler({
|
|
532
|
+
enabled: process.env.ENABLE_RIPPLO_TESTING === "true",
|
|
533
|
+
engine,
|
|
534
|
+
}),
|
|
535
|
+
);
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
The Hono adapter uses the Web `Request` / `Response` standard and runs on Node, Bun, Deno, and Cloudflare Workers.
|
|
539
|
+
|
|
540
|
+
### Koa
|
|
541
|
+
|
|
542
|
+
```ts
|
|
543
|
+
import Koa from "koa";
|
|
544
|
+
import mount from "koa-mount";
|
|
545
|
+
import { createKoaHandler } from "@ripplo/testing/koa";
|
|
546
|
+
import { engine } from "./test/engine.js";
|
|
547
|
+
|
|
548
|
+
const app = new Koa();
|
|
549
|
+
app.use(
|
|
550
|
+
mount(
|
|
551
|
+
"/ripplo",
|
|
552
|
+
createKoaHandler({
|
|
553
|
+
enabled: process.env.ENABLE_RIPPLO_TESTING === "true",
|
|
554
|
+
engine,
|
|
555
|
+
}),
|
|
556
|
+
),
|
|
557
|
+
);
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
The Koa adapter reads the raw request body itself — do not mount a body-parser in front of it. Mount it at any prefix with `koa-mount`.
|
|
561
|
+
|
|
562
|
+
### NestJS
|
|
563
|
+
|
|
564
|
+
```ts
|
|
565
|
+
import { Module } from "@nestjs/common";
|
|
566
|
+
import { RipploTestingModule } from "@ripplo/testing/nestjs";
|
|
567
|
+
import { engine } from "./test/engine.js";
|
|
568
|
+
|
|
569
|
+
@Module({
|
|
570
|
+
imports: [
|
|
571
|
+
RipploTestingModule.forRoot({
|
|
572
|
+
enabled: process.env.ENABLE_RIPPLO_TESTING === "true",
|
|
573
|
+
engine,
|
|
574
|
+
path: "ripplo",
|
|
575
|
+
}),
|
|
576
|
+
],
|
|
577
|
+
})
|
|
578
|
+
export class AppModule {}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
Requires the Express platform adapter (`@nestjs/platform-express`) and `reflect-metadata` at the entry point. `path` defaults to `"ripplo"`.
|
|
582
|
+
|
|
583
|
+
### Elysia
|
|
584
|
+
|
|
585
|
+
```ts
|
|
586
|
+
import { Elysia } from "elysia";
|
|
587
|
+
import { createElysiaHandler } from "@ripplo/testing/elysia";
|
|
588
|
+
import { engine } from "./test/engine.js";
|
|
589
|
+
|
|
590
|
+
const app = new Elysia().group("/ripplo", (app) =>
|
|
591
|
+
app.use(
|
|
592
|
+
createElysiaHandler({
|
|
593
|
+
enabled: process.env.ENABLE_RIPPLO_TESTING === "true",
|
|
594
|
+
engine,
|
|
595
|
+
}),
|
|
596
|
+
),
|
|
597
|
+
);
|
|
598
|
+
```
|
|
599
|
+
|
|
521
600
|
### Custom integration (raw engine)
|
|
522
601
|
|
|
523
|
-
If your framework isn't covered above
|
|
602
|
+
If your framework isn't covered above, use the engine directly. The adapters are thin wrappers over the same API.
|
|
524
603
|
|
|
525
604
|
```ts
|
|
526
605
|
import { buildSetCookieHeader, serializeCookie, verifyWebhookSignature } from "@ripplo/testing";
|
package/dist/actions.js
CHANGED
|
@@ -2,11 +2,12 @@ import {
|
|
|
2
2
|
readVariable,
|
|
3
3
|
toSpecLocator,
|
|
4
4
|
toStringValueRef
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-YJ4KB56W.js";
|
|
6
|
+
import "./chunk-DCJBLS2U.js";
|
|
6
7
|
import {
|
|
7
8
|
createStep
|
|
8
9
|
} from "./chunk-MGATMMCZ.js";
|
|
9
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-4MGIQFAJ.js";
|
|
10
11
|
|
|
11
12
|
// src/steps/actions.ts
|
|
12
13
|
function navigate(url) {
|
package/dist/assert.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
toSpecLocator,
|
|
3
|
-
toStringValueRef
|
|
4
|
-
} from "./chunk-P67G7RA7.js";
|
|
5
1
|
import {
|
|
6
2
|
readObserverBudget,
|
|
7
3
|
readObserverName
|
|
8
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XBYG5NBY.js";
|
|
5
|
+
import {
|
|
6
|
+
toSpecLocator,
|
|
7
|
+
toStringValueRef
|
|
8
|
+
} from "./chunk-YJ4KB56W.js";
|
|
9
|
+
import "./chunk-DCJBLS2U.js";
|
|
9
10
|
import {
|
|
10
11
|
createStep
|
|
11
12
|
} from "./chunk-MGATMMCZ.js";
|
|
12
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-4MGIQFAJ.js";
|
|
13
14
|
|
|
14
15
|
// src/steps/assert.ts
|
|
15
16
|
var assert = {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
__decorateClass,
|
|
15
|
+
__decorateParam
|
|
16
|
+
};
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
// src/types.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
var DEFAULT_WATCH_PATHS = [
|
|
4
|
-
"src/**",
|
|
5
|
-
"app/**",
|
|
6
|
-
"apps/**",
|
|
7
|
-
"pages/**",
|
|
8
|
-
"routes/**",
|
|
9
|
-
"components/**",
|
|
10
|
-
"server/**",
|
|
11
|
-
"api/**",
|
|
12
|
-
"backend/**",
|
|
13
|
-
"features/**",
|
|
14
|
-
"modules/**",
|
|
15
|
-
"views/**",
|
|
16
|
-
"ui/**",
|
|
17
|
-
"hooks/**",
|
|
18
|
-
"contexts/**",
|
|
19
|
-
"providers/**",
|
|
20
|
-
"controllers/**",
|
|
21
|
-
"handlers/**",
|
|
22
|
-
"resolvers/**",
|
|
23
|
-
"services/**",
|
|
24
|
-
"middleware/**",
|
|
25
|
-
"lib/**"
|
|
4
|
+
"**/src/**",
|
|
5
|
+
"**/app/**",
|
|
6
|
+
"**/apps/**",
|
|
7
|
+
"**/pages/**",
|
|
8
|
+
"**/routes/**",
|
|
9
|
+
"**/components/**",
|
|
10
|
+
"**/server/**",
|
|
11
|
+
"**/api/**",
|
|
12
|
+
"**/backend/**",
|
|
13
|
+
"**/features/**",
|
|
14
|
+
"**/modules/**",
|
|
15
|
+
"**/views/**",
|
|
16
|
+
"**/ui/**",
|
|
17
|
+
"**/hooks/**",
|
|
18
|
+
"**/contexts/**",
|
|
19
|
+
"**/providers/**",
|
|
20
|
+
"**/controllers/**",
|
|
21
|
+
"**/handlers/**",
|
|
22
|
+
"**/resolvers/**",
|
|
23
|
+
"**/services/**",
|
|
24
|
+
"**/middleware/**",
|
|
25
|
+
"**/lib/**"
|
|
26
26
|
];
|
|
27
27
|
var DEFAULT_IGNORE_PATHS = [
|
|
28
28
|
"**/*.gen.*",
|
package/dist/compiler.js
CHANGED
package/dist/control.js
CHANGED
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
readVariable,
|
|
5
5
|
toStringValueRef,
|
|
6
6
|
variable
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-MGATMMCZ.js";
|
|
7
|
+
} from "./chunk-YJ4KB56W.js";
|
|
9
8
|
import "./chunk-DCJBLS2U.js";
|
|
9
|
+
import "./chunk-MGATMMCZ.js";
|
|
10
|
+
import "./chunk-4MGIQFAJ.js";
|
|
10
11
|
export {
|
|
11
12
|
extract,
|
|
12
13
|
isVariable,
|
package/dist/elysia.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Elysia } from 'elysia';
|
|
2
|
+
import { R as RipploEngine } from './engine-CZ4F_Csu.js';
|
|
3
|
+
import './builder-BGr5JvrY.js';
|
|
4
|
+
import './types-Degkxs1f.js';
|
|
5
|
+
import 'zod';
|
|
6
|
+
import './step-De52hTLd.js';
|
|
7
|
+
import '@ripplo/spec';
|
|
8
|
+
|
|
9
|
+
interface CreateElysiaHandlerParams {
|
|
10
|
+
readonly enabled: boolean;
|
|
11
|
+
readonly engine: RipploEngine;
|
|
12
|
+
}
|
|
13
|
+
declare function createElysiaHandler(params: CreateElysiaHandlerParams): ReturnType<typeof build>;
|
|
14
|
+
declare function build({ enabled, engine }: CreateElysiaHandlerParams): Elysia<"", {
|
|
15
|
+
decorator: {};
|
|
16
|
+
store: {};
|
|
17
|
+
derive: {};
|
|
18
|
+
resolve: {};
|
|
19
|
+
}, {
|
|
20
|
+
typebox: {};
|
|
21
|
+
error: {};
|
|
22
|
+
}, {
|
|
23
|
+
schema: {};
|
|
24
|
+
standaloneSchema: {};
|
|
25
|
+
macro: {};
|
|
26
|
+
macroFn: {};
|
|
27
|
+
parser: {};
|
|
28
|
+
response: {};
|
|
29
|
+
}, {
|
|
30
|
+
"execute-preconditions": {
|
|
31
|
+
put: {
|
|
32
|
+
body: unknown;
|
|
33
|
+
params: {};
|
|
34
|
+
query: unknown;
|
|
35
|
+
headers: unknown;
|
|
36
|
+
response: {
|
|
37
|
+
200: Response;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
} & {
|
|
42
|
+
"execute-observer": {
|
|
43
|
+
put: {
|
|
44
|
+
body: unknown;
|
|
45
|
+
params: {};
|
|
46
|
+
query: unknown;
|
|
47
|
+
headers: unknown;
|
|
48
|
+
response: {
|
|
49
|
+
200: Response;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
} & {
|
|
54
|
+
"teardown-preconditions": {
|
|
55
|
+
put: {
|
|
56
|
+
body: unknown;
|
|
57
|
+
params: {};
|
|
58
|
+
query: unknown;
|
|
59
|
+
headers: unknown;
|
|
60
|
+
response: {
|
|
61
|
+
200: Response;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
}, {
|
|
66
|
+
derive: {};
|
|
67
|
+
resolve: {};
|
|
68
|
+
schema: {};
|
|
69
|
+
standaloneSchema: {};
|
|
70
|
+
response: {};
|
|
71
|
+
}, {
|
|
72
|
+
derive: {};
|
|
73
|
+
resolve: {};
|
|
74
|
+
schema: {};
|
|
75
|
+
standaloneSchema: {};
|
|
76
|
+
response: {};
|
|
77
|
+
}>;
|
|
78
|
+
|
|
79
|
+
export { type CreateElysiaHandlerParams, createElysiaHandler };
|
package/dist/elysia.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import {
|
|
2
|
+
batchRequestSchema,
|
|
3
|
+
buildSetCookieHeader,
|
|
4
|
+
observerRequestSchema,
|
|
5
|
+
serializeCookie,
|
|
6
|
+
teardownRequestSchema,
|
|
7
|
+
verifyWebhookSignature
|
|
8
|
+
} from "./chunk-TO3T2D2Y.js";
|
|
9
|
+
import "./chunk-4MGIQFAJ.js";
|
|
10
|
+
|
|
11
|
+
// src/adapters/elysia.ts
|
|
12
|
+
import { Elysia } from "elysia";
|
|
13
|
+
function createElysiaHandler(params) {
|
|
14
|
+
return build(params);
|
|
15
|
+
}
|
|
16
|
+
function build({ enabled, engine }) {
|
|
17
|
+
const app = new Elysia();
|
|
18
|
+
if (!enabled) {
|
|
19
|
+
return app.put("/execute-preconditions", () => notFoundResponse()).put("/execute-observer", () => notFoundResponse()).put("/teardown-preconditions", () => notFoundResponse());
|
|
20
|
+
}
|
|
21
|
+
const webhookSecret = engine.getConfig().webhookSecret;
|
|
22
|
+
return app.put("/execute-preconditions", async ({ request }) => {
|
|
23
|
+
const gate = await verifyAndReadBody(request, webhookSecret);
|
|
24
|
+
if ("response" in gate) {
|
|
25
|
+
return gate.response;
|
|
26
|
+
}
|
|
27
|
+
const parsed = parseWith(gate.body, batchRequestSchema);
|
|
28
|
+
if (parsed == null) {
|
|
29
|
+
return jsonResponse({ error: "Invalid request body", success: false }, 400);
|
|
30
|
+
}
|
|
31
|
+
const host = request.headers.get("host");
|
|
32
|
+
if (host == null || host.length === 0) {
|
|
33
|
+
return jsonResponse({ error: "Missing host header", success: false }, 400);
|
|
34
|
+
}
|
|
35
|
+
const proto = request.headers.get("x-forwarded-proto") ?? "http";
|
|
36
|
+
const appUrl = `${proto}://${host}`;
|
|
37
|
+
const result = await engine.executePreconditions(parsed.preconditions, { appUrl });
|
|
38
|
+
const headers = new Headers({ "content-type": "application/json" });
|
|
39
|
+
result.cookies.forEach((cookie) => {
|
|
40
|
+
headers.append("Set-Cookie", buildSetCookieHeader(serializeCookie(cookie)));
|
|
41
|
+
});
|
|
42
|
+
return new Response(
|
|
43
|
+
JSON.stringify({
|
|
44
|
+
data: result.data,
|
|
45
|
+
error: result.error,
|
|
46
|
+
executed: result.executed,
|
|
47
|
+
runId: result.runId,
|
|
48
|
+
success: result.success
|
|
49
|
+
}),
|
|
50
|
+
{ headers, status: 200 }
|
|
51
|
+
);
|
|
52
|
+
}).put("/execute-observer", async ({ request }) => {
|
|
53
|
+
const gate = await verifyAndReadBody(request, webhookSecret);
|
|
54
|
+
if ("response" in gate) {
|
|
55
|
+
return gate.response;
|
|
56
|
+
}
|
|
57
|
+
const parsed = parseWith(gate.body, observerRequestSchema);
|
|
58
|
+
if (parsed == null) {
|
|
59
|
+
return jsonResponse({ error: "Invalid request body", success: false }, 400);
|
|
60
|
+
}
|
|
61
|
+
const result = await engine.executeObserver(parsed.observer, parsed.params);
|
|
62
|
+
return jsonResponse(
|
|
63
|
+
{ error: result.error, outcome: result.outcome, success: result.success },
|
|
64
|
+
200
|
|
65
|
+
);
|
|
66
|
+
}).put("/teardown-preconditions", async ({ request }) => {
|
|
67
|
+
const gate = await verifyAndReadBody(request, webhookSecret);
|
|
68
|
+
if ("response" in gate) {
|
|
69
|
+
return gate.response;
|
|
70
|
+
}
|
|
71
|
+
const parsed = parseWith(gate.body, teardownRequestSchema);
|
|
72
|
+
if (parsed == null) {
|
|
73
|
+
return jsonResponse({ error: "Invalid request body", success: false }, 400);
|
|
74
|
+
}
|
|
75
|
+
await engine.teardown(parsed.preconditions, parsed.data);
|
|
76
|
+
return jsonResponse({ success: true }, 200);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async function verifyAndReadBody(req, webhookSecret) {
|
|
80
|
+
if (webhookSecret.length === 0) {
|
|
81
|
+
return { response: jsonResponse({ error: "Webhook secret not configured" }, 403) };
|
|
82
|
+
}
|
|
83
|
+
const body = await req.text();
|
|
84
|
+
const headers = {
|
|
85
|
+
"webhook-id": req.headers.get("webhook-id") ?? void 0,
|
|
86
|
+
"webhook-signature": req.headers.get("webhook-signature") ?? void 0,
|
|
87
|
+
"webhook-timestamp": req.headers.get("webhook-timestamp") ?? void 0
|
|
88
|
+
};
|
|
89
|
+
if (!verifyWebhookSignature(body, headers, webhookSecret)) {
|
|
90
|
+
return { response: jsonResponse({ error: "Invalid webhook signature" }, 401) };
|
|
91
|
+
}
|
|
92
|
+
return { body };
|
|
93
|
+
}
|
|
94
|
+
function parseWith(body, schema) {
|
|
95
|
+
const json = tryParseJson(body);
|
|
96
|
+
if (json == null) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const result = schema.safeParse(json);
|
|
100
|
+
return result.success ? result.data : null;
|
|
101
|
+
}
|
|
102
|
+
function tryParseJson(body) {
|
|
103
|
+
try {
|
|
104
|
+
return JSON.parse(body);
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function jsonResponse(payload, status) {
|
|
110
|
+
return new Response(JSON.stringify(payload), {
|
|
111
|
+
headers: { "content-type": "application/json" },
|
|
112
|
+
status
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function notFoundResponse() {
|
|
116
|
+
return jsonResponse({ error: "Not found" }, 404);
|
|
117
|
+
}
|
|
118
|
+
export {
|
|
119
|
+
createElysiaHandler
|
|
120
|
+
};
|
|
@@ -17,7 +17,7 @@ interface ObserverExecutionResult {
|
|
|
17
17
|
readonly outcome: ObserverOutcome | undefined;
|
|
18
18
|
readonly success: boolean;
|
|
19
19
|
}
|
|
20
|
-
interface
|
|
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
23
|
readonly getConfig: () => DslConfig;
|
|
@@ -42,6 +42,6 @@ interface EngineImpls<P extends PreconditionRegistry, O extends ObserverRegistry
|
|
|
42
42
|
readonly [K in keyof P]: NotImplemented | PreconditionImplFor<P[K]>;
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
-
declare function createEngine<P extends PreconditionRegistry, O extends ObserverRegistry>(ripplo: RipploInstance<P, O>, impls: EngineImpls<P, O>):
|
|
45
|
+
declare function createEngine<P extends PreconditionRegistry, O extends ObserverRegistry>(ripplo: RipploInstance<P, O>, impls: EngineImpls<P, O>): RipploEngine;
|
|
46
46
|
|
|
47
|
-
export { type EngineImpls as E, type NotImplemented as N, type ObserverImplFnFor as O, type
|
|
47
|
+
export { type EngineImpls as E, type NotImplemented as N, type ObserverImplFnFor as O, type PreconditionImplFor as P, type RipploEngine as R, type EngineResult as a, type ExecuteBatchOptions as b, createEngine as c, notImplemented as n };
|
package/dist/express.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import {
|
|
2
|
+
import { R as RipploEngine } from './engine-CZ4F_Csu.js';
|
|
3
3
|
import './builder-BGr5JvrY.js';
|
|
4
4
|
import './types-Degkxs1f.js';
|
|
5
5
|
import 'zod';
|
|
@@ -8,7 +8,7 @@ import '@ripplo/spec';
|
|
|
8
8
|
|
|
9
9
|
interface CreateExpressHandlerParams {
|
|
10
10
|
readonly enabled: boolean;
|
|
11
|
-
readonly engine:
|
|
11
|
+
readonly engine: RipploEngine;
|
|
12
12
|
}
|
|
13
13
|
declare function createExpressHandler({ enabled, engine }: CreateExpressHandlerParams): Router;
|
|
14
14
|
|
package/dist/express.js
CHANGED
package/dist/fastify.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify';
|
|
2
|
-
import {
|
|
2
|
+
import { R as RipploEngine } from './engine-CZ4F_Csu.js';
|
|
3
3
|
import './builder-BGr5JvrY.js';
|
|
4
4
|
import './types-Degkxs1f.js';
|
|
5
5
|
import 'zod';
|
|
@@ -8,7 +8,7 @@ import '@ripplo/spec';
|
|
|
8
8
|
|
|
9
9
|
interface RegisterFastifyHandlerParams {
|
|
10
10
|
readonly enabled: boolean;
|
|
11
|
-
readonly engine:
|
|
11
|
+
readonly engine: RipploEngine;
|
|
12
12
|
}
|
|
13
13
|
declare function registerFastifyHandler({ enabled, engine, }: RegisterFastifyHandlerParams): (fastify: FastifyInstance) => Promise<void>;
|
|
14
14
|
|
package/dist/fastify.js
CHANGED
package/dist/hono.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { R as RipploEngine } from './engine-CZ4F_Csu.js';
|
|
3
|
+
import './builder-BGr5JvrY.js';
|
|
4
|
+
import './types-Degkxs1f.js';
|
|
5
|
+
import 'zod';
|
|
6
|
+
import './step-De52hTLd.js';
|
|
7
|
+
import '@ripplo/spec';
|
|
8
|
+
|
|
9
|
+
interface CreateHonoHandlerParams {
|
|
10
|
+
readonly enabled: boolean;
|
|
11
|
+
readonly engine: RipploEngine;
|
|
12
|
+
}
|
|
13
|
+
interface HonoEnv {
|
|
14
|
+
Variables: {
|
|
15
|
+
rawBody: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
declare function createHonoHandler({ enabled, engine }: CreateHonoHandlerParams): Hono<HonoEnv>;
|
|
19
|
+
|
|
20
|
+
export { type CreateHonoHandlerParams, createHonoHandler };
|
package/dist/hono.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
batchRequestSchema,
|
|
3
|
+
buildSetCookieHeader,
|
|
4
|
+
observerRequestSchema,
|
|
5
|
+
serializeCookie,
|
|
6
|
+
teardownRequestSchema,
|
|
7
|
+
verifyWebhookSignature
|
|
8
|
+
} from "./chunk-TO3T2D2Y.js";
|
|
9
|
+
import "./chunk-4MGIQFAJ.js";
|
|
10
|
+
|
|
11
|
+
// src/adapters/hono.ts
|
|
12
|
+
import { Hono } from "hono";
|
|
13
|
+
function createHonoHandler({ enabled, engine }) {
|
|
14
|
+
const app = new Hono();
|
|
15
|
+
if (!enabled) {
|
|
16
|
+
return app;
|
|
17
|
+
}
|
|
18
|
+
const webhookSecret = engine.getConfig().webhookSecret;
|
|
19
|
+
app.use("*", createWebhookMiddleware(webhookSecret));
|
|
20
|
+
app.put("/execute-preconditions", async (c) => {
|
|
21
|
+
const body = tryParseJson(c.get("rawBody"));
|
|
22
|
+
const parsed = body == null ? null : batchRequestSchema.safeParse(body);
|
|
23
|
+
if (parsed == null || !parsed.success) {
|
|
24
|
+
return c.json({ error: "Invalid request body", success: false }, 400);
|
|
25
|
+
}
|
|
26
|
+
const host = c.req.header("host");
|
|
27
|
+
if (host == null || host.length === 0) {
|
|
28
|
+
return c.json({ error: "Missing host header", success: false }, 400);
|
|
29
|
+
}
|
|
30
|
+
const proto = c.req.header("x-forwarded-proto") ?? "http";
|
|
31
|
+
const appUrl = `${proto}://${host}`;
|
|
32
|
+
const result = await engine.executePreconditions(parsed.data.preconditions, { appUrl });
|
|
33
|
+
result.cookies.forEach((cookie) => {
|
|
34
|
+
c.header("Set-Cookie", buildSetCookieHeader(serializeCookie(cookie)), { append: true });
|
|
35
|
+
});
|
|
36
|
+
return c.json({
|
|
37
|
+
data: result.data,
|
|
38
|
+
error: result.error,
|
|
39
|
+
executed: result.executed,
|
|
40
|
+
runId: result.runId,
|
|
41
|
+
success: result.success
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
app.put("/execute-observer", async (c) => {
|
|
45
|
+
const body = tryParseJson(c.get("rawBody"));
|
|
46
|
+
const parsed = body == null ? null : observerRequestSchema.safeParse(body);
|
|
47
|
+
if (parsed == null || !parsed.success) {
|
|
48
|
+
return c.json({ error: "Invalid request body", success: false }, 400);
|
|
49
|
+
}
|
|
50
|
+
const result = await engine.executeObserver(parsed.data.observer, parsed.data.params);
|
|
51
|
+
return c.json({
|
|
52
|
+
error: result.error,
|
|
53
|
+
outcome: result.outcome,
|
|
54
|
+
success: result.success
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
app.put("/teardown-preconditions", async (c) => {
|
|
58
|
+
const body = tryParseJson(c.get("rawBody"));
|
|
59
|
+
const parsed = body == null ? null : teardownRequestSchema.safeParse(body);
|
|
60
|
+
if (parsed == null || !parsed.success) {
|
|
61
|
+
return c.json({ error: "Invalid request body", success: false }, 400);
|
|
62
|
+
}
|
|
63
|
+
await engine.teardown(parsed.data.preconditions, parsed.data.data);
|
|
64
|
+
return c.json({ success: true });
|
|
65
|
+
});
|
|
66
|
+
return app;
|
|
67
|
+
}
|
|
68
|
+
function createWebhookMiddleware(webhookSecret) {
|
|
69
|
+
return async (c, next) => {
|
|
70
|
+
if (webhookSecret.length === 0) {
|
|
71
|
+
return c.json({ error: "Webhook secret not configured" }, 403);
|
|
72
|
+
}
|
|
73
|
+
const body = await c.req.text();
|
|
74
|
+
const headers = {
|
|
75
|
+
"webhook-id": c.req.header("webhook-id"),
|
|
76
|
+
"webhook-signature": c.req.header("webhook-signature"),
|
|
77
|
+
"webhook-timestamp": c.req.header("webhook-timestamp")
|
|
78
|
+
};
|
|
79
|
+
if (!verifyWebhookSignature(body, headers, webhookSecret)) {
|
|
80
|
+
return c.json({ error: "Invalid webhook signature" }, 401);
|
|
81
|
+
}
|
|
82
|
+
c.set("rawBody", body);
|
|
83
|
+
await next();
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function tryParseJson(body) {
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(body);
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export {
|
|
94
|
+
createHonoHandler
|
|
95
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { 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-BGr5JvrY.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
|
|
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-CZ4F_Csu.js';
|
|
5
5
|
import { C as CookieEntry } from './types-Degkxs1f.js';
|
|
6
6
|
export { c as CookieOptions, D as DEFAULT_IGNORE_PATHS, d as DEFAULT_WATCH_PATHS, e as DslConfig, f as ObserverContext, g as ObserverDefinition, O as ObserverHandle, a as ObserverInput, h as ObserverOutcome, P as Precondition, i as PreconditionDeps, S as SetupContext, T as TeardownContext, j as TestDefinition } from './types-Degkxs1f.js';
|
|
7
7
|
export { D as DslNodeInput } from './step-De52hTLd.js';
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
readPreconditionName,
|
|
13
13
|
readTestValue,
|
|
14
14
|
userDslConfigSchema
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XBYG5NBY.js";
|
|
16
16
|
import {
|
|
17
17
|
compile
|
|
18
18
|
} from "./chunk-KNF4K4JH.js";
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
serializeCookie,
|
|
23
23
|
verifyWebhookSignature
|
|
24
24
|
} from "./chunk-TO3T2D2Y.js";
|
|
25
|
+
import "./chunk-4MGIQFAJ.js";
|
|
25
26
|
|
|
26
27
|
// src/observer.ts
|
|
27
28
|
function createPassOutcome() {
|
package/dist/koa.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Middleware } from 'koa';
|
|
2
|
+
import { R as RipploEngine } from './engine-CZ4F_Csu.js';
|
|
3
|
+
import './builder-BGr5JvrY.js';
|
|
4
|
+
import './types-Degkxs1f.js';
|
|
5
|
+
import 'zod';
|
|
6
|
+
import './step-De52hTLd.js';
|
|
7
|
+
import '@ripplo/spec';
|
|
8
|
+
|
|
9
|
+
interface CreateKoaHandlerParams {
|
|
10
|
+
readonly enabled: boolean;
|
|
11
|
+
readonly engine: RipploEngine;
|
|
12
|
+
}
|
|
13
|
+
declare function createKoaHandler({ enabled, engine }: CreateKoaHandlerParams): Middleware;
|
|
14
|
+
|
|
15
|
+
export { type CreateKoaHandlerParams, createKoaHandler };
|
package/dist/koa.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
batchRequestSchema,
|
|
3
|
+
buildSetCookieHeader,
|
|
4
|
+
observerRequestSchema,
|
|
5
|
+
serializeCookie,
|
|
6
|
+
teardownRequestSchema,
|
|
7
|
+
verifyWebhookSignature
|
|
8
|
+
} from "./chunk-TO3T2D2Y.js";
|
|
9
|
+
import "./chunk-4MGIQFAJ.js";
|
|
10
|
+
|
|
11
|
+
// src/adapters/koa.ts
|
|
12
|
+
function createKoaHandler({ enabled, engine }) {
|
|
13
|
+
if (!enabled) {
|
|
14
|
+
return async (ctx, next) => {
|
|
15
|
+
await next();
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const webhookSecret = engine.getConfig().webhookSecret;
|
|
19
|
+
return async (ctx, next) => {
|
|
20
|
+
if (ctx.method !== "PUT") {
|
|
21
|
+
await next();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const action = lastPathSegment(ctx.path);
|
|
25
|
+
if (action == null) {
|
|
26
|
+
await next();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (webhookSecret.length === 0) {
|
|
30
|
+
ctx.status = 403;
|
|
31
|
+
ctx.body = { error: "Webhook secret not configured" };
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const body = await readBody(ctx);
|
|
35
|
+
const headers = {
|
|
36
|
+
"webhook-id": headerString(ctx.get("webhook-id")),
|
|
37
|
+
"webhook-signature": headerString(ctx.get("webhook-signature")),
|
|
38
|
+
"webhook-timestamp": headerString(ctx.get("webhook-timestamp"))
|
|
39
|
+
};
|
|
40
|
+
if (!verifyWebhookSignature(body, headers, webhookSecret)) {
|
|
41
|
+
ctx.status = 401;
|
|
42
|
+
ctx.body = { error: "Invalid webhook signature" };
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
await dispatchAction({ action, body, ctx, engine });
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async function dispatchAction({ action, body, ctx, engine }) {
|
|
49
|
+
if (action === "execute-preconditions") {
|
|
50
|
+
await handleExecutePreconditions({ body, ctx, engine });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (action === "execute-observer") {
|
|
54
|
+
await handleExecuteObserver({ body, ctx, engine });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await handleTeardown({ body, ctx, engine });
|
|
58
|
+
}
|
|
59
|
+
async function handleExecutePreconditions({ body, ctx, engine }) {
|
|
60
|
+
const json = tryParseJson(body);
|
|
61
|
+
const parsed = json == null ? null : batchRequestSchema.safeParse(json);
|
|
62
|
+
if (parsed == null || !parsed.success) {
|
|
63
|
+
ctx.status = 400;
|
|
64
|
+
ctx.body = { error: "Invalid request body", success: false };
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const host = ctx.get("host");
|
|
68
|
+
if (host.length === 0) {
|
|
69
|
+
ctx.status = 400;
|
|
70
|
+
ctx.body = { error: "Missing host header", success: false };
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const proto = ctx.get("x-forwarded-proto").length > 0 ? ctx.get("x-forwarded-proto") : "http";
|
|
74
|
+
const appUrl = `${proto}://${host}`;
|
|
75
|
+
const result = await engine.executePreconditions(parsed.data.preconditions, { appUrl });
|
|
76
|
+
result.cookies.forEach((cookie) => {
|
|
77
|
+
ctx.append("Set-Cookie", buildSetCookieHeader(serializeCookie(cookie)));
|
|
78
|
+
});
|
|
79
|
+
ctx.status = 200;
|
|
80
|
+
ctx.body = {
|
|
81
|
+
data: result.data,
|
|
82
|
+
error: result.error,
|
|
83
|
+
executed: result.executed,
|
|
84
|
+
runId: result.runId,
|
|
85
|
+
success: result.success
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function handleExecuteObserver({ body, ctx, engine }) {
|
|
89
|
+
const json = tryParseJson(body);
|
|
90
|
+
const parsed = json == null ? null : observerRequestSchema.safeParse(json);
|
|
91
|
+
if (parsed == null || !parsed.success) {
|
|
92
|
+
ctx.status = 400;
|
|
93
|
+
ctx.body = { error: "Invalid request body", success: false };
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const result = await engine.executeObserver(parsed.data.observer, parsed.data.params);
|
|
97
|
+
ctx.status = 200;
|
|
98
|
+
ctx.body = { error: result.error, outcome: result.outcome, success: result.success };
|
|
99
|
+
}
|
|
100
|
+
async function handleTeardown({ body, ctx, engine }) {
|
|
101
|
+
const json = tryParseJson(body);
|
|
102
|
+
const parsed = json == null ? null : teardownRequestSchema.safeParse(json);
|
|
103
|
+
if (parsed == null || !parsed.success) {
|
|
104
|
+
ctx.status = 400;
|
|
105
|
+
ctx.body = { error: "Invalid request body", success: false };
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
await engine.teardown(parsed.data.preconditions, parsed.data.data);
|
|
109
|
+
ctx.status = 200;
|
|
110
|
+
ctx.body = { success: true };
|
|
111
|
+
}
|
|
112
|
+
function lastPathSegment(path) {
|
|
113
|
+
const segments = path.split("/").filter((s) => s.length > 0);
|
|
114
|
+
const last = segments.at(-1);
|
|
115
|
+
if (last === "execute-preconditions" || last === "execute-observer" || last === "teardown-preconditions") {
|
|
116
|
+
return last;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
async function readBody(ctx) {
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
const chunks = [];
|
|
123
|
+
ctx.req.on("data", (chunk) => chunks.push(chunk));
|
|
124
|
+
ctx.req.on("end", () => {
|
|
125
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
126
|
+
});
|
|
127
|
+
ctx.req.on("error", reject);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function headerString(value) {
|
|
131
|
+
return value.length > 0 ? value : void 0;
|
|
132
|
+
}
|
|
133
|
+
function tryParseJson(body) {
|
|
134
|
+
try {
|
|
135
|
+
return JSON.parse(body);
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export {
|
|
141
|
+
createKoaHandler
|
|
142
|
+
};
|
package/dist/locators.js
CHANGED
package/dist/lockfile.js
CHANGED
package/dist/nestjs.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { DynamicModule } from '@nestjs/common';
|
|
2
|
+
import { R as RipploEngine } from './engine-CZ4F_Csu.js';
|
|
3
|
+
import './builder-BGr5JvrY.js';
|
|
4
|
+
import './types-Degkxs1f.js';
|
|
5
|
+
import 'zod';
|
|
6
|
+
import './step-De52hTLd.js';
|
|
7
|
+
import '@ripplo/spec';
|
|
8
|
+
|
|
9
|
+
interface RipploTestingModuleOptions {
|
|
10
|
+
readonly enabled: boolean;
|
|
11
|
+
readonly engine: RipploEngine;
|
|
12
|
+
readonly path: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
declare class RipploTestingModule {
|
|
15
|
+
static forRoot(options: RipploTestingModuleOptions): DynamicModule;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { RipploTestingModule, type RipploTestingModuleOptions };
|
package/dist/nestjs.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import {
|
|
2
|
+
batchRequestSchema,
|
|
3
|
+
buildSetCookieHeader,
|
|
4
|
+
observerRequestSchema,
|
|
5
|
+
serializeCookie,
|
|
6
|
+
teardownRequestSchema,
|
|
7
|
+
verifyWebhookSignature
|
|
8
|
+
} from "./chunk-TO3T2D2Y.js";
|
|
9
|
+
import {
|
|
10
|
+
__decorateClass,
|
|
11
|
+
__decorateParam
|
|
12
|
+
} from "./chunk-4MGIQFAJ.js";
|
|
13
|
+
|
|
14
|
+
// src/adapters/nestjs.ts
|
|
15
|
+
import { Controller, Inject, Module, Put, Req, Res } from "@nestjs/common";
|
|
16
|
+
var RIPPLO_OPTIONS = "RIPPLO_TESTING_OPTIONS";
|
|
17
|
+
var RipploTestingModule = class {
|
|
18
|
+
static forRoot(options) {
|
|
19
|
+
const path = options.path ?? "ripplo";
|
|
20
|
+
const controller = createController(path);
|
|
21
|
+
return {
|
|
22
|
+
controllers: [controller],
|
|
23
|
+
module: RipploTestingModule,
|
|
24
|
+
providers: [
|
|
25
|
+
{
|
|
26
|
+
provide: RIPPLO_OPTIONS,
|
|
27
|
+
useValue: { enabled: options.enabled, engine: options.engine }
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
RipploTestingModule = __decorateClass([
|
|
34
|
+
Module({})
|
|
35
|
+
], RipploTestingModule);
|
|
36
|
+
function createController(path) {
|
|
37
|
+
let RipploController = class {
|
|
38
|
+
constructor(opts) {
|
|
39
|
+
this.opts = opts;
|
|
40
|
+
}
|
|
41
|
+
async executePreconditions(req, res) {
|
|
42
|
+
if (!guard(req, res, this.opts)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const parsed = batchRequestSchema.safeParse(req.body);
|
|
46
|
+
if (!parsed.success) {
|
|
47
|
+
res.status(400).json({ error: "Invalid request body", success: false });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const host = req.get("host") ?? "";
|
|
51
|
+
const proto = req.get("x-forwarded-proto") ?? req.protocol;
|
|
52
|
+
const appUrl = `${proto}://${host}`;
|
|
53
|
+
const result = await this.opts.engine.executePreconditions(parsed.data.preconditions, {
|
|
54
|
+
appUrl
|
|
55
|
+
});
|
|
56
|
+
result.cookies.forEach((cookie) => {
|
|
57
|
+
res.append("Set-Cookie", buildSetCookieHeader(serializeCookie(cookie)));
|
|
58
|
+
});
|
|
59
|
+
res.status(200).json({
|
|
60
|
+
data: result.data,
|
|
61
|
+
error: result.error,
|
|
62
|
+
executed: result.executed,
|
|
63
|
+
runId: result.runId,
|
|
64
|
+
success: result.success
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async executeObserver(req, res) {
|
|
68
|
+
if (!guard(req, res, this.opts)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const parsed = observerRequestSchema.safeParse(req.body);
|
|
72
|
+
if (!parsed.success) {
|
|
73
|
+
res.status(400).json({ error: "Invalid request body", success: false });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const result = await this.opts.engine.executeObserver(
|
|
77
|
+
parsed.data.observer,
|
|
78
|
+
parsed.data.params
|
|
79
|
+
);
|
|
80
|
+
res.status(200).json({ error: result.error, outcome: result.outcome, success: result.success });
|
|
81
|
+
}
|
|
82
|
+
async teardown(req, res) {
|
|
83
|
+
if (!guard(req, res, this.opts)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const parsed = teardownRequestSchema.safeParse(req.body);
|
|
87
|
+
if (!parsed.success) {
|
|
88
|
+
res.status(400).json({ error: "Invalid request body", success: false });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
await this.opts.engine.teardown(parsed.data.preconditions, parsed.data.data);
|
|
92
|
+
res.status(200).json({ success: true });
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
__decorateClass([
|
|
96
|
+
Put("execute-preconditions"),
|
|
97
|
+
__decorateParam(0, Req()),
|
|
98
|
+
__decorateParam(1, Res())
|
|
99
|
+
], RipploController.prototype, "executePreconditions", 1);
|
|
100
|
+
__decorateClass([
|
|
101
|
+
Put("execute-observer"),
|
|
102
|
+
__decorateParam(0, Req()),
|
|
103
|
+
__decorateParam(1, Res())
|
|
104
|
+
], RipploController.prototype, "executeObserver", 1);
|
|
105
|
+
__decorateClass([
|
|
106
|
+
Put("teardown-preconditions"),
|
|
107
|
+
__decorateParam(0, Req()),
|
|
108
|
+
__decorateParam(1, Res())
|
|
109
|
+
], RipploController.prototype, "teardown", 1);
|
|
110
|
+
RipploController = __decorateClass([
|
|
111
|
+
Controller(path),
|
|
112
|
+
__decorateParam(0, Inject(RIPPLO_OPTIONS))
|
|
113
|
+
], RipploController);
|
|
114
|
+
return RipploController;
|
|
115
|
+
}
|
|
116
|
+
function guard(req, res, opts) {
|
|
117
|
+
if (!opts.enabled) {
|
|
118
|
+
res.status(404).json({ error: "Not found" });
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
const webhookSecret = opts.engine.getConfig().webhookSecret;
|
|
122
|
+
if (webhookSecret.length === 0) {
|
|
123
|
+
res.status(403).json({ error: "Webhook secret not configured" });
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
const payload = JSON.stringify(req.body);
|
|
127
|
+
const headers = {
|
|
128
|
+
"webhook-id": headerString(req.get("webhook-id")),
|
|
129
|
+
"webhook-signature": headerString(req.get("webhook-signature")),
|
|
130
|
+
"webhook-timestamp": headerString(req.get("webhook-timestamp"))
|
|
131
|
+
};
|
|
132
|
+
if (!verifyWebhookSignature(payload, headers, webhookSecret)) {
|
|
133
|
+
res.status(401).json({ error: "Invalid webhook signature" });
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
function headerString(value) {
|
|
139
|
+
return value != null && value.length > 0 ? value : void 0;
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
RipploTestingModule
|
|
143
|
+
};
|
package/dist/nextjs.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { R as RipploEngine } from './engine-CZ4F_Csu.js';
|
|
2
2
|
import './builder-BGr5JvrY.js';
|
|
3
3
|
import './types-Degkxs1f.js';
|
|
4
4
|
import 'zod';
|
|
@@ -7,7 +7,7 @@ import '@ripplo/spec';
|
|
|
7
7
|
|
|
8
8
|
interface CreateNextHandlerParams {
|
|
9
9
|
readonly enabled: boolean;
|
|
10
|
-
readonly engine:
|
|
10
|
+
readonly engine: RipploEngine;
|
|
11
11
|
}
|
|
12
12
|
type NextHandler = (req: Request) => Promise<Response>;
|
|
13
13
|
declare function createNextHandler({ enabled, engine }: CreateNextHandlerParams): NextHandler;
|
package/dist/nextjs.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ripplo/testing",
|
|
3
3
|
"description": "TypeScript DSL for defining and running Ripplo e2e workflow tests",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.6",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -42,6 +42,11 @@
|
|
|
42
42
|
"import": "./dist/lockfile.js",
|
|
43
43
|
"default": "./dist/lockfile.js"
|
|
44
44
|
},
|
|
45
|
+
"./elysia": {
|
|
46
|
+
"types": "./dist/elysia.d.ts",
|
|
47
|
+
"import": "./dist/elysia.js",
|
|
48
|
+
"default": "./dist/elysia.js"
|
|
49
|
+
},
|
|
45
50
|
"./express": {
|
|
46
51
|
"types": "./dist/express.d.ts",
|
|
47
52
|
"import": "./dist/express.js",
|
|
@@ -52,6 +57,21 @@
|
|
|
52
57
|
"import": "./dist/fastify.js",
|
|
53
58
|
"default": "./dist/fastify.js"
|
|
54
59
|
},
|
|
60
|
+
"./hono": {
|
|
61
|
+
"types": "./dist/hono.d.ts",
|
|
62
|
+
"import": "./dist/hono.js",
|
|
63
|
+
"default": "./dist/hono.js"
|
|
64
|
+
},
|
|
65
|
+
"./koa": {
|
|
66
|
+
"types": "./dist/koa.d.ts",
|
|
67
|
+
"import": "./dist/koa.js",
|
|
68
|
+
"default": "./dist/koa.js"
|
|
69
|
+
},
|
|
70
|
+
"./nestjs": {
|
|
71
|
+
"types": "./dist/nestjs.d.ts",
|
|
72
|
+
"import": "./dist/nestjs.js",
|
|
73
|
+
"default": "./dist/nestjs.js"
|
|
74
|
+
},
|
|
55
75
|
"./nextjs": {
|
|
56
76
|
"types": "./dist/nextjs.d.ts",
|
|
57
77
|
"import": "./dist/nextjs.js",
|
|
@@ -63,31 +83,59 @@
|
|
|
63
83
|
"zod": "^4.3.6"
|
|
64
84
|
},
|
|
65
85
|
"devDependencies": {
|
|
86
|
+
"@nestjs/common": "^10.0.0",
|
|
87
|
+
"@nestjs/core": "^10.0.0",
|
|
66
88
|
"@types/express": "^5.0.2",
|
|
89
|
+
"@types/koa": "^2.15.0",
|
|
67
90
|
"@types/node": "catalog:",
|
|
91
|
+
"elysia": "^1.0.0",
|
|
68
92
|
"eslint": "catalog:",
|
|
69
93
|
"express": "^5.1.0",
|
|
70
94
|
"fastify": "^5.3.3",
|
|
95
|
+
"hono": "^4.0.0",
|
|
96
|
+
"koa": "^2.15.0",
|
|
97
|
+
"reflect-metadata": "^0.2.0",
|
|
98
|
+
"rxjs": "^7.8.0",
|
|
71
99
|
"tsup": "^8.5.1",
|
|
72
100
|
"typescript": "catalog:",
|
|
73
101
|
"vitest": "^4.1.4",
|
|
74
|
-
"@ripplo/
|
|
75
|
-
"@ripplo/
|
|
102
|
+
"@ripplo/eslint-config": "0.0.0",
|
|
103
|
+
"@ripplo/spec": "^0.0.0"
|
|
76
104
|
},
|
|
77
105
|
"peerDependencies": {
|
|
106
|
+
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
|
107
|
+
"@nestjs/core": "^10.0.0 || ^11.0.0",
|
|
78
108
|
"dotenv": "^16.0.0 || ^17.0.0",
|
|
109
|
+
"elysia": "^1.0.0",
|
|
79
110
|
"express": "^4.0.0 || ^5.0.0",
|
|
80
|
-
"fastify": "^5.0.0"
|
|
111
|
+
"fastify": "^5.0.0",
|
|
112
|
+
"hono": "^4.0.0",
|
|
113
|
+
"koa": "^2.0.0 || ^3.0.0"
|
|
81
114
|
},
|
|
82
115
|
"peerDependenciesMeta": {
|
|
116
|
+
"@nestjs/common": {
|
|
117
|
+
"optional": true
|
|
118
|
+
},
|
|
119
|
+
"@nestjs/core": {
|
|
120
|
+
"optional": true
|
|
121
|
+
},
|
|
83
122
|
"dotenv": {
|
|
84
123
|
"optional": true
|
|
85
124
|
},
|
|
125
|
+
"elysia": {
|
|
126
|
+
"optional": true
|
|
127
|
+
},
|
|
86
128
|
"express": {
|
|
87
129
|
"optional": true
|
|
88
130
|
},
|
|
89
131
|
"fastify": {
|
|
90
132
|
"optional": true
|
|
133
|
+
},
|
|
134
|
+
"hono": {
|
|
135
|
+
"optional": true
|
|
136
|
+
},
|
|
137
|
+
"koa": {
|
|
138
|
+
"optional": true
|
|
91
139
|
}
|
|
92
140
|
},
|
|
93
141
|
"scripts": {
|