ripplo 0.6.0 → 0.7.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/LICENSE.md +1 -0
- package/README.md +59 -30
- package/dist/assets/browser-trace.js +1 -0
- package/dist/assets/rrweb.js +54 -0
- package/dist/capture-worker.js +13886 -0
- package/dist/index.js +466 -586
- package/package.json +17 -11
package/LICENSE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
© Ripplo LLC. All rights reserved. Use is subject to Ripplo's [Terms of Service](https://ripplo.ai/terms).
|
package/README.md
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
# ripplo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Describe how your app should behave. Ripplo proves it does.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
You declare your app's state model and user flows in TypeScript. Ripplo seeds real data, drives a real browser, and checks the result against the model: the UI you asserted on and the rows that should have changed underneath it.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```ts
|
|
8
|
+
export const createTask = test("Create a task", () => {
|
|
9
|
+
const { me, project } = ownedProject();
|
|
10
|
+
return {
|
|
11
|
+
given: [me, project],
|
|
12
|
+
steps: [
|
|
13
|
+
goto`/projects/${project.id}/tasks`.expect(visible(button("New"))),
|
|
14
|
+
click(button("New")).expect(visible(textbox("Title"))),
|
|
15
|
+
fill(textbox("Title"), "Buy milk"),
|
|
16
|
+
click(button("Create")).expect(Task.created({ title: "Buy milk", projectId: project.id })),
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`ownedProject()` describes the starting state; Ripplo seeds it fresh for every run. `Task.created(...)` is checked against your actual database through a small engine you write once per entity.
|
|
8
23
|
|
|
9
|
-
|
|
24
|
+
## Setup with Claude Code
|
|
25
|
+
|
|
26
|
+
If you use Claude Code, this is the shortest path. The plugin handles auth, scaffolding, and adapter wiring, and adds hooks that keep tests in sync as the agent changes your code.
|
|
10
27
|
|
|
11
28
|
```
|
|
12
29
|
/plugin marketplace add ripplo/claude-plugin
|
|
@@ -14,62 +31,74 @@ If you use Claude Code, this is the shortest path. The plugin handles auth, scaf
|
|
|
14
31
|
/ripplo:setup
|
|
15
32
|
```
|
|
16
33
|
|
|
17
|
-
|
|
34
|
+
Skip the rest of this section unless you want to know what `/ripplo:setup` does, or you're not on Claude Code.
|
|
18
35
|
|
|
19
|
-
##
|
|
36
|
+
## Setup by hand
|
|
20
37
|
|
|
21
|
-
Three steps: authenticate, scaffold, mount the adapter in your app server.
|
|
38
|
+
Three steps: authenticate, scaffold, mount the engine adapter in your app server.
|
|
22
39
|
|
|
23
40
|
```sh
|
|
24
41
|
npx ripplo auth login # device-code flow, opens a browser
|
|
25
42
|
npx ripplo init # scaffolds .ripplo/ and writes RIPPLO_* env vars
|
|
26
43
|
```
|
|
27
44
|
|
|
28
|
-
`ripplo init`
|
|
45
|
+
`ripplo init` creates `.ripplo/` (entities, worlds, tests, plus a `project.json`), appends `RIPPLO_APP_URL`, `RIPPLO_ENGINE_URL`, and `RIPPLO_WEBHOOK_SECRET` to your env file, and installs [`@ripplo/testing`](https://www.npmjs.com/package/@ripplo/testing).
|
|
29
46
|
|
|
30
|
-
Then mount the adapter in your app
|
|
47
|
+
Then mount the adapter. This is the one piece of code that lives in your app: a signed-webhook endpoint Ripplo calls to seed and read state, active only when you flip the env var.
|
|
31
48
|
|
|
32
49
|
```ts
|
|
33
|
-
//
|
|
34
|
-
import {
|
|
35
|
-
import { engine } from "
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
engine,
|
|
40
|
-
|
|
50
|
+
// Express
|
|
51
|
+
import { createEngineHandler } from "@ripplo/testing/express";
|
|
52
|
+
import { engine } from "./test/engine.js";
|
|
53
|
+
|
|
54
|
+
app.use(
|
|
55
|
+
"/ripplo",
|
|
56
|
+
createEngineHandler({ enabled: process.env.ENABLE_RIPPLO_TESTING === "true", engine }),
|
|
57
|
+
);
|
|
41
58
|
```
|
|
42
59
|
|
|
43
|
-
|
|
60
|
+
`engine` is where you implement `seed` and `read` for each entity you declared; TypeScript flags any entity you forgot. More framework adapters are coming; any server that can mount an Express router works today.
|
|
61
|
+
|
|
62
|
+
Last, preload [`@ripplo/instrument`](https://www.npmjs.com/package/@ripplo/instrument) in your dev server so test runs capture your backend spans alongside browser actions:
|
|
44
63
|
|
|
45
|
-
|
|
64
|
+
```sh
|
|
65
|
+
node --import @ripplo/instrument server.js # or NODE_OPTIONS="--import @ripplo/instrument" next dev
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
It exports spans to the local daemon during runs and stays dormant otherwise, so it's safe to leave in your dev script permanently.
|
|
46
69
|
|
|
47
70
|
## Running tests
|
|
48
71
|
|
|
49
|
-
|
|
72
|
+
Start the daemon in one terminal and leave it running next to your dev server:
|
|
50
73
|
|
|
51
74
|
```sh
|
|
52
|
-
npx ripplo
|
|
75
|
+
npx ripplo daemon
|
|
53
76
|
```
|
|
54
77
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
In another, run tests:
|
|
78
|
+
It keeps a warm browser pool and executes runs in parallel as they come in. Then:
|
|
58
79
|
|
|
59
80
|
```sh
|
|
60
|
-
npx ripplo run # scope +
|
|
81
|
+
npx ripplo run # tests in scope + tests affected by your changes
|
|
61
82
|
npx ripplo run --all # everything
|
|
62
|
-
npx ripplo run my-test # by id
|
|
83
|
+
npx ripplo run my-test # one test by id
|
|
63
84
|
```
|
|
64
85
|
|
|
65
|
-
|
|
86
|
+
`run` connects to the daemon and streams results; if a daemon is absent it starts one for you.
|
|
87
|
+
|
|
88
|
+
## The lockfile
|
|
89
|
+
|
|
90
|
+
`.ripplo/` compiles to `.ripplo/ripplo.lock`. Commit it. The Ripplo server reads the lockfile on every push, so your dashboard and CI always reflect what's actually in the branch. `npx ripplo compile --check` in a pre-commit hook (installed by setup) keeps it fresh.
|
|
66
91
|
|
|
67
92
|
## Worktrees
|
|
68
93
|
|
|
69
|
-
Ripplo is built for parallel feature work via `git worktree`. Each worktree gets its own dev session, scope, debug artifacts, and lockfile checkout
|
|
94
|
+
Ripplo is built for parallel feature work via `git worktree`. Each worktree gets its own dev session, scope, debug artifacts, and lockfile checkout, while auth and project config are shared globally, so a fresh worktree is ready to run as-is.
|
|
70
95
|
|
|
71
|
-
Two things to
|
|
96
|
+
Two things to handle per worktree: copy your gitignored env file in (or point `envFiles` in `.ripplo/project.json` at a shared path outside the tree), and pick a distinct dev-server port, updating `RIPPLO_APP_URL` and `RIPPLO_ENGINE_URL` to match. `ripplo doctor` flags both if you forget.
|
|
72
97
|
|
|
73
98
|
## Other commands
|
|
74
99
|
|
|
75
|
-
`npx ripplo --help` lists everything.
|
|
100
|
+
`npx ripplo --help` lists everything. Beyond setup you'll mostly use `lint` (compile + typecheck your `.ripplo/`), `doctor` (auth, env, and lockfile health), and `scope` (manage which flows the current session is responsible for).
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
© Ripplo LLC. All rights reserved. Use is subject to Ripplo's [Terms of Service](https://ripplo.ai/terms).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(()=>{var o=globalThis.fetch;function a(n){return Array.from(crypto.getRandomValues(new Uint8Array(n)),r=>r.toString(16).padStart(2,"0")).join("")}globalThis.fetch=(n,r)=>{let e=new Headers(r?.headers);e.has("traceparent")||e.set("traceparent",`00-${a(16)}-${a(8)}-01`);let t=globalThis.__ripploRunId;return t!=null&&!e.has("baggage")&&e.set("baggage",`ripplo.run=${encodeURIComponent(t)}`),o(n,{...r,headers:e})};})();
|