jev-devtools 0.3.0 → 0.4.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 CHANGED
@@ -1,10 +1,10 @@
1
1
  # Jev DevTools
2
2
 
3
- [![npm version 0.3.0](https://img.shields.io/badge/npm-v0.3.0-E551BA?labelColor=0D0A0F&logo=npm)](https://www.npmjs.com/package/jev-devtools/v/0.3.0)
3
+ [![npm version 0.4.1](https://img.shields.io/badge/npm-v0.4.1-E551BA?labelColor=0D0A0F&logo=npm)](https://www.npmjs.com/package/jev-devtools/v/0.4.1)
4
4
 
5
5
  A React drawer and Node adapter for inspecting TypeSafe Jev requests with animated diagrams. Includes request history, context, questions, typed answers, transport attempts, and raw snapshots.
6
6
 
7
- **Experimental (pre-1.0).** React 18/19 and Node 22.12+ are supported. APIs may change before 1.0. Verification uses sample responses through the real TypeSafe SDK, not live inference.
7
+ **Version 0.4.1 is prepared for release; publication is pending.** Check the release history for verified publication status. **Experimental (pre-1.0).** React 18/19 and Node 22.12+ are supported. APIs may change before 1.0. Verification uses sample responses through the real TypeSafe SDK, not live inference.
8
8
 
9
9
  [Changelog and release history](https://github.com/diegoag64/jev-devtools/blob/main/CHANGELOG.md) — published versions and unreleased work.
10
10
 
@@ -31,27 +31,26 @@ The [setup skill](https://github.com/diegoag64/jev-devtools/blob/main/packages/j
31
31
 
32
32
  ## Install
33
33
 
34
- Install from npm:
34
+ The npm command installs the latest published release (0.4.0 at preparation time):
35
35
 
36
36
  ```sh
37
- npm install jev-devtools
37
+ npm install jev-devtools @typesafe-ai/sdk
38
38
  ```
39
39
 
40
- To try unpublished local changes instead:
40
+ To try local changes before a future release:
41
41
 
42
42
  ```sh
43
43
  # From the jev-devtools repository root:
44
44
  npm ci
45
45
  npm run pack:devtools
46
46
  # From the consuming app:
47
- npm install /absolute/path/to/jev-devtools-0.3.0.tgz
47
+ npm install /absolute/path/to/jev-devtools-0.4.1.tgz
48
48
  ```
49
49
 
50
- Mount once:
50
+ Mount once. The drawer loads its own styles; no CSS import is needed:
51
51
 
52
52
  ```tsx
53
53
  import { JevDevtools } from 'jev-devtools';
54
- import 'jev-devtools/styles.css';
55
54
 
56
55
  <JevDevtools enabled={import.meta.env.DEV} defaultOpen={false} />
57
56
  ```
@@ -73,7 +72,7 @@ app.use(inspector.middleware); // Mount at root, before fallback handlers.
73
72
 
74
73
  Only calls through that client are captured. The React component subscribes via same-origin SSE, receiving an initial snapshot followed by changed traces. Older client/server combinations remain compatible through full snapshots. Both sides default to disabled. API keys remain server-side. Browser code is SSR-safe. The drawer uses a Shadow DOM boundary and its own stylesheet so host page selectors cannot alter its internal layout. Existing stylesheet imports remain supported; no Tailwind or shadcn installation is required. Drag the Flow canvas to pan, scroll/pinch to zoom, or use Fit.
75
74
 
76
- History is bounded to 50 calls in memory by default. API routes and inspector endpoints must share an adapter instance; distributed processes and edge runtimes are not supported. Non-loopback use requires explicit authorization. Captures include bounded/redacted context and answers, not hidden model reasoning.
75
+ History is bounded to 50 calls in memory by default. API routes and inspector endpoints must share an adapter instance; distributed processes and edge runtimes are not supported. Without `authorize`, the middleware accepts only loopback connections; Web Request handlers can't see the connection's address, so run those servers on `127.0.0.1`. Captures include bounded/redacted context and answers, not hidden model reasoning.
77
76
 
78
77
  For Next.js App Router, use `getJevDevtools(appKey, optionsFactory)` from `/server` to share an adapter across server reloads, expose `handleNodeRoute` through an optional catch-all Route Handler, and mount the root component in a client boundary. See the [Next.js guide](https://github.com/diegoag64/jev-devtools/blob/main/docs/nextjs.md) and runnable example. No Next dependency is required by this package.
79
78
 
@@ -81,9 +80,9 @@ For Next.js App Router, use `getJevDevtools(appKey, optionsFactory)` from `/serv
81
80
 
82
81
  MIT for project code. See bundled LICENSE and THIRD_PARTY_NOTICES.txt for original design attribution and TypeSafe branding.
83
82
 
84
- ## Connected Jev flows
83
+ ## Connected Jev flows with `jev-devtools/flow`
85
84
 
86
- New in **0.3.0**: define calls, conditions, parallel checks, and named outcomes with `jev-devtools/flow`. The drawer connects each request's complete diagram on one canvas. Condition cards show the incoming value and its requirement; optional titles and descriptions explain each step. Existing single-request integrations need no changes.
85
+ Define calls, conditions, parallel checks, and named outcomes with `jev-devtools/flow`. **0.4.0 adds** server function steps (`task`), runtime request factories, bounded collections (`map`), returning conditionals (`if`), stateful iteration (`loop`), and strict numeric `.gt()` comparisons. The drawer connects request diagrams, function results, selected paths, collection items, and loop iterations on one canvas. Existing single-request integrations remain supported.
87
86
 
88
87
  On the server, reuse the instrumented `jev` client and `inspector` from the setup above:
89
88
 
@@ -112,7 +111,7 @@ const result = await review.run({ message: 'Please review my ticket.' }, {
112
111
  // Handle result.outcome in your app. The flow does not execute business actions.
113
112
  ```
114
113
 
115
- Define once in a Node module and run per request. Use `flow.parallel` for concurrent named lanes; downstream calls wait for every lane to succeed. Branches are terminal in their block. Runs are in-process, with bounded inspector history, SDK retries, and cancellation support; they are not durable workflows.
114
+ Define once in a Node module and run per request. Use `flow.parallel` for concurrent named lanes; downstream calls wait for every lane to succeed. `flow.branch` is terminal in its block; use `flow.if` when selected values feed shared downstream work. Tasks execute application code on Node with JSON-compatible inputs/results; maps and loops have explicit bounds. Runs have SDK retries and cooperative cancellation, with bounded in-memory inspector history. They do not provide durable or distributed execution. Keep the 0.4.0 server and drawer together to inspect its new step kinds.
116
115
 
117
116
  [Full flow guide](https://github.com/diegoag64/jev-devtools/blob/main/docs/flows.md) · [AI flow recipe](https://github.com/diegoag64/jev-devtools/blob/main/packages/jev-devtools/skills/setup-jev-devtools/references/flows.md)
118
117
 
@@ -14,17 +14,19 @@ type Ast = {
14
14
  op: "literal";
15
15
  value: unknown;
16
16
  } | {
17
- op: "eq" | "lt" | "gte" | "all" | "any" | "not";
17
+ op: "eq" | "lt" | "gt" | "gte" | "all" | "any" | "not";
18
18
  args: Ast[];
19
19
  };
20
+ declare const valueTypeBrand: unique symbol;
20
21
  /** Inspectable expression, not an application callback. Use get() for input fields. */
21
22
  export declare class FlowExpression<T> {
22
23
  readonly ast: Ast;
23
- private readonly valueType;
24
+ protected readonly [valueTypeBrand]: T;
24
25
  constructor(ast: Ast, owner?: object);
25
26
  get<K extends keyof T & (string | number)>(key: K): FlowExpression<T[K]>;
26
27
  eq(value: T | FlowExpression<T>): FlowExpression<boolean>;
27
28
  lt(this: FlowExpression<number>, value: number | FlowExpression<number>): FlowExpression<boolean>;
29
+ gt(this: FlowExpression<number>, value: number | FlowExpression<number>): FlowExpression<boolean>;
28
30
  gte(this: FlowExpression<number>, value: number | FlowExpression<number>): FlowExpression<boolean>;
29
31
  }
30
32
  export declare const all: (...conditions: FlowExpression<boolean>[]) => FlowExpression<boolean>;
@@ -35,6 +37,10 @@ export type FlowOutcome = {
35
37
  value?: unknown;
36
38
  };
37
39
  type AnswerRefs<Q extends Questions> = {
40
+ /** All answers, including question names created by a runtime request factory. */
41
+ readonly answers: FlowExpression<{
42
+ [K in keyof Q]: ResultFor<Q[K]>;
43
+ }>;
38
44
  answer<K extends keyof Q & string>(name: K): {
39
45
  [F in keyof ResultFor<Q[K]>]: FlowExpression<ResultFor<Q[K]>[F]>;
40
46
  };
@@ -42,6 +48,28 @@ type AnswerRefs<Q extends Questions> = {
42
48
  type Request<Q extends Questions> = Omit<SystemOneRequest<Q>, "state"> & {
43
49
  state: unknown;
44
50
  };
51
+ /** Resolve nested references before invoking a server callback. */
52
+ export type Resolved<T> = T extends FlowExpression<infer V> ? V : T extends readonly unknown[] ? {
53
+ [K in keyof T]: Resolved<T[K]>;
54
+ } : T extends object ? {
55
+ [K in keyof T]: Resolved<T[K]>;
56
+ } : T;
57
+ export type TaskContext = {
58
+ signal: AbortSignal;
59
+ runId: string;
60
+ stepId: string;
61
+ executionId: string;
62
+ };
63
+ export type FlowTask<I, O> = {
64
+ input: I;
65
+ run: (input: Resolved<I>, context: TaskContext) => O | Promise<O>;
66
+ timeoutMs?: number;
67
+ };
68
+ export type FlowRequestFactory<I, Q extends Questions> = {
69
+ input: I;
70
+ /** Synchronous construction only; the runner executes the SDK request. */
71
+ request: (input: Resolved<I>) => SystemOneRequest<Q>;
72
+ };
45
73
  /** Observer contract used by createJevDevtools; execution never depends on successful recording. */
46
74
  export type FlowObserver = {
47
75
  startFlow(definition: FlowDefinition, run: FlowRun): {
@@ -58,9 +86,43 @@ export type FlowRunOptions = {
58
86
  devtools?: FlowObserver;
59
87
  signal?: AbortSignal;
60
88
  };
89
+ export declare class FlowLoopLimitError extends Error {
90
+ readonly name = "FlowLoopLimitError";
91
+ constructor();
92
+ }
61
93
  export interface FlowBuilder<T> {
62
94
  readonly input: FlowExpression<T>;
63
95
  jev<const Q extends Questions>(id: string, request: Request<Q>, display?: FlowDisplay): AnswerRefs<Q>;
96
+ jev<I, const Q extends Questions>(id: string, request: FlowRequestFactory<I, Q>, display?: FlowDisplay): AnswerRefs<Q>;
97
+ task<I, O>(id: string, task: FlowTask<I, O>, display?: FlowDisplay): FlowExpression<Awaited<O>>;
98
+ /** Execute a reusable item program with bounded concurrency; return results in input order. */
99
+ map<I, C, O>(id: string, options: {
100
+ items: FlowExpression<readonly I[]> | readonly I[];
101
+ input: C;
102
+ concurrency?: number;
103
+ maxItems: number;
104
+ }, build: (item: FlowBuilder<{
105
+ item: I;
106
+ index: number;
107
+ context: Resolved<C>;
108
+ }>) => FlowExpression<O>, display?: FlowDisplay): FlowExpression<O[]>;
109
+ /** Pre-test loop with JSON state, explicit context and a required iteration limit. */
110
+ loop<S, C>(id: string, options: {
111
+ initial: S | FlowExpression<S>;
112
+ input: C;
113
+ while: (state: FlowExpression<NoInfer<S>>) => FlowExpression<boolean>;
114
+ maxIterations: number;
115
+ }, build: (iteration: FlowBuilder<{
116
+ state: S;
117
+ index: number;
118
+ context: Resolved<C>;
119
+ }>) => FlowExpression<NoInfer<S>>, display?: FlowDisplay): FlowExpression<S>;
120
+ /** Execute one value-returning path, then continue with its typed result. */
121
+ if<A, B>(id: string, paths: {
122
+ when: FlowExpression<boolean>;
123
+ then: (path: FlowBuilder<T>) => FlowExpression<A>;
124
+ else: (path: FlowBuilder<T>) => FlowExpression<B>;
125
+ }, display?: FlowDisplay): FlowExpression<A | B>;
64
126
  /** Terminal decision: each callback must return output() or another branch(). */
65
127
  branch(id: string, paths: {
66
128
  when: FlowExpression<boolean>;