@ripplo/testing 0.0.11 → 0.1.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.
@@ -1,50 +1,119 @@
1
- // src/types.ts
2
- import { z } from "zod";
3
- var DEFAULT_WATCH_PATHS = [
4
- "src/**",
5
- "app/**",
6
- "apps/**",
7
- "pages/**",
8
- "routes/**",
9
- "components/**"
10
- ];
11
- var DEFAULT_IGNORE_PATHS = [
12
- "**/*.gen.*",
13
- "**/generated/**",
14
- "**/*.d.ts",
15
- "**/*.test.*",
16
- "**/*.spec.*",
17
- "**/node_modules/**",
18
- "**/dist/**",
19
- "**/build/**",
20
- ".ripplo/**",
21
- "**/*.md"
22
- ];
23
- var dslConfigSchema = z.object({
24
- appUrl: z.string(),
25
- ignorePaths: z.array(z.string()).optional(),
26
- preconditionsUrl: z.string(),
27
- projectId: z.string(),
28
- watchPaths: z.array(z.string()).optional(),
29
- webhookSecret: z.string()
30
- });
31
- function readTestValue(value) {
32
- return value.value;
1
+ import {
2
+ createTestValue,
3
+ makeObserverHandle,
4
+ readTestValue
5
+ } from "./chunk-3IL457A7.js";
6
+
7
+ // src/observer.ts
8
+ function createPassOutcome() {
9
+ return { kind: "pass" };
33
10
  }
34
- function createTestValue(value) {
35
- return { value };
11
+ function createRetryOutcome(reason) {
12
+ return { kind: "retry", reason };
36
13
  }
37
- function readPreconditionName(p) {
38
- return p.name;
14
+ function createFailOutcome(reason) {
15
+ return { kind: "fail", reason };
16
+ }
17
+ function buildObserver({ name, observers }) {
18
+ let description = "";
19
+ let budget = "fast";
20
+ const self = {
21
+ budget(tier) {
22
+ budget = tier;
23
+ return self;
24
+ },
25
+ description(text) {
26
+ description = text;
27
+ return self;
28
+ },
29
+ input() {
30
+ return buildObserverReady({
31
+ name,
32
+ observers,
33
+ getBudget: () => budget,
34
+ getDescription: () => description
35
+ });
36
+ }
37
+ };
38
+ return self;
39
+ }
40
+ function buildObserverReady({
41
+ getBudget,
42
+ getDescription,
43
+ name,
44
+ observers
45
+ }) {
46
+ return {
47
+ contract() {
48
+ pushObserverStub({ budget: getBudget(), description: getDescription(), name, observers });
49
+ return makeObserverHandle(name, getBudget());
50
+ },
51
+ notImplemented() {
52
+ pushObserverStub({ budget: getBudget(), description: getDescription(), name, observers });
53
+ return makeObserverHandle(name, getBudget());
54
+ }
55
+ };
56
+ }
57
+ function pushObserverStub({ budget, description, name, observers }) {
58
+ observers.push({
59
+ budget,
60
+ description,
61
+ implemented: false,
62
+ name,
63
+ run: () => Promise.resolve(createFailOutcome(`observer "${name}" not implemented`))
64
+ });
39
65
  }
40
66
 
41
67
  // src/engine.ts
42
68
  function createEngine(ripplo) {
43
69
  return {
44
70
  executeBatch: (names, options) => executeBatch(ripplo, names, options),
71
+ executeObserver: (name, params) => executeObserver(ripplo, name, params),
45
72
  teardown: (names, data) => teardown(ripplo, names, data)
46
73
  };
47
74
  }
75
+ async function executeObserver(ripplo, name, params) {
76
+ const lookup = findObserver(ripplo.getObservers(), name);
77
+ if (lookup.error != null) {
78
+ return { error: lookup.error, outcome: void 0, success: false };
79
+ }
80
+ const ctx = createObserverContext(crypto.randomUUID().slice(0, 12));
81
+ return invokeRun(lookup.def, ctx, params);
82
+ }
83
+ function findObserver(defs, name) {
84
+ const def = defs.find((o) => o.name === name);
85
+ if (def == null) {
86
+ return { def: STUB_OBSERVER, error: `Unknown observer: "${name}"` };
87
+ }
88
+ if (!def.implemented) {
89
+ return { def, error: `Observer "${name}" is not implemented` };
90
+ }
91
+ return { def, error: void 0 };
92
+ }
93
+ var STUB_OBSERVER = {
94
+ budget: "fast",
95
+ description: "",
96
+ implemented: false,
97
+ name: "",
98
+ run: () => Promise.resolve(createFailOutcome("stub"))
99
+ };
100
+ async function invokeRun(def, ctx, params) {
101
+ try {
102
+ const outcome = await def.run(ctx, params);
103
+ return { error: void 0, outcome, success: true };
104
+ } catch (error) {
105
+ const message = error instanceof Error ? error.message : String(error);
106
+ return { error: void 0, outcome: createFailOutcome(message), success: true };
107
+ }
108
+ }
109
+ function createObserverContext(runId) {
110
+ return {
111
+ runId,
112
+ fail: (reason) => createFailOutcome(reason),
113
+ pass: () => createPassOutcome(),
114
+ retry: (reason) => createRetryOutcome(reason)
115
+ };
116
+ }
48
117
  async function executeBatch(ripplo, names, options) {
49
118
  const runId = crypto.randomUUID().slice(0, 12);
50
119
  const cookies = [];
@@ -177,13 +246,27 @@ function deriveDefaultDomain(baseUrl) {
177
246
 
178
247
  // src/adapters/shared.ts
179
248
  import { Webhook, WebhookVerificationError } from "standardwebhooks";
180
- import { z as z2 } from "zod";
181
- var batchRequestSchema = z2.object({
182
- preconditions: z2.array(z2.string().min(1))
249
+ import { z } from "zod";
250
+ var batchRequestSchema = z.object({
251
+ preconditions: z.array(z.string().min(1))
252
+ });
253
+ var teardownRequestSchema = z.object({
254
+ data: z.record(z.string(), z.record(z.string(), z.string())),
255
+ preconditions: z.array(z.string().min(1))
256
+ });
257
+ var observerRequestSchema = z.object({
258
+ observer: z.string().min(1).max(200),
259
+ params: z.record(z.string().max(200), z.string())
183
260
  });
184
- var teardownRequestSchema = z2.object({
185
- data: z2.record(z2.string(), z2.record(z2.string(), z2.string())),
186
- preconditions: z2.array(z2.string().min(1))
261
+ var observerOutcomeSchema = z.discriminatedUnion("kind", [
262
+ z.object({ kind: z.literal("pass") }),
263
+ z.object({ kind: z.literal("retry"), reason: z.string() }),
264
+ z.object({ kind: z.literal("fail"), reason: z.string() })
265
+ ]);
266
+ var observerResponseSchema = z.object({
267
+ error: z.string().optional(),
268
+ outcome: observerOutcomeSchema.optional(),
269
+ success: z.boolean()
187
270
  });
188
271
  function verifyWebhookSignature(payload, headers, secret) {
189
272
  try {
@@ -238,13 +321,11 @@ function buildSetCookieHeader(cookie) {
238
321
  }
239
322
 
240
323
  export {
241
- DEFAULT_WATCH_PATHS,
242
- DEFAULT_IGNORE_PATHS,
243
- dslConfigSchema,
244
- readPreconditionName,
324
+ buildObserver,
245
325
  createEngine,
246
326
  batchRequestSchema,
247
327
  teardownRequestSchema,
328
+ observerRequestSchema,
248
329
  verifyWebhookSignature,
249
330
  serializeCookie,
250
331
  buildSetCookieHeader
@@ -6,6 +6,7 @@ import {
6
6
  // src/compiler.ts
7
7
  function compile(ripplo) {
8
8
  const preconditionDefs = ripplo.getPreconditions();
9
+ const observerDefs = ripplo.getObservers();
9
10
  const testDefs = ripplo.getTests();
10
11
  validateUniqueIds(testDefs);
11
12
  const preconditions = {};
@@ -16,8 +17,15 @@ function compile(ripplo) {
16
17
  returns: [...def.returns]
17
18
  };
18
19
  });
20
+ const observers = {};
21
+ observerDefs.forEach((def) => {
22
+ observers[def.name] = {
23
+ budget: def.budget,
24
+ description: def.description
25
+ };
26
+ });
19
27
  const tests = testDefs.map((def) => compileTest(def, preconditionDefs));
20
- return { config: ripplo.getConfig(), preconditions, tests };
28
+ return { config: ripplo.getConfig(), observers, preconditions, tests };
21
29
  }
22
30
  function validateUniqueIds(defs) {
23
31
  const seen = /* @__PURE__ */ new Map();
@@ -35,7 +43,7 @@ function compileTest(def, preconditionDefs) {
35
43
  const startsAtUrl = def.startsAtFn == null ? void 0 : def.startsAtFn(vars);
36
44
  const userSteps = def.stepsFn == null ? [] : def.stepsFn(vars);
37
45
  const allSteps = startsAtUrl == null ? userSteps : [createGotoStep(startsAtUrl), ...userSteps];
38
- const spec = compileSteps(allSteps, accessedKeys, def.requiresKeys);
46
+ const spec = compileSteps(allSteps, accessedKeys, def.requiresKeys, def.uiOnly);
39
47
  const warnings = [];
40
48
  const hasRequires = Object.keys(def.requiresKeys).length > 0;
41
49
  if (hasRequires && accessedKeys.size === 0 && def.implemented) {
@@ -81,7 +89,7 @@ function buildPlaceholderVars(requiresKeys) {
81
89
  });
82
90
  return { accessedKeys, vars };
83
91
  }
84
- function compileSteps(steps, accessedKeys, requiresKeys) {
92
+ function compileSteps(steps, accessedKeys, requiresKeys, uiOnly) {
85
93
  const nodes = {};
86
94
  steps.forEach((step, index) => {
87
95
  const id = `step-${String(index)}`;
@@ -93,7 +101,7 @@ function compileSteps(steps, accessedKeys, requiresKeys) {
93
101
  variables[key] = { default: `test-${key}`, type: "string" };
94
102
  });
95
103
  const variableNamespaces = { ...requiresKeys };
96
- return { entryNode: "step-0", nodes, variableNamespaces, variables };
104
+ return { entryNode: "step-0", nodes, uiOnly, variableNamespaces, variables };
97
105
  }
98
106
  function compileNode(step, id, next) {
99
107
  const { label, node: raw } = readStep(step);
@@ -1,10 +1,12 @@
1
- import { Precondition, WorkflowSpec } from '@ripplo/spec';
2
- import { c as DslConfig, R as RipploBuilder } from './builder-1kySbit_.js';
1
+ import { Observer, Precondition, WorkflowSpec } from '@ripplo/spec';
2
+ import { R as RipploBuilder } from './builder-c7tXey03.js';
3
+ import { f as DslConfig } from './types-oYS_Yv4G.js';
4
+ import './step-De52hTLd.js';
3
5
  import 'zod';
4
- import './step-DLfkKI3V.js';
5
6
 
6
7
  interface CompileResult {
7
8
  readonly config: DslConfig;
9
+ readonly observers: Record<string, Observer>;
8
10
  readonly preconditions: Record<string, Precondition>;
9
11
  readonly tests: ReadonlyArray<CompiledTest>;
10
12
  }
package/dist/compiler.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  compile
3
- } from "./chunk-LEIKZ6BE.js";
3
+ } from "./chunk-KNF4K4JH.js";
4
4
  import "./chunk-MGATMMCZ.js";
5
5
  export {
6
6
  compile
package/dist/control.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { U as UnlabeledStep } from './step-DLfkKI3V.js';
1
+ import { U as UnlabeledStep } from './step-De52hTLd.js';
2
2
  import { AnyLocator } from './locators.js';
3
3
  import '@ripplo/spec';
4
4
 
package/dist/express.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { Router } from 'express';
2
- import { R as RipploBuilder } from './builder-1kySbit_.js';
2
+ import { R as RipploBuilder } from './builder-c7tXey03.js';
3
+ import './types-oYS_Yv4G.js';
3
4
  import 'zod';
4
- import './step-DLfkKI3V.js';
5
+ import './step-De52hTLd.js';
5
6
  import '@ripplo/spec';
6
7
 
7
8
  interface CreateExpressHandlerParams {
package/dist/express.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  batchRequestSchema,
3
3
  createEngine,
4
+ observerRequestSchema,
4
5
  serializeCookie,
5
6
  teardownRequestSchema,
6
7
  verifyWebhookSignature
7
- } from "./chunk-7ETQVVAA.js";
8
+ } from "./chunk-CD3M7H5A.js";
9
+ import "./chunk-3IL457A7.js";
8
10
 
9
11
  // src/adapters/express.ts
10
12
  import { Router, json } from "express";
@@ -29,7 +31,7 @@ function createExpressHandler({ enabled, ripplo }) {
29
31
  }
30
32
  next();
31
33
  });
32
- router.put("/execute-batch", (req, res) => {
34
+ router.put("/execute-preconditions", (req, res) => {
33
35
  const parsed = batchRequestSchema.safeParse(req.body);
34
36
  if (!parsed.success) {
35
37
  res.status(400).json({ error: "Invalid request body", success: false });
@@ -50,7 +52,21 @@ function createExpressHandler({ enabled, ripplo }) {
50
52
  });
51
53
  });
52
54
  });
53
- router.put("/teardown", (req, res) => {
55
+ router.put("/execute-observer", (req, res) => {
56
+ const parsed = observerRequestSchema.safeParse(req.body);
57
+ if (!parsed.success) {
58
+ res.status(400).json({ error: "Invalid request body", success: false });
59
+ return;
60
+ }
61
+ void engine.executeObserver(parsed.data.observer, parsed.data.params).then((result) => {
62
+ res.json({
63
+ error: result.error,
64
+ outcome: result.outcome,
65
+ success: result.success
66
+ });
67
+ });
68
+ });
69
+ router.put("/teardown-preconditions", (req, res) => {
54
70
  const parsed = teardownRequestSchema.safeParse(req.body);
55
71
  if (!parsed.success) {
56
72
  res.status(400).json({ error: "Invalid request body", success: false });
package/dist/fastify.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { FastifyInstance } from 'fastify';
2
- import { R as RipploBuilder } from './builder-1kySbit_.js';
2
+ import { R as RipploBuilder } from './builder-c7tXey03.js';
3
+ import './types-oYS_Yv4G.js';
3
4
  import 'zod';
4
- import './step-DLfkKI3V.js';
5
+ import './step-De52hTLd.js';
5
6
  import '@ripplo/spec';
6
7
 
7
8
  interface RegisterFastifyHandlerParams {
package/dist/fastify.js CHANGED
@@ -2,10 +2,12 @@ import {
2
2
  batchRequestSchema,
3
3
  buildSetCookieHeader,
4
4
  createEngine,
5
+ observerRequestSchema,
5
6
  serializeCookie,
6
7
  teardownRequestSchema,
7
8
  verifyWebhookSignature
8
- } from "./chunk-7ETQVVAA.js";
9
+ } from "./chunk-CD3M7H5A.js";
10
+ import "./chunk-3IL457A7.js";
9
11
 
10
12
  // src/adapters/fastify.ts
11
13
  function registerFastifyHandler({
@@ -33,7 +35,7 @@ function registerFastifyHandler({
33
35
  return reply.code(401).send({ error: "Invalid webhook signature" });
34
36
  }
35
37
  });
36
- fastify.put("/execute-batch", async (req, reply) => {
38
+ fastify.put("/execute-preconditions", async (req, reply) => {
37
39
  const parsed = batchRequestSchema.safeParse(req.body);
38
40
  if (!parsed.success) {
39
41
  return reply.code(400).send({ error: "Invalid request body", success: false });
@@ -52,7 +54,19 @@ function registerFastifyHandler({
52
54
  success: result.success
53
55
  });
54
56
  });
55
- fastify.put("/teardown", async (req, reply) => {
57
+ fastify.put("/execute-observer", async (req, reply) => {
58
+ const parsed = observerRequestSchema.safeParse(req.body);
59
+ if (!parsed.success) {
60
+ return reply.code(400).send({ error: "Invalid request body", success: false });
61
+ }
62
+ const result = await engine.executeObserver(parsed.data.observer, parsed.data.params);
63
+ return reply.send({
64
+ error: result.error,
65
+ outcome: result.outcome,
66
+ success: result.success
67
+ });
68
+ });
69
+ fastify.put("/teardown-preconditions", async (req, reply) => {
56
70
  const parsed = teardownRequestSchema.safeParse(req.body);
57
71
  if (!parsed.success) {
58
72
  return reply.code(400).send({ error: "Invalid request body", success: false });
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
- import { C as CookieEntry, R as RipploBuilder } from './builder-1kySbit_.js';
2
- export { a as CookieOptions, D as DEFAULT_IGNORE_PATHS, b as DEFAULT_WATCH_PATHS, c as DslConfig, P as Precondition, d as PreconditionDeps, e as PreconditionImpl, f as PreconditionRecord, g as ResolveDeps, S as SetupContext, T as TeardownContext, h as createRipplo } from './builder-1kySbit_.js';
1
+ import { R as RipploBuilder } from './builder-c7tXey03.js';
2
+ export { O as ObserverImplFn, P as PreconditionImpl, a as PreconditionRecord, b as ResolveDeps, c as createRipplo } from './builder-c7tXey03.js';
3
3
  import { CompileResult } from './compiler.js';
4
4
  export { CompiledTest, compile } from './compiler.js';
5
- export { D as DslNodeInput } from './step-DLfkKI3V.js';
6
- import 'zod';
5
+ import { C as CookieEntry, c as ObserverOutcome } from './types-oYS_Yv4G.js';
6
+ export { d as CookieOptions, D as DEFAULT_IGNORE_PATHS, e as DEFAULT_WATCH_PATHS, f as DslConfig, g as ObserverContext, h as ObserverDefinition, O as ObserverHandle, a as ObserverInput, P as Precondition, i as PreconditionDeps, S as SetupContext, T as TeardownContext } from './types-oYS_Yv4G.js';
7
+ export { D as DslNodeInput } from './step-De52hTLd.js';
7
8
  import '@ripplo/spec';
9
+ import 'zod';
8
10
 
9
11
  interface LintDiagnostic {
10
12
  readonly message: string;
@@ -28,8 +30,14 @@ interface EngineResult {
28
30
  interface ExecuteBatchOptions {
29
31
  readonly appUrl: string | undefined;
30
32
  }
33
+ interface ObserverExecutionResult {
34
+ readonly error: string | undefined;
35
+ readonly outcome: ObserverOutcome | undefined;
36
+ readonly success: boolean;
37
+ }
31
38
  interface PreconditionEngine {
32
39
  readonly executeBatch: (names: ReadonlyArray<string>, options?: ExecuteBatchOptions) => Promise<EngineResult>;
40
+ readonly executeObserver: (name: string, params: Record<string, string>) => Promise<ObserverExecutionResult>;
33
41
  readonly teardown: (names: ReadonlyArray<string>, data: Record<string, Record<string, string>>) => Promise<void>;
34
42
  }
35
43
  declare function createEngine(ripplo: RipploBuilder): PreconditionEngine;
@@ -53,4 +61,4 @@ interface SerializedCookie {
53
61
  declare function serializeCookie(cookie: CookieEntry): SerializedCookie;
54
62
  declare function buildSetCookieHeader(cookie: SerializedCookie): string;
55
63
 
56
- export { CompileResult, CookieEntry, type EngineResult, type ExecuteBatchOptions, type LintDiagnostic, type LintResult, type PreconditionEngine, RipploBuilder, type SerializedCookie, buildSetCookieHeader, createEngine, lint, serializeCookie, verifyWebhookSignature };
64
+ export { CompileResult, CookieEntry, type EngineResult, type ExecuteBatchOptions, type LintDiagnostic, type LintResult, ObserverOutcome, type PreconditionEngine, RipploBuilder, type SerializedCookie, buildSetCookieHeader, createEngine, lint, serializeCookie, verifyWebhookSignature };