jev-devtools 0.5.0 → 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 CHANGED
@@ -1,10 +1,10 @@
1
1
  # Jev DevTools
2
2
 
3
- [![npm version 0.5.0](https://img.shields.io/badge/npm-v0.5.0-E551BA?labelColor=0D0A0F&logo=npm)](https://www.npmjs.com/package/jev-devtools/v/0.5.0)
3
+ [![npm version 0.5.1](https://img.shields.io/badge/npm-v0.5.1-E551BA?labelColor=0D0A0F&logo=npm)](https://www.npmjs.com/package/jev-devtools/v/0.5.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
- **Source version: 0.5.0.** Check the [release history](https://github.com/diegoag64/jev-devtools/blob/main/CHANGELOG.md) for npm availability. If trying this source before publication, install the local archive below. **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
+ **Source version: 0.5.1.** Check the [release history](https://github.com/diegoag64/jev-devtools/blob/main/CHANGELOG.md) for npm availability. If trying this source before publication, install the local archive below. **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
  [Website](https://jevtools.dev/) · [Documentation](https://docs.jevtools.dev/) · [Quick start](https://docs.jevtools.dev/getting-started/) · [API reference](https://docs.jevtools.dev/api/reference/) · [Release history](https://docs.jevtools.dev/changelog/)
10
10
 
@@ -39,6 +39,8 @@ Install from npm:
39
39
  pnpm add jev-devtools @typesafe-ai/sdk
40
40
  ```
41
41
 
42
+ Use ESM imports, matching React/React DOM **18.x or 19.x**, Node **22.12+**, and TypeSafe SDK **0.7.x**. TypeScript consumers need **5.4+** and compatible React/Node types. React and React DOM are required package peers even for server-only installations. See [compatibility and all public imports](https://github.com/diegoag64/jev-devtools/blob/main/docs/integration.md#compatibility-and-public-imports); `/server` and `/flow` run only on Node.
43
+
42
44
  To try local changes before a future release:
43
45
 
44
46
  ```sh
@@ -46,7 +48,7 @@ To try local changes before a future release:
46
48
  npm ci
47
49
  npm run pack:devtools
48
50
  # From the consuming app:
49
- npm install /absolute/path/to/jev-devtools-0.5.0.tgz
51
+ npm install /absolute/path/to/jev-devtools-0.5.1.tgz
50
52
  ```
51
53
 
52
54
  Mount once. The drawer loads its own styles; no CSS import is needed:
@@ -72,7 +74,7 @@ app.use(inspector.middleware); // Mount at root, before fallback handlers.
72
74
  // Call inspector.dispose() on shutdown.
73
75
  ```
74
76
 
75
- 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.
77
+ 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. No Tailwind or shadcn installation is required. Drag the Flow canvas to pan, scroll/pinch to zoom, or use Fit.
76
78
 
77
79
  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.
78
80
 
@@ -26,7 +26,8 @@ export declare class FlowExpression<T> {
26
26
  readonly ast: Ast;
27
27
  protected readonly [valueTypeBrand]: T;
28
28
  constructor(ast: Ast, owner?: object);
29
- get<K extends keyof T & (string | number)>(key: K): FlowExpression<T[K]>;
29
+ /** Select an own field from an object or array; primitive properties are unsupported. */
30
+ get<K extends keyof T & (string | number)>(this: FlowExpression<object>, key: K): FlowExpression<T[K]>;
30
31
  eq(value: T | FlowExpression<T>): FlowExpression<boolean>;
31
32
  lt(this: FlowExpression<number>, value: number | FlowExpression<number>): FlowExpression<boolean>;
32
33
  gt(this: FlowExpression<number>, value: number | FlowExpression<number>): FlowExpression<boolean>;
package/lib/flow/index.js CHANGED
@@ -32,6 +32,7 @@ export class FlowExpression {
32
32
  freezeAst(ast);
33
33
  Object.freeze(this);
34
34
  }
35
+ /** Select an own field from an object or array; primitive properties are unsupported. */
35
36
  get(key) {
36
37
  return expression({ op: "get", from: this.ast, key: String(key) }, [this]);
37
38
  }
@@ -89,7 +90,7 @@ const plain = (v) => !!v &&
89
90
  typeof v === "object" &&
90
91
  (Object.getPrototypeOf(v) === Object.prototype ||
91
92
  Object.getPrototypeOf(v) === null);
92
- function mapValue(value, ref, seen = new Set(), depth = 0) {
93
+ function mapValue(value, ref, rejectSymbolKeys = false, seen = new Set(), depth = 0) {
93
94
  if (depth > 40)
94
95
  throw new Error("Flow value nesting exceeds 40");
95
96
  if (value instanceof FlowExpression)
@@ -98,14 +99,17 @@ function mapValue(value, ref, seen = new Set(), depth = 0) {
98
99
  throw new Error("Flow values cannot contain callbacks");
99
100
  if (!Array.isArray(value) && !plain(value))
100
101
  return value; // Preserve SDK assets.
102
+ // Task templates must reject keys that copying would otherwise silently discard.
103
+ if (rejectSymbolKeys && Object.getOwnPropertySymbols(value).length)
104
+ throw new TypeError("Task data cannot contain symbol keys");
101
105
  if (seen.has(value))
102
106
  throw new Error("Cyclic flow value");
103
107
  seen.add(value);
104
108
  const result = Array.isArray(value)
105
- ? value.map((v) => mapValue(v, ref, seen, depth + 1))
109
+ ? value.map((v) => mapValue(v, ref, rejectSymbolKeys, seen, depth + 1))
106
110
  : Object.fromEntries(Object.entries(value).map(([k, v]) => [
107
111
  k,
108
- mapValue(v, ref, seen, depth + 1),
112
+ mapValue(v, ref, rejectSymbolKeys, seen, depth + 1),
109
113
  ]));
110
114
  seen.delete(value);
111
115
  return result;
@@ -380,7 +384,7 @@ export function defineJevFlow(name, build) {
380
384
  task.timeoutMs < 1 ||
381
385
  task.timeoutMs > 2_147_483_647))
382
386
  throw new Error("Task timeoutMs must be a positive 32-bit integer");
383
- const frozen = mapValue(task.input, (expr) => expr);
387
+ const frozen = mapValue(task.input, (expr) => expr, true);
384
388
  const step = add(id, "task", frozen, display);
385
389
  step.input = frozen;
386
390
  step.task = task.run;
@@ -807,7 +811,7 @@ export function defineJevFlow(name, build) {
807
811
  abortCheck();
808
812
  }
809
813
  else if (step.kind === "task") {
810
- const input = jsonSnapshot(mapValue(step.input, (e) => evaluate(e.ast, data, values)));
814
+ const input = jsonSnapshot(mapValue(step.input, (e) => evaluate(e.ast, data, values), true));
811
815
  state.data = { input };
812
816
  emit();
813
817
  value = jsonSnapshot(await runCallback((signal) => step.task(jsonSnapshot(input), {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jev-devtools",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.12"
@@ -17,16 +17,30 @@ Inspect the app's package manager/lockfile, React and Node versions, root compon
17
17
  - **React with a Node backend, including Vite:** read [references/react-node.md](references/react-node.md). A browser-only app needs a Node server for Jev; the Vite development-server option is local only.
18
18
  - **Other Node frameworks:** use the Web Request/Response or middleware API described in the React/Node reference and verify that framework's routing. Do not assume the Next.js recipe applies to Pages Router. Edge runtimes and cross-process/serverless history aggregation are unsupported.
19
19
 
20
- Install `jev-devtools` using the app's existing package manager (npm: `npm install jev-devtools @typesafe-ai/sdk`; pnpm: `pnpm add jev-devtools @typesafe-ai/sdk`; Yarn: `yarn add jev-devtools @typesafe-ai/sdk`; Bun: `bun add jev-devtools @typesafe-ai/sdk`). `@typesafe-ai/sdk` is a peer dependency: keep the app's existing SDK version when it satisfies the peer range. Install in the workspace(s) that actually import it. It is a regular dependency when application modules import it, even though capture/display are development-only. React 18/19 and Node 22.12+ are required. Do not replace the app's framework or upgrade unrelated dependencies to force compatibility.
20
+ Install `jev-devtools` using the app's existing package manager (npm: `npm install jev-devtools @typesafe-ai/sdk`; pnpm: `pnpm add jev-devtools @typesafe-ai/sdk`; Yarn: `yarn add jev-devtools @typesafe-ai/sdk`; Bun: `bun add jev-devtools @typesafe-ai/sdk`). `@typesafe-ai/sdk` is a peer dependency: keep the app's existing SDK version when it satisfies the peer range. Install in the workspace(s) that actually import it. It is a regular dependency when application modules import it, even though capture/display are development-only. Do not replace the app's framework or upgrade unrelated dependencies to force compatibility.
21
+
22
+ ### Consumer compatibility (0.5.1)
23
+
24
+ | Requirement | Contract |
25
+ | --- | --- |
26
+ | Node.js | 22.12+ for the adapter and flow execution; Edge runtimes are unsupported. |
27
+ | React / React DOM | Matching 18.x or 19.x versions; both remain required peers even for server-only installations. |
28
+ | TypeSafe SDK | `>=0.7.0 <0.8.0`; verified with 0.7.0. |
29
+ | TypeScript | 5.4+ for the public declarations; verified with 5.4.5. The flow API uses built-in `NoInfer`, which 5.3.3 cannot resolve. |
30
+ | Modules | ESM imports with export-aware `Bundler` or `NodeNext` resolution and a matching module setting. There is no CommonJS `require` entry. |
31
+
32
+ Use React types matching the app's React major, Node types compatible with its runtime/compiler, and DOM types for Web `Request`/`Response` APIs. The packed declarations passed strict checks without `skipLibCheck` using TypeScript 5.4.5, SDK 0.7.0, React 19.3.1, `@types/react` 19.3.0, and `@types/node` 22.10.2. Frameworks and newer type packages can require a newer compiler; preserve compatible app dependencies rather than downgrading to this verification baseline.
33
+
34
+ Import application APIs from `jev-devtools` (React and browser-safe capture helpers/types), or the Node-only `jev-devtools/server` and `jev-devtools/flow` entries. Internal paths and `jev-devtools/package.json` are not exported. Inspect the installed version through the package manager, and read declaration/skill files from disk rather than importing internal package paths. Always supply the browser `origin` to the server adapter, including when `enabled: false`.
21
35
 
22
36
  ## Optional connected flow API
23
37
 
24
- When asked to build or inspect connected calls, read [references/flows.md](references/flows.md) after the framework recipe. For 0.5.0 it covers requests, server function tasks, runtime request factories, parallel checks, terminal branches, returning conditionals, bounded collections, stateful loops, captured-answer replay, and fixture comparison. It includes sample verification, scope/execution rules, and privacy limits. For an older installation, use its bundled recipe and declarations rather than assuming these operations exist. Preserve existing application policies; do not convert unrelated calls just to install the drawer. Flow execution is server-only and continues even with the inspector disabled.
38
+ When asked to build or inspect connected calls, read [references/flows.md](references/flows.md) after the framework recipe. For 0.5.1 it covers requests, server function tasks, runtime request factories, parallel checks, terminal branches, returning conditionals, bounded collections, stateful loops, captured-answer replay, and fixture comparison. It includes sample verification, scope/execution rules, and privacy limits. For an older installation, use its bundled recipe and declarations rather than assuming these operations exist. Preserve existing application policies; do not convert unrelated calls just to install the drawer. Flow execution is server-only and continues even with the inspector disabled.
25
39
 
26
40
  ## Integration invariants
27
41
 
28
42
  - Use only public imports: `jev-devtools` and server-only `jev-devtools/server` and `jev-devtools/flow`. No provider, TanStack DevTools shell, Tailwind, or shadcn setup is needed; TanStack Virtual is an internal dependency.
29
- - Mount `<JevDevtools />` once with an explicit development flag. The drawer injects its own stylesheet into a Shadow DOM host under `document.body`; do not add a `jev-devtools/styles.css` import during setup. The CSS export remains supported for existing integrations. Do not add global `section`/`main` padding overrides to repair the drawer. Keep document-level transforms/filters on an app wrapper rather than `html`/`body`, which can affect fixed overlays. Server capture must also be explicitly enabled for development; both default to off.
43
+ - Mount `<JevDevtools />` once with an explicit development flag. The drawer injects its own stylesheet into a Shadow DOM host under `document.body`; no CSS import is needed. Do not add global `section`/`main` padding overrides to repair the drawer. Keep document-level transforms/filters on an app wrapper rather than `html`/`body`, which can affect fixed overlays. Server capture must also be explicitly enabled for development; both default to off.
30
44
  - Create a shared adapter; the inspector routes and application client must use it in the same Node realm/process. Replace existing `new TypeSafeClient(options)` construction with `inspector.createClient(options)` while preserving keys, transport options, retries, and call semantics. Keep existing `systemOne(...)` inputs and application behavior.
31
45
  - Browser and server `endpoint` paths must agree (default `/api/jev-devtools`). Route the base path and its subpaths. The server `origin` must exactly match the browser scheme, hostname, and port. A browser endpoint is a same-origin path, not an absolute backend URL.
32
46
  - Keep keys in server environment variables, never `VITE_*` or `NEXT_PUBLIC_*`. Use the host's environment loader and an ignored local env file. If a key is unavailable, complete setup and use the clearly labeled sample verification in [references/verify.md](references/verify.md); report that live inference remains unverified.
@@ -1,6 +1,6 @@
1
1
  # Build and inspect a Jev flow
2
2
 
3
- For `jev-devtools` **0.5.0**. Check the repository changelog for publication status when installing from source. Read the installed package version and `lib/flow/index.d.ts` first; online `main` may describe newer APIs. Use this recipe when the user asks to connect multiple Jev requests, inspect branches, run server function steps, or visualize parallel checks, collections, and loops. Adding the inspector alone does not require converting existing calls to flows.
3
+ For `jev-devtools` **0.5.1**. Check the repository changelog for publication status when installing from source. Read the installed package version and `lib/flow/index.d.ts` first; online `main` may describe newer APIs. Use this recipe when the user asks to connect multiple Jev requests, inspect branches, run server function steps, or visualize parallel checks, collections, and loops. Adding the inspector alone does not require converting existing calls to flows.
4
4
 
5
5
  ## Plan the integration
6
6
 
@@ -9,7 +9,7 @@ For `jev-devtools` **0.5.0**. Check the repository changelog for publication sta
9
9
  3. Import `defineJevFlow` (and `all`, `any`, `not` when needed) from **server-only** `jev-devtools/flow`. Define the graph once at module scope and call `run()` from the existing Node handler/action. In Next.js, mark the module `server-only` and keep the executing route/segment on the Node runtime. No new client provider or root component is needed.
10
10
  4. A named output returns data and does not execute an action. Existing business actions can remain in the application after the returned outcome; use `task()` when the user wants application-owned server work included in the flow. Preserve authorization, idempotency, input validation, and error handling. Cancellation cannot undo side effects.
11
11
 
12
- If importing question helpers as below, install `@typesafe-ai/sdk` as a direct dependency in the server workspace using its package manager. Version 0.5.0 requires SDK `>=0.7.0 <0.8.0` and is verified with 0.7.0; preserve a compatible existing version rather than silently changing SDK semantics. The flow also accepts ordinary SDK question objects.
12
+ If importing question helpers as below, install `@typesafe-ai/sdk` as a direct dependency in the server workspace using its package manager. Version 0.5.1 requires SDK `>=0.7.0 <0.8.0` and is verified with 0.7.0; preserve a compatible existing version rather than silently changing SDK semantics. The flow also accepts ordinary SDK question objects.
13
13
 
14
14
  ## Complete example: parallel checks, one decision, two outcomes
15
15
 
@@ -94,7 +94,7 @@ For custom server-derived sessions, use `await inspector.run(request, () => revi
94
94
  | `flow.if(id, { when, then, else }, display?)` | Only the selected path executes. Each arm returns an expression; the merged result can feed shared downstream steps. |
95
95
  | `flow.map(id, { items, input, concurrency?, maxItems }, buildItem, display?)` | Run a bounded item program over an array; preserve result order. Pass outer values through `input`; return an expression from the item builder. |
96
96
  | `flow.loop(id, { initial, input, while, maxIterations }, buildIteration, display?)` | Check a state expression before each iteration; return a same-type state expression from the body. Reject unfinished limit exhaustion with `FlowLoopLimitError`. |
97
- | `flow.input.get('field')` | A typed expression referencing the run input, not a value available during definition. |
97
+ | `flow.input.get('field')` | Select an own object field, array index, or array `length` as a typed reference resolved during execution. Primitive properties (string length/indexes included) are unsupported; compute them in a task callback. |
98
98
  | `result.answer('name')` | Typed answer fields: `.choice`, `.probabilities.get('label')`, `.confidence`, `.noul`, `.score`, `.legend`, or `.box` as supported by that question's SDK result. Select nested fields with `.get(key)`. |
99
99
  | `value.eq(other)`, `.lt(number)`, `.gt(number)`, `.gte(number)` | Matching scalar equality or finite numeric comparisons. Noul values are in 0–1; use `0.9`, not `90`. There is no `.lte()` method. |
100
100
  | `all(...)`, `any(...)`, `not(...)` | Compose Boolean expressions. Every operand evaluates; missing values are errors even in an otherwise short-circuited expression. Do not use JavaScript `if`, `&&`, or `>=` on expression objects. |
@@ -170,7 +170,7 @@ Remove temporary verification routes unless requested as a demo. Report the inst
170
170
 
171
171
  Use `task()` for application-owned server work and the request-factory overload of `jev()` when later questions depend on earlier results. Both are part of 0.4.0.
172
172
 
173
- - `flow.task(id, { input, run, timeoutMs? }, display?)`: resolve nested input references, run server code, and return a typed reference. `run(input, { signal, runId, stepId, executionId })` may return a value or Promise. Inputs/outputs must be plain JSON-compatible values; return null rather than undefined. No implicit retry.
173
+ - `flow.task(id, { input, run, timeoutMs? }, display?)`: resolve nested input references, run server code, and return a typed reference. `run(input, { signal, runId, stepId, executionId })` may return a value or Promise. Inputs/outputs must be plain JSON-compatible values; return null rather than undefined. Task input literals with own symbol keys (including nested objects and arrays) fail during definition; referenced values are checked before the callback runs. No implicit retry.
174
174
  - `flow.jev(id, { input, request: resolved => ({ state, questions, ...sdkOptions }) }, display?)`: construct a synchronous SDK request at execution time. Do asynchronous work in a preceding task. Keep Jev calls here to retain full linked diagrams. Never return unresolved flow references or a Promise from the factory.
175
175
  - Every Jev step also has `.answers`, a typed reference to the complete answer map. Pass it to a task when a request factory generates question names dynamically; do not create new `.answer()` references inside a runtime callback.
176
176
  - Neither callback executes at definition time. Runtime input snapshots protect previous values from mutation. Task timeouts/cancellation stop downstream work and signal the callback, but cannot undo side effects or forcibly interrupt synchronous code.