From b10868d710ee995a081bef9775574ea349509559 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Sat, 1 Aug 2026 22:26:02 +0000 Subject: [PATCH 1/8] feat(rpc): accept any Standard Schema for RPC args/returns and validate at runtime Widen `args`/`returns` from valibot's `GenericSchema` to the `StandardSchemaV1` interface so valibot, zod, arktype, and any other Standard Schema validator work interchangeably, and infer handler types from the schema's Standard Schema input types. Every invocation path (local, over-the-wire, agent/MCP) now validates declared `args`/`returns` at the boundary via `getRpcHandler`, throwing coded diagnostics DF0043 / DF0044 on failure. Validation is guard-only: payloads are never rewritten, so schemas describing a subset of an object don't strip the sender's extra fields. The MCP JSON-schema surface keeps valibot's converter for precise schemas and degrades non-valibot vendors to a permissive object schema. --- docs/errors/DF0043.md | 38 +++++ docs/errors/DF0044.md | 33 +++++ docs/guide/rpc.md | 4 +- packages/devframe/package.json | 1 + .../mcp/__tests__/to-json-schema.test.ts | 47 +++++-- .../devframe/src/adapters/mcp/build-server.ts | 10 +- .../src/adapters/mcp/to-json-schema.ts | 33 +++-- packages/devframe/src/node/host-agent.ts | 3 +- packages/devframe/src/rpc/diagnostics.ts | 10 ++ packages/devframe/src/rpc/handler.ts | 32 ++++- packages/devframe/src/rpc/index.ts | 1 + packages/devframe/src/rpc/types.test.ts | 24 ++++ packages/devframe/src/rpc/types.ts | 28 ++-- packages/devframe/src/rpc/utils.ts | 16 +-- packages/devframe/src/rpc/validate-io.test.ts | 131 ++++++++++++++++++ packages/devframe/src/rpc/validate-io.ts | 84 +++++++++++ pnpm-lock.yaml | 6 + pnpm-workspace.yaml | 1 + .../tsnapi/devframe/rpc.snapshot.d.ts | 2 + .../tsnapi/devframe/rpc.snapshot.js | 2 + 20 files changed, 451 insertions(+), 55 deletions(-) create mode 100644 docs/errors/DF0043.md create mode 100644 docs/errors/DF0044.md create mode 100644 packages/devframe/src/rpc/validate-io.test.ts create mode 100644 packages/devframe/src/rpc/validate-io.ts diff --git a/docs/errors/DF0043.md b/docs/errors/DF0043.md new file mode 100644 index 00000000..a55f0d33 --- /dev/null +++ b/docs/errors/DF0043.md @@ -0,0 +1,38 @@ +--- +outline: deep +--- + +# DF0043: Invalid RPC Argument + +## Message + +> RPC function "`{name}`" received an invalid argument at position `{index}`: `{issues}` + +## Cause + +When an RPC function declares `args` schemas, each incoming argument is validated against its positional [Standard Schema](https://standardschema.dev/) (valibot, zod, arktype, …) before the handler runs — on every path: local calls, over-the-wire calls, and the agent/MCP bridge. The argument at `{index}` failed that schema. Validation guards the payload without rewriting it, so extra object fields the schema doesn't mention still reach the handler. + +## Example + +```ts +const greet = defineRpcFunction({ + name: 'greet', + args: [v.string()], + returns: v.string(), + handler: name => `hi ${name}`, +}) + +// ✓ Good +await ctx.rpc.functions.greet('ada') + +// ✗ Bad — a number where a string is required → DF0043 at position 0 +await ctx.rpc.functions.greet(42 as never) +``` + +## Fix + +Pass a value that satisfies the `args` schema declared for the function, or widen the schema if the value is legitimately allowed. + +## Source + +- [`packages/devframe/src/rpc/validate-io.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/rpc/validate-io.ts) — `validateRpcArgs()` throws `DF0043` on the first argument that fails its declared schema. diff --git a/docs/errors/DF0044.md b/docs/errors/DF0044.md new file mode 100644 index 00000000..3cbdb157 --- /dev/null +++ b/docs/errors/DF0044.md @@ -0,0 +1,33 @@ +--- +outline: deep +--- + +# DF0044: Invalid RPC Return Value + +## Message + +> RPC function "`{name}`" returned a value that failed its `returns` schema: `{issues}` + +## Cause + +When an RPC function declares a `returns` schema, the handler's resolved value is validated against that [Standard Schema](https://standardschema.dev/) (valibot, zod, arktype, …) before it is sent back to the caller. The value the handler produced does not satisfy the schema — a bug in the handler or a schema that is narrower than the real result. Validation guards the payload without rewriting it, so a value that merely carries extra object fields is accepted. + +## Example + +```ts +const count = defineRpcFunction({ + name: 'count', + args: [], + returns: v.number(), + // ✗ Bad — returns a string where a number is declared → DF0044 + handler: () => 'twelve' as never, +}) +``` + +## Fix + +Make the handler return a value that satisfies the `returns` schema, or relax the schema so it describes the value the handler actually produces. + +## Source + +- [`packages/devframe/src/rpc/validate-io.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/rpc/validate-io.ts) — `validateRpcReturn()` throws `DF0044` when the handler's resolved value fails its declared schema. diff --git a/docs/guide/rpc.md b/docs/guide/rpc.md index 6a185a0c..00ba0fc1 100644 --- a/docs/guide/rpc.md +++ b/docs/guide/rpc.md @@ -4,7 +4,7 @@ outline: deep # RPC -Devframe's RPC layer is type-safe bidirectional communication between your server (Node.js) and client (browser), built on [`birpc`](https://github.com/antfu/birpc) and validated at runtime with [`valibot`](https://valibot.dev/). In dev mode it runs over WebSocket; in build / SPA mode it serves a pre-computed static dump so the client still works offline. +Devframe's RPC layer is type-safe bidirectional communication between your server (Node.js) and client (browser), built on [`birpc`](https://github.com/antfu/birpc) and validated at runtime against any [Standard Schema](https://standardschema.dev/) validator — valibot, zod, arktype, and others all work. In dev mode it runs over WebSocket; in build / SPA mode it serves a pre-computed static dump so the client still works offline. ## Overview @@ -75,7 +75,7 @@ Use `static` for data collected once during `setup` and shipped to read-only sta ### Handler arguments -Handlers accept any serializable arguments. With `args` valibot schemas, arguments are validated at the boundary: +Handlers accept any serializable arguments. Declare `args` schemas — any [Standard Schema](https://standardschema.dev/) validator, valibot below — and each argument is validated at the boundary before the handler runs; a mismatch is rejected with a coded diagnostic. Validation guards the payload without rewriting it, so extra object fields the schema doesn't mention still reach the handler: ```ts defineRpcFunction({ diff --git a/packages/devframe/package.json b/packages/devframe/package.json index 0333afa4..41c74bab 100644 --- a/packages/devframe/package.json +++ b/packages/devframe/package.json @@ -82,6 +82,7 @@ } }, "dependencies": { + "@standard-schema/spec": "catalog:deps", "@valibot/to-json-schema": "catalog:deps", "birpc": "catalog:deps", "crossws": "catalog:deps", diff --git a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts b/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts index b275ec57..cd4954df 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts +++ b/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts @@ -1,16 +1,16 @@ import * as v from 'valibot' import { describe, expect, it } from 'vitest' -import { valibotArgsToJsonSchema, valibotReturnToJsonSchema } from '../to-json-schema' +import { argsToJsonSchema, returnToJsonSchema } from '../to-json-schema' -describe('valibotArgsToJsonSchema', () => { +describe('argsToJsonSchema', () => { it('returns an empty object schema when no args', () => { - const { schema, unwrapped } = valibotArgsToJsonSchema(undefined) + const { schema, unwrapped } = argsToJsonSchema(undefined) expect(unwrapped).toBe(false) expect(schema).toEqual({ type: 'object', properties: {} }) }) it('wraps multiple positional args under arg0/arg1/...', () => { - const { schema, unwrapped } = valibotArgsToJsonSchema([v.string(), v.number()]) + const { schema, unwrapped } = argsToJsonSchema([v.string(), v.number()]) expect(unwrapped).toBe(false) expect(schema).toMatchObject({ type: 'object', @@ -23,7 +23,7 @@ describe('valibotArgsToJsonSchema', () => { }) it('unwraps a single object schema for nicer agent UX', () => { - const { schema, unwrapped } = valibotArgsToJsonSchema([ + const { schema, unwrapped } = argsToJsonSchema([ v.object({ name: v.string(), age: v.number() }), ]) expect(unwrapped).toBe(true) @@ -34,20 +34,49 @@ describe('valibotArgsToJsonSchema', () => { }) it('keeps arg0 shape when the single arg is a primitive', () => { - const { schema, unwrapped } = valibotArgsToJsonSchema([v.string()]) + const { schema, unwrapped } = argsToJsonSchema([v.string()]) expect(unwrapped).toBe(false) expect(schema).toMatchObject({ type: 'object', required: ['arg0'] }) }) }) -describe('valibotReturnToJsonSchema', () => { +describe('returnToJsonSchema', () => { it('returns undefined when no schema is provided', () => { - expect(valibotReturnToJsonSchema(undefined)).toBeUndefined() + expect(returnToJsonSchema(undefined)).toBeUndefined() }) it('converts a simple schema', () => { - const schema = valibotReturnToJsonSchema(v.object({ ok: v.boolean() })) + const schema = returnToJsonSchema(v.object({ ok: v.boolean() })) expect((schema as any).type).toBe('object') expect((schema as any).properties.ok).toMatchObject({ type: 'boolean' }) }) }) + +describe('non-valibot Standard Schemas', () => { + // A minimal Standard Schema from a made-up vendor (mirrors zod/arktype, + // which devframe core does not depend on) — no valibot internals. + const foreign = { + '~standard': { + version: 1 as const, + vendor: 'acme', + validate: (value: unknown) => ({ value }), + }, + } + + it('falls back to a permissive object per positional arg', () => { + const { schema, unwrapped } = argsToJsonSchema([foreign, foreign]) + expect(unwrapped).toBe(false) + expect(schema).toMatchObject({ type: 'object', required: ['arg0', 'arg1'] }) + expect((schema as any).properties.arg0).toEqual({ type: 'object', additionalProperties: true }) + }) + + it('unwraps a single foreign arg to the permissive object', () => { + const { schema, unwrapped } = argsToJsonSchema([foreign]) + expect(unwrapped).toBe(true) + expect(schema).toEqual({ type: 'object', additionalProperties: true }) + }) + + it('falls back to a permissive object schema for returns', () => { + expect(returnToJsonSchema(foreign)).toEqual({ type: 'object', additionalProperties: true }) + }) +}) diff --git a/packages/devframe/src/adapters/mcp/build-server.ts b/packages/devframe/src/adapters/mcp/build-server.ts index 5bb0ba4f..0e3840bb 100644 --- a/packages/devframe/src/adapters/mcp/build-server.ts +++ b/packages/devframe/src/adapters/mcp/build-server.ts @@ -1,6 +1,6 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec' import type { RpcFunctionDefinitionAnyWithContext } from 'devframe/rpc' import type { AgentTool, DevframeDefinition, DevframeHost, DevframeNodeContext } from 'devframe/types' -import type { GenericSchema } from 'valibot' import { homedir } from 'node:os' import process from 'node:process' import { Server } from '@modelcontextprotocol/sdk/server/index.js' @@ -14,7 +14,7 @@ import { createHostContext } from 'devframe/node' import { join } from 'pathe' import { diagnostics } from '../../node/diagnostics' import { formatMcpError, stringifyForMcp } from './stringify' -import { valibotArgsToJsonSchema, valibotReturnToJsonSchema } from './to-json-schema' +import { argsToJsonSchema, returnToJsonSchema } from './to-json-schema' export interface CreateMcpServerOptions { /** @@ -276,8 +276,8 @@ function computeInputSchema(tool: AgentTool, ctx: DevframeNodeContext): unknown const def = ctx.rpc.definitions.get(tool.rpcName) as RpcFunctionDefinitionAnyWithContext | undefined if (!def) return { type: 'object', properties: {} } - const args = def.args as readonly GenericSchema[] | undefined - return valibotArgsToJsonSchema(args).schema + const args = def.args as readonly StandardSchemaV1[] | undefined + return argsToJsonSchema(args).schema } function computeOutputSchema(tool: AgentTool, ctx: DevframeNodeContext): unknown { @@ -286,7 +286,7 @@ function computeOutputSchema(tool: AgentTool, ctx: DevframeNodeContext): unknown const def = ctx.rpc.definitions.get(tool.rpcName) as RpcFunctionDefinitionAnyWithContext | undefined if (!def) return undefined - return valibotReturnToJsonSchema(def.returns as GenericSchema | undefined) + return returnToJsonSchema(def.returns as StandardSchemaV1 | undefined) } function parseResourceUri(uri: string): { kind: 'resource', id: string } | { kind: 'state', key: string } | { kind: 'unknown' } { diff --git a/packages/devframe/src/adapters/mcp/to-json-schema.ts b/packages/devframe/src/adapters/mcp/to-json-schema.ts index ccf1b144..dd0d2708 100644 --- a/packages/devframe/src/adapters/mcp/to-json-schema.ts +++ b/packages/devframe/src/adapters/mcp/to-json-schema.ts @@ -1,34 +1,33 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec' import type { GenericSchema } from 'valibot' import { toJsonSchema } from '@valibot/to-json-schema' const FALLBACK_OBJECT_SCHEMA = Object.freeze({ type: 'object', additionalProperties: true }) /** - * Convert a valibot return schema to JSON Schema. + * Convert a Standard Schema return value to JSON Schema for the agent + * surface. valibot schemas convert precisely; other Standard Schema + * vendors (zod, arktype, …) have no universal JSON Schema mapping and + * fall back to a permissive object schema. * @internal */ -export function valibotReturnToJsonSchema(schema: GenericSchema | undefined): unknown { +export function returnToJsonSchema(schema: StandardSchemaV1 | undefined): unknown { if (!schema) return undefined - try { - return toJsonSchema(schema as any) - } - catch { - return FALLBACK_OBJECT_SCHEMA - } + return safeToJsonSchema(schema) } /** * Convert positional RPC args schemas to a single MCP-friendly object - * schema. When the RPC declares `args: [v.object(...)]`, unwrap the - * single-object schema directly (nicer agent UX than `{ arg0: {...} }`). + * schema. When the RPC declares `args: [object(...)]`, unwrap the single + * object schema directly (nicer agent UX than `{ arg0: {...} }`). * * Returns `undefined` when there are no args (the MCP SDK treats this * as `{ type: 'object', properties: {} }`). * @internal */ -export function valibotArgsToJsonSchema( - args: readonly GenericSchema[] | undefined, +export function argsToJsonSchema( + args: readonly StandardSchemaV1[] | undefined, ): { schema: unknown, unwrapped: boolean } { if (!args || args.length === 0) return { schema: { type: 'object', properties: {} }, unwrapped: false } @@ -48,7 +47,7 @@ export function valibotArgsToJsonSchema( const s = safeToJsonSchema(args[i]!) properties[key] = s // Conservatively mark every positional arg as required — the RPC - // layer validates against valibot anyway. + // layer validates against the declared schema anyway. required.push(key) } @@ -63,9 +62,13 @@ export function valibotArgsToJsonSchema( } } -function safeToJsonSchema(schema: GenericSchema): unknown { +function safeToJsonSchema(schema: StandardSchemaV1): unknown { + // Only valibot exposes a JSON Schema converter; other vendors degrade + // to a permissive object schema rather than throwing. + if (schema['~standard']?.vendor !== 'valibot') + return FALLBACK_OBJECT_SCHEMA try { - return toJsonSchema(schema as any) + return toJsonSchema(schema as unknown as GenericSchema) } catch { return FALLBACK_OBJECT_SCHEMA diff --git a/packages/devframe/src/node/host-agent.ts b/packages/devframe/src/node/host-agent.ts index 5ccb6441..12245734 100644 --- a/packages/devframe/src/node/host-agent.ts +++ b/packages/devframe/src/node/host-agent.ts @@ -199,7 +199,8 @@ export class DevframeAgentHost implements DevframeAgentHostType { rpcName: name, examples: agent.examples, // Schemas are carried by the definition itself — consumers - // (e.g. the MCP adapter) convert valibot → JSON Schema on demand. + // (e.g. the MCP adapter) convert the Standard Schema → JSON Schema + // on demand. }) } return out diff --git a/packages/devframe/src/rpc/diagnostics.ts b/packages/devframe/src/rpc/diagnostics.ts index d8df5841..3ba5b711 100644 --- a/packages/devframe/src/rpc/diagnostics.ts +++ b/packages/devframe/src/rpc/diagnostics.ts @@ -41,5 +41,15 @@ export const diagnostics = defineDiagnostics({ why: (p: { name: string, type: string }) => `Function "${p.name}" with type "${p.type}" cannot use \`snapshot: true\`. Only "query" functions support this sugar; "static" functions have equivalent default behavior already.`, fix: 'Remove `snapshot: true`, or change the function type to `query`.', }, + DF0043: { + why: (p: { name: string, index: number, issues: string }) => + `RPC function "${p.name}" received an invalid argument at position ${p.index}: ${p.issues}`, + fix: 'Pass a value that satisfies the `args` schema declared for this function.', + }, + DF0044: { + why: (p: { name: string, issues: string }) => + `RPC function "${p.name}" returned a value that failed its \`returns\` schema: ${p.issues}`, + fix: 'Make the handler return a value that satisfies the `returns` schema, or relax the schema.', + }, }, }) diff --git a/packages/devframe/src/rpc/handler.ts b/packages/devframe/src/rpc/handler.ts index f522b79e..5b1a23b5 100644 --- a/packages/devframe/src/rpc/handler.ts +++ b/packages/devframe/src/rpc/handler.ts @@ -1,5 +1,6 @@ import type { RpcFunctionDefinition, RpcFunctionSetupResult, RpcFunctionType } from './types' import { diagnostics } from './diagnostics' +import { validateRpcArgs, validateRpcReturn } from './validate-io' export async function getRpcResolvedSetupResult< NAME extends string, @@ -58,12 +59,31 @@ export async function getRpcHandler< definition: RpcFunctionDefinition, context: CONTEXT, ): Promise<(...args: ARGS) => RETURN> { - if (definition.handler) { - return definition.handler + let handler = definition.handler + if (!handler) { + const result = await getRpcResolvedSetupResult(definition, context) + if (!result.handler) { + throw diagnostics.DF0024({ name: definition.name }) + } + handler = result.handler + } + + // When `args`/`returns` Standard Schemas are declared, validate inputs + // before the handler runs and the output after it returns (guard-only — + // payloads are never rewritten). Wrapping here means every invocation + // path — local, over-the-wire, and the agent/MCP bridge — funnels + // through the same validation. + const argsSchema = definition.args + const returnSchema = definition.returns + if (!argsSchema && !returnSchema) { + return handler } - const result = await getRpcResolvedSetupResult(definition, context) - if (!result.handler) { - throw diagnostics.DF0024({ name: definition.name }) + + const inner = handler + const validating = async (...args: ARGS): Promise => { + const validatedArgs = await validateRpcArgs(definition.name, argsSchema, args) + const output = await inner(...(validatedArgs as ARGS)) + return await validateRpcReturn(definition.name, returnSchema, output) as RETURN } - return result.handler + return validating as (...args: ARGS) => RETURN } diff --git a/packages/devframe/src/rpc/index.ts b/packages/devframe/src/rpc/index.ts index 8201c21a..00448b0d 100644 --- a/packages/devframe/src/rpc/index.ts +++ b/packages/devframe/src/rpc/index.ts @@ -26,6 +26,7 @@ export * from './define' export * from './handler' export * from './serialization' export * from './types' +export * from './validate-io' export * from './validation' /** @deprecated Import from `devframe/rpc/dump` instead. */ diff --git a/packages/devframe/src/rpc/types.test.ts b/packages/devframe/src/rpc/types.test.ts index f4a25a80..5d690b80 100644 --- a/packages/devframe/src/rpc/types.test.ts +++ b/packages/devframe/src/rpc/types.test.ts @@ -1,4 +1,5 @@ /* eslint-disable unused-imports/no-unused-vars */ +import type { StandardSchemaV1 } from '@standard-schema/spec' import type { RpcDefinitionsToFunctions, RpcFunctionDefinitionToFunction, @@ -8,6 +9,17 @@ import * as v from 'valibot' import { describe, it } from 'vitest' import { defineRpcFunction } from '.' +/** Fake a typed Standard Schema from a non-valibot vendor. */ +function schema(): StandardSchemaV1 { + return { + '~standard': { + version: 1, + vendor: 'test', + validate: value => ({ value: value as Output }), + }, + } +} + describe('rpcFunctionDefinitionToFunction', () => { it('should infer types from generic parameters when no schemas', () => { const fn = defineRpcFunction({ @@ -59,6 +71,18 @@ describe('rpcFunctionDefinitionToFunction', () => { type _Test = AssertEqual number> }) + it('should infer types from any Standard Schema vendor', () => { + const fn = defineRpcFunction({ + name: 'anyVendor', + args: [schema(), schema()], + returns: schema(), + handler: (a, b) => a.length > b, + }) + + type Result = RpcFunctionDefinitionToFunction + type _Test = AssertEqual boolean> + }) + it('should work with setup function instead of handler', () => { const fn = defineRpcFunction({ name: 'withSetup', diff --git a/packages/devframe/src/rpc/types.ts b/packages/devframe/src/rpc/types.ts index 370c08e6..ed75147a 100644 --- a/packages/devframe/src/rpc/types.ts +++ b/packages/devframe/src/rpc/types.ts @@ -1,4 +1,4 @@ -import type { GenericSchema } from 'valibot' +import type { StandardSchemaV1 } from '@standard-schema/spec' import type { InferArgsType, InferReturnType } from './utils' export type { BirpcFn, BirpcReturn } from 'birpc' @@ -93,10 +93,20 @@ export interface RpcFunctionSetupResult< dump?: RpcDumpDefinition } -/** Valibot schema array for validating function arguments */ -export type RpcArgsSchema = readonly GenericSchema[] -/** Valibot schema for validating function return value */ -export type RpcReturnSchema = GenericSchema +/** + * Positional argument schemas for an RPC function. Each entry is any + * [Standard Schema](https://standardschema.dev)-compliant validator + * (valibot, zod, arktype, …); the entry at index `i` validates argument + * `i` at call time and drives that argument's inferred type. + */ +export type RpcArgsSchema = readonly StandardSchemaV1[] +/** + * Return-value schema for an RPC function. Any + * [Standard Schema](https://standardschema.dev)-compliant validator; it + * validates the handler's resolved return value and drives its inferred + * type. + */ +export type RpcReturnSchema = StandardSchemaV1 /** * Serialized representation of a thrown value in a dump record. @@ -233,9 +243,9 @@ export type RpcFunctionDefinition< type?: TYPE /** Whether the function results should be cached */ cacheable?: boolean - /** Valibot schema array for validating function arguments */ + /** Standard Schema array validating (and typing) the arguments */ args?: AS - /** Valibot schema for validating function return value */ + /** Standard Schema validating (and typing) the return value */ returns?: RS /** * Declares whether this function's args/return are JSON-serializable @@ -281,9 +291,9 @@ export type RpcFunctionDefinition< type?: TYPE /** Whether the function results should be cached */ cacheable?: boolean - /** Valibot schema array for validating function arguments */ + /** Standard Schema array validating (and typing) the arguments */ args: AS - /** Valibot schema for validating function return value */ + /** Standard Schema validating (and typing) the return value */ returns: RS /** * Declares whether this function's args/return are JSON-serializable diff --git a/packages/devframe/src/rpc/utils.ts b/packages/devframe/src/rpc/utils.ts index 18e3fb46..16f2323e 100644 --- a/packages/devframe/src/rpc/utils.ts +++ b/packages/devframe/src/rpc/utils.ts @@ -1,4 +1,4 @@ -import type { GenericSchema, InferInput } from 'valibot' +import type { StandardSchemaV1 } from '@standard-schema/spec' import type { RpcArgsSchema, RpcReturnSchema } from './types' /** Type-level assertion that two types are equal */ @@ -6,19 +6,19 @@ export type AssertEqual = (() => T extends X ? 1 : 2) extends (() => T extends Y ? 1 : 2) ? true : never -/** Infers TypeScript tuple type from Valibot schema array */ +/** Infers a TypeScript argument tuple from a Standard Schema array */ export type InferArgsType = S extends readonly [] ? [] : S extends readonly [infer H, ...infer T] - ? H extends GenericSchema - ? T extends readonly GenericSchema[] - ? [InferInput, ...InferArgsType] + ? H extends StandardSchemaV1 + ? T extends readonly StandardSchemaV1[] + ? [StandardSchemaV1.InferInput, ...InferArgsType] : never : never : never -/** Infers TypeScript return type from Valibot return schema */ +/** Infers a TypeScript return type from a Standard Schema */ export type InferReturnType - = S extends RpcReturnSchema - ? InferInput + = S extends StandardSchemaV1 + ? StandardSchemaV1.InferInput : void diff --git a/packages/devframe/src/rpc/validate-io.test.ts b/packages/devframe/src/rpc/validate-io.test.ts new file mode 100644 index 00000000..4e05515e --- /dev/null +++ b/packages/devframe/src/rpc/validate-io.test.ts @@ -0,0 +1,131 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec' +import * as v from 'valibot' +import { describe, expect, it } from 'vitest' +import { defineRpcFunction } from './define' +import { getRpcHandler } from './handler' +import { validateRpcArgs, validateRpcReturn } from './validate-io' + +/** A minimal async Standard Schema from a non-valibot vendor. */ +function asyncPositive(): StandardSchemaV1 { + return { + '~standard': { + version: 1, + vendor: 'test', + async validate(value) { + await Promise.resolve() + if (typeof value === 'number' && value > 0) + return { value } + return { issues: [{ message: 'expected a positive number' }] } + }, + }, + } +} + +describe('validateRpcArgs', () => { + it('returns args untouched when no schema is declared', async () => { + await expect(validateRpcArgs('fn', undefined, ['a', 1])).resolves.toEqual(['a', 1]) + }) + + it('passes the original values through without rewriting them', async () => { + // A transform would coerce to `5`; guard-only validation keeps `'hello'`. + const schema = [v.pipe(v.string(), v.transform(s => s.length))] as const + await expect(validateRpcArgs('fn', schema, ['hello'])).resolves.toEqual(['hello']) + }) + + it('preserves object fields beyond the schema (no key stripping)', async () => { + const schema = [v.object({ id: v.string() })] as const + const value = { id: 'x', extra: true } + const [out] = await validateRpcArgs('fn', schema, [value]) + expect(out).toEqual({ id: 'x', extra: true }) + }) + + it('leaves un-schema-ed trailing args in place', async () => { + const schema = [v.string()] as const + await expect(validateRpcArgs('fn', schema, ['a', 'passthrough'])).resolves.toEqual(['a', 'passthrough']) + }) + + it('throws DF0043 with the failing index', async () => { + const schema = [v.string(), v.number()] as const + await expect(validateRpcArgs('fn', schema, ['ok', 'nope'])).rejects.toThrow(/position 1/) + }) + + it('awaits async validators from any vendor', async () => { + const schema = [asyncPositive()] as const + await expect(validateRpcArgs('fn', schema, [3])).resolves.toEqual([3]) + await expect(validateRpcArgs('fn', schema, [-1])).rejects.toThrow(/positive number/) + }) +}) + +describe('validateRpcReturn', () => { + it('passes through when no schema is declared', async () => { + await expect(validateRpcReturn('fn', undefined, { a: 1 })).resolves.toEqual({ a: 1 }) + }) + + it('returns the original value, preserving fields beyond the schema', async () => { + // Mirrors a real plugin (terminals) whose return schema is a subset of + // the payload — validation must not drop the undeclared field. + const schema = v.object({ id: v.string() }) + const value = { id: 'x', restartable: false } + await expect(validateRpcReturn('fn', schema, value)).resolves.toEqual({ id: 'x', restartable: false }) + }) + + it('throws DF0044 when the return fails its schema', async () => { + await expect(validateRpcReturn('fn', v.number(), 'not-a-number')).rejects.toThrow(/returns` schema/) + }) +}) + +describe('getRpcHandler validation wrapping', () => { + it('returns the raw handler untouched when no schemas are declared', async () => { + const original = (a: number, b: number): number => a + b + const fn = defineRpcFunction({ name: 'add', handler: original }) + const handler = await getRpcHandler(fn, undefined) + expect(handler).toBe(original) + expect(handler(2, 3)).toBe(5) + }) + + it('validates args before the handler runs', async () => { + const fn = defineRpcFunction({ + name: 'len', + args: [v.string()], + returns: v.number(), + handler: (s: string) => s.length * 2, + }) + const handler = await getRpcHandler(fn, undefined) + await expect(handler('hello')).resolves.toBe(10) + }) + + it('rejects invalid arguments with DF0043', async () => { + const fn = defineRpcFunction({ + name: 'greet', + args: [v.string()], + returns: v.string(), + handler: (name: string) => `hi ${name}`, + }) + const handler = await getRpcHandler(fn, undefined) + await expect(handler(42 as never)).rejects.toThrow(/invalid argument at position 0/) + }) + + it('rejects an invalid return value with DF0044', async () => { + const fn = defineRpcFunction({ + name: 'bad-return', + args: [], + returns: v.number(), + // Handler lies about its return type at runtime. + handler: () => 'not-a-number' as never, + }) + const handler = await getRpcHandler(fn, undefined) + await expect(handler()).rejects.toThrow(/failed its `returns` schema/) + }) + + it('works with a setup-provided handler', async () => { + const fn = defineRpcFunction({ + name: 'setup-fn', + args: [v.number()], + returns: v.number(), + setup: () => ({ handler: (n: number) => n + 1 }), + }) + const handler = await getRpcHandler(fn, undefined) + await expect(handler(1)).resolves.toBe(2) + await expect(handler('x' as never)).rejects.toThrow(/position 0/) + }) +}) diff --git a/packages/devframe/src/rpc/validate-io.ts b/packages/devframe/src/rpc/validate-io.ts new file mode 100644 index 00000000..286bc968 --- /dev/null +++ b/packages/devframe/src/rpc/validate-io.ts @@ -0,0 +1,84 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec' +import type { RpcArgsSchema, RpcReturnSchema } from './types' +import { diagnostics } from './diagnostics' + +/** + * Run a single [Standard Schema](https://standardschema.dev) validator, + * awaiting the result when the validator is asynchronous. + */ +async function runStandardSchema( + schema: T, + value: unknown, +): Promise>> { + const result = schema['~standard'].validate(value) + return result instanceof Promise ? await result : result +} + +/** + * Render Standard Schema issues into a single human-readable line for a + * diagnostic message, prefixing each with its dotted path when present. + */ +function formatIssues(issues: readonly StandardSchemaV1.Issue[]): string { + return issues + .map((issue) => { + const path = issue.path + ?.map(segment => (typeof segment === 'object' ? segment.key : segment)) + .join('.') + return path ? `${path}: ${issue.message}` : issue.message + }) + .join('; ') +} + +/** + * Validate positional arguments against their declared schemas. Only + * indices with a schema are checked; extra arguments pass through + * untouched. Throws `DF0038` on the first failing argument. + * + * Validation guards the payload without rewriting it: the original values + * are handed to the handler unchanged, so a schema that describes a subset + * of an object never silently strips the sender's extra fields (and any + * declared transforms stay a purely type-level concern). + * + * @internal + */ +export async function validateRpcArgs( + name: string, + argsSchema: RpcArgsSchema | undefined, + args: readonly unknown[], +): Promise { + const original = args.slice() + if (!argsSchema || argsSchema.length === 0) + return original + + for (let index = 0; index < argsSchema.length; index++) { + const schema = argsSchema[index] + if (!schema) + continue + const result = await runStandardSchema(schema, args[index]) + if (result.issues) + throw diagnostics.DF0043({ name, index, issues: formatIssues(result.issues) }) + } + + return original +} + +/** + * Validate a handler's resolved return value against its declared schema. + * Throws `DF0039` when the value fails the schema, otherwise returns the + * original value unchanged (guard-only, never rewriting the payload — see + * {@link validateRpcArgs}). Passes through when no return schema is set. + * + * @internal + */ +export async function validateRpcReturn( + name: string, + returnSchema: RpcReturnSchema | undefined, + value: unknown, +): Promise { + if (!returnSchema) + return value + const result = await runStandardSchema(returnSchema, value) + if (result.issues) + throw diagnostics.DF0044({ name, issues: formatIssues(result.issues) }) + return value +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc3a6773..bde9988e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,6 +46,9 @@ catalogs: '@modelcontextprotocol/sdk': specifier: ^1.30.0 version: 1.30.0 + '@standard-schema/spec': + specifier: ^1.1.0 + version: 1.1.0 '@valibot/to-json-schema': specifier: ^1.7.1 version: 1.7.1 @@ -782,6 +785,9 @@ importers: packages/devframe: dependencies: + '@standard-schema/spec': + specifier: catalog:deps + version: 1.1.0 '@valibot/to-json-schema': specifier: catalog:deps version: 1.7.1(valibot@1.4.2(typescript@6.0.3)) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c5d35479..24c023c7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -58,6 +58,7 @@ catalogs: deps: '@json-render/core': ^0.19.0 '@modelcontextprotocol/sdk': ^1.30.0 + '@standard-schema/spec': ^1.1.0 '@valibot/to-json-schema': ^1.7.1 birpc: ^4.0.0 cac: ^7.0.0 diff --git a/tests/__snapshots__/tsnapi/devframe/rpc.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/rpc.snapshot.d.ts index f4c2a8b6..0bd4e7ac 100644 --- a/tests/__snapshots__/tsnapi/devframe/rpc.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/rpc.snapshot.d.ts @@ -52,4 +52,6 @@ export { STRUCTURED_CLONE_PREFIX } export { Thenable } export { validateDefinition } export { validateDefinitions } +export { validateRpcArgs } +export { validateRpcReturn } // #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/devframe/rpc.snapshot.js b/tests/__snapshots__/tsnapi/devframe/rpc.snapshot.js index f7ed8933..6b1b818e 100644 --- a/tests/__snapshots__/tsnapi/devframe/rpc.snapshot.js +++ b/tests/__snapshots__/tsnapi/devframe/rpc.snapshot.js @@ -55,4 +55,6 @@ export { strictJsonStringify } export { STRUCTURED_CLONE_PREFIX } export { validateDefinition } export { validateDefinitions } +export { validateRpcArgs } +export { validateRpcReturn } // #endregion \ No newline at end of file From 864ae06bb896dbf9040c5ff26a06cda588626119 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Sat, 1 Aug 2026 22:33:11 +0000 Subject: [PATCH 2/8] docs(rpc): flag runtime schema enforcement as breaking --- docs/guide/rpc.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/guide/rpc.md b/docs/guide/rpc.md index 00ba0fc1..3fcc8487 100644 --- a/docs/guide/rpc.md +++ b/docs/guide/rpc.md @@ -94,6 +94,9 @@ defineRpcFunction({ Prefer a single object argument (`args: [v.object({ ... })]`) over positional args — property names are self-describing and agents/IDEs work best with object shapes. +> [!WARNING] +> Declared `args`/`returns` schemas are enforced at runtime — a call whose arguments, or a handler whose return value, fail the schema is rejected with `DF0043` / `DF0044`. Make sure each schema matches what the function actually accepts and returns; a schema stricter than reality will now reject calls that previously ran. + ### Setup vs handler Two ways to wire a handler: From df12ef67465f991f6a9c237767fd2b2c9afc0cc9 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Mon, 3 Aug 2026 00:22:49 +0000 Subject: [PATCH 3/8] feat(rpc)!: make devframe fully validator-neutral (drop forced valibot dep) Remove valibot and @valibot/to-json-schema from devframe's runtime dependencies so nothing forces a specific Standard Schema validator on users. Consumers bring their own (valibot, zod, arktype) or use the new built-in zero-dependency builder. - Add devframe/utils/schema: a tiny zero-dep Standard Schema builder (s) exposing string/number/boolean/void/null/picklist/array/object/ optional/nullable, used by the recipes. - Convert recipes (common-rpc-functions, interactive-auth) off valibot to the built-in builder. - Make the CLI-flags parser validator-neutral: validate via the ~standard contract and duck-type the schema kind (works for the builder and valibot; other vendors degrade to value-flags). - Drop @valibot/to-json-schema; the agent/MCP surface advertises a permissive object schema for any declared args/returns. - valibot stays a devDependency for tests only. BREAKING CHANGE: the exported recipe definitions (openInEditor, openInFinder, interactive-auth functions) now type their args/returns as the built-in DevframeSchema instead of valibot schema types, and @valibot/to-json-schema is no longer a devframe dependency. Agent tool JSON schemas are now permissive objects rather than precise valibot conversions. --- alias.ts | 1 + docs/errors/DF0019.md | 2 +- docs/guide/rpc.md | 22 +- docs/guide/standalone-cli.md | 2 +- docs/helpers/common-rpc-functions.md | 2 +- packages/devframe/package.json | 6 +- packages/devframe/src/adapters/flags.ts | 45 ++-- .../mcp/__tests__/to-json-schema.test.ts | 71 ++---- .../src/adapters/mcp/to-json-schema.ts | 60 ++---- .../__tests__/common-rpc-functions.test.ts | 16 +- .../src/recipes/common-rpc-functions.ts | 12 +- .../devframe/src/recipes/interactive-auth.ts | 24 +-- packages/devframe/src/types/devframe.ts | 9 +- packages/devframe/src/utils/schema.test.ts | 71 ++++++ packages/devframe/src/utils/schema.ts | 203 ++++++++++++++++++ packages/devframe/tsdown.config.ts | 2 + pnpm-lock.yaml | 9 +- .../plugin-assets/rpc.snapshot.d.ts | 20 +- .../common-rpc-functions.snapshot.d.ts | 16 +- .../recipes/open-helpers.snapshot.d.ts | 8 +- .../devframe/utils/schema.snapshot.d.ts | 18 ++ .../tsnapi/devframe/utils/schema.snapshot.js | 20 ++ tsconfig.base.json | 3 + 23 files changed, 455 insertions(+), 187 deletions(-) create mode 100644 packages/devframe/src/utils/schema.test.ts create mode 100644 packages/devframe/src/utils/schema.ts create mode 100644 tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.d.ts create mode 100644 tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.js diff --git a/alias.ts b/alias.ts index e284552c..cfb8846e 100644 --- a/alias.ts +++ b/alias.ts @@ -26,6 +26,7 @@ export const alias = { 'devframe/utils/nanoid': r('devframe/src/utils/nanoid.ts'), 'devframe/utils/open': r('devframe/src/utils/open.ts'), 'devframe/utils/promise': r('devframe/src/utils/promise.ts'), + 'devframe/utils/schema': r('devframe/src/utils/schema.ts'), 'devframe/utils/scope': r('devframe/src/utils/scope.ts'), 'devframe/utils/serve-static': r('devframe/src/utils/serve-static.ts'), 'devframe/utils/shared-state': r('devframe/src/utils/shared-state.ts'), diff --git a/docs/errors/DF0019.md b/docs/errors/DF0019.md index 51c3c4c2..a02d3ebe 100644 --- a/docs/errors/DF0019.md +++ b/docs/errors/DF0019.md @@ -10,7 +10,7 @@ outline: deep ## Cause -The `agent` field exposes an RPC function as an MCP tool. MCP and the underlying schema-conversion path (`@valibot/to-json-schema`) only consume JSON-shaped data. Functions whose payloads can include `Map`, `Set`, `Date`, `BigInt`, circular references, or class instances cannot be safely advertised to agents. +The `agent` field exposes an RPC function as an MCP tool. MCP only consumes JSON-shaped data. Functions whose payloads can include `Map`, `Set`, `Date`, `BigInt`, circular references, or class instances cannot be safely advertised to agents. A registered function is rejected when `agent` is present and `jsonSerializable` is not explicitly `true`. diff --git a/docs/guide/rpc.md b/docs/guide/rpc.md index 3fcc8487..fe3b536c 100644 --- a/docs/guide/rpc.md +++ b/docs/guide/rpc.md @@ -22,13 +22,13 @@ sequenceDiagram ```ts import { defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import { s } from 'devframe/utils/schema' // or bring your own: valibot / zod / arktype export const getModules = defineRpcFunction({ name: 'get-modules', // bare — the scope namespaces it to `my-devframe:get-modules` type: 'query', - args: [v.object({ limit: v.number() })], - returns: v.array(v.object({ id: v.string(), size: v.number() })), + args: [s.object({ limit: s.number() })], + returns: s.array(s.object({ id: s.string(), size: s.number() })), setup: ctx => ({ handler: async ({ limit }) => { // `ctx` is the full DevframeNodeContext. @@ -75,14 +75,16 @@ Use `static` for data collected once during `setup` and shipped to read-only sta ### Handler arguments -Handlers accept any serializable arguments. Declare `args` schemas — any [Standard Schema](https://standardschema.dev/) validator, valibot below — and each argument is validated at the boundary before the handler runs; a mismatch is rejected with a coded diagnostic. Validation guards the payload without rewriting it, so extra object fields the schema doesn't mention still reach the handler: +Handlers accept any serializable arguments. Declare `args` schemas — any [Standard Schema](https://standardschema.dev/) validator (valibot, zod, arktype, …) — and each argument is validated at the boundary before the handler runs; a mismatch is rejected with a coded diagnostic. Validation guards the payload without rewriting it, so extra object fields the schema doesn't mention still reach the handler. + +Devframe forces no validator on you. Bring the one you already use, or reach for the built-in zero-dependency builder at `devframe/utils/schema` (imported as `s` below): ```ts defineRpcFunction({ name: 'get-file', type: 'query', - args: [v.object({ path: v.string(), includeSource: v.optional(v.boolean()) })], - returns: v.object({ path: v.string(), source: v.optional(v.string()) }), + args: [s.object({ path: s.string(), includeSource: s.optional(s.boolean()) })], + returns: s.object({ path: s.string(), source: s.optional(s.string()) }), setup: () => ({ handler: async ({ path, includeSource }) => ({ path, @@ -92,7 +94,7 @@ defineRpcFunction({ }) ``` -Prefer a single object argument (`args: [v.object({ ... })]`) over positional args — property names are self-describing and agents/IDEs work best with object shapes. +Prefer a single object argument (`args: [s.object({ ... })]`) over positional args — property names are self-describing and agents/IDEs work best with object shapes. > [!WARNING] > Declared `args`/`returns` schemas are enforced at runtime — a call whose arguments, or a handler whose return value, fail the schema is rejected with `DF0043` / `DF0044`. Make sure each schema matches what the function actually accepts and returns; a schema stricter than reality will now reject calls that previously ran. @@ -245,7 +247,7 @@ defineRpcFunction({ name: 'build-meta', type: 'static', args: [], - returns: v.object({ version: v.string(), builtAt: v.number() }), + returns: s.object({ version: s.string(), builtAt: s.number() }), setup: () => ({ handler: async () => ({ version: '1.0.0', builtAt: Date.now() }), }), @@ -309,8 +311,8 @@ defineRpcFunction({ name: 'get-modules', type: 'query', jsonSerializable: true, - args: [v.object({ limit: v.number() })], - returns: v.array(v.object({ id: v.string(), size: v.number() })), + args: [s.object({ limit: s.number() })], + returns: s.array(s.object({ id: s.string(), size: s.number() })), agent: { description: 'List the N largest modules in the current build. Safe to call freely.', title: 'List modules', diff --git a/docs/guide/standalone-cli.md b/docs/guide/standalone-cli.md index 3239d8f6..e184581f 100644 --- a/docs/guide/standalone-cli.md +++ b/docs/guide/standalone-cli.md @@ -168,7 +168,7 @@ const payload = await my.rpc.call('get-payload') ## Typed CLI flags -For flags that are specific to your tool, declare them as valibot schemas so they're validated at parse time and typed at the call site: +For flags that are specific to your tool, declare them with any [Standard Schema](https://standardschema.dev/) validator (valibot below, or zod / arktype / devframe's built-in `s`) so they're validated at parse time and typed at the call site: ```ts import type { InferCliFlags } from 'devframe/adapters/cac' diff --git a/docs/helpers/common-rpc-functions.md b/docs/helpers/common-rpc-functions.md index b600c3ca..a0794873 100644 --- a/docs/helpers/common-rpc-functions.md +++ b/docs/helpers/common-rpc-functions.md @@ -28,7 +28,7 @@ defineDevframe({ | `KNOWN_EDITORS` | — | `readonly string[]` | — | The editor commands `openInEditor`'s `editor` argument accepts (`code`, `vim`, `subl`, `idea`, …). | | `KnownEditor` | — | type | — | Union of `KNOWN_EDITORS`. | -Both functions are `action`-type RPCs returning `void` and use `valibot` schemas for their arguments — `openInEditor`'s `editor` argument is `v.optional(v.picklist(KNOWN_EDITORS))`, so a value outside `KNOWN_EDITORS` fails validation rather than reaching the underlying `launch-editor` process spawn. Both handlers dynamically `import()` their underlying `devframe/utils/*` implementation, so the `launch-editor` and `open` dependencies only load when the recipe actually runs. +Both functions are `action`-type RPCs returning `void` and declare their arguments with devframe's built-in zero-dependency `s` builder from `devframe/utils/schema` — `openInEditor`'s `editor` argument is `s.optional(s.picklist(KNOWN_EDITORS))`, so a value outside `KNOWN_EDITORS` fails validation rather than reaching the underlying `launch-editor` process spawn. Both handlers dynamically `import()` their underlying `devframe/utils/*` implementation, so the `launch-editor` and `open` dependencies only load when the recipe actually runs. The `devframe/recipes/open-helpers` entry (`openHelpers`) remains as a deprecated alias for this module — new code should import `commonRpcFunctions` from `devframe/recipes/common-rpc-functions`. diff --git a/packages/devframe/package.json b/packages/devframe/package.json index 781af18a..d5c1c2b5 100644 --- a/packages/devframe/package.json +++ b/packages/devframe/package.json @@ -50,6 +50,7 @@ "./utils/nanoid": "./dist/utils/nanoid.mjs", "./utils/open": "./dist/utils/open.mjs", "./utils/promise": "./dist/utils/promise.mjs", + "./utils/schema": "./dist/utils/schema.mjs", "./utils/scope": "./dist/utils/scope.mjs", "./utils/serve-static": "./dist/utils/serve-static.mjs", "./utils/shared-state": "./dist/utils/shared-state.mjs", @@ -83,7 +84,6 @@ }, "dependencies": { "@standard-schema/spec": "catalog:deps", - "@valibot/to-json-schema": "catalog:deps", "birpc": "catalog:deps", "crossws": "catalog:deps", "destr": "catalog:deps", @@ -91,8 +91,7 @@ "mrmime": "catalog:deps", "nostics": "catalog:deps", "pathe": "catalog:deps", - "ufo": "catalog:deps", - "valibot": "catalog:deps" + "ufo": "catalog:deps" }, "devDependencies": { "@modelcontextprotocol/client": "catalog:deps", @@ -111,6 +110,7 @@ "tinyglobby": "catalog:deps", "tsdown": "catalog:build", "ua-parser-modern": "catalog:inlined", + "valibot": "catalog:deps", "whenexpr": "catalog:deps", "ws": "catalog:deps" } diff --git a/packages/devframe/src/adapters/flags.ts b/packages/devframe/src/adapters/flags.ts index b104219a..d69a0ddf 100644 --- a/packages/devframe/src/adapters/flags.ts +++ b/packages/devframe/src/adapters/flags.ts @@ -1,14 +1,14 @@ -import type { GenericSchema, InferOutput } from 'valibot' -import { safeParse } from 'valibot' +import type { StandardSchemaV1 } from '@standard-schema/spec' /** * Schema map for typed CLI flags. Keys are flag names in camelCase — * this matches CAC's parsed-flag output ( `--no-open` → `noOpen` ). Each - * value is a valibot schema used to both (a) derive the CAC option type - * when the flag is registered and (b) validate / coerce the parsed - * value before it's forwarded to `setup(ctx, { flags })`. + * value is any [Standard Schema](https://standardschema.dev/) validator + * (valibot, zod, arktype, devframe's built-in `s`, …) used to both (a) + * derive the CAC option type when the flag is registered and (b) validate + * the parsed value before it's forwarded to `setup(ctx, { flags })`. */ -export type CliFlagsSchema = Record +export type CliFlagsSchema = Record /** * Identity helper that preserves the literal schema-map type — use this @@ -36,16 +36,18 @@ export function defineCliFlags(flags: T): T { /** Extract the parsed-output type from a {@link CliFlagsSchema}. */ export type InferCliFlags = { - [K in keyof T]: InferOutput + [K in keyof T]: StandardSchemaV1.InferOutput } /** - * Best-effort probe of a valibot schema to decide whether the - * corresponding CAC option takes a value. Unwraps `optional` / `nullable` - * / `nullish` / `default` / `pipe` wrappers then matches on the inner - * type's kind. + * Best-effort, dependency-free probe of a schema to decide whether the + * corresponding CAC option takes a value. Duck-types the `type` / + * `wrapped` / `inner` / `pipe` fields exposed by valibot and by devframe's + * built-in `s` builder, unwrapping `optional` / `nullable` / `nullish` / + * `pipe` wrappers then matching on the inner kind. Validators that don't + * expose these fields (e.g. zod) fall through to a value-taking option. */ -function getSchemaKind(schema: GenericSchema): string { +function getSchemaKind(schema: StandardSchemaV1): string { let current: any = schema while (current) { const kind = current.type @@ -57,17 +59,17 @@ function getSchemaKind(schema: GenericSchema): string { current = current.pipe[0] continue } - return kind + return kind ?? 'unknown' } return 'unknown' } /** Whether the CAC option for this schema should be a boolean flag. */ -export function isBooleanFlag(schema: GenericSchema): boolean { +export function isBooleanFlag(schema: StandardSchemaV1): boolean { return getSchemaKind(schema) === 'boolean' } -/** Validate and coerce the raw cac-parsed bag against a {@link CliFlagsSchema}. */ +/** Validate the raw cac-parsed bag against a {@link CliFlagsSchema}. */ export function parseCliFlags( schema: CliFlagsSchema, raw: Record, @@ -75,13 +77,18 @@ export function parseCliFlags( const flags: Record = {} const issues: string[] = [] for (const [key, fieldSchema] of Object.entries(schema)) { - const result = safeParse(fieldSchema, raw[key]) - if (result.success) { - flags[key] = result.output + const result = fieldSchema['~standard'].validate(raw[key]) + if (result instanceof Promise) { + // CLI parsing is synchronous; an async validator can't be awaited here. + issues.push(`--${toKebab(key)}: async flag validation is not supported`) + continue } - else { + if (result.issues) { issues.push(`--${toKebab(key)}: ${result.issues.map(i => i.message).join(', ')}`) } + else { + flags[key] = result.value + } } // Preserve any raw flags that aren't in the schema (e.g. --host, --port, // or options contributed via cli.configure) so authors keep access to diff --git a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts b/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts index cd4954df..59221fbd 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts +++ b/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts @@ -1,7 +1,9 @@ -import * as v from 'valibot' +import { s } from 'devframe/utils/schema' import { describe, expect, it } from 'vitest' import { argsToJsonSchema, returnToJsonSchema } from '../to-json-schema' +const PERMISSIVE = { type: 'object', additionalProperties: true } + describe('argsToJsonSchema', () => { it('returns an empty object schema when no args', () => { const { schema, unwrapped } = argsToJsonSchema(undefined) @@ -9,8 +11,8 @@ describe('argsToJsonSchema', () => { expect(schema).toEqual({ type: 'object', properties: {} }) }) - it('wraps multiple positional args under arg0/arg1/...', () => { - const { schema, unwrapped } = argsToJsonSchema([v.string(), v.number()]) + it('advertises each positional arg as a permissive object under arg0/arg1/...', () => { + const { schema, unwrapped } = argsToJsonSchema([s.string(), s.number()]) expect(unwrapped).toBe(false) expect(schema).toMatchObject({ type: 'object', @@ -18,25 +20,23 @@ describe('argsToJsonSchema', () => { additionalProperties: false, }) const props = (schema as any).properties - expect(props.arg0).toMatchObject({ type: 'string' }) - expect(props.arg1).toMatchObject({ type: 'number' }) - }) - - it('unwraps a single object schema for nicer agent UX', () => { - const { schema, unwrapped } = argsToJsonSchema([ - v.object({ name: v.string(), age: v.number() }), - ]) - expect(unwrapped).toBe(true) - expect((schema as any).type).toBe('object') - const props = (schema as any).properties - expect(props.name).toBeDefined() - expect(props.age).toBeDefined() + expect(props.arg0).toEqual(PERMISSIVE) + expect(props.arg1).toEqual(PERMISSIVE) }) - it('keeps arg0 shape when the single arg is a primitive', () => { - const { schema, unwrapped } = argsToJsonSchema([v.string()]) + it('wraps a single arg under arg0 (no vendor-specific unwrapping)', () => { + const { schema, unwrapped } = argsToJsonSchema([s.object({ name: s.string() })]) expect(unwrapped).toBe(false) expect(schema).toMatchObject({ type: 'object', required: ['arg0'] }) + expect((schema as any).properties.arg0).toEqual(PERMISSIVE) + }) + + it('works with any Standard Schema vendor (falls back the same way)', () => { + const foreign = { + '~standard': { version: 1 as const, vendor: 'acme', validate: (value: unknown) => ({ value }) }, + } + const { schema } = argsToJsonSchema([foreign]) + expect((schema as any).properties.arg0).toEqual(PERMISSIVE) }) }) @@ -45,38 +45,7 @@ describe('returnToJsonSchema', () => { expect(returnToJsonSchema(undefined)).toBeUndefined() }) - it('converts a simple schema', () => { - const schema = returnToJsonSchema(v.object({ ok: v.boolean() })) - expect((schema as any).type).toBe('object') - expect((schema as any).properties.ok).toMatchObject({ type: 'boolean' }) - }) -}) - -describe('non-valibot Standard Schemas', () => { - // A minimal Standard Schema from a made-up vendor (mirrors zod/arktype, - // which devframe core does not depend on) — no valibot internals. - const foreign = { - '~standard': { - version: 1 as const, - vendor: 'acme', - validate: (value: unknown) => ({ value }), - }, - } - - it('falls back to a permissive object per positional arg', () => { - const { schema, unwrapped } = argsToJsonSchema([foreign, foreign]) - expect(unwrapped).toBe(false) - expect(schema).toMatchObject({ type: 'object', required: ['arg0', 'arg1'] }) - expect((schema as any).properties.arg0).toEqual({ type: 'object', additionalProperties: true }) - }) - - it('unwraps a single foreign arg to the permissive object', () => { - const { schema, unwrapped } = argsToJsonSchema([foreign]) - expect(unwrapped).toBe(true) - expect(schema).toEqual({ type: 'object', additionalProperties: true }) - }) - - it('falls back to a permissive object schema for returns', () => { - expect(returnToJsonSchema(foreign)).toEqual({ type: 'object', additionalProperties: true }) + it('advertises a permissive object for any declared return schema', () => { + expect(returnToJsonSchema(s.object({ ok: s.boolean() }))).toEqual(PERMISSIVE) }) }) diff --git a/packages/devframe/src/adapters/mcp/to-json-schema.ts b/packages/devframe/src/adapters/mcp/to-json-schema.ts index dd0d2708..aa1f118e 100644 --- a/packages/devframe/src/adapters/mcp/to-json-schema.ts +++ b/packages/devframe/src/adapters/mcp/to-json-schema.ts @@ -1,29 +1,27 @@ import type { StandardSchemaV1 } from '@standard-schema/spec' -import type { GenericSchema } from 'valibot' -import { toJsonSchema } from '@valibot/to-json-schema' const FALLBACK_OBJECT_SCHEMA = Object.freeze({ type: 'object', additionalProperties: true }) /** - * Convert a Standard Schema return value to JSON Schema for the agent - * surface. valibot schemas convert precisely; other Standard Schema - * vendors (zod, arktype, …) have no universal JSON Schema mapping and - * fall back to a permissive object schema. + * JSON Schema for an RPC return value on the agent/MCP surface. + * + * [Standard Schema](https://standardschema.dev/) deliberately exposes no + * JSON Schema, and devframe stays validator-neutral, so a declared return + * schema advertises a permissive object rather than a precise shape. * @internal */ export function returnToJsonSchema(schema: StandardSchemaV1 | undefined): unknown { - if (!schema) - return undefined - return safeToJsonSchema(schema) + return schema ? FALLBACK_OBJECT_SCHEMA : undefined } /** - * Convert positional RPC args schemas to a single MCP-friendly object - * schema. When the RPC declares `args: [object(...)]`, unwrap the single - * object schema directly (nicer agent UX than `{ arg0: {...} }`). + * JSON Schema for an RPC function's positional args on the agent/MCP + * surface. Each positional arg is advertised as a permissive object under + * `arg0` / `arg1` / … — matching how the agent bridge coerces the incoming + * object payload back into positional arguments. * - * Returns `undefined` when there are no args (the MCP SDK treats this - * as `{ type: 'object', properties: {} }`). + * Returns `{ type: 'object', properties: {} }` when there are no args (the + * MCP SDK treats this as "no input"). * @internal */ export function argsToJsonSchema( @@ -32,22 +30,11 @@ export function argsToJsonSchema( if (!args || args.length === 0) return { schema: { type: 'object', properties: {} }, unwrapped: false } - // Single-object arg: unwrap. - if (args.length === 1) { - const inner = safeToJsonSchema(args[0]!) - if (isObjectJsonSchema(inner)) - return { schema: inner, unwrapped: true } - // Non-object single arg (e.g. a string): fall through to arg0 shape. - } - const properties: Record = {} const required: string[] = [] for (let i = 0; i < args.length; i++) { const key = `arg${i}` - const s = safeToJsonSchema(args[i]!) - properties[key] = s - // Conservatively mark every positional arg as required — the RPC - // layer validates against the declared schema anyway. + properties[key] = FALLBACK_OBJECT_SCHEMA required.push(key) } @@ -61,24 +48,3 @@ export function argsToJsonSchema( unwrapped: false, } } - -function safeToJsonSchema(schema: StandardSchemaV1): unknown { - // Only valibot exposes a JSON Schema converter; other vendors degrade - // to a permissive object schema rather than throwing. - if (schema['~standard']?.vendor !== 'valibot') - return FALLBACK_OBJECT_SCHEMA - try { - return toJsonSchema(schema as unknown as GenericSchema) - } - catch { - return FALLBACK_OBJECT_SCHEMA - } -} - -function isObjectJsonSchema(value: unknown): boolean { - return ( - !!value - && typeof value === 'object' - && (value as { type?: unknown }).type === 'object' - ) -} diff --git a/packages/devframe/src/recipes/__tests__/common-rpc-functions.test.ts b/packages/devframe/src/recipes/__tests__/common-rpc-functions.test.ts index a4db8438..37b21f37 100644 --- a/packages/devframe/src/recipes/__tests__/common-rpc-functions.test.ts +++ b/packages/devframe/src/recipes/__tests__/common-rpc-functions.test.ts @@ -1,8 +1,16 @@ -import * as v from 'valibot' +import type { StandardSchemaV1 } from '@standard-schema/spec' import { describe, expect, it } from 'vitest' import { commonRpcFunctions, KNOWN_EDITORS, openInEditor, openInFinder } from '../common-rpc-functions' import { openHelpers } from '../open-helpers' +/** Synchronously check whether a value satisfies a Standard Schema. */ +function accepts(schema: StandardSchemaV1, value: unknown): boolean { + const result = schema['~standard'].validate(value) + if (result instanceof Promise) + throw new TypeError('unexpected async validator') + return !result.issues +} + describe('recipes/common-rpc-functions', () => { it('exposes `openInEditor` as a devframe-namespaced action', () => { expect(openInEditor.name).toBe('devframe:open-in-editor') @@ -16,10 +24,10 @@ describe('recipes/common-rpc-functions', () => { expect(KNOWN_EDITORS).toContain('vim') const editorSchema = openInEditor.args[1] - expect(v.safeParse(editorSchema, undefined).success).toBe(true) + expect(accepts(editorSchema, undefined)).toBe(true) for (const editor of KNOWN_EDITORS) - expect(v.safeParse(editorSchema, editor).success).toBe(true) - expect(v.safeParse(editorSchema, 'not-a-real-editor').success).toBe(false) + expect(accepts(editorSchema, editor)).toBe(true) + expect(accepts(editorSchema, 'not-a-real-editor')).toBe(false) }) it('exposes `openInFinder` as a devframe-namespaced action', () => { diff --git a/packages/devframe/src/recipes/common-rpc-functions.ts b/packages/devframe/src/recipes/common-rpc-functions.ts index 927164be..1c03c8d9 100644 --- a/packages/devframe/src/recipes/common-rpc-functions.ts +++ b/packages/devframe/src/recipes/common-rpc-functions.ts @@ -1,4 +1,4 @@ -import * as v from 'valibot' +import { s } from 'devframe/utils/schema' import { defineRpcFunction } from '../rpc/define' /** @@ -42,7 +42,7 @@ export type KnownEditor | 'goland' | 'rider' -/** Runtime list of every {@link KnownEditor}, in the order `v.picklist` reports them. */ +/** Runtime list of every {@link KnownEditor}. */ export const KNOWN_EDITORS: KnownEditor[] = [ 'atom', 'subl', @@ -103,8 +103,8 @@ export const openInEditor = defineRpcFunction({ name: 'devframe:open-in-editor', type: 'action', jsonSerializable: true, - args: [v.string(), v.optional(v.picklist(KNOWN_EDITORS))], - returns: v.void(), + args: [s.string(), s.optional(s.picklist(KNOWN_EDITORS))], + returns: s.void(), async handler(filename: string, editor?: KnownEditor) { const { launchEditor } = await import('devframe/utils/launch-editor') launchEditor(filename, editor) @@ -126,8 +126,8 @@ export const openInFinder = defineRpcFunction({ name: 'devframe:open-in-finder', type: 'action', jsonSerializable: true, - args: [v.string()], - returns: v.void(), + args: [s.string()], + returns: s.void(), async handler(path: string) { const { open } = await import('devframe/utils/open') await open(path) diff --git a/packages/devframe/src/recipes/interactive-auth.ts b/packages/devframe/src/recipes/interactive-auth.ts index a52ea783..663d138d 100644 --- a/packages/devframe/src/recipes/interactive-auth.ts +++ b/packages/devframe/src/recipes/interactive-auth.ts @@ -1,7 +1,7 @@ import type { DevframeNodeContext, DevframeNodeRpcSession } from 'devframe/types' import type { DevframeAuthHandler } from '../node/auth' import { colors } from 'devframe/utils/colors' -import * as v from 'valibot' +import { s } from 'devframe/utils/schema' import { DEVFRAME_AUTH_TOKEN_QUERY_PARAM, isAnonymousRpcMethod } from '../constants' import { buildOtpAuthUrl, exchangeTempAuthCode, getTempAuthCode, verifyAuthToken } from '../node/auth/state' import { getInternalContext } from '../node/hub-internals/context' @@ -86,12 +86,12 @@ export function createInteractiveAuth( name: 'anonymous:devframe:auth', type: 'action', jsonSerializable: true, - args: [v.object({ - authToken: v.string(), - ua: v.string(), - origin: v.string(), + args: [s.object({ + authToken: s.string(), + ua: s.string(), + origin: s.string(), })], - returns: v.object({ isTrusted: v.boolean() }), + returns: s.object({ isTrusted: s.boolean() }), handler(params) { const session = context.rpc.getCurrentRpcSession() if (!session) @@ -111,12 +111,12 @@ export function createInteractiveAuth( name: 'anonymous:devframe:auth:exchange', type: 'action', jsonSerializable: true, - args: [v.object({ - code: v.string(), - ua: v.string(), - origin: v.string(), + args: [s.object({ + code: s.string(), + ua: s.string(), + origin: s.string(), })], - returns: v.object({ authToken: v.nullable(v.string()) }), + returns: s.object({ authToken: s.nullable(s.string()) }), handler(params) { const session = context.rpc.getCurrentRpcSession() if (!session) @@ -134,7 +134,7 @@ export function createInteractiveAuth( type: 'action', jsonSerializable: true, args: [], - returns: v.void(), + returns: s.void(), async handler() { const session = context.rpc.getCurrentRpcSession() const token = session?.meta.clientAuthToken diff --git a/packages/devframe/src/types/devframe.ts b/packages/devframe/src/types/devframe.ts index cd650357..77dbde86 100644 --- a/packages/devframe/src/types/devframe.ts +++ b/packages/devframe/src/types/devframe.ts @@ -163,10 +163,11 @@ export interface DevframeCliOptions { */ configure?: (cli: CAC) => void /** - * Typed CLI flags for the default `dev` command, backed by valibot - * schemas. The adapter registers matching `--kebab-key` options on - * CAC, validates the parsed values, and forwards the typed bag to - * `setup(ctx, { flags })`. + * Typed CLI flags for the default `dev` command, backed by any + * [Standard Schema](https://standardschema.dev/) validator (valibot, + * zod, arktype, or devframe's built-in `s`). The adapter registers + * matching `--kebab-key` options on CAC, validates the parsed values, + * and forwards the typed bag to `setup(ctx, { flags })`. * * Use {@link defineCliFlags} to preserve the literal schema-map * shape, and {@link InferCliFlags} to recover the typed output at the diff --git a/packages/devframe/src/utils/schema.test.ts b/packages/devframe/src/utils/schema.test.ts new file mode 100644 index 00000000..b6ceb04c --- /dev/null +++ b/packages/devframe/src/utils/schema.test.ts @@ -0,0 +1,71 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec' +import { describe, expect, it } from 'vitest' +import { s } from './schema' + +function run(schema: T, value: unknown): StandardSchemaV1.Result> { + const result = schema['~standard'].validate(value) + if (result instanceof Promise) + throw new TypeError('unexpected async validator') + return result +} + +function accepts(schema: StandardSchemaV1, value: unknown): boolean { + return !run(schema, value).issues +} + +describe('utils/schema builder', () => { + it('produces valid Standard Schema objects', () => { + const schema = s.string() + expect(schema['~standard'].version).toBe(1) + expect(schema['~standard'].vendor).toBe('devframe') + expect(typeof schema['~standard'].validate).toBe('function') + }) + + it('validates primitives', () => { + expect(accepts(s.string(), 'x')).toBe(true) + expect(accepts(s.string(), 1)).toBe(false) + expect(accepts(s.number(), 3)).toBe(true) + expect(accepts(s.number(), Number.NaN)).toBe(false) + expect(accepts(s.boolean(), true)).toBe(true) + expect(accepts(s.boolean(), 'true')).toBe(false) + expect(accepts(s.void(), undefined)).toBe(true) + expect(accepts(s.void(), null)).toBe(false) + expect(accepts(s.null(), null)).toBe(true) + }) + + it('handles picklist', () => { + const schema = s.picklist(['a', 'b'] as const) + expect(accepts(schema, 'a')).toBe(true) + expect(accepts(schema, 'c')).toBe(false) + }) + + it('handles optional and nullable wrappers', () => { + expect(accepts(s.optional(s.string()), undefined)).toBe(true) + expect(accepts(s.optional(s.string()), 'x')).toBe(true) + expect(accepts(s.optional(s.string()), 1)).toBe(false) + expect(accepts(s.nullable(s.number()), null)).toBe(true) + expect(accepts(s.nullable(s.number()), 2)).toBe(true) + expect(accepts(s.nullable(s.number()), 'x')).toBe(false) + }) + + it('exposes duck-typed kind markers for CLI-flag introspection', () => { + expect((s.boolean() as any).type).toBe('boolean') + expect((s.optional(s.boolean()) as any).type).toBe('optional') + expect((s.optional(s.boolean()) as any).wrapped['~standard'].vendor).toBe('devframe') + }) + + it('validates objects and reports issue paths', () => { + const schema = s.object({ id: s.string(), count: s.number() }) + expect(accepts(schema, { id: 'x', count: 1 })).toBe(true) + const bad = run(schema, { id: 'x', count: 'nope' }) + expect(bad.issues?.[0]?.path).toEqual(['count']) + }) + + it('keeps object keys beyond the schema (guard-only)', () => { + const schema = s.object({ id: s.string() }) + const result = run(schema, { id: 'x', extra: true }) + expect(result.issues).toBeUndefined() + if (!result.issues) + expect(result.value).toEqual({ id: 'x', extra: true }) + }) +}) diff --git a/packages/devframe/src/utils/schema.ts b/packages/devframe/src/utils/schema.ts new file mode 100644 index 00000000..a0a6349f --- /dev/null +++ b/packages/devframe/src/utils/schema.ts @@ -0,0 +1,203 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec' + +/** + * A tiny, zero-dependency [Standard Schema](https://standardschema.dev/) + * builder. + * + * Devframe's RPC and CLI-flag layers accept **any** Standard Schema + * validator — valibot, zod, arktype, and others — so bring your own if you + * already use one. This builder exists so devframe's own recipes (and + * simple apps) can declare `args`/`returns`/flag schemas without pulling in + * a validator dependency at all. It implements only the primitives those + * surfaces need. + * + * ```ts + * import { s } from 'devframe/utils/schema' + * + * defineRpcFunction({ + * name: 'greet', + * args: [s.object({ name: s.string() })], + * returns: s.string(), + * handler: ({ name }) => `hi ${name}`, + * }) + * ``` + */ + +/** + * A Standard Schema produced by this builder. It carries a duck-typed + * `type` marker (and `wrapped` for wrappers) alongside the standard + * `~standard` prop so the CLI-flags adapter can introspect the schema kind + * without importing any validator. + */ +export interface DevframeSchema extends StandardSchemaV1 { + /** Schema kind marker, e.g. `'string'`, `'boolean'`, `'optional'`. */ + readonly type: string + /** Inner schema for wrapper kinds (`optional` / `nullable`). */ + readonly wrapped?: StandardSchemaV1 + /** Optional human description (surfaced as CLI option help). */ + readonly description?: string +} + +type Issue = StandardSchemaV1.Issue + +function ok(value: T): StandardSchemaV1.Result { + return { value } +} + +function fail(message: string, path?: Issue['path']): StandardSchemaV1.FailureResult { + return { issues: [path ? { message, path } : { message }] } +} + +function make( + type: string, + validate: StandardSchemaV1.Props['validate'], + extra?: Record, +): DevframeSchema { + return { + type, + ...extra, + '~standard': { + version: 1, + vendor: 'devframe', + validate, + }, + } as DevframeSchema +} + +/** Run a Standard Schema synchronously, rejecting async validators. */ +function runSync( + schema: T, + value: unknown, +): StandardSchemaV1.Result> { + const result = schema['~standard'].validate(value) + if (result instanceof Promise) + throw new TypeError('[devframe/utils/schema] async validators are not supported inside object()/optional()/nullable()') + return result +} + +/** Any string. */ +export function string(): DevframeSchema { + return make('string', v => (typeof v === 'string' ? ok(v) : fail('Expected a string'))) +} + +/** A finite number (rejects `NaN`). */ +export function number(): DevframeSchema { + return make('number', v => (typeof v === 'number' && !Number.isNaN(v) ? ok(v) : fail('Expected a number'))) +} + +/** A boolean. */ +export function boolean(): DevframeSchema { + return make('boolean', v => (typeof v === 'boolean' ? ok(v) : fail('Expected a boolean'))) +} + +/** `undefined` — mirrors valibot's `void`. */ +export function voidType(): DevframeSchema { + return make('void', v => (v === undefined ? ok(undefined) : fail('Expected undefined'))) +} + +/** `null`. */ +export function nullType(): DevframeSchema { + return make('null', v => (v === null ? ok(null) : fail('Expected null'))) +} + +/** One of a fixed set of literal values. */ +export function picklist( + values: T, +): DevframeSchema { + const set = new Set(values) + return make( + 'picklist', + v => (set.has(v) ? ok(v as T[number]) : fail(`Expected one of: ${values.join(', ')}`)), + { values }, + ) +} + +/** An array whose every element satisfies the item schema. */ +export function array( + item: T, +): DevframeSchema[], StandardSchemaV1.InferOutput[]> { + return make('array', (v) => { + if (!Array.isArray(v)) + return fail('Expected an array') + const issues: Issue[] = [] + for (let i = 0; i < v.length; i++) { + const result = runSync(item, v[i]) + if (result.issues) { + for (const issue of result.issues) + issues.push({ message: issue.message, path: [i, ...(issue.path ?? [])] }) + } + } + return issues.length ? { issues } : ok(v as any) + }) +} + +/** An object whose known keys each satisfy their schema (extra keys are kept). */ +export function object>( + shape: T, +): DevframeSchema< + { [K in keyof T]: StandardSchemaV1.InferInput }, + { [K in keyof T]: StandardSchemaV1.InferOutput } +> { + const entries = Object.entries(shape) + return make('object', (v) => { + if (typeof v !== 'object' || v === null || Array.isArray(v)) + return fail('Expected an object') + const obj = v as Record + const issues: Issue[] = [] + for (const [key, schema] of entries) { + const result = runSync(schema, obj[key]) + if (result.issues) { + for (const issue of result.issues) + issues.push({ message: issue.message, path: [key, ...(issue.path ?? [])] }) + } + } + // Guard-only: return the original object so extra keys survive. + return issues.length ? { issues } : ok(v as any) + }) +} + +/** Allow `undefined` in addition to the inner schema. */ +export function optional( + inner: T, +): DevframeSchema | undefined, StandardSchemaV1.InferOutput | undefined> { + return make( + 'optional', + v => (v === undefined ? ok(undefined) : runSync(inner, v)), + { wrapped: inner }, + ) +} + +/** Allow `null` in addition to the inner schema. */ +export function nullable( + inner: T, +): DevframeSchema | null, StandardSchemaV1.InferOutput | null> { + return make( + 'nullable', + v => (v === null ? ok(null) : runSync(inner, v)), + { wrapped: inner }, + ) +} + +/** Attach a human-readable description (used for CLI option help). */ +export function describe>(schema: T, description: string): T { + return { ...schema, description } +} + +/** + * Grouped access to every builder — `s.string()`, `s.object({ ... })`, + * `s.void()`, etc. Handy for a valibot-like `import { s } from + * 'devframe/utils/schema'` call site. + */ +export const s = { + string, + number, + boolean, + void: voidType, + null: nullType, + picklist, + array, + object, + optional, + nullable, + describe, +} as const diff --git a/packages/devframe/tsdown.config.ts b/packages/devframe/tsdown.config.ts index ee003592..ef4d17a7 100644 --- a/packages/devframe/tsdown.config.ts +++ b/packages/devframe/tsdown.config.ts @@ -75,6 +75,7 @@ const clientEntries = { 'utils/hash': 'src/utils/hash.ts', 'utils/nanoid': 'src/utils/nanoid.ts', 'utils/promise': 'src/utils/promise.ts', + 'utils/schema': 'src/utils/schema.ts', 'utils/scope': 'src/utils/scope.ts', 'utils/shared-state': 'src/utils/shared-state.ts', 'utils/streaming-channel': 'src/utils/streaming-channel.ts', @@ -148,6 +149,7 @@ export default defineConfig([ resolve(distDir, 'utils/hash.mjs'), resolve(distDir, 'utils/nanoid.mjs'), resolve(distDir, 'utils/promise.mjs'), + resolve(distDir, 'utils/schema.mjs'), resolve(distDir, 'utils/scope.mjs'), resolve(distDir, 'utils/shared-state.mjs'), resolve(distDir, 'utils/streaming-channel.mjs'), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4175ea8d..4931dd38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -791,9 +791,6 @@ importers: '@standard-schema/spec': specifier: catalog:deps version: 1.1.0 - '@valibot/to-json-schema': - specifier: catalog:deps - version: 1.7.1(valibot@1.4.2(typescript@6.0.3)) birpc: specifier: catalog:deps version: 4.0.0 @@ -818,9 +815,6 @@ importers: ufo: specifier: catalog:deps version: 1.6.4 - valibot: - specifier: catalog:deps - version: 1.4.2(typescript@6.0.3) devDependencies: '@modelcontextprotocol/client': specifier: catalog:deps @@ -870,6 +864,9 @@ importers: ua-parser-modern: specifier: catalog:inlined version: 0.1.1 + valibot: + specifier: catalog:deps + version: 1.4.2(typescript@6.0.3) whenexpr: specifier: catalog:deps version: 0.1.2 diff --git a/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts index 245abdd6..2c2170b0 100644 --- a/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts @@ -137,21 +137,21 @@ export declare const list: { agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: (() => { path: string; - type: "image" | "font" | "video" | "audio" | "text" | "other"; + type: "text" | "image" | "font" | "video" | "audio" | "other"; publicPath: string; size: number; mtime: number; }[]) | undefined; dump?: import("devframe/rpc").RpcDump<[], { path: string; - type: "image" | "font" | "video" | "audio" | "text" | "other"; + type: "text" | "image" | "font" | "video" | "audio" | "other"; publicPath: string; size: number; mtime: number; @@ -159,14 +159,14 @@ export declare const list: { snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable { path: string; - type: "image" | "font" | "video" | "audio" | "text" | "other"; + type: "text" | "image" | "font" | "video" | "audio" | "other"; publicPath: string; size: number; mtime: number; @@ -445,7 +445,7 @@ export declare const rename: { newName: string; }], { path: string; - type: "image" | "font" | "video" | "audio" | "text" | "other"; + type: "text" | "image" | "font" | "video" | "audio" | "other"; publicPath: string; size: number; mtime: number; @@ -456,7 +456,7 @@ export declare const rename: { newName: string; }], { path: string; - type: "image" | "font" | "video" | "audio" | "text" | "other"; + type: "text" | "image" | "font" | "video" | "audio" | "other"; publicPath: string; size: number; mtime: number; @@ -466,7 +466,7 @@ export declare const rename: { newName: string; }], { path: string; - type: "image" | "font" | "video" | "audio" | "text" | "other"; + type: "text" | "image" | "font" | "video" | "audio" | "other"; publicPath: string; size: number; mtime: number; diff --git a/tests/__snapshots__/tsnapi/devframe/recipes/common-rpc-functions.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/recipes/common-rpc-functions.snapshot.d.ts index 7114a897..17cf6a05 100644 --- a/tests/__snapshots__/tsnapi/devframe/recipes/common-rpc-functions.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/recipes/common-rpc-functions.snapshot.d.ts @@ -10,8 +10,8 @@ export declare const commonRpcFunctions: readonly [{ name: "devframe:open-in-editor"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [v.StringSchema, v.OptionalSchema, undefined>]; - returns: v.VoidSchema; + args: readonly [DevframeSchema, DevframeSchema]; + returns: DevframeSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; @@ -24,8 +24,8 @@ export declare const commonRpcFunctions: readonly [{ name: "devframe:open-in-finder"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [v.StringSchema]; - returns: v.VoidSchema; + args: readonly [DevframeSchema]; + returns: DevframeSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; @@ -40,8 +40,8 @@ export declare const openInEditor: { name: "devframe:open-in-editor"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [v.StringSchema, v.OptionalSchema, undefined>]; - returns: v.VoidSchema; + args: readonly [DevframeSchema, DevframeSchema]; + returns: DevframeSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; @@ -55,8 +55,8 @@ export declare const openInFinder: { name: "devframe:open-in-finder"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [v.StringSchema]; - returns: v.VoidSchema; + args: readonly [DevframeSchema]; + returns: DevframeSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; diff --git a/tests/__snapshots__/tsnapi/devframe/recipes/open-helpers.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/recipes/open-helpers.snapshot.d.ts index 436d011f..70f5c060 100644 --- a/tests/__snapshots__/tsnapi/devframe/recipes/open-helpers.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/recipes/open-helpers.snapshot.d.ts @@ -8,8 +8,8 @@ export declare const openInEditor: { name: "devframe:open-in-editor"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [v.StringSchema, v.OptionalSchema, undefined>]; - returns: v.VoidSchema; + args: readonly [DevframeSchema, DevframeSchema]; + returns: DevframeSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; @@ -23,8 +23,8 @@ export declare const openInFinder: { name: "devframe:open-in-finder"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [v.StringSchema]; - returns: v.VoidSchema; + args: readonly [DevframeSchema]; + returns: DevframeSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; diff --git a/tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.d.ts new file mode 100644 index 00000000..6c9e93c2 --- /dev/null +++ b/tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.d.ts @@ -0,0 +1,18 @@ +/** + * Generated by tsnapi — public API snapshot of `devframe/utils/schema` + */ +// #region Other +export { array } +export { boolean } +export { describe } +export { DevframeSchema } +export { nullable } +export { nullType } +export { number } +export { object } +export { optional } +export { picklist } +export { s } +export { string } +export { voidType } +// #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.js b/tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.js new file mode 100644 index 00000000..7db6e54c --- /dev/null +++ b/tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.js @@ -0,0 +1,20 @@ +/** + * Generated by tsnapi — public API snapshot of `devframe/utils/schema` + */ +// #region Functions +export function array(_) {} +export function boolean() {} +export function describe(_, _) {} +export function nullable(_) {} +export function nullType() {} +export function number() {} +export function object(_) {} +export function optional(_) {} +export function picklist(_) {} +export function string() {} +export function voidType() {} +// #endregion + +// #region Variables +export var s /* const */ +// #endregion \ No newline at end of file diff --git a/tsconfig.base.json b/tsconfig.base.json index 87e66489..b02df753 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -64,6 +64,9 @@ "devframe/utils/promise": [ "./packages/devframe/src/utils/promise.ts" ], + "devframe/utils/schema": [ + "./packages/devframe/src/utils/schema.ts" + ], "devframe/utils/scope": [ "./packages/devframe/src/utils/scope.ts" ], From e6c03ea74a8d6727e1cfe0ed2b7694f61590e38a Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Mon, 3 Aug 2026 00:50:18 +0000 Subject: [PATCH 4/8] refactor(rpc): rename schema builder to simple-schema; use it in plugins; docs use valibot - Rename devframe/utils/schema -> devframe/utils/simple-schema and rename the exported type DevframeSchema -> SimpleSchema. The builder is now explicitly documented as discouraged for app code (a minimal, best-effort validator for devframe's own first-party packages). - Extend the builder with record/union/literal and make object() infer optional keys for optional() fields (matching valibot/zod). - Migrate the built-in plugins (assets, og, terminals) off valibot onto devframe/utils/simple-schema, and drop valibot from their dependencies. - Docs: never reference the built-in builder; all schema examples use valibot with an explicit install hint (npm i valibot). BREAKING CHANGE: devframe/utils/schema is renamed to devframe/utils/simple-schema and its exported type DevframeSchema is renamed to SimpleSchema. --- alias.ts | 2 +- docs/guide/devframe-definition.md | 2 +- docs/guide/rpc.md | 20 +- docs/guide/standalone-cli.md | 4 +- docs/guide/streaming.md | 2 +- docs/helpers/common-rpc-functions.md | 2 +- packages/devframe/package.json | 2 +- .../mcp/__tests__/to-json-schema.test.ts | 2 +- .../src/recipes/common-rpc-functions.ts | 2 +- .../devframe/src/recipes/interactive-auth.ts | 2 +- .../{schema.test.ts => simple-schema.test.ts} | 4 +- .../src/utils/{schema.ts => simple-schema.ts} | 126 ++++-- packages/devframe/tsdown.config.ts | 4 +- plugins/assets/package.json | 3 +- .../assets/src/rpc/functions/capabilities.ts | 8 +- plugins/assets/src/rpc/functions/delete.ts | 6 +- plugins/assets/src/rpc/functions/list.ts | 16 +- plugins/assets/src/rpc/functions/mkdir.ts | 6 +- .../src/rpc/functions/open-in-editor.ts | 6 +- .../src/rpc/functions/read-image-meta.ts | 12 +- plugins/assets/src/rpc/functions/read-text.ts | 6 +- plugins/assets/src/rpc/functions/rename.ts | 4 +- .../src/rpc/functions/reveal-in-folder.ts | 6 +- plugins/assets/src/rpc/functions/upload.ts | 6 +- plugins/og/package.json | 3 +- .../og/src/rpc/functions/resolve-metadata.ts | 24 +- plugins/terminals/package.json | 1 - .../src/rpc/functions/clear-exited.ts | 4 +- plugins/terminals/src/rpc/functions/list.ts | 4 +- .../terminals/src/rpc/functions/presets.ts | 4 +- plugins/terminals/src/rpc/functions/remove.ts | 6 +- plugins/terminals/src/rpc/functions/rename.ts | 6 +- plugins/terminals/src/rpc/functions/resize.ts | 12 +- .../terminals/src/rpc/functions/restart.ts | 4 +- .../terminals/src/rpc/functions/terminate.ts | 6 +- plugins/terminals/src/rpc/functions/write.ts | 6 +- plugins/terminals/src/rpc/schemas.ts | 72 +-- pnpm-lock.yaml | 9 - .../plugin-assets/rpc.snapshot.d.ts | 416 +++++++++++------- .../@devframes/plugin-og/rpc.snapshot.d.ts | 40 +- .../plugin-terminals/rpc.snapshot.d.ts | 414 ++++++++++------- .../common-rpc-functions.snapshot.d.ts | 16 +- .../recipes/open-helpers.snapshot.d.ts | 8 +- ...pshot.d.ts => simple-schema.snapshot.d.ts} | 7 +- ....snapshot.js => simple-schema.snapshot.js} | 5 +- tsconfig.base.json | 4 +- 46 files changed, 787 insertions(+), 537 deletions(-) rename packages/devframe/src/utils/{schema.test.ts => simple-schema.test.ts} (97%) rename packages/devframe/src/utils/{schema.ts => simple-schema.ts} (50%) rename tests/__snapshots__/tsnapi/devframe/utils/{schema.snapshot.d.ts => simple-schema.snapshot.d.ts} (77%) rename tests/__snapshots__/tsnapi/devframe/utils/{schema.snapshot.js => simple-schema.snapshot.js} (82%) diff --git a/alias.ts b/alias.ts index cfb8846e..2ea324ee 100644 --- a/alias.ts +++ b/alias.ts @@ -26,7 +26,7 @@ export const alias = { 'devframe/utils/nanoid': r('devframe/src/utils/nanoid.ts'), 'devframe/utils/open': r('devframe/src/utils/open.ts'), 'devframe/utils/promise': r('devframe/src/utils/promise.ts'), - 'devframe/utils/schema': r('devframe/src/utils/schema.ts'), + 'devframe/utils/simple-schema': r('devframe/src/utils/simple-schema.ts'), 'devframe/utils/scope': r('devframe/src/utils/scope.ts'), 'devframe/utils/serve-static': r('devframe/src/utils/serve-static.ts'), 'devframe/utils/shared-state': r('devframe/src/utils/shared-state.ts'), diff --git a/docs/guide/devframe-definition.md b/docs/guide/devframe-definition.md index 9c87de34..e389d7e4 100644 --- a/docs/guide/devframe-definition.md +++ b/docs/guide/devframe-definition.md @@ -10,7 +10,7 @@ Every Devframe tool starts with a single `defineDevframe` call. The returned `De ```ts twoslash import { defineDevframe, defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import * as v from 'valibot' // npm i valibot export default defineDevframe({ id: 'my-devframe', diff --git a/docs/guide/rpc.md b/docs/guide/rpc.md index fe3b536c..1f5057aa 100644 --- a/docs/guide/rpc.md +++ b/docs/guide/rpc.md @@ -22,13 +22,13 @@ sequenceDiagram ```ts import { defineRpcFunction } from 'devframe' -import { s } from 'devframe/utils/schema' // or bring your own: valibot / zod / arktype +import * as v from 'valibot' // npm i valibot (or use zod / arktype) export const getModules = defineRpcFunction({ name: 'get-modules', // bare — the scope namespaces it to `my-devframe:get-modules` type: 'query', - args: [s.object({ limit: s.number() })], - returns: s.array(s.object({ id: s.string(), size: s.number() })), + args: [v.object({ limit: v.number() })], + returns: v.array(v.object({ id: v.string(), size: v.number() })), setup: ctx => ({ handler: async ({ limit }) => { // `ctx` is the full DevframeNodeContext. @@ -77,14 +77,14 @@ Use `static` for data collected once during `setup` and shipped to read-only sta Handlers accept any serializable arguments. Declare `args` schemas — any [Standard Schema](https://standardschema.dev/) validator (valibot, zod, arktype, …) — and each argument is validated at the boundary before the handler runs; a mismatch is rejected with a coded diagnostic. Validation guards the payload without rewriting it, so extra object fields the schema doesn't mention still reach the handler. -Devframe forces no validator on you. Bring the one you already use, or reach for the built-in zero-dependency builder at `devframe/utils/schema` (imported as `s` below): +Devframe forces no validator on you: bring whichever [Standard Schema](https://standardschema.dev/) validator you prefer (valibot, zod, arktype) and install it yourself. The examples here use valibot (`npm i valibot`): ```ts defineRpcFunction({ name: 'get-file', type: 'query', - args: [s.object({ path: s.string(), includeSource: s.optional(s.boolean()) })], - returns: s.object({ path: s.string(), source: s.optional(s.string()) }), + args: [v.object({ path: v.string(), includeSource: v.optional(v.boolean()) })], + returns: v.object({ path: v.string(), source: v.optional(v.string()) }), setup: () => ({ handler: async ({ path, includeSource }) => ({ path, @@ -94,7 +94,7 @@ defineRpcFunction({ }) ``` -Prefer a single object argument (`args: [s.object({ ... })]`) over positional args — property names are self-describing and agents/IDEs work best with object shapes. +Prefer a single object argument (`args: [v.object({ ... })]`) over positional args — property names are self-describing and agents/IDEs work best with object shapes. > [!WARNING] > Declared `args`/`returns` schemas are enforced at runtime — a call whose arguments, or a handler whose return value, fail the schema is rejected with `DF0043` / `DF0044`. Make sure each schema matches what the function actually accepts and returns; a schema stricter than reality will now reject calls that previously ran. @@ -247,7 +247,7 @@ defineRpcFunction({ name: 'build-meta', type: 'static', args: [], - returns: s.object({ version: s.string(), builtAt: s.number() }), + returns: v.object({ version: v.string(), builtAt: v.number() }), setup: () => ({ handler: async () => ({ version: '1.0.0', builtAt: Date.now() }), }), @@ -311,8 +311,8 @@ defineRpcFunction({ name: 'get-modules', type: 'query', jsonSerializable: true, - args: [s.object({ limit: s.number() })], - returns: s.array(s.object({ id: s.string(), size: s.number() })), + args: [v.object({ limit: v.number() })], + returns: v.array(v.object({ id: v.string(), size: v.number() })), agent: { description: 'List the N largest modules in the current build. Safe to call freely.', title: 'List modules', diff --git a/docs/guide/standalone-cli.md b/docs/guide/standalone-cli.md index e184581f..15d61333 100644 --- a/docs/guide/standalone-cli.md +++ b/docs/guide/standalone-cli.md @@ -168,13 +168,13 @@ const payload = await my.rpc.call('get-payload') ## Typed CLI flags -For flags that are specific to your tool, declare them with any [Standard Schema](https://standardschema.dev/) validator (valibot below, or zod / arktype / devframe's built-in `s`) so they're validated at parse time and typed at the call site: +For flags that are specific to your tool, declare them with any [Standard Schema](https://standardschema.dev/) validator (valibot below — `npm i valibot` — or zod / arktype) so they're validated at parse time and typed at the call site: ```ts import type { InferCliFlags } from 'devframe/adapters/cac' import { defineDevframe } from 'devframe' import { defineCliFlags } from 'devframe/adapters/cac' -import * as v from 'valibot' +import * as v from 'valibot' // npm i valibot const appFlags = defineCliFlags({ depth: v.pipe(v.number(), v.integer()), diff --git a/docs/guide/streaming.md b/docs/guide/streaming.md index d5198e37..31852db4 100644 --- a/docs/guide/streaming.md +++ b/docs/guide/streaming.md @@ -29,7 +29,7 @@ Create the channel once in `setup`. Channels are framework-neutral, so the same ```ts import { defineDevframe, defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import * as v from 'valibot' // npm i valibot export default defineDevframe({ id: 'my-devframe', diff --git a/docs/helpers/common-rpc-functions.md b/docs/helpers/common-rpc-functions.md index a0794873..8efa0d43 100644 --- a/docs/helpers/common-rpc-functions.md +++ b/docs/helpers/common-rpc-functions.md @@ -28,7 +28,7 @@ defineDevframe({ | `KNOWN_EDITORS` | — | `readonly string[]` | — | The editor commands `openInEditor`'s `editor` argument accepts (`code`, `vim`, `subl`, `idea`, …). | | `KnownEditor` | — | type | — | Union of `KNOWN_EDITORS`. | -Both functions are `action`-type RPCs returning `void` and declare their arguments with devframe's built-in zero-dependency `s` builder from `devframe/utils/schema` — `openInEditor`'s `editor` argument is `s.optional(s.picklist(KNOWN_EDITORS))`, so a value outside `KNOWN_EDITORS` fails validation rather than reaching the underlying `launch-editor` process spawn. Both handlers dynamically `import()` their underlying `devframe/utils/*` implementation, so the `launch-editor` and `open` dependencies only load when the recipe actually runs. +Both functions are `action`-type RPCs returning `void`, and their arguments are schema-validated — `openInEditor`'s `editor` argument is restricted to `KNOWN_EDITORS`, so a value outside that list fails validation rather than reaching the underlying `launch-editor` process spawn. Both handlers dynamically `import()` their underlying `devframe/utils/*` implementation, so the `launch-editor` and `open` dependencies only load when the recipe actually runs. The `devframe/recipes/open-helpers` entry (`openHelpers`) remains as a deprecated alias for this module — new code should import `commonRpcFunctions` from `devframe/recipes/common-rpc-functions`. diff --git a/packages/devframe/package.json b/packages/devframe/package.json index d5c1c2b5..29680312 100644 --- a/packages/devframe/package.json +++ b/packages/devframe/package.json @@ -50,7 +50,7 @@ "./utils/nanoid": "./dist/utils/nanoid.mjs", "./utils/open": "./dist/utils/open.mjs", "./utils/promise": "./dist/utils/promise.mjs", - "./utils/schema": "./dist/utils/schema.mjs", + "./utils/simple-schema": "./dist/utils/simple-schema.mjs", "./utils/scope": "./dist/utils/scope.mjs", "./utils/serve-static": "./dist/utils/serve-static.mjs", "./utils/shared-state": "./dist/utils/shared-state.mjs", diff --git a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts b/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts index 59221fbd..0a8472f1 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts +++ b/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts @@ -1,4 +1,4 @@ -import { s } from 'devframe/utils/schema' +import { s } from 'devframe/utils/simple-schema' import { describe, expect, it } from 'vitest' import { argsToJsonSchema, returnToJsonSchema } from '../to-json-schema' diff --git a/packages/devframe/src/recipes/common-rpc-functions.ts b/packages/devframe/src/recipes/common-rpc-functions.ts index 1c03c8d9..60f8b9fd 100644 --- a/packages/devframe/src/recipes/common-rpc-functions.ts +++ b/packages/devframe/src/recipes/common-rpc-functions.ts @@ -1,4 +1,4 @@ -import { s } from 'devframe/utils/schema' +import { s } from 'devframe/utils/simple-schema' import { defineRpcFunction } from '../rpc/define' /** diff --git a/packages/devframe/src/recipes/interactive-auth.ts b/packages/devframe/src/recipes/interactive-auth.ts index 663d138d..4af42d98 100644 --- a/packages/devframe/src/recipes/interactive-auth.ts +++ b/packages/devframe/src/recipes/interactive-auth.ts @@ -1,7 +1,7 @@ import type { DevframeNodeContext, DevframeNodeRpcSession } from 'devframe/types' import type { DevframeAuthHandler } from '../node/auth' import { colors } from 'devframe/utils/colors' -import { s } from 'devframe/utils/schema' +import { s } from 'devframe/utils/simple-schema' import { DEVFRAME_AUTH_TOKEN_QUERY_PARAM, isAnonymousRpcMethod } from '../constants' import { buildOtpAuthUrl, exchangeTempAuthCode, getTempAuthCode, verifyAuthToken } from '../node/auth/state' import { getInternalContext } from '../node/hub-internals/context' diff --git a/packages/devframe/src/utils/schema.test.ts b/packages/devframe/src/utils/simple-schema.test.ts similarity index 97% rename from packages/devframe/src/utils/schema.test.ts rename to packages/devframe/src/utils/simple-schema.test.ts index b6ceb04c..e4d5fc82 100644 --- a/packages/devframe/src/utils/schema.test.ts +++ b/packages/devframe/src/utils/simple-schema.test.ts @@ -1,6 +1,6 @@ import type { StandardSchemaV1 } from '@standard-schema/spec' import { describe, expect, it } from 'vitest' -import { s } from './schema' +import { s } from './simple-schema' function run(schema: T, value: unknown): StandardSchemaV1.Result> { const result = schema['~standard'].validate(value) @@ -13,7 +13,7 @@ function accepts(schema: StandardSchemaV1, value: unknown): boolean { return !run(schema, value).issues } -describe('utils/schema builder', () => { +describe('utils/simple-schema builder', () => { it('produces valid Standard Schema objects', () => { const schema = s.string() expect(schema['~standard'].version).toBe(1) diff --git a/packages/devframe/src/utils/schema.ts b/packages/devframe/src/utils/simple-schema.ts similarity index 50% rename from packages/devframe/src/utils/schema.ts rename to packages/devframe/src/utils/simple-schema.ts index a0a6349f..2124d94e 100644 --- a/packages/devframe/src/utils/schema.ts +++ b/packages/devframe/src/utils/simple-schema.ts @@ -4,32 +4,36 @@ import type { StandardSchemaV1 } from '@standard-schema/spec' * A tiny, zero-dependency [Standard Schema](https://standardschema.dev/) * builder. * - * Devframe's RPC and CLI-flag layers accept **any** Standard Schema - * validator — valibot, zod, arktype, and others — so bring your own if you - * already use one. This builder exists so devframe's own recipes (and - * simple apps) can declare `args`/`returns`/flag schemas without pulling in - * a validator dependency at all. It implements only the primitives those - * surfaces need. + * ⚠️ **Discouraged for app code.** This is a deliberately minimal, + * best-effort validator that exists only so devframe's own first-party + * packages (recipes, built-in plugins) can declare `args`/`returns`/flag + * schemas without taking on a validator dependency. It implements a small + * subset of primitives and approximates refinements — it is not a + * general-purpose validator. + * + * For your own code, prefer a real Standard Schema validator — **valibot**, + * **zod**, or **arktype**. Devframe's RPC and CLI-flag layers accept any of + * them; install the one you like and use it directly: * * ```ts - * import { s } from 'devframe/utils/schema' + * import * as v from 'valibot' // npm i valibot * * defineRpcFunction({ * name: 'greet', - * args: [s.object({ name: s.string() })], - * returns: s.string(), + * args: [v.object({ name: v.string() })], + * returns: v.string(), * handler: ({ name }) => `hi ${name}`, * }) * ``` */ /** - * A Standard Schema produced by this builder. It carries a duck-typed - * `type` marker (and `wrapped` for wrappers) alongside the standard - * `~standard` prop so the CLI-flags adapter can introspect the schema kind - * without importing any validator. + * A Standard Schema produced by the {@link s} builder. It carries a + * duck-typed `type` marker (and `wrapped` for wrappers) alongside the + * standard `~standard` prop so the CLI-flags adapter can introspect the + * schema kind without importing any validator. */ -export interface DevframeSchema extends StandardSchemaV1 { +export interface SimpleSchema extends StandardSchemaV1 { /** Schema kind marker, e.g. `'string'`, `'boolean'`, `'optional'`. */ readonly type: string /** Inner schema for wrapper kinds (`optional` / `nullable`). */ @@ -52,7 +56,7 @@ function make( type: string, validate: StandardSchemaV1.Props['validate'], extra?: Record, -): DevframeSchema { +): SimpleSchema { return { type, ...extra, @@ -61,7 +65,7 @@ function make( vendor: 'devframe', validate, }, - } as DevframeSchema + } as SimpleSchema } /** Run a Standard Schema synchronously, rejecting async validators. */ @@ -71,39 +75,39 @@ function runSync( ): StandardSchemaV1.Result> { const result = schema['~standard'].validate(value) if (result instanceof Promise) - throw new TypeError('[devframe/utils/schema] async validators are not supported inside object()/optional()/nullable()') + throw new TypeError('[devframe/utils/simple-schema] async validators are not supported inside object()/optional()/nullable()') return result } /** Any string. */ -export function string(): DevframeSchema { +export function string(): SimpleSchema { return make('string', v => (typeof v === 'string' ? ok(v) : fail('Expected a string'))) } /** A finite number (rejects `NaN`). */ -export function number(): DevframeSchema { +export function number(): SimpleSchema { return make('number', v => (typeof v === 'number' && !Number.isNaN(v) ? ok(v) : fail('Expected a number'))) } /** A boolean. */ -export function boolean(): DevframeSchema { +export function boolean(): SimpleSchema { return make('boolean', v => (typeof v === 'boolean' ? ok(v) : fail('Expected a boolean'))) } /** `undefined` — mirrors valibot's `void`. */ -export function voidType(): DevframeSchema { +export function voidType(): SimpleSchema { return make('void', v => (v === undefined ? ok(undefined) : fail('Expected undefined'))) } /** `null`. */ -export function nullType(): DevframeSchema { +export function nullType(): SimpleSchema { return make('null', v => (v === null ? ok(null) : fail('Expected null'))) } /** One of a fixed set of literal values. */ export function picklist( values: T, -): DevframeSchema { +): SimpleSchema { const set = new Set(values) return make( 'picklist', @@ -112,10 +116,52 @@ export function picklist ) } +/** A single literal value (string / number / boolean). */ +export function literal(value: T): SimpleSchema { + return make('literal', v => (v === value ? ok(v as T) : fail(`Expected ${JSON.stringify(value)}`)), { value }) +} + +/** A value matching any one of the given schemas. */ +export function union( + options: T, +): SimpleSchema, StandardSchemaV1.InferOutput> { + return make('union', (v) => { + const issues: Issue[] = [] + for (const option of options) { + const result = runSync(option, v) + if (!result.issues) + return ok(v as any) + issues.push(...result.issues) + } + return { issues } + }, { options }) +} + +/** A record with string keys whose values each satisfy the value schema. */ +export function record( + _key: StandardSchemaV1, + value: V, +): SimpleSchema>, Record>> { + return make('record', (v) => { + if (typeof v !== 'object' || v === null || Array.isArray(v)) + return fail('Expected an object') + const obj = v as Record + const issues: Issue[] = [] + for (const key of Object.keys(obj)) { + const result = runSync(value, obj[key]) + if (result.issues) { + for (const issue of result.issues) + issues.push({ message: issue.message, path: [key, ...(issue.path ?? [])] }) + } + } + return issues.length ? { issues } : ok(v as any) + }) +} + /** An array whose every element satisfies the item schema. */ export function array( item: T, -): DevframeSchema[], StandardSchemaV1.InferOutput[]> { +): SimpleSchema[], StandardSchemaV1.InferOutput[]> { return make('array', (v) => { if (!Array.isArray(v)) return fail('Expected an array') @@ -131,13 +177,26 @@ export function array( }) } +/** Flatten an intersection into a single object literal for readable types. */ +type Prettify = { [K in keyof T]: T[K] } & {} + +/** + * Map a shape to its object type, turning fields whose type includes + * `undefined` (i.e. `optional()`) into optional keys — mirroring how + * valibot/zod render `optional` object entries. + */ +type InferField + = Mode extends 'input' ? StandardSchemaV1.InferInput : StandardSchemaV1.InferOutput + +type InferObject, Mode extends 'input' | 'output'> = Prettify< + & { [K in keyof T as undefined extends InferField ? never : K]: InferField } + & { [K in keyof T as undefined extends InferField ? K : never]?: InferField } +> + /** An object whose known keys each satisfy their schema (extra keys are kept). */ export function object>( shape: T, -): DevframeSchema< - { [K in keyof T]: StandardSchemaV1.InferInput }, - { [K in keyof T]: StandardSchemaV1.InferOutput } -> { +): SimpleSchema, InferObject> { const entries = Object.entries(shape) return make('object', (v) => { if (typeof v !== 'object' || v === null || Array.isArray(v)) @@ -159,7 +218,7 @@ export function object>( /** Allow `undefined` in addition to the inner schema. */ export function optional( inner: T, -): DevframeSchema | undefined, StandardSchemaV1.InferOutput | undefined> { +): SimpleSchema | undefined, StandardSchemaV1.InferOutput | undefined> { return make( 'optional', v => (v === undefined ? ok(undefined) : runSync(inner, v)), @@ -170,7 +229,7 @@ export function optional( /** Allow `null` in addition to the inner schema. */ export function nullable( inner: T, -): DevframeSchema | null, StandardSchemaV1.InferOutput | null> { +): SimpleSchema | null, StandardSchemaV1.InferOutput | null> { return make( 'nullable', v => (v === null ? ok(null) : runSync(inner, v)), @@ -179,14 +238,14 @@ export function nullable( } /** Attach a human-readable description (used for CLI option help). */ -export function describe>(schema: T, description: string): T { +export function describe>(schema: T, description: string): T { return { ...schema, description } } /** * Grouped access to every builder — `s.string()`, `s.object({ ... })`, * `s.void()`, etc. Handy for a valibot-like `import { s } from - * 'devframe/utils/schema'` call site. + * 'devframe/utils/simple-schema'` call site. */ export const s = { string, @@ -194,7 +253,10 @@ export const s = { boolean, void: voidType, null: nullType, + literal, picklist, + union, + record, array, object, optional, diff --git a/packages/devframe/tsdown.config.ts b/packages/devframe/tsdown.config.ts index ef4d17a7..6f1ffe4f 100644 --- a/packages/devframe/tsdown.config.ts +++ b/packages/devframe/tsdown.config.ts @@ -75,7 +75,7 @@ const clientEntries = { 'utils/hash': 'src/utils/hash.ts', 'utils/nanoid': 'src/utils/nanoid.ts', 'utils/promise': 'src/utils/promise.ts', - 'utils/schema': 'src/utils/schema.ts', + 'utils/simple-schema': 'src/utils/simple-schema.ts', 'utils/scope': 'src/utils/scope.ts', 'utils/shared-state': 'src/utils/shared-state.ts', 'utils/streaming-channel': 'src/utils/streaming-channel.ts', @@ -149,7 +149,7 @@ export default defineConfig([ resolve(distDir, 'utils/hash.mjs'), resolve(distDir, 'utils/nanoid.mjs'), resolve(distDir, 'utils/promise.mjs'), - resolve(distDir, 'utils/schema.mjs'), + resolve(distDir, 'utils/simple-schema.mjs'), resolve(distDir, 'utils/scope.mjs'), resolve(distDir, 'utils/shared-state.mjs'), resolve(distDir, 'utils/streaming-channel.mjs'), diff --git a/plugins/assets/package.json b/plugins/assets/package.json index 212430ab..067e6981 100644 --- a/plugins/assets/package.json +++ b/plugins/assets/package.json @@ -64,8 +64,7 @@ "pathe": "catalog:deps", "perfect-debounce": "catalog:deps", "tinyglobby": "catalog:deps", - "ufo": "catalog:deps", - "valibot": "catalog:deps" + "ufo": "catalog:deps" }, "devDependencies": { "@antfu/design": "catalog:frontend", diff --git a/plugins/assets/src/rpc/functions/capabilities.ts b/plugins/assets/src/rpc/functions/capabilities.ts index 09c9997c..97f38ad5 100644 --- a/plugins/assets/src/rpc/functions/capabilities.ts +++ b/plugins/assets/src/rpc/functions/capabilities.ts @@ -1,6 +1,6 @@ import type { DevframeNodeContext } from 'devframe/types' import { createDefineWrapperWithContext } from 'devframe/rpc' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getAssetsContext } from '../../node/context' const defineAssetsRpc = createDefineWrapperWithContext() @@ -22,9 +22,9 @@ export const capabilities = defineAssetsRpc({ snapshot: true, jsonSerializable: true, args: [], - returns: v.object({ - write: v.boolean(), - uploadExtensions: v.union([v.array(v.string()), v.literal('*')]), + returns: s.object({ + write: s.boolean(), + uploadExtensions: s.union([s.array(s.string()), s.literal('*')]), }), agent: { title: 'Read assets capabilities', diff --git a/plugins/assets/src/rpc/functions/delete.ts b/plugins/assets/src/rpc/functions/delete.ts index 3e363f5a..e5654e56 100644 --- a/plugins/assets/src/rpc/functions/delete.ts +++ b/plugins/assets/src/rpc/functions/delete.ts @@ -1,7 +1,7 @@ import type { DevframeNodeContext } from 'devframe/types' import fsp from 'node:fs/promises' import { createDefineWrapperWithContext } from 'devframe/rpc' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getAssetsContext } from '../../node/context' const defineAssetsRpc = createDefineWrapperWithContext() @@ -11,8 +11,8 @@ export const deleteAssets = defineAssetsRpc({ name: 'devframes:plugin:assets:delete', type: 'action', jsonSerializable: true, - args: [v.object({ paths: v.array(v.string()) })], - returns: v.object({ deleted: v.array(v.string()) }), + args: [s.object({ paths: s.array(s.string()) })], + returns: s.object({ deleted: s.array(s.string()) }), agent: { title: 'Delete assets', description: 'Delete one or more assets from the managed directory.', diff --git a/plugins/assets/src/rpc/functions/list.ts b/plugins/assets/src/rpc/functions/list.ts index 5134c9d0..9116a9d3 100644 --- a/plugins/assets/src/rpc/functions/list.ts +++ b/plugins/assets/src/rpc/functions/list.ts @@ -1,18 +1,18 @@ import type { DevframeNodeContext } from 'devframe/types' import type { AssetInfo } from '../../types' import { createDefineWrapperWithContext } from 'devframe/rpc' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getAssetsContext } from '../../node/context' import { scanAssets } from '../../node/scanner' const defineAssetsRpc = createDefineWrapperWithContext() -export const assetInfoSchema = v.object({ - path: v.string(), - type: v.picklist(['image', 'font', 'video', 'audio', 'text', 'other']), - publicPath: v.string(), - size: v.number(), - mtime: v.number(), +export const assetInfoSchema = s.object({ + path: s.string(), + type: s.picklist(['image', 'font', 'video', 'audio', 'text', 'other']), + publicPath: s.string(), + size: s.number(), + mtime: s.number(), }) export const list = defineAssetsRpc({ @@ -21,7 +21,7 @@ export const list = defineAssetsRpc({ snapshot: true, jsonSerializable: true, args: [], - returns: v.array(assetInfoSchema), + returns: s.array(assetInfoSchema), agent: { title: 'List managed assets', description: 'List every file under the managed directory with its type, size, and last-modified time.', diff --git a/plugins/assets/src/rpc/functions/mkdir.ts b/plugins/assets/src/rpc/functions/mkdir.ts index 8b5d085e..6ecbd744 100644 --- a/plugins/assets/src/rpc/functions/mkdir.ts +++ b/plugins/assets/src/rpc/functions/mkdir.ts @@ -1,7 +1,7 @@ import type { DevframeNodeContext } from 'devframe/types' import fsp from 'node:fs/promises' import { createDefineWrapperWithContext } from 'devframe/rpc' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { diagnostics } from '../../diagnostics' import { getAssetsContext } from '../../node/context' @@ -11,8 +11,8 @@ export const mkdir = defineAssetsRpc({ name: 'devframes:plugin:assets:mkdir', type: 'action', jsonSerializable: true, - args: [v.object({ path: v.string() })], - returns: v.void(), + args: [s.object({ path: s.string() })], + returns: s.void(), agent: { title: 'Create a folder', description: 'Create a folder under the managed directory, including any missing parent folders.', diff --git a/plugins/assets/src/rpc/functions/open-in-editor.ts b/plugins/assets/src/rpc/functions/open-in-editor.ts index a0810232..10b0fcd1 100644 --- a/plugins/assets/src/rpc/functions/open-in-editor.ts +++ b/plugins/assets/src/rpc/functions/open-in-editor.ts @@ -1,7 +1,7 @@ import type { DevframeNodeContext } from 'devframe/types' import { createDefineWrapperWithContext } from 'devframe/rpc' import { launchEditor } from 'devframe/utils/launch-editor' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getAssetsContext } from '../../node/context' const defineAssetsRpc = createDefineWrapperWithContext() @@ -16,8 +16,8 @@ export const openInEditor = defineAssetsRpc({ name: 'devframes:plugin:assets:open-in-editor', type: 'action', jsonSerializable: true, - args: [v.string()], - returns: v.void(), + args: [s.string()], + returns: s.void(), agent: { title: 'Open an asset in the editor', description: 'Open an asset in the user\'s configured editor.', diff --git a/plugins/assets/src/rpc/functions/read-image-meta.ts b/plugins/assets/src/rpc/functions/read-image-meta.ts index 26b5e643..f35abda7 100644 --- a/plugins/assets/src/rpc/functions/read-image-meta.ts +++ b/plugins/assets/src/rpc/functions/read-image-meta.ts @@ -2,8 +2,8 @@ import type { DevframeNodeContext } from 'devframe/types' import type { AssetImageMeta } from '../../types' import fsp from 'node:fs/promises' import { createDefineWrapperWithContext } from 'devframe/rpc' +import { s } from 'devframe/utils/simple-schema' import { imageMeta } from 'image-meta' -import * as v from 'valibot' import { getAssetsContext } from '../../node/context' const defineAssetsRpc = createDefineWrapperWithContext() @@ -12,11 +12,11 @@ export const readImageMeta = defineAssetsRpc({ name: 'devframes:plugin:assets:read-image-meta', type: 'query', jsonSerializable: true, - args: [v.string()], - returns: v.nullable(v.object({ - width: v.optional(v.number()), - height: v.optional(v.number()), - orientation: v.optional(v.number()), + args: [s.string()], + returns: s.nullable(s.object({ + width: s.optional(s.number()), + height: s.optional(s.number()), + orientation: s.optional(s.number()), })), agent: { title: 'Read image dimensions', diff --git a/plugins/assets/src/rpc/functions/read-text.ts b/plugins/assets/src/rpc/functions/read-text.ts index e1b9da04..36563216 100644 --- a/plugins/assets/src/rpc/functions/read-text.ts +++ b/plugins/assets/src/rpc/functions/read-text.ts @@ -1,7 +1,7 @@ import type { DevframeNodeContext } from 'devframe/types' import fsp from 'node:fs/promises' import { createDefineWrapperWithContext } from 'devframe/rpc' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getAssetsContext } from '../../node/context' const defineAssetsRpc = createDefineWrapperWithContext() @@ -12,8 +12,8 @@ export const readText = defineAssetsRpc({ name: 'devframes:plugin:assets:read-text', type: 'query', jsonSerializable: true, - args: [v.string(), v.optional(v.number())], - returns: v.nullable(v.string()), + args: [s.string(), s.optional(s.number())], + returns: s.nullable(s.string()), agent: { title: 'Read a text asset', description: 'Read the (possibly truncated) text content of a text-type asset, for preview or editing.', diff --git a/plugins/assets/src/rpc/functions/rename.ts b/plugins/assets/src/rpc/functions/rename.ts index 2f555d50..5dda4489 100644 --- a/plugins/assets/src/rpc/functions/rename.ts +++ b/plugins/assets/src/rpc/functions/rename.ts @@ -2,8 +2,8 @@ import type { DevframeNodeContext } from 'devframe/types' import type { AssetInfo } from '../../types' import fsp from 'node:fs/promises' import { createDefineWrapperWithContext } from 'devframe/rpc' +import { s } from 'devframe/utils/simple-schema' import { dirname, extname } from 'pathe' -import * as v from 'valibot' import { diagnostics } from '../../diagnostics' import { getAssetsContext } from '../../node/context' import { statToAssetInfo } from '../../node/scanner' @@ -27,7 +27,7 @@ export const rename = defineAssetsRpc({ name: 'devframes:plugin:assets:rename', type: 'action', jsonSerializable: true, - args: [v.object({ path: v.string(), newName: v.string() })], + args: [s.object({ path: s.string(), newName: s.string() })], returns: assetInfoSchema, agent: { title: 'Rename an asset', diff --git a/plugins/assets/src/rpc/functions/reveal-in-folder.ts b/plugins/assets/src/rpc/functions/reveal-in-folder.ts index 0c2b3d8a..c3c9df6a 100644 --- a/plugins/assets/src/rpc/functions/reveal-in-folder.ts +++ b/plugins/assets/src/rpc/functions/reveal-in-folder.ts @@ -1,8 +1,8 @@ import type { DevframeNodeContext } from 'devframe/types' import { createDefineWrapperWithContext } from 'devframe/rpc' import { open } from 'devframe/utils/open' +import { s } from 'devframe/utils/simple-schema' import { dirname } from 'pathe' -import * as v from 'valibot' import { getAssetsContext } from '../../node/context' const defineAssetsRpc = createDefineWrapperWithContext() @@ -17,8 +17,8 @@ export const revealInFolder = defineAssetsRpc({ name: 'devframes:plugin:assets:reveal-in-folder', type: 'action', jsonSerializable: true, - args: [v.string()], - returns: v.void(), + args: [s.string()], + returns: s.void(), agent: { title: 'Reveal an asset in the file manager', description: 'Open the OS file manager at the asset\'s containing folder.', diff --git a/plugins/assets/src/rpc/functions/upload.ts b/plugins/assets/src/rpc/functions/upload.ts index e86aa1b3..ca5f656e 100644 --- a/plugins/assets/src/rpc/functions/upload.ts +++ b/plugins/assets/src/rpc/functions/upload.ts @@ -2,8 +2,8 @@ import type { DevframeNodeContext } from 'devframe/types' import { createWriteStream } from 'node:fs' import fsp from 'node:fs/promises' import { createDefineWrapperWithContext } from 'devframe/rpc' +import { s } from 'devframe/utils/simple-schema' import { dirname, extname } from 'pathe' -import * as v from 'valibot' import { diagnostics } from '../../diagnostics' import { getAssetsContext } from '../../node/context' @@ -28,8 +28,8 @@ export const upload = defineAssetsRpc({ name: 'devframes:plugin:assets:upload', type: 'action', jsonSerializable: true, - args: [v.object({ path: v.string() })], - returns: v.object({ uploadId: v.string() }), + args: [s.object({ path: s.string() })], + returns: s.object({ uploadId: s.string() }), agent: { title: 'Upload an asset', description: 'Allocate an upload slot for a new file at the given path. The caller streams the bytes over the paired upload channel.', diff --git a/plugins/og/package.json b/plugins/og/package.json index b80aa855..808efdb2 100644 --- a/plugins/og/package.json +++ b/plugins/og/package.json @@ -60,8 +60,7 @@ "dependencies": { "cac": "catalog:deps", "nostics": "catalog:deps", - "parse5": "catalog:deps", - "valibot": "catalog:deps" + "parse5": "catalog:deps" }, "devDependencies": { "@antfu/design": "catalog:frontend", diff --git a/plugins/og/src/rpc/functions/resolve-metadata.ts b/plugins/og/src/rpc/functions/resolve-metadata.ts index f12f3445..5b36b02d 100644 --- a/plugins/og/src/rpc/functions/resolve-metadata.ts +++ b/plugins/og/src/rpc/functions/resolve-metadata.ts @@ -1,7 +1,7 @@ import type { DevframeNodeContext } from 'devframe/types' import type { OgFetch, OgSnapshot } from '../../types' import { createDefineWrapperWithContext } from 'devframe/rpc' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { diagnostics } from '../../diagnostics' import { fetchOgMetadata } from '../../node/metadata' @@ -18,18 +18,18 @@ const EMPTY_SNAPSHOT: OgSnapshot = { tags: [], } -const tagSchema = v.object({ - tag: v.picklist(['html', 'link', 'meta', 'title']), - name: v.string(), - value: v.string(), +const tagSchema = s.object({ + tag: s.picklist(['html', 'link', 'meta', 'title']), + name: s.string(), + value: s.string(), }) -const snapshotSchema = v.object({ - requestedUrl: v.string(), - url: v.string(), - status: v.number(), - fetchedAt: v.number(), - tags: v.array(tagSchema), +const snapshotSchema = s.object({ + requestedUrl: s.string(), + url: s.string(), + status: s.number(), + fetchedAt: s.number(), + tags: s.array(tagSchema), }) const defineOgRpc = createDefineWrapperWithContext() @@ -39,7 +39,7 @@ export function createResolveMetadataRpc(options: ResolveMetadataOptions = {}) { name: 'devframes:plugin:og:resolve-metadata', type: 'query', jsonSerializable: true, - args: [v.object({ url: v.optional(v.string()) })], + args: [s.object({ url: s.optional(s.string()) })], returns: snapshotSchema, agent: { title: 'Inspect Open Graph metadata', diff --git a/plugins/terminals/package.json b/plugins/terminals/package.json index b0de54f5..b07e3e94 100644 --- a/plugins/terminals/package.json +++ b/plugins/terminals/package.json @@ -62,7 +62,6 @@ "cac": "catalog:deps", "nostics": "catalog:deps", "pathe": "catalog:deps", - "valibot": "catalog:deps", "zigpty": "catalog:deps" }, "devDependencies": { diff --git a/plugins/terminals/src/rpc/functions/clear-exited.ts b/plugins/terminals/src/rpc/functions/clear-exited.ts index 83c302d4..5753716b 100644 --- a/plugins/terminals/src/rpc/functions/clear-exited.ts +++ b/plugins/terminals/src/rpc/functions/clear-exited.ts @@ -1,5 +1,5 @@ import { defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getTerminalManager } from '../../node/context' export const clearExited = defineRpcFunction({ @@ -7,7 +7,7 @@ export const clearExited = defineRpcFunction({ type: 'action', jsonSerializable: true, args: [], - returns: v.void(), + returns: s.void(), agent: { description: 'Discard every stopped (exited or errored) terminal session at once. Running sessions are left untouched.', safety: 'destructive', diff --git a/plugins/terminals/src/rpc/functions/list.ts b/plugins/terminals/src/rpc/functions/list.ts index 1f01bcf2..759bf3e9 100644 --- a/plugins/terminals/src/rpc/functions/list.ts +++ b/plugins/terminals/src/rpc/functions/list.ts @@ -1,5 +1,5 @@ import { defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getTerminalManager } from '../../node/context' import { sessionInfoSchema } from '../schemas' @@ -9,7 +9,7 @@ export const list = defineRpcFunction({ jsonSerializable: true, snapshot: true, args: [], - returns: v.array(sessionInfoSchema), + returns: s.array(sessionInfoSchema), agent: { description: 'List the current terminal sessions with their status, mode, and command.', safety: 'read', diff --git a/plugins/terminals/src/rpc/functions/presets.ts b/plugins/terminals/src/rpc/functions/presets.ts index e5c71fee..518f05f5 100644 --- a/plugins/terminals/src/rpc/functions/presets.ts +++ b/plugins/terminals/src/rpc/functions/presets.ts @@ -1,5 +1,5 @@ import { defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getTerminalManager } from '../../node/context' import { presetSchema } from '../schemas' @@ -9,7 +9,7 @@ export const presets = defineRpcFunction({ jsonSerializable: true, snapshot: true, args: [], - returns: v.array(presetSchema), + returns: s.array(presetSchema), setup: ctx => ({ handler: () => getTerminalManager(ctx).getPresets().map(p => ({ id: p.id, diff --git a/plugins/terminals/src/rpc/functions/remove.ts b/plugins/terminals/src/rpc/functions/remove.ts index be91aa1c..4bf31a6e 100644 --- a/plugins/terminals/src/rpc/functions/remove.ts +++ b/plugins/terminals/src/rpc/functions/remove.ts @@ -1,13 +1,13 @@ import { defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getTerminalManager } from '../../node/context' export const remove = defineRpcFunction({ name: 'devframes:plugin:terminals:remove', type: 'action', jsonSerializable: true, - args: [v.object({ id: v.string() })], - returns: v.void(), + args: [s.object({ id: s.string() })], + returns: s.void(), agent: { description: 'Kill a terminal session and discard it (process, stream, and scrollback).', safety: 'destructive', diff --git a/plugins/terminals/src/rpc/functions/rename.ts b/plugins/terminals/src/rpc/functions/rename.ts index 8eac1d0c..4239a2b4 100644 --- a/plugins/terminals/src/rpc/functions/rename.ts +++ b/plugins/terminals/src/rpc/functions/rename.ts @@ -1,13 +1,13 @@ import { defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getTerminalManager } from '../../node/context' export const rename = defineRpcFunction({ name: 'devframes:plugin:terminals:rename', type: 'action', jsonSerializable: true, - args: [v.object({ id: v.string(), title: v.string() })], - returns: v.void(), + args: [s.object({ id: s.string(), title: s.string() })], + returns: s.void(), setup: ctx => ({ handler: ({ id, title }) => { getTerminalManager(ctx).rename(id, title) diff --git a/plugins/terminals/src/rpc/functions/resize.ts b/plugins/terminals/src/rpc/functions/resize.ts index e92e0e0e..7c29285d 100644 --- a/plugins/terminals/src/rpc/functions/resize.ts +++ b/plugins/terminals/src/rpc/functions/resize.ts @@ -1,17 +1,17 @@ import { defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getTerminalManager } from '../../node/context' export const resize = defineRpcFunction({ name: 'devframes:plugin:terminals:resize', type: 'action', jsonSerializable: true, - args: [v.object({ - id: v.string(), - cols: v.pipe(v.number(), v.integer(), v.minValue(1)), - rows: v.pipe(v.number(), v.integer(), v.minValue(1)), + args: [s.object({ + id: s.string(), + cols: s.number(), + rows: s.number(), })], - returns: v.void(), + returns: s.void(), setup: ctx => ({ handler: ({ id, cols, rows }) => { getTerminalManager(ctx).resize(id, cols, rows) diff --git a/plugins/terminals/src/rpc/functions/restart.ts b/plugins/terminals/src/rpc/functions/restart.ts index eb8f9f9c..5fe4d46e 100644 --- a/plugins/terminals/src/rpc/functions/restart.ts +++ b/plugins/terminals/src/rpc/functions/restart.ts @@ -1,5 +1,5 @@ import { defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getTerminalManager } from '../../node/context' import { sessionInfoSchema } from '../schemas' @@ -7,7 +7,7 @@ export const restart = defineRpcFunction({ name: 'devframes:plugin:terminals:restart', type: 'action', jsonSerializable: true, - args: [v.object({ id: v.string() })], + args: [s.object({ id: s.string() })], returns: sessionInfoSchema, setup: ctx => ({ handler: ({ id }) => getTerminalManager(ctx).restart(id), diff --git a/plugins/terminals/src/rpc/functions/terminate.ts b/plugins/terminals/src/rpc/functions/terminate.ts index b3fc0dc6..e58c8c33 100644 --- a/plugins/terminals/src/rpc/functions/terminate.ts +++ b/plugins/terminals/src/rpc/functions/terminate.ts @@ -1,13 +1,13 @@ import { defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getTerminalManager } from '../../node/context' export const terminate = defineRpcFunction({ name: 'devframes:plugin:terminals:terminate', type: 'action', jsonSerializable: true, - args: [v.object({ id: v.string() })], - returns: v.void(), + args: [s.object({ id: s.string() })], + returns: s.void(), agent: { description: 'Terminate a terminal session\'s running process. The session and its scrollback are kept; use restart to run it again.', safety: 'destructive', diff --git a/plugins/terminals/src/rpc/functions/write.ts b/plugins/terminals/src/rpc/functions/write.ts index 147acbc3..c175e2a9 100644 --- a/plugins/terminals/src/rpc/functions/write.ts +++ b/plugins/terminals/src/rpc/functions/write.ts @@ -1,13 +1,13 @@ import { defineRpcFunction } from 'devframe' -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' import { getTerminalManager } from '../../node/context' export const write = defineRpcFunction({ name: 'devframes:plugin:terminals:write', type: 'action', jsonSerializable: true, - args: [v.object({ id: v.string(), data: v.string() })], - returns: v.void(), + args: [s.object({ id: s.string(), data: s.string() })], + returns: s.void(), setup: ctx => ({ handler: ({ id, data }) => { getTerminalManager(ctx).write(id, data) diff --git a/plugins/terminals/src/rpc/schemas.ts b/plugins/terminals/src/rpc/schemas.ts index 96d9b17d..d368edc3 100644 --- a/plugins/terminals/src/rpc/schemas.ts +++ b/plugins/terminals/src/rpc/schemas.ts @@ -1,45 +1,45 @@ -import * as v from 'valibot' +import { s } from 'devframe/utils/simple-schema' -export const terminalModeSchema = v.picklist(['interactive', 'readonly']) +export const terminalModeSchema = s.picklist(['interactive', 'readonly']) -export const spawnRequestSchema = v.object({ - presetId: v.optional(v.string()), - command: v.optional(v.string()), - args: v.optional(v.array(v.string())), - cwd: v.optional(v.string()), - mode: v.optional(terminalModeSchema), - title: v.optional(v.string()), - cols: v.optional(v.number()), - rows: v.optional(v.number()), - env: v.optional(v.record(v.string(), v.string())), +export const spawnRequestSchema = s.object({ + presetId: s.optional(s.string()), + command: s.optional(s.string()), + args: s.optional(s.array(s.string())), + cwd: s.optional(s.string()), + mode: s.optional(terminalModeSchema), + title: s.optional(s.string()), + cols: s.optional(s.number()), + rows: s.optional(s.number()), + env: s.optional(s.record(s.string(), s.string())), }) -export const sessionInfoSchema = v.object({ - id: v.string(), - title: v.string(), - processName: v.optional(v.string()), - customTitle: v.optional(v.string()), +export const sessionInfoSchema = s.object({ + id: s.string(), + title: s.string(), + processName: s.optional(s.string()), + customTitle: s.optional(s.string()), mode: terminalModeSchema, - status: v.picklist(['running', 'exited', 'error']), - backend: v.picklist(['pty', 'pipe']), - command: v.string(), - args: v.array(v.string()), - cwd: v.string(), - cols: v.number(), - rows: v.number(), - pid: v.optional(v.number()), - exitCode: v.optional(v.number()), - icon: v.optional(v.string()), - channel: v.optional(v.string()), - presetId: v.optional(v.string()), - createdAt: v.number(), + status: s.picklist(['running', 'exited', 'error']), + backend: s.picklist(['pty', 'pipe']), + command: s.string(), + args: s.array(s.string()), + cwd: s.string(), + cols: s.number(), + rows: s.number(), + pid: s.optional(s.number()), + exitCode: s.optional(s.number()), + icon: s.optional(s.string()), + channel: s.optional(s.string()), + presetId: s.optional(s.string()), + createdAt: s.number(), }) -export const presetSchema = v.object({ - id: v.string(), - title: v.string(), - command: v.string(), - args: v.array(v.string()), +export const presetSchema = s.object({ + id: s.string(), + title: s.string(), + command: s.string(), + args: s.array(s.string()), mode: terminalModeSchema, - icon: v.optional(v.string()), + icon: s.optional(s.string()), }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4931dd38..842924f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1106,9 +1106,6 @@ importers: ufo: specifier: catalog:deps version: 1.6.4 - valibot: - specifier: catalog:deps - version: 1.4.2(typescript@6.0.3) devDependencies: '@antfu/design': specifier: catalog:frontend @@ -1559,9 +1556,6 @@ importers: parse5: specifier: catalog:deps version: 8.0.1 - valibot: - specifier: catalog:deps - version: 1.4.2(typescript@6.0.3) devDependencies: '@antfu/design': specifier: catalog:frontend @@ -1623,9 +1617,6 @@ importers: pathe: specifier: catalog:deps version: 2.0.3 - valibot: - specifier: catalog:deps - version: 1.4.2(typescript@6.0.3) zigpty: specifier: catalog:deps version: 0.2.1 diff --git a/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts index 2c2170b0..fce1061a 100644 --- a/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts @@ -17,8 +17,8 @@ export declare const alwaysFunctions: readonly [{ name: "devframes:plugin:assets:open-in-editor"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [import("valibot").StringSchema]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; @@ -31,8 +31,8 @@ export declare const alwaysFunctions: readonly [{ name: "devframes:plugin:assets:reveal-in-folder"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [import("valibot").StringSchema]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; @@ -42,22 +42,31 @@ export declare const alwaysFunctions: readonly [{ __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; }]; -export declare const assetInfoSchema: v.ObjectSchema<{ - readonly path: v.StringSchema; - readonly type: v.PicklistSchema<["image", "font", "video", "audio", "text", "other"], undefined>; - readonly publicPath: v.StringSchema; - readonly size: v.NumberSchema; - readonly mtime: v.NumberSchema; -}, undefined>; +export declare const assetInfoSchema: import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + type: "text" | "image" | "font" | "video" | "audio" | "other"; + publicPath: string; + size: number; + mtime: number; +}, { + path: string; + type: "text" | "image" | "font" | "video" | "audio" | "other"; + publicPath: string; + size: number; + mtime: number; +}>; export declare const capabilities: { name: "devframes:plugin:assets:capabilities"; type?: "query" | undefined; cacheable?: boolean; args: readonly []; - returns: v.ObjectSchema<{ - readonly write: v.BooleanSchema; - readonly uploadExtensions: v.UnionSchema<[v.ArraySchema, undefined>, v.LiteralSchema<"*", undefined>], undefined>; - }, undefined>; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + write: boolean; + uploadExtensions: string[] | "*"; + }, { + write: boolean; + uploadExtensions: string[] | "*"; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable, undefined>; - }, undefined>]; - returns: v.ObjectSchema<{ - readonly deleted: v.ArraySchema, undefined>; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + paths: string[]; + }, { + paths: string[]; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + deleted: string[]; + }, { + deleted: string[]; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable; - readonly type: v.PicklistSchema<["image", "font", "video", "audio", "text", "other"], undefined>; - readonly publicPath: v.StringSchema; - readonly size: v.NumberSchema; - readonly mtime: v.NumberSchema; - }, undefined>, undefined>; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + type: "text" | "image" | "font" | "video" | "audio" | "other"; + publicPath: string; + size: number; + mtime: number; + }[], { + path: string; + type: "text" | "image" | "font" | "video" | "audio" | "other"; + publicPath: string; + size: number; + mtime: number; + }[]>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable; - }, undefined>]; - returns: v.VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + }, { + path: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable]; - returns: v.VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; @@ -219,13 +240,19 @@ export declare const readFunctions: readonly [{ type?: "query" | undefined; cacheable?: boolean; args: readonly []; - returns: import("valibot").ArraySchema; - readonly type: import("valibot").PicklistSchema<["image", "font", "video", "audio", "text", "other"], undefined>; - readonly publicPath: import("valibot").StringSchema; - readonly size: import("valibot").NumberSchema; - readonly mtime: import("valibot").NumberSchema; - }, undefined>, undefined>; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + type: "image" | "font" | "video" | "audio" | "text" | "other"; + publicPath: string; + size: number; + mtime: number; + }[], { + path: string; + type: "image" | "font" | "video" | "audio" | "text" | "other"; + publicPath: string; + size: number; + mtime: number; + }[]>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable]; - returns: import("valibot").NullableSchema, undefined>; - readonly height: import("valibot").OptionalSchema, undefined>; - readonly orientation: import("valibot").OptionalSchema, undefined>; - }, undefined>, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + width?: number | undefined; + height?: number | undefined; + orientation?: number | undefined; + } | null, { + width?: number | undefined; + height?: number | undefined; + orientation?: number | undefined; + } | null>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable, import("valibot").OptionalSchema, undefined>]; - returns: import("valibot").NullableSchema, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema, import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; @@ -321,10 +352,13 @@ export declare const readFunctions: readonly [{ type?: "query" | undefined; cacheable?: boolean; args: readonly []; - returns: import("valibot").ObjectSchema<{ - readonly write: import("valibot").BooleanSchema; - readonly uploadExtensions: import("valibot").UnionSchema<[import("valibot").ArraySchema, undefined>, import("valibot").LiteralSchema<"*", undefined>], undefined>; - }, undefined>; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + write: boolean; + uploadExtensions: string[] | "*"; + }, { + write: boolean; + uploadExtensions: string[] | "*"; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable]; - returns: v.NullableSchema, undefined>; - readonly height: v.OptionalSchema, undefined>; - readonly orientation: v.OptionalSchema, undefined>; - }, undefined>, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + width?: number | undefined; + height?: number | undefined; + orientation?: number | undefined; + } | null, { + width?: number | undefined; + height?: number | undefined; + orientation?: number | undefined; + } | null>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable, v.OptionalSchema, undefined>]; - returns: v.NullableSchema, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema, import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; @@ -407,17 +445,26 @@ export declare const rename: { name: "devframes:plugin:assets:rename"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [v.ObjectSchema<{ - readonly path: v.StringSchema; - readonly newName: v.StringSchema; - }, undefined>]; - returns: v.ObjectSchema<{ - readonly path: v.StringSchema; - readonly type: v.PicklistSchema<["image", "font", "video", "audio", "text", "other"], undefined>; - readonly publicPath: v.StringSchema; - readonly size: v.NumberSchema; - readonly mtime: v.NumberSchema; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + newName: string; + }, { + path: string; + newName: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + type: "text" | "image" | "font" | "video" | "audio" | "other"; + publicPath: string; + size: number; + mtime: number; + }, { + path: string; + type: "text" | "image" | "font" | "video" | "audio" | "other"; + publicPath: string; + size: number; + mtime: number; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable]; - returns: v.VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; @@ -492,13 +539,19 @@ export declare const serverFunctions: readonly [{ type?: "query" | undefined; cacheable?: boolean; args: readonly []; - returns: import("valibot").ArraySchema; - readonly type: import("valibot").PicklistSchema<["image", "font", "video", "audio", "text", "other"], undefined>; - readonly publicPath: import("valibot").StringSchema; - readonly size: import("valibot").NumberSchema; - readonly mtime: import("valibot").NumberSchema; - }, undefined>, undefined>; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + type: "image" | "font" | "video" | "audio" | "text" | "other"; + publicPath: string; + size: number; + mtime: number; + }[], { + path: string; + type: "image" | "font" | "video" | "audio" | "text" | "other"; + publicPath: string; + size: number; + mtime: number; + }[]>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable]; - returns: import("valibot").NullableSchema, undefined>; - readonly height: import("valibot").OptionalSchema, undefined>; - readonly orientation: import("valibot").OptionalSchema, undefined>; - }, undefined>, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + width?: number | undefined; + height?: number | undefined; + orientation?: number | undefined; + } | null, { + width?: number | undefined; + height?: number | undefined; + orientation?: number | undefined; + } | null>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable, import("valibot").OptionalSchema, undefined>]; - returns: import("valibot").NullableSchema, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema, import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; @@ -594,10 +651,13 @@ export declare const serverFunctions: readonly [{ type?: "query" | undefined; cacheable?: boolean; args: readonly []; - returns: import("valibot").ObjectSchema<{ - readonly write: import("valibot").BooleanSchema; - readonly uploadExtensions: import("valibot").UnionSchema<[import("valibot").ArraySchema, undefined>, import("valibot").LiteralSchema<"*", undefined>], undefined>; - }, undefined>; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + write: boolean; + uploadExtensions: string[] | "*"; + }, { + write: boolean; + uploadExtensions: string[] | "*"; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; @@ -639,8 +699,8 @@ export declare const serverFunctions: readonly [{ name: "devframes:plugin:assets:reveal-in-folder"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [import("valibot").StringSchema]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; @@ -653,12 +713,16 @@ export declare const serverFunctions: readonly [{ name: "devframes:plugin:assets:upload"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [import("valibot").ObjectSchema<{ - readonly path: import("valibot").StringSchema; - }, undefined>]; - returns: import("valibot").ObjectSchema<{ - readonly uploadId: import("valibot").StringSchema; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + }, { + path: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + uploadId: string; + }, { + uploadId: string; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; - readonly newName: import("valibot").StringSchema; - }, undefined>]; - returns: import("valibot").ObjectSchema<{ - readonly path: import("valibot").StringSchema; - readonly type: import("valibot").PicklistSchema<["image", "font", "video", "audio", "text", "other"], undefined>; - readonly publicPath: import("valibot").StringSchema; - readonly size: import("valibot").NumberSchema; - readonly mtime: import("valibot").NumberSchema; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + newName: string; + }, { + path: string; + newName: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + type: "image" | "font" | "video" | "audio" | "text" | "other"; + publicPath: string; + size: number; + mtime: number; + }, { + path: string; + type: "image" | "font" | "video" | "audio" | "text" | "other"; + publicPath: string; + size: number; + mtime: number; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable, undefined>; - }, undefined>]; - returns: import("valibot").ObjectSchema<{ - readonly deleted: import("valibot").ArraySchema, undefined>; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + paths: string[]; + }, { + paths: string[]; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + deleted: string[]; + }, { + deleted: string[]; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; - }, undefined>]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + }, { + path: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; - }, undefined>]; - returns: v.ObjectSchema<{ - readonly uploadId: v.StringSchema; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + }, { + path: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + uploadId: string; + }, { + uploadId: string; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable; - }, undefined>]; - returns: import("valibot").ObjectSchema<{ - readonly uploadId: import("valibot").StringSchema; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + }, { + path: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + uploadId: string; + }, { + uploadId: string; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; - readonly newName: import("valibot").StringSchema; - }, undefined>]; - returns: import("valibot").ObjectSchema<{ - readonly path: import("valibot").StringSchema; - readonly type: import("valibot").PicklistSchema<["image", "font", "video", "audio", "text", "other"], undefined>; - readonly publicPath: import("valibot").StringSchema; - readonly size: import("valibot").NumberSchema; - readonly mtime: import("valibot").NumberSchema; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + newName: string; + }, { + path: string; + newName: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + type: "image" | "font" | "video" | "audio" | "text" | "other"; + publicPath: string; + size: number; + mtime: number; + }, { + path: string; + type: "image" | "font" | "video" | "audio" | "text" | "other"; + publicPath: string; + size: number; + mtime: number; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable, undefined>; - }, undefined>]; - returns: import("valibot").ObjectSchema<{ - readonly deleted: import("valibot").ArraySchema, undefined>; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + paths: string[]; + }, { + paths: string[]; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + deleted: string[]; + }, { + deleted: string[]; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; - }, undefined>]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + path: string; + }, { + path: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable, undefined>; - }, undefined>]; - returns: import("valibot").ObjectSchema<{ - readonly requestedUrl: import("valibot").StringSchema; - readonly url: import("valibot").StringSchema; - readonly status: import("valibot").NumberSchema; - readonly fetchedAt: import("valibot").NumberSchema; - readonly tags: import("valibot").ArraySchema; - readonly name: import("valibot").StringSchema; - readonly value: import("valibot").StringSchema; - }, undefined>, undefined>; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + url?: string | undefined; + }, { + url?: string | undefined; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + requestedUrl: string; + url: string; + status: number; + fetchedAt: number; + tags: { + tag: "html" | "link" | "meta" | "title"; + name: string; + value: string; + }[]; + }, { + requestedUrl: string; + url: string; + status: number; + fetchedAt: number; + tags: { + tag: "html" | "link" | "meta" | "title"; + name: string; + value: string; + }[]; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; - readonly title: import("valibot").StringSchema; - readonly processName: import("valibot").OptionalSchema, undefined>; - readonly customTitle: import("valibot").OptionalSchema, undefined>; - readonly mode: import("valibot").PicklistSchema<["interactive", "readonly"], undefined>; - readonly status: import("valibot").PicklistSchema<["running", "exited", "error"], undefined>; - readonly backend: import("valibot").PicklistSchema<["pty", "pipe"], undefined>; - readonly command: import("valibot").StringSchema; - readonly args: import("valibot").ArraySchema, undefined>; - readonly cwd: import("valibot").StringSchema; - readonly cols: import("valibot").NumberSchema; - readonly rows: import("valibot").NumberSchema; - readonly pid: import("valibot").OptionalSchema, undefined>; - readonly exitCode: import("valibot").OptionalSchema, undefined>; - readonly icon: import("valibot").OptionalSchema, undefined>; - readonly channel: import("valibot").OptionalSchema, undefined>; - readonly presetId: import("valibot").OptionalSchema, undefined>; - readonly createdAt: import("valibot").NumberSchema; - }, undefined>, undefined>; - jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; + jsonSerializable?: boolean; + agent?: import("devframe").RpcFunctionAgentOptions; + setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: (() => { id: string; title: string; - processName?: string | undefined; - customTitle?: string | undefined; mode: "interactive" | "readonly"; status: "running" | "exited" | "error"; backend: "pty" | "pipe"; @@ -62,18 +79,18 @@ export declare const serverFunctions: readonly [{ cwd: string; cols: number; rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; pid?: number | undefined; exitCode?: number | undefined; icon?: string | undefined; channel?: string | undefined; presetId?: string | undefined; - createdAt: number; }[]) | undefined; dump?: import("devframe/rpc").RpcDump<[], { id: string; title: string; - processName?: string | undefined; - customTitle?: string | undefined; mode: "interactive" | "readonly"; status: "running" | "exited" | "error"; backend: "pty" | "pipe"; @@ -82,19 +99,19 @@ export declare const serverFunctions: readonly [{ cwd: string; cols: number; rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; pid?: number | undefined; exitCode?: number | undefined; icon?: string | undefined; channel?: string | undefined; presetId?: string | undefined; - createdAt: number; }[], import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; }, { name: "devframes:plugin:terminals:presets"; type?: "query" | undefined; cacheable?: boolean; args: readonly []; - returns: import("valibot").ArraySchema; - readonly title: import("valibot").StringSchema; - readonly command: import("valibot").StringSchema; - readonly args: import("valibot").ArraySchema, undefined>; - readonly mode: import("valibot").PicklistSchema<["interactive", "readonly"], undefined>; - readonly icon: import("valibot").OptionalSchema, undefined>; - }, undefined>, undefined>; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + id: string; + title: string; + command: string; + args: string[]; + mode: "interactive" | "readonly"; + icon?: string | undefined; + }[], { + id: string; + title: string; + command: string; + args: string[]; + mode: "interactive" | "readonly"; + icon?: string | undefined; + }[]>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable, undefined>; - readonly command: import("valibot").OptionalSchema, undefined>; - readonly args: import("valibot").OptionalSchema, undefined>, undefined>; - readonly cwd: import("valibot").OptionalSchema, undefined>; - readonly mode: import("valibot").OptionalSchema, undefined>; - readonly title: import("valibot").OptionalSchema, undefined>; - readonly cols: import("valibot").OptionalSchema, undefined>; - readonly rows: import("valibot").OptionalSchema, undefined>; - readonly env: import("valibot").OptionalSchema, import("valibot").StringSchema, undefined>, undefined>; - }, undefined>]; - returns: import("valibot").ObjectSchema<{ - readonly id: import("valibot").StringSchema; - readonly title: import("valibot").StringSchema; - readonly processName: import("valibot").OptionalSchema, undefined>; - readonly customTitle: import("valibot").OptionalSchema, undefined>; - readonly mode: import("valibot").PicklistSchema<["interactive", "readonly"], undefined>; - readonly status: import("valibot").PicklistSchema<["running", "exited", "error"], undefined>; - readonly backend: import("valibot").PicklistSchema<["pty", "pipe"], undefined>; - readonly command: import("valibot").StringSchema; - readonly args: import("valibot").ArraySchema, undefined>; - readonly cwd: import("valibot").StringSchema; - readonly cols: import("valibot").NumberSchema; - readonly rows: import("valibot").NumberSchema; - readonly pid: import("valibot").OptionalSchema, undefined>; - readonly exitCode: import("valibot").OptionalSchema, undefined>; - readonly icon: import("valibot").OptionalSchema, undefined>; - readonly channel: import("valibot").OptionalSchema, undefined>; - readonly presetId: import("valibot").OptionalSchema, undefined>; - readonly createdAt: import("valibot").NumberSchema; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + presetId?: string | undefined; + command?: string | undefined; + args?: string[] | undefined; + cwd?: string | undefined; + mode?: "interactive" | "readonly" | undefined; + title?: string | undefined; + cols?: number | undefined; + rows?: number | undefined; + env?: Record | undefined; + }, { + presetId?: string | undefined; + command?: string | undefined; + args?: string[] | undefined; + cwd?: string | undefined; + mode?: "interactive" | "readonly" | undefined; + title?: string | undefined; + cols?: number | undefined; + rows?: number | undefined; + env?: Record | undefined; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + id: string; + title: string; + mode: "interactive" | "readonly"; + status: "running" | "exited" | "error"; + backend: "pty" | "pipe"; + command: string; + args: string[]; + cwd: string; + cols: number; + rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; + pid?: number | undefined; + exitCode?: number | undefined; + icon?: string | undefined; + channel?: string | undefined; + presetId?: string | undefined; + }, { + id: string; + title: string; + mode: "interactive" | "readonly"; + status: "running" | "exited" | "error"; + backend: "pty" | "pipe"; + command: string; + args: string[]; + cwd: string; + cols: number; + rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; + pid?: number | undefined; + exitCode?: number | undefined; + icon?: string | undefined; + channel?: string | undefined; + presetId?: string | undefined; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable | undefined; }], { id: string; title: string; - processName?: string | undefined; - customTitle?: string | undefined; mode: "interactive" | "readonly"; status: "running" | "exited" | "error"; backend: "pty" | "pipe"; @@ -248,12 +299,14 @@ export declare const serverFunctions: readonly [{ cwd: string; cols: number; rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; pid?: number | undefined; exitCode?: number | undefined; icon?: string | undefined; channel?: string | undefined; presetId?: string | undefined; - createdAt: number; }>>) | undefined; handler?: ((args_0: { presetId?: string | undefined; @@ -264,14 +317,10 @@ export declare const serverFunctions: readonly [{ title?: string | undefined; cols?: number | undefined; rows?: number | undefined; - env?: { - [x: string]: string; - } | undefined; + env?: Record | undefined; }) => { id: string; title: string; - processName?: string | undefined; - customTitle?: string | undefined; mode: "interactive" | "readonly"; status: "running" | "exited" | "error"; backend: "pty" | "pipe"; @@ -280,12 +329,14 @@ export declare const serverFunctions: readonly [{ cwd: string; cols: number; rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; pid?: number | undefined; exitCode?: number | undefined; icon?: string | undefined; channel?: string | undefined; presetId?: string | undefined; - createdAt: number; }) | undefined; dump?: import("devframe/rpc").RpcDump<[{ presetId?: string | undefined; @@ -296,14 +347,10 @@ export declare const serverFunctions: readonly [{ title?: string | undefined; cols?: number | undefined; rows?: number | undefined; - env?: { - [x: string]: string; - } | undefined; + env?: Record | undefined; }], { id: string; title: string; - processName?: string | undefined; - customTitle?: string | undefined; mode: "interactive" | "readonly"; status: "running" | "exited" | "error"; backend: "pty" | "pipe"; @@ -312,12 +359,14 @@ export declare const serverFunctions: readonly [{ cwd: string; cols: number; rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; pid?: number | undefined; exitCode?: number | undefined; icon?: string | undefined; channel?: string | undefined; presetId?: string | undefined; - createdAt: number; }, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap | undefined; }], { id: string; title: string; - processName?: string | undefined; - customTitle?: string | undefined; mode: "interactive" | "readonly"; status: "running" | "exited" | "error"; backend: "pty" | "pipe"; @@ -345,12 +390,14 @@ export declare const serverFunctions: readonly [{ cwd: string; cols: number; rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; pid?: number | undefined; exitCode?: number | undefined; icon?: string | undefined; channel?: string | undefined; presetId?: string | undefined; - createdAt: number; }>>> | undefined; __promise?: import("devframe/rpc").Thenable | undefined; }], { id: string; title: string; - processName?: string | undefined; - customTitle?: string | undefined; mode: "interactive" | "readonly"; status: "running" | "exited" | "error"; backend: "pty" | "pipe"; @@ -377,22 +420,27 @@ export declare const serverFunctions: readonly [{ cwd: string; cols: number; rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; pid?: number | undefined; exitCode?: number | undefined; icon?: string | undefined; channel?: string | undefined; presetId?: string | undefined; - createdAt: number; }>> | undefined; }, { name: "devframes:plugin:terminals:write"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [import("valibot").ObjectSchema<{ - readonly id: import("valibot").StringSchema; - readonly data: import("valibot").StringSchema; - }, undefined>]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + id: string; + data: string; + }, { + id: string; + data: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; - readonly cols: import("valibot").SchemaWithPipe, import("valibot").IntegerAction, import("valibot").MinValueAction]>; - readonly rows: import("valibot").SchemaWithPipe, import("valibot").IntegerAction, import("valibot").MinValueAction]>; - }, undefined>]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + id: string; + cols: number; + rows: number; + }, { + id: string; + cols: number; + rows: number; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; - }, undefined>]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + id: string; + }, { + id: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; - }, undefined>]; - returns: import("valibot").ObjectSchema<{ - readonly id: import("valibot").StringSchema; - readonly title: import("valibot").StringSchema; - readonly processName: import("valibot").OptionalSchema, undefined>; - readonly customTitle: import("valibot").OptionalSchema, undefined>; - readonly mode: import("valibot").PicklistSchema<["interactive", "readonly"], undefined>; - readonly status: import("valibot").PicklistSchema<["running", "exited", "error"], undefined>; - readonly backend: import("valibot").PicklistSchema<["pty", "pipe"], undefined>; - readonly command: import("valibot").StringSchema; - readonly args: import("valibot").ArraySchema, undefined>; - readonly cwd: import("valibot").StringSchema; - readonly cols: import("valibot").NumberSchema; - readonly rows: import("valibot").NumberSchema; - readonly pid: import("valibot").OptionalSchema, undefined>; - readonly exitCode: import("valibot").OptionalSchema, undefined>; - readonly icon: import("valibot").OptionalSchema, undefined>; - readonly channel: import("valibot").OptionalSchema, undefined>; - readonly presetId: import("valibot").OptionalSchema, undefined>; - readonly createdAt: import("valibot").NumberSchema; - }, undefined>; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + id: string; + }, { + id: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema<{ + id: string; + title: string; + mode: "interactive" | "readonly"; + status: "running" | "exited" | "error"; + backend: "pty" | "pipe"; + command: string; + args: string[]; + cwd: string; + cols: number; + rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; + pid?: number | undefined; + exitCode?: number | undefined; + icon?: string | undefined; + channel?: string | undefined; + presetId?: string | undefined; + }, { + id: string; + title: string; + mode: "interactive" | "readonly"; + status: "running" | "exited" | "error"; + backend: "pty" | "pipe"; + command: string; + args: string[]; + cwd: string; + cols: number; + rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; + pid?: number | undefined; + exitCode?: number | undefined; + icon?: string | undefined; + channel?: string | undefined; + presetId?: string | undefined; + }>; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: { id: string; }) => { id: string; title: string; - processName?: string | undefined; - customTitle?: string | undefined; mode: "interactive" | "readonly"; status: "running" | "exited" | "error"; backend: "pty" | "pipe"; @@ -546,20 +619,20 @@ export declare const serverFunctions: readonly [{ cwd: string; cols: number; rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; pid?: number | undefined; exitCode?: number | undefined; icon?: string | undefined; channel?: string | undefined; presetId?: string | undefined; - createdAt: number; }) | undefined; dump?: import("devframe/rpc").RpcDump<[{ id: string; }], { id: string; title: string; - processName?: string | undefined; - customTitle?: string | undefined; mode: "interactive" | "readonly"; status: "running" | "exited" | "error"; backend: "pty" | "pipe"; @@ -568,12 +641,14 @@ export declare const serverFunctions: readonly [{ cwd: string; cols: number; rows: number; + createdAt: number; + processName?: string | undefined; + customTitle?: string | undefined; pid?: number | undefined; exitCode?: number | undefined; icon?: string | undefined; channel?: string | undefined; presetId?: string | undefined; - createdAt: number; }, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; }, { name: "devframes:plugin:terminals:rename"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [import("valibot").ObjectSchema<{ - readonly id: import("valibot").StringSchema; - readonly title: import("valibot").StringSchema; - }, undefined>]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + id: string; + title: string; + }, { + id: string; + title: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; - }, undefined>]; - returns: import("valibot").VoidSchema; + args: readonly [import("devframe/utils/simple-schema").SimpleSchema<{ + id: string; + }, { + id: string; + }>]; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable; + returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; diff --git a/tests/__snapshots__/tsnapi/devframe/recipes/common-rpc-functions.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/recipes/common-rpc-functions.snapshot.d.ts index 17cf6a05..6c50a713 100644 --- a/tests/__snapshots__/tsnapi/devframe/recipes/common-rpc-functions.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/recipes/common-rpc-functions.snapshot.d.ts @@ -10,8 +10,8 @@ export declare const commonRpcFunctions: readonly [{ name: "devframe:open-in-editor"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [DevframeSchema, DevframeSchema]; - returns: DevframeSchema; + args: readonly [SimpleSchema, SimpleSchema]; + returns: SimpleSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; @@ -24,8 +24,8 @@ export declare const commonRpcFunctions: readonly [{ name: "devframe:open-in-finder"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [DevframeSchema]; - returns: DevframeSchema; + args: readonly [SimpleSchema]; + returns: SimpleSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; @@ -40,8 +40,8 @@ export declare const openInEditor: { name: "devframe:open-in-editor"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [DevframeSchema, DevframeSchema]; - returns: DevframeSchema; + args: readonly [SimpleSchema, SimpleSchema]; + returns: SimpleSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; @@ -55,8 +55,8 @@ export declare const openInFinder: { name: "devframe:open-in-finder"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [DevframeSchema]; - returns: DevframeSchema; + args: readonly [SimpleSchema]; + returns: SimpleSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; diff --git a/tests/__snapshots__/tsnapi/devframe/recipes/open-helpers.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/recipes/open-helpers.snapshot.d.ts index 70f5c060..b1198606 100644 --- a/tests/__snapshots__/tsnapi/devframe/recipes/open-helpers.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/recipes/open-helpers.snapshot.d.ts @@ -8,8 +8,8 @@ export declare const openInEditor: { name: "devframe:open-in-editor"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [DevframeSchema, DevframeSchema]; - returns: DevframeSchema; + args: readonly [SimpleSchema, SimpleSchema]; + returns: SimpleSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; @@ -23,8 +23,8 @@ export declare const openInFinder: { name: "devframe:open-in-finder"; type?: "action" | undefined; cacheable?: boolean; - args: readonly [DevframeSchema]; - returns: DevframeSchema; + args: readonly [SimpleSchema]; + returns: SimpleSchema; jsonSerializable?: boolean; agent?: RpcFunctionAgentOptions; setup?: ((context: undefined) => Thenable>) | undefined; diff --git a/tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/utils/simple-schema.snapshot.d.ts similarity index 77% rename from tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.d.ts rename to tests/__snapshots__/tsnapi/devframe/utils/simple-schema.snapshot.d.ts index 6c9e93c2..849c1046 100644 --- a/tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/utils/simple-schema.snapshot.d.ts @@ -1,18 +1,21 @@ /** - * Generated by tsnapi — public API snapshot of `devframe/utils/schema` + * Generated by tsnapi — public API snapshot of `devframe/utils/simple-schema` */ // #region Other export { array } export { boolean } export { describe } -export { DevframeSchema } +export { literal } export { nullable } export { nullType } export { number } export { object } export { optional } export { picklist } +export { record } export { s } +export { SimpleSchema } export { string } +export { union } export { voidType } // #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.js b/tests/__snapshots__/tsnapi/devframe/utils/simple-schema.snapshot.js similarity index 82% rename from tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.js rename to tests/__snapshots__/tsnapi/devframe/utils/simple-schema.snapshot.js index 7db6e54c..cac1376f 100644 --- a/tests/__snapshots__/tsnapi/devframe/utils/schema.snapshot.js +++ b/tests/__snapshots__/tsnapi/devframe/utils/simple-schema.snapshot.js @@ -1,17 +1,20 @@ /** - * Generated by tsnapi — public API snapshot of `devframe/utils/schema` + * Generated by tsnapi — public API snapshot of `devframe/utils/simple-schema` */ // #region Functions export function array(_) {} export function boolean() {} export function describe(_, _) {} +export function literal(_) {} export function nullable(_) {} export function nullType() {} export function number() {} export function object(_) {} export function optional(_) {} export function picklist(_) {} +export function record(_, _) {} export function string() {} +export function union(_) {} export function voidType() {} // #endregion diff --git a/tsconfig.base.json b/tsconfig.base.json index b02df753..4e2e01f3 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -64,8 +64,8 @@ "devframe/utils/promise": [ "./packages/devframe/src/utils/promise.ts" ], - "devframe/utils/schema": [ - "./packages/devframe/src/utils/schema.ts" + "devframe/utils/simple-schema": [ + "./packages/devframe/src/utils/simple-schema.ts" ], "devframe/utils/scope": [ "./packages/devframe/src/utils/scope.ts" From 59222e6db0be8f59de86ae33c69dc482feb38c3f Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Mon, 3 Aug 2026 01:05:40 +0000 Subject: [PATCH 5/8] feat(mcp): vendor-neutral JSON-schema via @standard-community/standard-json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adopt @standard-community/standard-json (dispatches on the schema's ~standard vendor, per-vendor converters are optional peers) so the agent/MCP surface and the inspect plugin produce precise JSON schema for whatever validator a user brings — valibot, zod, arktype — while forcing none. Missing/unknown converters degrade to a permissive object schema. - Core MCP to-json-schema + build-server tool projection are now async (lazy vendor-converter import). - inspect's schema converter switches from @valibot/to-json-schema to standard-json (async), gaining any-vendor support. - Docs: recommend valibot as the lightest validator, but note that json-render and the MCP server already use zod, so users pulling zod via those integrations should prefer zod to reuse the dependency. - AGENTS.md: first-party packages (devframe, @devframes/*) must stay validator-neutral (no preferred validator dependency); docs guide users to valibot or zod for their own integrations. --- AGENTS.md | 1 + docs/guide/rpc.md | 5 +- docs/guide/standalone-cli.md | 2 +- packages/devframe/package.json | 2 + .../mcp/__tests__/to-json-schema.test.ts | 36 +++++------ .../devframe/src/adapters/mcp/build-server.ts | 16 ++--- .../src/adapters/mcp/to-json-schema.ts | 46 +++++++++----- plugins/inspect/package.json | 4 +- plugins/inspect/src/rpc/functions/_schema.ts | 46 +++++++------- .../src/rpc/functions/list-functions.ts | 4 +- pnpm-lock.yaml | 61 +++++++++++++++++++ pnpm-workspace.yaml | 2 + .../plugin-assets/rpc.snapshot.d.ts | 32 +++++----- 13 files changed, 172 insertions(+), 85 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 701838b1..f966e1ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,6 +36,7 @@ The `pnpm test` script intentionally runs `build` first so `tsnapi` snapshots co ## Conventions - RPC functions must use `defineRpcFunction`; always namespace IDs `devframes:plugin::` (matching the plugin's `@devframes/plugin-` package name). +- **Stay validator-neutral.** `devframe` and every `@devframes/*` package must not introduce a preferred schema validator dependency — no `valibot`, `zod`, `arktype`, etc. in their runtime `dependencies`. `args`/`returns`/flag schemas are typed against [Standard Schema](https://standardschema.dev/) (`@standard-schema/spec`, types-only); first-party code that needs to author a schema uses the built-in zero-dep `devframe/utils/simple-schema` builder (deliberately minimal — not a general validator). JSON-schema conversion goes through `@standard-community/standard-json`, whose per-vendor converters are optional peers, so no validator is forced. Docs, by contrast, should point *users* at a real validator for their own integrations — recommend **valibot** (lightest) or **zod** (worth reusing if they already pull it via the JSON-render or MCP integrations). - Shared state via `devframe/utils/shared-state`; keep values serializable. - Utility imports use the package-path form `devframe/utils/*`, never relative `../utils/*`. - Dependencies go through the pnpm catalogs in `pnpm-workspace.yaml` (`cli`, `inlined`, `testing`, `types`) — add to a catalog and reference as `catalog:`, don't pin versions in `package.json`. diff --git a/docs/guide/rpc.md b/docs/guide/rpc.md index 1f5057aa..31e37f39 100644 --- a/docs/guide/rpc.md +++ b/docs/guide/rpc.md @@ -77,7 +77,10 @@ Use `static` for data collected once during `setup` and shipped to read-only sta Handlers accept any serializable arguments. Declare `args` schemas — any [Standard Schema](https://standardschema.dev/) validator (valibot, zod, arktype, …) — and each argument is validated at the boundary before the handler runs; a mismatch is rejected with a coded diagnostic. Validation guards the payload without rewriting it, so extra object fields the schema doesn't mention still reach the handler. -Devframe forces no validator on you: bring whichever [Standard Schema](https://standardschema.dev/) validator you prefer (valibot, zod, arktype) and install it yourself. The examples here use valibot (`npm i valibot`): +Devframe forces no validator on you: bring whichever [Standard Schema](https://standardschema.dev/) validator you prefer (valibot, zod, arktype) and install it yourself. The examples here use valibot (`npm i valibot`) — it's the lightest option and a good default. + +> [!TIP] +> If your app already pulls in **zod** — the JSON-render integration and the MCP server both use it — prefer zod for your RPC schemas too, and you'll reuse a dependency you're already shipping instead of adding valibot. Any Standard Schema validator works either way; this is purely about dependency reuse. ```ts defineRpcFunction({ diff --git a/docs/guide/standalone-cli.md b/docs/guide/standalone-cli.md index 15d61333..9968a211 100644 --- a/docs/guide/standalone-cli.md +++ b/docs/guide/standalone-cli.md @@ -168,7 +168,7 @@ const payload = await my.rpc.call('get-payload') ## Typed CLI flags -For flags that are specific to your tool, declare them with any [Standard Schema](https://standardschema.dev/) validator (valibot below — `npm i valibot` — or zod / arktype) so they're validated at parse time and typed at the call site: +For flags that are specific to your tool, declare them with any [Standard Schema](https://standardschema.dev/) validator (valibot below — `npm i valibot`, the lightest option — or zod / arktype) so they're validated at parse time and typed at the call site. If you already depend on zod through the JSON-render or MCP integrations, prefer zod here to avoid adding a second validator: ```ts import type { InferCliFlags } from 'devframe/adapters/cac' diff --git a/packages/devframe/package.json b/packages/devframe/package.json index 29680312..7720d655 100644 --- a/packages/devframe/package.json +++ b/packages/devframe/package.json @@ -83,6 +83,7 @@ } }, "dependencies": { + "@standard-community/standard-json": "catalog:deps", "@standard-schema/spec": "catalog:deps", "birpc": "catalog:deps", "crossws": "catalog:deps", @@ -91,6 +92,7 @@ "mrmime": "catalog:deps", "nostics": "catalog:deps", "pathe": "catalog:deps", + "quansync": "catalog:deps", "ufo": "catalog:deps" }, "devDependencies": { diff --git a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts b/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts index 0a8472f1..9f604f56 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts +++ b/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts @@ -1,18 +1,18 @@ -import { s } from 'devframe/utils/simple-schema' +import * as v from 'valibot' import { describe, expect, it } from 'vitest' import { argsToJsonSchema, returnToJsonSchema } from '../to-json-schema' const PERMISSIVE = { type: 'object', additionalProperties: true } describe('argsToJsonSchema', () => { - it('returns an empty object schema when no args', () => { - const { schema, unwrapped } = argsToJsonSchema(undefined) + it('returns an empty object schema when no args', async () => { + const { schema, unwrapped } = await argsToJsonSchema(undefined) expect(unwrapped).toBe(false) expect(schema).toEqual({ type: 'object', properties: {} }) }) - it('advertises each positional arg as a permissive object under arg0/arg1/...', () => { - const { schema, unwrapped } = argsToJsonSchema([s.string(), s.number()]) + it('advertises each positional arg under arg0/arg1/... with precise per-vendor conversion', async () => { + const { schema, unwrapped } = await argsToJsonSchema([v.string(), v.number()]) expect(unwrapped).toBe(false) expect(schema).toMatchObject({ type: 'object', @@ -20,32 +20,28 @@ describe('argsToJsonSchema', () => { additionalProperties: false, }) const props = (schema as any).properties - expect(props.arg0).toEqual(PERMISSIVE) - expect(props.arg1).toEqual(PERMISSIVE) + // valibot vendor → precise conversion via @standard-community/standard-json. + expect(props.arg0).toMatchObject({ type: 'string' }) + expect(props.arg1).toMatchObject({ type: 'number' }) }) - it('wraps a single arg under arg0 (no vendor-specific unwrapping)', () => { - const { schema, unwrapped } = argsToJsonSchema([s.object({ name: s.string() })]) - expect(unwrapped).toBe(false) - expect(schema).toMatchObject({ type: 'object', required: ['arg0'] }) - expect((schema as any).properties.arg0).toEqual(PERMISSIVE) - }) - - it('works with any Standard Schema vendor (falls back the same way)', () => { + it('falls back to a permissive object for vendors without a converter', async () => { const foreign = { '~standard': { version: 1 as const, vendor: 'acme', validate: (value: unknown) => ({ value }) }, } - const { schema } = argsToJsonSchema([foreign]) + const { schema } = await argsToJsonSchema([foreign]) expect((schema as any).properties.arg0).toEqual(PERMISSIVE) }) }) describe('returnToJsonSchema', () => { - it('returns undefined when no schema is provided', () => { - expect(returnToJsonSchema(undefined)).toBeUndefined() + it('returns undefined when no schema is provided', async () => { + expect(await returnToJsonSchema(undefined)).toBeUndefined() }) - it('advertises a permissive object for any declared return schema', () => { - expect(returnToJsonSchema(s.object({ ok: s.boolean() }))).toEqual(PERMISSIVE) + it('converts a declared return schema precisely for known vendors', async () => { + const schema = await returnToJsonSchema(v.object({ ok: v.boolean() })) + expect((schema as any).type).toBe('object') + expect((schema as any).properties.ok).toMatchObject({ type: 'boolean' }) }) }) diff --git a/packages/devframe/src/adapters/mcp/build-server.ts b/packages/devframe/src/adapters/mcp/build-server.ts index b2aac021..0fab1e2f 100644 --- a/packages/devframe/src/adapters/mcp/build-server.ts +++ b/packages/devframe/src/adapters/mcp/build-server.ts @@ -148,7 +148,7 @@ export async function createMcpServer( function registerToolHandlers(server: Server, ctx: DevframeNodeContext): void { server.setRequestHandler('tools/list', async () => { - const tools = ctx.agent.list().tools.map(tool => projectTool(tool, ctx)) + const tools = await Promise.all(ctx.agent.list().tools.map(tool => projectTool(tool, ctx))) return { tools } }) @@ -157,7 +157,7 @@ function registerToolHandlers(server: Server, ctx: DevframeNodeContext): void { try { const tool = ctx.agent.getTool(name) const outputSchema = tool - ? tool.outputSchema ?? computeOutputSchema(tool, ctx) + ? tool.outputSchema ?? await computeOutputSchema(tool, ctx) : undefined const result = await ctx.agent.invoke(name, args ?? {}) return { @@ -248,9 +248,9 @@ function registerResourceHandlers( }) } -function projectTool(tool: AgentTool, ctx: DevframeNodeContext): Tool { - const inputSchema = tool.inputSchema ?? computeInputSchema(tool, ctx) - const outputSchema = tool.outputSchema ?? computeOutputSchema(tool, ctx) +async function projectTool(tool: AgentTool, ctx: DevframeNodeContext): Promise { + const inputSchema = tool.inputSchema ?? await computeInputSchema(tool, ctx) + const outputSchema = tool.outputSchema ?? await computeOutputSchema(tool, ctx) return { name: tool.id, title: tool.title, @@ -265,17 +265,17 @@ function projectTool(tool: AgentTool, ctx: DevframeNodeContext): Tool { } as Tool } -function computeInputSchema(tool: AgentTool, ctx: DevframeNodeContext): unknown { +async function computeInputSchema(tool: AgentTool, ctx: DevframeNodeContext): Promise { if (tool.kind !== 'rpc' || !tool.rpcName) return { type: 'object', properties: {} } const def = ctx.rpc.definitions.get(tool.rpcName) as RpcFunctionDefinitionAnyWithContext | undefined if (!def) return { type: 'object', properties: {} } const args = def.args as readonly StandardSchemaV1[] | undefined - return argsToJsonSchema(args).schema + return (await argsToJsonSchema(args)).schema } -function computeOutputSchema(tool: AgentTool, ctx: DevframeNodeContext): unknown { +async function computeOutputSchema(tool: AgentTool, ctx: DevframeNodeContext): Promise { if (tool.kind !== 'rpc' || !tool.rpcName) return undefined const def = ctx.rpc.definitions.get(tool.rpcName) as RpcFunctionDefinitionAnyWithContext | undefined diff --git a/packages/devframe/src/adapters/mcp/to-json-schema.ts b/packages/devframe/src/adapters/mcp/to-json-schema.ts index aa1f118e..a3650b8f 100644 --- a/packages/devframe/src/adapters/mcp/to-json-schema.ts +++ b/packages/devframe/src/adapters/mcp/to-json-schema.ts @@ -1,32 +1,50 @@ import type { StandardSchemaV1 } from '@standard-schema/spec' +import { toJsonSchema } from '@standard-community/standard-json' const FALLBACK_OBJECT_SCHEMA = Object.freeze({ type: 'object', additionalProperties: true }) /** - * JSON Schema for an RPC return value on the agent/MCP surface. + * Convert a Standard Schema to JSON Schema for the agent/MCP surface. * - * [Standard Schema](https://standardschema.dev/) deliberately exposes no - * JSON Schema, and devframe stays validator-neutral, so a declared return - * schema advertises a permissive object rather than a precise shape. + * `@standard-community/standard-json` dispatches on the schema's + * `~standard` vendor (valibot, zod, arktype, …) and lazily loads that + * vendor's converter, so precise schemas require the matching converter + * to be installed (e.g. `@valibot/to-json-schema`, `zod-to-json-schema`). + * When no converter is available — or conversion fails — we degrade to a + * permissive object schema so the surface never throws and no validator is + * forced. + */ +async function safeToJsonSchema(schema: StandardSchemaV1): Promise { + try { + return await toJsonSchema(schema) + } + catch { + return FALLBACK_OBJECT_SCHEMA + } +} + +/** + * JSON Schema for an RPC return value on the agent/MCP surface. * @internal */ -export function returnToJsonSchema(schema: StandardSchemaV1 | undefined): unknown { - return schema ? FALLBACK_OBJECT_SCHEMA : undefined +export async function returnToJsonSchema(schema: StandardSchemaV1 | undefined): Promise { + if (!schema) + return undefined + return safeToJsonSchema(schema) } /** * JSON Schema for an RPC function's positional args on the agent/MCP - * surface. Each positional arg is advertised as a permissive object under - * `arg0` / `arg1` / … — matching how the agent bridge coerces the incoming - * object payload back into positional arguments. + * surface. Each positional arg is advertised under `arg0` / `arg1` / … — + * matching how the agent bridge coerces the incoming object payload back + * into positional arguments. * - * Returns `{ type: 'object', properties: {} }` when there are no args (the - * MCP SDK treats this as "no input"). + * Returns `{ type: 'object', properties: {} }` when there are no args. * @internal */ -export function argsToJsonSchema( +export async function argsToJsonSchema( args: readonly StandardSchemaV1[] | undefined, -): { schema: unknown, unwrapped: boolean } { +): Promise<{ schema: unknown, unwrapped: boolean }> { if (!args || args.length === 0) return { schema: { type: 'object', properties: {} }, unwrapped: false } @@ -34,7 +52,7 @@ export function argsToJsonSchema( const required: string[] = [] for (let i = 0; i < args.length; i++) { const key = `arg${i}` - properties[key] = FALLBACK_OBJECT_SCHEMA + properties[key] = await safeToJsonSchema(args[i]!) required.push(key) } diff --git a/plugins/inspect/package.json b/plugins/inspect/package.json index 22f48e75..664047b7 100644 --- a/plugins/inspect/package.json +++ b/plugins/inspect/package.json @@ -56,9 +56,11 @@ } }, "dependencies": { + "@standard-community/standard-json": "catalog:deps", "@valibot/to-json-schema": "catalog:deps", "cac": "catalog:deps", - "nostics": "catalog:deps" + "nostics": "catalog:deps", + "quansync": "catalog:deps" }, "devDependencies": { "@antfu/design": "catalog:frontend", diff --git a/plugins/inspect/src/rpc/functions/_schema.ts b/plugins/inspect/src/rpc/functions/_schema.ts index 3f4bc967..2af2c429 100644 --- a/plugins/inspect/src/rpc/functions/_schema.ts +++ b/plugins/inspect/src/rpc/functions/_schema.ts @@ -1,17 +1,11 @@ -import { toJsonSchema } from '@valibot/to-json-schema' +import type { StandardSchemaV1 } from '@standard-schema/spec' +import { toJsonSchema } from '@standard-community/standard-json' const FALLBACK_SCHEMA = Object.freeze({ type: 'object', additionalProperties: true }) -/** - * Convert a valibot return schema to JSON Schema, swallowing - * conversion failures (unsupported valibot actions) into a permissive - * fallback so introspection never throws. - */ -export function returnSchemaToJson(schema: unknown): unknown { - if (!schema) - return undefined +async function convert(schema: unknown): Promise { try { - return toJsonSchema(schema as never) + return await toJsonSchema(schema as StandardSchemaV1) } catch { return FALLBACK_SCHEMA @@ -19,22 +13,30 @@ export function returnSchemaToJson(schema: unknown): unknown { } /** - * Convert the positional args valibot schemas to a single JSON Schema - * tuple (`type: 'array'` + `prefixItems`). Returns `undefined` when the - * function declares no args. + * Convert an RPC return schema to JSON Schema, swallowing conversion + * failures (unsupported vendor / missing converter) into a permissive + * fallback so introspection never throws. + * + * Conversion is vendor-neutral via `@standard-community/standard-json`, + * which loads the matching per-vendor converter on demand (valibot, zod, + * arktype, …) — install the converter for the vendor you inspect. + */ +export async function returnSchemaToJson(schema: unknown): Promise { + if (!schema) + return undefined + return convert(schema) +} + +/** + * Convert positional args schemas to a single JSON Schema tuple + * (`type: 'array'` + `prefixItems`). Returns `undefined` when the function + * declares no args. */ -export function argsSchemaToJson(args: readonly unknown[] | undefined): unknown { +export async function argsSchemaToJson(args: readonly unknown[] | undefined): Promise { if (!args || args.length === 0) return undefined return { type: 'array', - prefixItems: args.map((arg) => { - try { - return toJsonSchema(arg as never) - } - catch { - return FALLBACK_SCHEMA - } - }), + prefixItems: await Promise.all(args.map(arg => convert(arg))), } } diff --git a/plugins/inspect/src/rpc/functions/list-functions.ts b/plugins/inspect/src/rpc/functions/list-functions.ts index 000c56af..f022121f 100644 --- a/plugins/inspect/src/rpc/functions/list-functions.ts +++ b/plugins/inspect/src/rpc/functions/list-functions.ts @@ -46,8 +46,8 @@ export const listFunctions = defineInspectRpc({ hasHandler: !!fn.handler, invokable: INVOKABLE_TYPES.has(type), agent, - argsSchema: argsSchemaToJson(fn.args as readonly unknown[] | undefined), - returnsSchema: returnSchemaToJson(fn.returns), + argsSchema: await argsSchemaToJson(fn.args as readonly unknown[] | undefined), + returnsSchema: await returnSchemaToJson(fn.returns), }) } out.sort((a, b) => a.name.localeCompare(b.name)) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 842924f7..db998b56 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,6 +49,9 @@ catalogs: '@modelcontextprotocol/server': specifier: ^2.0.0 version: 2.0.0 + '@standard-community/standard-json': + specifier: ^0.3.5 + version: 0.3.5 '@standard-schema/spec': specifier: ^1.1.0 version: 1.1.0 @@ -109,6 +112,9 @@ catalogs: perfect-debounce: specifier: ^2.1.0 version: 2.1.0 + quansync: + specifier: ^0.2.11 + version: 0.2.11 structured-clone-es: specifier: ^2.0.1 version: 2.0.1 @@ -788,6 +794,9 @@ importers: packages/devframe: dependencies: + '@standard-community/standard-json': + specifier: catalog:deps + version: 0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3)))(quansync@0.2.11)(valibot@1.4.2(typescript@6.0.3))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) '@standard-schema/spec': specifier: catalog:deps version: 1.1.0 @@ -812,6 +821,9 @@ importers: pathe: specifier: catalog:deps version: 2.0.3 + quansync: + specifier: catalog:deps + version: 0.2.11 ufo: specifier: catalog:deps version: 1.6.4 @@ -1401,6 +1413,9 @@ importers: plugins/inspect: dependencies: + '@standard-community/standard-json': + specifier: catalog:deps + version: 0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3)))(quansync@0.2.11)(valibot@1.4.2(typescript@6.0.3))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) '@valibot/to-json-schema': specifier: catalog:deps version: 1.7.1(valibot@1.4.2(typescript@6.0.3)) @@ -1410,6 +1425,9 @@ importers: nostics: specifier: catalog:deps version: 1.2.0 + quansync: + specifier: catalog:deps + version: 0.2.11 devDependencies: '@antfu/design': specifier: catalog:frontend @@ -4286,6 +4304,38 @@ packages: '@speed-highlight/core@1.2.15': resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} + '@standard-community/standard-json@0.3.5': + resolution: {integrity: sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA==} + peerDependencies: + '@standard-schema/spec': ^1.0.0 + '@types/json-schema': ^7.0.15 + '@valibot/to-json-schema': ^1.3.0 + arktype: ^2.1.20 + effect: ^3.16.8 + quansync: ^0.2.11 + sury: ^10.0.0 + typebox: ^1.0.17 + valibot: ^1.1.0 + zod: ^3.25.0 || ^4.0.0 + zod-to-json-schema: ^3.24.5 + peerDependenciesMeta: + '@valibot/to-json-schema': + optional: true + arktype: + optional: true + effect: + optional: true + sury: + optional: true + typebox: + optional: true + valibot: + optional: true + zod: + optional: true + zod-to-json-schema: + optional: true + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -11997,6 +12047,17 @@ snapshots: '@speed-highlight/core@1.2.15': {} + '@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3)))(quansync@0.2.11)(valibot@1.4.2(typescript@6.0.3))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3)': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/json-schema': 7.0.15 + quansync: 0.2.11 + optionalDependencies: + '@valibot/to-json-schema': 1.7.1(valibot@1.4.2(typescript@6.0.3)) + valibot: 1.4.2(typescript@6.0.3) + zod: 4.4.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) + '@standard-schema/spec@1.1.0': {} '@storybook/addon-a11y@10.5.5(storybook@10.5.5(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 63b349fc..1b4ed560 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -59,6 +59,7 @@ catalogs: '@json-render/core': ^0.19.0 '@modelcontextprotocol/client': ^2.0.0 '@modelcontextprotocol/server': ^2.0.0 + '@standard-community/standard-json': ^0.3.5 '@standard-schema/spec': ^1.1.0 '@valibot/to-json-schema': ^1.7.1 birpc: ^4.0.0 @@ -81,6 +82,7 @@ catalogs: parse5: ^8.0.1 pathe: ^2.0.3 perfect-debounce: ^2.1.0 + quansync: ^0.2.11 structured-clone-es: ^2.0.1 tinyexec: ^1.2.4 tinyglobby: ^0.2.17 diff --git a/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts index fce1061a..0c20b598 100644 --- a/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts @@ -44,13 +44,13 @@ export declare const alwaysFunctions: readonly [{ }]; export declare const assetInfoSchema: import("devframe/utils/simple-schema").SimpleSchema<{ path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; }, { path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; @@ -141,13 +141,13 @@ export declare const list: { args: readonly []; returns: import("devframe/utils/simple-schema").SimpleSchema<{ path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; }[], { path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; @@ -156,21 +156,21 @@ export declare const list: { agent?: import("devframe").RpcFunctionAgentOptions; setup?: ((context: DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: (() => { path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; }[]) | undefined; dump?: import("devframe/rpc").RpcDump<[], { path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; @@ -178,14 +178,14 @@ export declare const list: { snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable]; returns: import("devframe/utils/simple-schema").SimpleSchema<{ path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; }, { path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; @@ -472,7 +472,7 @@ export declare const rename: { newName: string; }], { path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; @@ -482,7 +482,7 @@ export declare const rename: { newName: string; }) => { path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; @@ -492,7 +492,7 @@ export declare const rename: { newName: string; }], { path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; @@ -503,7 +503,7 @@ export declare const rename: { newName: string; }], { path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; @@ -513,7 +513,7 @@ export declare const rename: { newName: string; }], { path: string; - type: "text" | "image" | "font" | "video" | "audio" | "other"; + type: "image" | "font" | "video" | "audio" | "text" | "other"; publicPath: string; size: number; mtime: number; From 4ee6bc615711466ec0fe9bacfba96292b83be15f Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 3 Aug 2026 10:16:33 +0900 Subject: [PATCH 6/8] chore: update deps --- plugins/inspect/package.json | 1 - pnpm-lock.yaml | 567 +++-------------------------------- pnpm-workspace.yaml | 16 +- 3 files changed, 56 insertions(+), 528 deletions(-) diff --git a/plugins/inspect/package.json b/plugins/inspect/package.json index 664047b7..67ea89ce 100644 --- a/plugins/inspect/package.json +++ b/plugins/inspect/package.json @@ -57,7 +57,6 @@ }, "dependencies": { "@standard-community/standard-json": "catalog:deps", - "@valibot/to-json-schema": "catalog:deps", "cac": "catalog:deps", "nostics": "catalog:deps", "quansync": "catalog:deps" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db998b56..d1507593 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,9 +55,6 @@ catalogs: '@standard-schema/spec': specifier: ^1.1.0 version: 1.1.0 - '@valibot/to-json-schema': - specifier: ^1.7.1 - version: 1.7.1 birpc: specifier: ^4.0.0 version: 4.0.0 @@ -352,8 +349,23 @@ catalogs: version: 8.18.1 overrides: + '@devframe/hub': workspace:* + '@devframe/json-render': workspace:* + '@devframe/json-render-ui': workspace:* + '@devframe/next': workspace:* + '@devframe/nuxt': workspace:* + '@devframe/plugin-a11y': workspace:* + '@devframe/plugin-assets': workspace:* + '@devframe/plugin-code-server': workspace:* + '@devframe/plugin-data-inspector': workspace:* + '@devframe/plugin-git': workspace:* + '@devframe/plugin-inspect': workspace:* + '@devframe/plugin-messages': workspace:* + '@devframe/plugin-og': workspace:* + '@devframe/plugin-terminals': workspace:* chokidar: ^5.0.0 crossws: ^0.4.10 + devframe: workspace:* semver: ^7.8.5 shell-quote: ^1.10.0 @@ -1035,7 +1047,7 @@ importers: version: link:../devframe nuxt: specifier: catalog:build - version: 4.5.1(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.0(supports-color@10.2.2)))(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(db0@0.3.4)(esbuild@0.28.0)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.10.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.60.3))(rollup@4.60.3)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.47.1)(tsx@4.23.1)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(yaml@2.9.0) + version: 4.5.1(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.0(supports-color@10.2.2)))(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(db0@0.3.4)(esbuild@0.28.0)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.10.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.60.3))(rollup@4.60.3)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.47.1)(tsx@4.23.1)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(yaml@2.9.0) tsdown: specifier: catalog:build version: 0.22.14(@volar/typescript@2.4.28)(oxc-resolver@11.21.3)(tsx@4.23.1)(typescript@6.0.3) @@ -1416,9 +1428,6 @@ importers: '@standard-community/standard-json': specifier: catalog:deps version: 0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3)))(quansync@0.2.11)(valibot@1.4.2(typescript@6.0.3))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) - '@valibot/to-json-schema': - specifier: catalog:deps - version: 1.7.1(valibot@1.4.2(typescript@6.0.3)) cac: specifier: catalog:deps version: 7.0.0 @@ -2057,13 +2066,13 @@ packages: '@devframes/hub@0.7.14': resolution: {integrity: sha512-Ym3YHkBnpdwhPw7y4YgURZYntQPShb9raRfo60D1Yk4jb1OlnL2GGHw6pt4iLZqHL4vp1P4T6YpBFPPVx4G38A==} peerDependencies: - devframe: 0.7.14 + devframe: workspace:* '@devframes/json-render@0.7.14': resolution: {integrity: sha512-Bo+IiRpEdceQjLr/tcFNXFYlBkSeUv7PWsFc4Fu5PLUyxnnqciLVyFjZz6IlEtKrBIA667L4QbQe5ZExlPhlPA==} peerDependencies: '@devframes/hub': 0.7.14 - devframe: 0.7.14 + devframe: workspace:* peerDependenciesMeta: '@devframes/hub': optional: true @@ -2390,12 +2399,6 @@ packages: '@floating-ui/vue@1.1.11': resolution: {integrity: sha512-HzHKCNVxnGS35r9fCHBc3+uCnjw9IWIlCPL683cGgM9Kgj2BiAl8x1mS7vtvP6F9S/e/q4O6MApwSHj8hNLGfw==} - '@hono/node-server@1.19.14': - resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} - engines: {node: '>=18.14.1'} - peerDependencies: - hono: ^4 - '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -2676,16 +2679,6 @@ packages: resolution: {integrity: sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==} engines: {node: '>=20'} - '@modelcontextprotocol/sdk@1.30.0': - resolution: {integrity: sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==} - engines: {node: '>=18'} - peerDependencies: - '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@cfworker/json-schema': - optional: true - '@modelcontextprotocol/server@2.0.0': resolution: {integrity: sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==} engines: {node: '>=20'} @@ -5267,10 +5260,6 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -5295,20 +5284,9 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - ajv@8.20.0: - resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - alien-signals@3.2.1: resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} @@ -5512,10 +5490,6 @@ packages: birpc@4.0.0: resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} - body-parser@2.2.2: - resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} - engines: {node: '>=18'} - boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -5558,10 +5532,6 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - c12@3.3.4: resolution: {integrity: sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==} peerDependencies: @@ -5724,14 +5694,6 @@ packages: constantinople@4.0.1: resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} - content-disposition@1.1.0: - resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} - engines: {node: '>=18'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - convert-hrtime@5.0.0: resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} engines: {node: '>=12'} @@ -5748,24 +5710,12 @@ packages: cookie-es@3.1.1: resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cors@2.8.6: - resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} - engines: {node: '>= 0.10'} - cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} @@ -6097,17 +6047,6 @@ packages: devalue@5.8.2: resolution: {integrity: sha512-DObPPAfdtFbXjxLqK8s2Xk9ZuWz5+ZoFEhC7J76es4GU/rEiXwHTmbImoCdyoCOcBH1UF3+Cz6Z2sYD4hyl5TA==} - devframe@0.7.14: - resolution: {integrity: sha512-AtGfI3LKjZL11eBcGArZn6QMZTrmGZY7DCwJ9Wot8Vt6/jqaV4wv/Z/VsYneGc4Z/OmRmHucfMB0xlDrE6GYJw==} - peerDependencies: - '@modelcontextprotocol/sdk': ^1.0.0 - cac: ^7.0.0 - peerDependenciesMeta: - '@modelcontextprotocol/sdk': - optional: true - cac: - optional: true - devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -6519,16 +6458,6 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} - express-rate-limit@8.5.1: - resolution: {integrity: sha512-5O6KYmyJEpuPJV5hNTXKbAHWRqrzyu+OI3vUnSd2kXFubIVpG7ezpgxQy76Zo5GQZtrQBg86hF+CM/NX+cioiQ==} - engines: {node: '>= 16'} - peerDependencies: - express: '>= 4.11' - - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} - exsolve@1.1.0: resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} @@ -6562,9 +6491,6 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-uri@3.1.2: - resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} - fast-wrap-ansi@0.2.0: resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} @@ -6594,10 +6520,6 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} - find-up-simple@1.0.1: resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} engines: {node: '>=18'} @@ -6636,10 +6558,6 @@ packages: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} @@ -6815,10 +6733,6 @@ packages: resolution: {integrity: sha512-H7ph5MF3lrc8BE47hhyHi+ZgoRiGi5kqxnA5Ou6rg9dVHtPnmUU8/Man19X/H3Cj1C/DF+jsN/ch9eV7nfpzMQ==} engines: {node: '>=8.0.0'} - hono@4.12.18: - resolution: {integrity: sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==} - engines: {node: '>=16.9.0'} - hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} @@ -6857,10 +6771,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} - engines: {node: '>=0.10.0'} - identifier-regex@1.1.0: resolution: {integrity: sha512-SLX4H/vtcYlYnL7XqnuJKHU7Z8517TgsW9nmQiGOgMCjQ8V/deLYu6bEmbGoXe7WMMhc9+EUGyFFneHja8KabA==} engines: {node: '>=18'} @@ -6925,14 +6835,6 @@ packages: resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==} engines: {node: '>=12.22.0'} - ip-address@10.2.0: - resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} - engines: {node: '>= 12'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -6999,9 +6901,6 @@ packages: is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -7087,12 +6986,6 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -7390,18 +7283,10 @@ packages: mdn-data@2.28.1: resolution: {integrity: sha512-U9w+PzSZ00Z5m9rZ5ARVFL5xOfuCHdKYi/1RRwDCJsboFgJDNT3zT6PIPD7mZQYaQLhsZM3GfDRgSMRHhSmVng==} - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - merge-anything@5.1.7: resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} engines: {node: '>=12.13'} - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -7592,10 +7477,6 @@ packages: resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} engines: {node: '>=18'} - negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} - next@16.2.12: resolution: {integrity: sha512-iD59eYQWmbFcEbX7v/acG5DRym9iw1DdaPoD0WTA920naWsE25wShzJW4+UvAs8MK9EC2kBfIH6vtto1H1PHGw==} engines: {node: '>=20.9.0'} @@ -7714,10 +7595,6 @@ packages: object-identity@0.2.3: resolution: {integrity: sha512-2J8Joz2Tf7aaylhqFvIUJHNgpuGR38Hh75Voq9GzTbStBxJUaOtN0K1aOd3cV5qp+ij1pMqRbPrGCGMOyX303w==} - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - obug@2.1.4: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} @@ -7739,9 +7616,6 @@ packages: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@6.0.0: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} @@ -7874,9 +7748,6 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - path-to-regexp@8.4.2: - resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} - pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -8155,10 +8026,6 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - pug-attrs@3.0.0: resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==} @@ -8199,10 +8066,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.15.1: - resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} - engines: {node: '>=0.6'} - quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -8223,10 +8086,6 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} - rc9@3.0.1: resolution: {integrity: sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==} @@ -8311,10 +8170,6 @@ packages: peerDependencies: vue: '>= 3.4.0' - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - reserved-identifiers@1.2.0: resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} engines: {node: '>=18'} @@ -8404,10 +8259,6 @@ packages: roughjs@4.6.6: resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - run-applescript@7.1.0: resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} @@ -8498,22 +8349,6 @@ packages: resolution: {integrity: sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw==} engines: {node: '>=20'} - side-channel-list@1.0.1: - resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -8947,10 +8782,6 @@ packages: resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} engines: {node: '>=20'} - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} - type-level-regexp@0.1.17: resolution: {integrity: sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==} @@ -9073,10 +8904,6 @@ packages: '@unocss/webpack': optional: true - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - unplugin-utils@0.3.2: resolution: {integrity: sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==} engines: {node: '>=20.19.0'} @@ -9226,10 +9053,6 @@ packages: typescript: optional: true - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - verkit@0.1.2: resolution: {integrity: sha512-WqkT8n3hqizuCu71W3bUzf5fjBmkbXcudsehe/NbxA8PgqoKnSOY5K0Ba2ckg1qaRaSpSz7as/n9K1R9JXjQKg==} engines: {node: '>=18.12.0'} @@ -9572,9 +9395,6 @@ packages: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.21.1: resolution: {integrity: sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==} engines: {node: '>=10.0.0'} @@ -10034,25 +9854,25 @@ snapshots: '@colordx/core@5.4.3': {} - '@devframes/hub@0.7.14(devframe@0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3))': + '@devframes/hub@0.7.14(devframe@packages+devframe)': dependencies: birpc: 4.0.0 destr: 2.0.5 - devframe: 0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3) + devframe: link:packages/devframe nostics: 1.2.0 pathe: 2.0.3 perfect-debounce: 2.1.0 tinyexec: 1.2.4 zigpty: 0.2.1 - '@devframes/json-render@0.7.14(@devframes/hub@0.7.14(devframe@0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3)))(devframe@0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3))': + '@devframes/json-render@0.7.14(@devframes/hub@0.7.14(devframe@packages+devframe))(devframe@packages+devframe)': dependencies: '@json-render/core': 0.19.0(zod@4.4.3) - devframe: 0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3) + devframe: link:packages/devframe nostics: 1.2.0 zod: 4.4.3 optionalDependencies: - '@devframes/hub': 0.7.14(devframe@0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3)) + '@devframes/hub': 0.7.14(devframe@packages+devframe) '@discoveryjs/discovery@1.0.0-beta.99': dependencies: @@ -10376,11 +10196,6 @@ snapshots: - '@vue/composition-api' - vue - '@hono/node-server@1.19.14(hono@4.12.18)': - dependencies: - hono: 4.12.18 - optional: true - '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -10642,29 +10457,6 @@ snapshots: dependencies: zod: 4.4.3 - '@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3)': - dependencies: - '@hono/node-server': 1.19.14(hono@4.12.18) - ajv: 8.20.0 - ajv-formats: 3.0.1(ajv@8.20.0) - content-type: 1.0.5 - cors: 2.8.6 - cross-spawn: 7.0.6 - eventsource: 3.0.7 - eventsource-parser: 3.0.8 - express: 5.2.1(supports-color@10.2.2) - express-rate-limit: 8.5.1(express@5.2.1(supports-color@10.2.2)) - hono: 4.12.18 - jose: 6.2.3 - json-schema-typed: 8.0.2 - pkce-challenge: 5.0.1 - raw-body: 3.0.2 - zod: 4.4.3 - zod-to-json-schema: 3.25.2(zod@4.4.3) - transitivePeerDependencies: - - supports-color - optional: true - '@modelcontextprotocol/server@2.0.0': dependencies: '@modelcontextprotocol/core': 2.0.0 @@ -10932,11 +10724,11 @@ snapshots: - rolldown - unplugin - '@nuxt/nitro-server@4.5.1(e7761c5a620feb94b1a6c4dc96de07af)': + '@nuxt/nitro-server@4.5.1(71bc86bb730f85a69e3066bf13024c10)': dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/kit': 4.5.1(magic-string@1.0.0)(magicast@0.5.2)(oxc-parser@0.140.0)(rolldown@1.2.0)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))) - '@unhead/vue': 3.2.3(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(esbuild@0.28.0)(lightningcss@1.32.0)(rolldown@1.2.0)(rollup@4.60.3)(srvx@0.11.22)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3)) + '@unhead/vue': 3.2.3(esbuild@0.28.0)(lightningcss@1.32.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3)) '@vue/shared': 3.5.40 consola: 3.4.2 defu: 6.1.7 @@ -10951,7 +10743,7 @@ snapshots: mocked-exports: 0.1.1 nitropack: 2.13.4(oxc-parser@0.140.0)(rolldown@1.2.0)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)) nostics: 1.2.0 - nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.0(supports-color@10.2.2)))(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(db0@0.3.4)(esbuild@0.28.0)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.10.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.60.3))(rollup@4.60.3)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.47.1)(tsx@4.23.1)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(yaml@2.9.0) + nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.0(supports-color@10.2.2)))(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(db0@0.3.4)(esbuild@0.28.0)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.10.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.60.3))(rollup@4.60.3)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.47.1)(tsx@4.23.1)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(yaml@2.9.0) nypm: 0.6.8 ohash: 2.0.11 pathe: 2.0.3 @@ -10977,7 +10769,6 @@ snapshots: - '@electric-sql/pglite' - '@farmfe/core' - '@libsql/client' - - '@modelcontextprotocol/sdk' - '@netlify/blobs' - '@planetscale/database' - '@rspack/core' @@ -10991,7 +10782,6 @@ snapshots: - bare-buffer - better-sqlite3 - bun-types-no-globals - - cac - db0 - drizzle-orm - encoding @@ -11035,7 +10825,7 @@ snapshots: rc9: 3.0.1 std-env: 4.2.0 - '@nuxt/vite-builder@4.5.1(38c9b88e29bb939367de5d602b22a832)': + '@nuxt/vite-builder@4.5.1(abf4b1bdbb10956a12cdcb700c0fc641)': dependencies: '@nuxt/kit': 4.5.1(magic-string@1.0.0)(magicast@0.5.2)(oxc-parser@0.140.0)(rolldown@1.2.0)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))) '@vitejs/plugin-vue': 6.0.8(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3)) @@ -11053,7 +10843,7 @@ snapshots: knitwork: 1.3.0 mlly: 1.8.2 mocked-exports: 0.1.1 - nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.0(supports-color@10.2.2)))(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(db0@0.3.4)(esbuild@0.28.0)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.10.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.60.3))(rollup@4.60.3)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.47.1)(tsx@4.23.1)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(yaml@2.9.0) + nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.0(supports-color@10.2.2)))(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(db0@0.3.4)(esbuild@0.28.0)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.10.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.60.3))(rollup@4.60.3)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.47.1)(tsx@4.23.1)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(yaml@2.9.0) nypm: 0.6.8 pathe: 2.0.3 pkg-types: 2.3.1 @@ -12638,9 +12428,9 @@ snapshots: '@ungap/structured-clone@1.3.1': {} - '@unhead/bundler@3.2.3(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(esbuild@0.28.0)(lightningcss@1.32.0)(rolldown@1.2.0)(rollup@4.60.3)(srvx@0.11.22)(typescript@6.0.3)(unhead@3.2.3(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))': + '@unhead/bundler@3.2.3(esbuild@0.28.0)(lightningcss@1.32.0)(rolldown@1.2.0)(rollup@4.60.3)(unhead@3.2.3(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))': dependencies: - '@vitejs/devtools-kit': 0.4.9(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)) + '@vitejs/devtools-kit': 0.4.9(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)) magic-string: 1.0.0 oxc-parser: 0.140.0 oxc-walker: 1.0.0(esbuild@0.28.0)(oxc-parser@0.140.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)) @@ -12654,18 +12444,14 @@ snapshots: vite: 8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0) transitivePeerDependencies: - '@farmfe/core' - - '@modelcontextprotocol/sdk' - '@rspack/core' - bun-types-no-globals - - cac - rollup - - srvx - - typescript - unloader - '@unhead/vue@3.2.3(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(esbuild@0.28.0)(lightningcss@1.32.0)(rolldown@1.2.0)(rollup@4.60.3)(srvx@0.11.22)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3))': + '@unhead/vue@3.2.3(esbuild@0.28.0)(lightningcss@1.32.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3))': dependencies: - '@unhead/bundler': 3.2.3(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(esbuild@0.28.0)(lightningcss@1.32.0)(rolldown@1.2.0)(rollup@4.60.3)(srvx@0.11.22)(typescript@6.0.3)(unhead@3.2.3(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)) + '@unhead/bundler': 3.2.3(esbuild@0.28.0)(lightningcss@1.32.0)(rolldown@1.2.0)(rollup@4.60.3)(unhead@3.2.3(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)) hookable: 6.1.1 unhead: 3.2.3(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)) unplugin: 3.3.0(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)) @@ -12674,17 +12460,13 @@ snapshots: vite: 8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0) transitivePeerDependencies: - '@farmfe/core' - - '@modelcontextprotocol/sdk' - '@rspack/core' - '@unhead/cli' - bun-types-no-globals - - cac - esbuild - lightningcss - rolldown - rollup - - srvx - - typescript - unloader '@unocss/cli@66.7.5': @@ -12833,6 +12615,7 @@ snapshots: '@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3))': dependencies: valibot: 1.4.2(typescript@6.0.3) + optional: true '@vercel/nft@1.5.0(rollup@4.60.3)(supports-color@10.2.2)': dependencies: @@ -12853,21 +12636,16 @@ snapshots: - rollup - supports-color - '@vitejs/devtools-kit@0.4.9(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))': + '@vitejs/devtools-kit@0.4.9(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))': dependencies: - '@devframes/hub': 0.7.14(devframe@0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3)) - '@devframes/json-render': 0.7.14(@devframes/hub@0.7.14(devframe@0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3)))(devframe@0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3)) - devframe: 0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3) + '@devframes/hub': 0.7.14(devframe@packages+devframe) + '@devframes/json-render': 0.7.14(@devframes/hub@0.7.14(devframe@packages+devframe))(devframe@packages+devframe) + devframe: link:packages/devframe local-pkg: 1.2.1 mlly: 1.8.2 nostics: 1.2.0 nypm: 0.6.8 vite: 8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0) - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - cac - - srvx - - typescript '@vitejs/plugin-react-oxc@0.4.3(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))': dependencies: @@ -13221,12 +12999,6 @@ snapshots: dependencies: event-target-shim: 5.0.1 - accepts@2.0.0: - dependencies: - mime-types: 3.0.2 - negotiator: 1.0.0 - optional: true - acorn-import-attributes@1.9.5(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -13241,11 +13013,6 @@ snapshots: agent-base@7.1.4: {} - ajv-formats@3.0.1(ajv@8.20.0): - optionalDependencies: - ajv: 8.20.0 - optional: true - ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 @@ -13253,14 +13020,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.20.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - optional: true - alien-signals@3.2.1: {} ansi-regex@5.0.1: {} @@ -13440,21 +13199,6 @@ snapshots: birpc@4.0.0: {} - body-parser@2.2.2(supports-color@10.2.2): - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.3(supports-color@10.2.2) - http-errors: 2.0.1 - iconv-lite: 0.7.2 - on-finished: 2.4.1 - qs: 6.15.1 - raw-body: 3.0.2 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - optional: true - boolbase@1.0.0: {} brace-expansion@2.1.0: @@ -13504,9 +13248,6 @@ snapshots: dependencies: run-applescript: 7.1.0 - bytes@3.1.2: - optional: true - c12@3.3.4(magicast@0.5.2): dependencies: chokidar: 5.0.0 @@ -13655,12 +13396,6 @@ snapshots: '@babel/parser': 7.29.7 '@babel/types': 7.29.7 - content-disposition@1.1.0: - optional: true - - content-type@1.0.5: - optional: true - convert-hrtime@5.0.0: {} convert-source-map@2.0.0: {} @@ -13671,24 +13406,12 @@ snapshots: cookie-es@3.1.1: {} - cookie-signature@1.2.2: - optional: true - - cookie@0.7.2: - optional: true - core-js-compat@3.49.0: dependencies: browserslist: 4.28.6 core-util-is@1.0.3: {} - cors@2.8.6: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - optional: true - cose-base@1.0.3: dependencies: layout-base: 1.0.2 @@ -14028,24 +13751,6 @@ snapshots: devalue@5.8.2: {} - devframe@0.7.14(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(srvx@0.11.22)(typescript@6.0.3): - dependencies: - '@valibot/to-json-schema': 1.7.1(valibot@1.4.2(typescript@6.0.3)) - birpc: 4.0.0 - crossws: 0.4.10(srvx@0.11.22) - destr: 2.0.5 - h3: 2.0.1-rc.26(crossws@0.4.10(srvx@0.11.22)) - mrmime: 2.0.1 - nostics: 1.2.0 - pathe: 2.0.3 - ufo: 1.6.4 - valibot: 1.4.2(typescript@6.0.3) - optionalDependencies: - '@modelcontextprotocol/sdk': 1.30.0(supports-color@10.2.2)(zod@4.4.3) - transitivePeerDependencies: - - srvx - - typescript - devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -14512,46 +14217,6 @@ snapshots: expect-type@1.3.0: {} - express-rate-limit@8.5.1(express@5.2.1(supports-color@10.2.2)): - dependencies: - express: 5.2.1(supports-color@10.2.2) - ip-address: 10.2.0 - optional: true - - express@5.2.1(supports-color@10.2.2): - dependencies: - accepts: 2.0.0 - body-parser: 2.2.2(supports-color@10.2.2) - content-disposition: 1.1.0 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3(supports-color@10.2.2) - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.1(supports-color@10.2.2) - fresh: 2.0.0 - http-errors: 2.0.1 - merge-descriptors: 2.0.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.15.1 - range-parser: 1.2.1 - router: 2.2.0(supports-color@10.2.2) - send: 1.2.1(supports-color@10.2.2) - serve-static: 2.2.1(supports-color@10.2.2) - statuses: 2.0.2 - type-is: 2.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - optional: true - exsolve@1.1.0: {} extend-shallow@2.0.1: @@ -14582,9 +14247,6 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.2: - optional: true - fast-wrap-ansi@0.2.0: dependencies: fast-string-width: 3.0.2 @@ -14611,18 +14273,6 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@2.1.1(supports-color@10.2.2): - dependencies: - debug: 4.4.3(supports-color@10.2.2) - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - optional: true - find-up-simple@1.0.1: {} find-up@5.0.0: @@ -14663,9 +14313,6 @@ snapshots: format@0.2.2: {} - forwarded@0.2.0: - optional: true - fraction.js@5.3.4: {} fresh@2.0.0: {} @@ -14803,13 +14450,6 @@ snapshots: transitivePeerDependencies: - srvx - h3@2.0.1-rc.26(crossws@0.4.10(srvx@0.11.22)): - dependencies: - rou3: 0.9.1 - srvx: 0.12.4 - optionalDependencies: - crossws: 0.4.10(srvx@0.11.22) - h3@2.0.1-rc.26(crossws@0.4.10(srvx@0.12.4)): dependencies: rou3: 0.9.1 @@ -14855,9 +14495,6 @@ snapshots: dependencies: ansi-styles: 3.2.1 - hono@4.12.18: - optional: true - hookable@5.5.3: {} hookable@6.1.1: {} @@ -14893,11 +14530,6 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.2: - dependencies: - safer-buffer: 2.1.2 - optional: true - identifier-regex@1.1.0: dependencies: reserved-identifiers: 1.2.0 @@ -14964,12 +14596,6 @@ snapshots: transitivePeerDependencies: - supports-color - ip-address@10.2.0: - optional: true - - ipaddr.js@1.9.1: - optional: true - iron-webcrypto@1.2.1: {} is-builtin-module@5.0.0: @@ -15021,9 +14647,6 @@ snapshots: is-promise@2.2.2: {} - is-promise@4.0.0: - optional: true - is-reference@1.2.1: dependencies: '@types/estree': 1.0.9 @@ -15094,12 +14717,6 @@ snapshots: json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: - optional: true - - json-schema-typed@8.0.2: - optional: true - json-stable-stringify-without-jsonify@1.0.1: {} json5@2.2.3: {} @@ -15475,16 +15092,10 @@ snapshots: mdn-data@2.28.1: {} - media-typer@1.1.0: - optional: true - merge-anything@5.1.7: dependencies: is-what: 4.1.16 - merge-descriptors@2.0.0: - optional: true - merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -15787,9 +15398,6 @@ snapshots: natural-orderby@5.0.0: {} - negotiator@1.0.0: - optional: true - next@16.2.12(@babel/core@7.29.0(supports-color@10.2.2))(@playwright/test@1.62.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: '@next/env': 16.2.12 @@ -15971,17 +15579,17 @@ snapshots: dependencies: boolbase: 1.0.0 - nuxt@4.5.1(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.0(supports-color@10.2.2)))(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(db0@0.3.4)(esbuild@0.28.0)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.10.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.60.3))(rollup@4.60.3)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.47.1)(tsx@4.23.1)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(yaml@2.9.0): + nuxt@4.5.1(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.0(supports-color@10.2.2)))(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(db0@0.3.4)(esbuild@0.28.0)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.10.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.2)(optionator@0.9.4)(oxc-parser@0.140.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.60.3))(rollup@4.60.3)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.47.1)(tsx@4.23.1)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(yaml@2.9.0): dependencies: '@dxup/nuxt': 0.5.3(esbuild@0.28.0)(magicast@0.5.2)(oxc-parser@0.140.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)) '@nuxt/cli': 3.37.0(@nuxt/schema@4.5.1)(magicast@0.5.2)(supports-color@10.2.2) '@nuxt/devtools': 3.3.1(db0@0.3.4)(ioredis@5.10.1(supports-color@10.2.2))(magic-string@1.0.0)(oxc-parser@0.140.0)(rolldown@1.2.0)(srvx@0.11.22)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)))(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3)) '@nuxt/kit': 4.5.1(magic-string@1.0.0)(magicast@0.5.2)(oxc-parser@0.140.0)(rolldown@1.2.0)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))) - '@nuxt/nitro-server': 4.5.1(e7761c5a620feb94b1a6c4dc96de07af) + '@nuxt/nitro-server': 4.5.1(71bc86bb730f85a69e3066bf13024c10) '@nuxt/schema': 4.5.1 '@nuxt/telemetry': 2.8.0(@nuxt/kit@4.5.1(magic-string@1.0.0)(magicast@0.5.2)(oxc-parser@0.140.0)(rolldown@1.2.0)(unplugin@3.3.0(esbuild@0.28.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0)))) - '@nuxt/vite-builder': 4.5.1(38c9b88e29bb939367de5d602b22a832) - '@unhead/vue': 3.2.3(@modelcontextprotocol/sdk@1.30.0(supports-color@10.2.2)(zod@4.4.3))(esbuild@0.28.0)(lightningcss@1.32.0)(rolldown@1.2.0)(rollup@4.60.3)(srvx@0.11.22)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3)) + '@nuxt/vite-builder': 4.5.1(abf4b1bdbb10956a12cdcb700c0fc641) + '@unhead/vue': 3.2.3(esbuild@0.28.0)(lightningcss@1.32.0)(rolldown@1.2.0)(rollup@4.60.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.1)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3)) '@vue/shared': 3.5.40 chokidar: 5.0.0 compatx: 0.2.0 @@ -16051,7 +15659,6 @@ snapshots: - '@electric-sql/pglite' - '@farmfe/core' - '@libsql/client' - - '@modelcontextprotocol/sdk' - '@netlify/blobs' - '@pinia/colada' - '@planetscale/database' @@ -16123,9 +15730,6 @@ snapshots: object-identity@0.2.3: {} - object-inspect@1.13.4: - optional: true - obug@2.1.4: {} ofetch@1.5.1: @@ -16144,11 +15748,6 @@ snapshots: dependencies: ee-first: 1.1.1 - once@1.4.0: - dependencies: - wrappy: 1.0.2 - optional: true - onetime@6.0.0: dependencies: mimic-fn: 4.0.0 @@ -16391,9 +15990,6 @@ snapshots: lru-cache: 11.3.6 minipass: 7.1.3 - path-to-regexp@8.4.2: - optional: true - pathe@1.1.2: {} pathe@2.0.3: {} @@ -16648,12 +16244,6 @@ snapshots: property-information@7.1.0: {} - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - optional: true - pug-attrs@3.0.0: dependencies: constantinople: 4.0.1 @@ -16723,11 +16313,6 @@ snapshots: punycode@2.3.1: {} - qs@6.15.1: - dependencies: - side-channel: 1.1.0 - optional: true - quansync@0.2.11: {} quansync@1.0.0: {} @@ -16740,14 +16325,6 @@ snapshots: range-parser@1.2.1: {} - raw-body@3.0.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.7.2 - unpipe: 1.0.0 - optional: true - rc9@3.0.1: dependencies: defu: 6.1.7 @@ -16882,9 +16459,6 @@ snapshots: transitivePeerDependencies: - '@vue/composition-api' - require-from-string@2.0.2: - optional: true - reserved-identifiers@1.2.0: {} resolve-from@5.0.0: {} @@ -17032,17 +16606,6 @@ snapshots: points-on-curve: 0.2.0 points-on-path: 0.2.1 - router@2.2.0(supports-color@10.2.2): - dependencies: - debug: 4.4.3(supports-color@10.2.2) - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.4.2 - transitivePeerDependencies: - - supports-color - optional: true - run-applescript@7.1.0: {} run-parallel@1.2.0: @@ -17166,38 +16729,6 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 - side-channel-list@1.0.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - optional: true - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - optional: true - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - optional: true - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.1 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - optional: true - siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -17681,13 +17212,6 @@ snapshots: dependencies: tagged-tag: 1.0.0 - type-is@2.0.1: - dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 - mime-types: 3.0.2 - optional: true - type-level-regexp@0.1.17: {} typescript@5.9.3: {} @@ -17845,9 +17369,6 @@ snapshots: transitivePeerDependencies: - vite - unpipe@1.0.0: - optional: true - unplugin-utils@0.3.2: dependencies: pathe: 2.0.3 @@ -17939,9 +17460,6 @@ snapshots: optionalDependencies: typescript: 6.0.3 - vary@1.1.2: - optional: true - verkit@0.1.2: {} verkit@0.2.0: {} @@ -18360,9 +17878,6 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.2.0 - wrappy@1.0.2: - optional: true - ws@8.21.1: {} wsl-utils@0.1.0: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1b4ed560..d4c34628 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -37,8 +37,23 @@ refs: vueuse: &vueuse ^14.3.0 overrides: + '@devframe/hub': 'workspace:*' + '@devframe/json-render': 'workspace:*' + '@devframe/json-render-ui': 'workspace:*' + '@devframe/next': 'workspace:*' + '@devframe/nuxt': 'workspace:*' + '@devframe/plugin-a11y': 'workspace:*' + '@devframe/plugin-assets': 'workspace:*' + '@devframe/plugin-code-server': 'workspace:*' + '@devframe/plugin-data-inspector': 'workspace:*' + '@devframe/plugin-git': 'workspace:*' + '@devframe/plugin-inspect': 'workspace:*' + '@devframe/plugin-messages': 'workspace:*' + '@devframe/plugin-og': 'workspace:*' + '@devframe/plugin-terminals': 'workspace:*' chokidar: ^5.0.0 crossws: ^0.4.10 + devframe: 'workspace:*' semver: ^7.8.5 shell-quote: ^1.10.0 @@ -61,7 +76,6 @@ catalogs: '@modelcontextprotocol/server': ^2.0.0 '@standard-community/standard-json': ^0.3.5 '@standard-schema/spec': ^1.1.0 - '@valibot/to-json-schema': ^1.7.1 birpc: ^4.0.0 cac: ^7.0.0 chokidar: ^5.0.0 From 9f29cfd5820b81f2b0dbf6d75407c614da20938a Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Mon, 3 Aug 2026 01:30:07 +0000 Subject: [PATCH 7/8] refactor(mcp): convert via native Standard JSON Schema; drop standard-json + quansync Replace @standard-community/standard-json with the schema's own Standard JSON Schema converter (~standard.jsonSchema from @standard-schema/spec, implemented by e.g. zod 4). This keeps JSON-schema generation vendor-neutral and precise for validators that ship a converter, while removing the @standard-community/standard-json + quansync dependencies and reverting the async conversion back to synchronous. Validators without a native converter degrade to a permissive object schema. The inspect plugin additionally keeps @valibot/to-json-schema as a fallback so it still converts valibot schemas precisely. Technique adapted from PR #155. --- AGENTS.md | 2 +- packages/devframe/package.json | 2 - .../mcp/__tests__/to-json-schema.test.ts | 59 +++++---- .../devframe/src/adapters/mcp/build-server.ts | 16 +-- .../src/adapters/mcp/to-json-schema.ts | 42 +++--- plugins/inspect/package.json | 5 +- plugins/inspect/src/rpc/functions/_schema.ts | 35 +++-- .../src/rpc/functions/list-functions.ts | 4 +- pnpm-lock.yaml | 76 +---------- pnpm-workspace.yaml | 3 +- .../plugin-assets/rpc.snapshot.d.ts | 120 +++++++++--------- 11 files changed, 158 insertions(+), 206 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f966e1ca..b371f76a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,7 +36,7 @@ The `pnpm test` script intentionally runs `build` first so `tsnapi` snapshots co ## Conventions - RPC functions must use `defineRpcFunction`; always namespace IDs `devframes:plugin::` (matching the plugin's `@devframes/plugin-` package name). -- **Stay validator-neutral.** `devframe` and every `@devframes/*` package must not introduce a preferred schema validator dependency — no `valibot`, `zod`, `arktype`, etc. in their runtime `dependencies`. `args`/`returns`/flag schemas are typed against [Standard Schema](https://standardschema.dev/) (`@standard-schema/spec`, types-only); first-party code that needs to author a schema uses the built-in zero-dep `devframe/utils/simple-schema` builder (deliberately minimal — not a general validator). JSON-schema conversion goes through `@standard-community/standard-json`, whose per-vendor converters are optional peers, so no validator is forced. Docs, by contrast, should point *users* at a real validator for their own integrations — recommend **valibot** (lightest) or **zod** (worth reusing if they already pull it via the JSON-render or MCP integrations). +- **Stay validator-neutral.** `devframe` and every `@devframes/*` package must not introduce a preferred schema validator dependency — no `valibot`, `zod`, `arktype`, etc. in their runtime `dependencies`. `args`/`returns`/flag schemas are typed against [Standard Schema](https://standardschema.dev/) (`@standard-schema/spec`, types-only); first-party code that needs to author a schema uses the built-in zero-dep `devframe/utils/simple-schema` builder (deliberately minimal — not a general validator). JSON-schema conversion uses each schema's own Standard JSON Schema converter (`~standard.jsonSchema`, implemented by e.g. zod 4) when present and degrades to a permissive object otherwise — no converter library and no vendor dependency is required. Docs, by contrast, should point *users* at a real validator for their own integrations — recommend **valibot** (lightest) or **zod** (worth reusing if they already pull it via the JSON-render or MCP integrations). - Shared state via `devframe/utils/shared-state`; keep values serializable. - Utility imports use the package-path form `devframe/utils/*`, never relative `../utils/*`. - Dependencies go through the pnpm catalogs in `pnpm-workspace.yaml` (`cli`, `inlined`, `testing`, `types`) — add to a catalog and reference as `catalog:`, don't pin versions in `package.json`. diff --git a/packages/devframe/package.json b/packages/devframe/package.json index 7720d655..29680312 100644 --- a/packages/devframe/package.json +++ b/packages/devframe/package.json @@ -83,7 +83,6 @@ } }, "dependencies": { - "@standard-community/standard-json": "catalog:deps", "@standard-schema/spec": "catalog:deps", "birpc": "catalog:deps", "crossws": "catalog:deps", @@ -92,7 +91,6 @@ "mrmime": "catalog:deps", "nostics": "catalog:deps", "pathe": "catalog:deps", - "quansync": "catalog:deps", "ufo": "catalog:deps" }, "devDependencies": { diff --git a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts b/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts index 9f604f56..132f95df 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts +++ b/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts @@ -1,47 +1,56 @@ +import type { StandardSchemaV1 } from '@standard-schema/spec' import * as v from 'valibot' import { describe, expect, it } from 'vitest' import { argsToJsonSchema, returnToJsonSchema } from '../to-json-schema' const PERMISSIVE = { type: 'object', additionalProperties: true } +/** A Standard Schema that also implements the Standard JSON Schema converter (like zod 4). */ +function withJsonSchema(json: Record): StandardSchemaV1 { + return { + '~standard': { + version: 1, + vendor: 'test', + validate: (value: unknown) => ({ value }), + jsonSchema: { + input: () => json, + output: () => json, + }, + } as StandardSchemaV1['~standard'], + } +} + describe('argsToJsonSchema', () => { - it('returns an empty object schema when no args', async () => { - const { schema, unwrapped } = await argsToJsonSchema(undefined) + it('returns an empty object schema when no args', () => { + const { schema, unwrapped } = argsToJsonSchema(undefined) expect(unwrapped).toBe(false) expect(schema).toEqual({ type: 'object', properties: {} }) }) - it('advertises each positional arg under arg0/arg1/... with precise per-vendor conversion', async () => { - const { schema, unwrapped } = await argsToJsonSchema([v.string(), v.number()]) - expect(unwrapped).toBe(false) - expect(schema).toMatchObject({ - type: 'object', - required: ['arg0', 'arg1'], - additionalProperties: false, - }) - const props = (schema as any).properties - // valibot vendor → precise conversion via @standard-community/standard-json. - expect(props.arg0).toMatchObject({ type: 'string' }) - expect(props.arg1).toMatchObject({ type: 'number' }) + it('uses the schema\'s own Standard JSON Schema converter when present', () => { + const { schema } = argsToJsonSchema([withJsonSchema({ type: 'string' })]) + expect((schema as any).properties.arg0).toEqual({ type: 'string' }) }) - it('falls back to a permissive object for vendors without a converter', async () => { - const foreign = { - '~standard': { version: 1 as const, vendor: 'acme', validate: (value: unknown) => ({ value }) }, - } - const { schema } = await argsToJsonSchema([foreign]) + it('falls back to a permissive object for validators without a native converter (valibot)', () => { + const { schema } = argsToJsonSchema([v.string(), v.number()]) expect((schema as any).properties.arg0).toEqual(PERMISSIVE) + expect((schema as any).properties.arg1).toEqual(PERMISSIVE) + expect(schema).toMatchObject({ type: 'object', required: ['arg0', 'arg1'], additionalProperties: false }) }) }) describe('returnToJsonSchema', () => { - it('returns undefined when no schema is provided', async () => { - expect(await returnToJsonSchema(undefined)).toBeUndefined() + it('returns undefined when no schema is provided', () => { + expect(returnToJsonSchema(undefined)).toBeUndefined() + }) + + it('uses the native converter when present', () => { + expect(returnToJsonSchema(withJsonSchema({ type: 'object', properties: { ok: { type: 'boolean' } } }))) + .toEqual({ type: 'object', properties: { ok: { type: 'boolean' } } }) }) - it('converts a declared return schema precisely for known vendors', async () => { - const schema = await returnToJsonSchema(v.object({ ok: v.boolean() })) - expect((schema as any).type).toBe('object') - expect((schema as any).properties.ok).toMatchObject({ type: 'boolean' }) + it('falls back to permissive for validators without a native converter', () => { + expect(returnToJsonSchema(v.object({ ok: v.boolean() }))).toEqual(PERMISSIVE) }) }) diff --git a/packages/devframe/src/adapters/mcp/build-server.ts b/packages/devframe/src/adapters/mcp/build-server.ts index 0fab1e2f..b2aac021 100644 --- a/packages/devframe/src/adapters/mcp/build-server.ts +++ b/packages/devframe/src/adapters/mcp/build-server.ts @@ -148,7 +148,7 @@ export async function createMcpServer( function registerToolHandlers(server: Server, ctx: DevframeNodeContext): void { server.setRequestHandler('tools/list', async () => { - const tools = await Promise.all(ctx.agent.list().tools.map(tool => projectTool(tool, ctx))) + const tools = ctx.agent.list().tools.map(tool => projectTool(tool, ctx)) return { tools } }) @@ -157,7 +157,7 @@ function registerToolHandlers(server: Server, ctx: DevframeNodeContext): void { try { const tool = ctx.agent.getTool(name) const outputSchema = tool - ? tool.outputSchema ?? await computeOutputSchema(tool, ctx) + ? tool.outputSchema ?? computeOutputSchema(tool, ctx) : undefined const result = await ctx.agent.invoke(name, args ?? {}) return { @@ -248,9 +248,9 @@ function registerResourceHandlers( }) } -async function projectTool(tool: AgentTool, ctx: DevframeNodeContext): Promise { - const inputSchema = tool.inputSchema ?? await computeInputSchema(tool, ctx) - const outputSchema = tool.outputSchema ?? await computeOutputSchema(tool, ctx) +function projectTool(tool: AgentTool, ctx: DevframeNodeContext): Tool { + const inputSchema = tool.inputSchema ?? computeInputSchema(tool, ctx) + const outputSchema = tool.outputSchema ?? computeOutputSchema(tool, ctx) return { name: tool.id, title: tool.title, @@ -265,17 +265,17 @@ async function projectTool(tool: AgentTool, ctx: DevframeNodeContext): Promise { +function computeInputSchema(tool: AgentTool, ctx: DevframeNodeContext): unknown { if (tool.kind !== 'rpc' || !tool.rpcName) return { type: 'object', properties: {} } const def = ctx.rpc.definitions.get(tool.rpcName) as RpcFunctionDefinitionAnyWithContext | undefined if (!def) return { type: 'object', properties: {} } const args = def.args as readonly StandardSchemaV1[] | undefined - return (await argsToJsonSchema(args)).schema + return argsToJsonSchema(args).schema } -async function computeOutputSchema(tool: AgentTool, ctx: DevframeNodeContext): Promise { +function computeOutputSchema(tool: AgentTool, ctx: DevframeNodeContext): unknown { if (tool.kind !== 'rpc' || !tool.rpcName) return undefined const def = ctx.rpc.definitions.get(tool.rpcName) as RpcFunctionDefinitionAnyWithContext | undefined diff --git a/packages/devframe/src/adapters/mcp/to-json-schema.ts b/packages/devframe/src/adapters/mcp/to-json-schema.ts index a3650b8f..6a678e89 100644 --- a/packages/devframe/src/adapters/mcp/to-json-schema.ts +++ b/packages/devframe/src/adapters/mcp/to-json-schema.ts @@ -1,33 +1,37 @@ -import type { StandardSchemaV1 } from '@standard-schema/spec' -import { toJsonSchema } from '@standard-community/standard-json' +import type { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec' const FALLBACK_OBJECT_SCHEMA = Object.freeze({ type: 'object', additionalProperties: true }) +/** A `~standard` prop that may also carry the Standard JSON Schema converter. */ +type MaybeJsonSchema = StandardSchemaV1['~standard'] & Partial + /** * Convert a Standard Schema to JSON Schema for the agent/MCP surface. * - * `@standard-community/standard-json` dispatches on the schema's - * `~standard` vendor (valibot, zod, arktype, …) and lazily loads that - * vendor's converter, so precise schemas require the matching converter - * to be installed (e.g. `@valibot/to-json-schema`, `zod-to-json-schema`). - * When no converter is available — or conversion fails — we degrade to a - * permissive object schema so the surface never throws and no validator is - * forced. + * Devframe stays validator-neutral, so conversion uses the schema's own + * [Standard JSON Schema](https://standardschema.dev/) converter + * (`~standard.jsonSchema`) when the validator provides one — zod 4 does, + * for example. Validators without a native converter (e.g. valibot) degrade + * to a permissive object schema rather than pulling in a converter library. */ -async function safeToJsonSchema(schema: StandardSchemaV1): Promise { - try { - return await toJsonSchema(schema) - } - catch { - return FALLBACK_OBJECT_SCHEMA +function safeToJsonSchema(schema: StandardSchemaV1): unknown { + const standard = schema['~standard'] as MaybeJsonSchema + if (standard.jsonSchema) { + try { + return standard.jsonSchema.input({ target: 'draft-2020-12' }) + } + catch { + return FALLBACK_OBJECT_SCHEMA + } } + return FALLBACK_OBJECT_SCHEMA } /** * JSON Schema for an RPC return value on the agent/MCP surface. * @internal */ -export async function returnToJsonSchema(schema: StandardSchemaV1 | undefined): Promise { +export function returnToJsonSchema(schema: StandardSchemaV1 | undefined): unknown { if (!schema) return undefined return safeToJsonSchema(schema) @@ -42,9 +46,9 @@ export async function returnToJsonSchema(schema: StandardSchemaV1 | undefined): * Returns `{ type: 'object', properties: {} }` when there are no args. * @internal */ -export async function argsToJsonSchema( +export function argsToJsonSchema( args: readonly StandardSchemaV1[] | undefined, -): Promise<{ schema: unknown, unwrapped: boolean }> { +): { schema: unknown, unwrapped: boolean } { if (!args || args.length === 0) return { schema: { type: 'object', properties: {} }, unwrapped: false } @@ -52,7 +56,7 @@ export async function argsToJsonSchema( const required: string[] = [] for (let i = 0; i < args.length; i++) { const key = `arg${i}` - properties[key] = await safeToJsonSchema(args[i]!) + properties[key] = safeToJsonSchema(args[i]!) required.push(key) } diff --git a/plugins/inspect/package.json b/plugins/inspect/package.json index 67ea89ce..22f48e75 100644 --- a/plugins/inspect/package.json +++ b/plugins/inspect/package.json @@ -56,10 +56,9 @@ } }, "dependencies": { - "@standard-community/standard-json": "catalog:deps", + "@valibot/to-json-schema": "catalog:deps", "cac": "catalog:deps", - "nostics": "catalog:deps", - "quansync": "catalog:deps" + "nostics": "catalog:deps" }, "devDependencies": { "@antfu/design": "catalog:frontend", diff --git a/plugins/inspect/src/rpc/functions/_schema.ts b/plugins/inspect/src/rpc/functions/_schema.ts index 2af2c429..0562e26e 100644 --- a/plugins/inspect/src/rpc/functions/_schema.ts +++ b/plugins/inspect/src/rpc/functions/_schema.ts @@ -1,11 +1,25 @@ -import type { StandardSchemaV1 } from '@standard-schema/spec' -import { toJsonSchema } from '@standard-community/standard-json' +import type { StandardJSONSchemaV1, StandardSchemaV1 } from '@standard-schema/spec' +import { toJsonSchema } from '@valibot/to-json-schema' const FALLBACK_SCHEMA = Object.freeze({ type: 'object', additionalProperties: true }) -async function convert(schema: unknown): Promise { +/** A `~standard` prop that may also carry the Standard JSON Schema converter. */ +type MaybeJsonSchema = StandardSchemaV1['~standard'] & Partial + +/** + * Convert a schema to JSON Schema for the inspector, vendor-neutrally. + * + * Prefers the schema's own Standard JSON Schema converter + * (`~standard.jsonSchema`, implemented by e.g. zod 4), then falls back to + * valibot's converter, then to a permissive object — so introspection never + * throws regardless of which validator produced the schema. + */ +function convert(schema: unknown): unknown { + const standard = (schema as StandardSchemaV1)['~standard'] as MaybeJsonSchema try { - return await toJsonSchema(schema as StandardSchemaV1) + if (standard.jsonSchema) + return standard.jsonSchema.input({ target: 'draft-2020-12' }) + return toJsonSchema(schema as never) } catch { return FALLBACK_SCHEMA @@ -14,14 +28,9 @@ async function convert(schema: unknown): Promise { /** * Convert an RPC return schema to JSON Schema, swallowing conversion - * failures (unsupported vendor / missing converter) into a permissive - * fallback so introspection never throws. - * - * Conversion is vendor-neutral via `@standard-community/standard-json`, - * which loads the matching per-vendor converter on demand (valibot, zod, - * arktype, …) — install the converter for the vendor you inspect. + * failures into a permissive fallback so introspection never throws. */ -export async function returnSchemaToJson(schema: unknown): Promise { +export function returnSchemaToJson(schema: unknown): unknown { if (!schema) return undefined return convert(schema) @@ -32,11 +41,11 @@ export async function returnSchemaToJson(schema: unknown): Promise { * (`type: 'array'` + `prefixItems`). Returns `undefined` when the function * declares no args. */ -export async function argsSchemaToJson(args: readonly unknown[] | undefined): Promise { +export function argsSchemaToJson(args: readonly unknown[] | undefined): unknown { if (!args || args.length === 0) return undefined return { type: 'array', - prefixItems: await Promise.all(args.map(arg => convert(arg))), + prefixItems: args.map(arg => convert(arg)), } } diff --git a/plugins/inspect/src/rpc/functions/list-functions.ts b/plugins/inspect/src/rpc/functions/list-functions.ts index f022121f..000c56af 100644 --- a/plugins/inspect/src/rpc/functions/list-functions.ts +++ b/plugins/inspect/src/rpc/functions/list-functions.ts @@ -46,8 +46,8 @@ export const listFunctions = defineInspectRpc({ hasHandler: !!fn.handler, invokable: INVOKABLE_TYPES.has(type), agent, - argsSchema: await argsSchemaToJson(fn.args as readonly unknown[] | undefined), - returnsSchema: await returnSchemaToJson(fn.returns), + argsSchema: argsSchemaToJson(fn.args as readonly unknown[] | undefined), + returnsSchema: returnSchemaToJson(fn.returns), }) } out.sort((a, b) => a.name.localeCompare(b.name)) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d1507593..9957fc33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,12 +49,12 @@ catalogs: '@modelcontextprotocol/server': specifier: ^2.0.0 version: 2.0.0 - '@standard-community/standard-json': - specifier: ^0.3.5 - version: 0.3.5 '@standard-schema/spec': specifier: ^1.1.0 version: 1.1.0 + '@valibot/to-json-schema': + specifier: ^1.7.1 + version: 1.7.1 birpc: specifier: ^4.0.0 version: 4.0.0 @@ -109,9 +109,6 @@ catalogs: perfect-debounce: specifier: ^2.1.0 version: 2.1.0 - quansync: - specifier: ^0.2.11 - version: 0.2.11 structured-clone-es: specifier: ^2.0.1 version: 2.0.1 @@ -806,9 +803,6 @@ importers: packages/devframe: dependencies: - '@standard-community/standard-json': - specifier: catalog:deps - version: 0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3)))(quansync@0.2.11)(valibot@1.4.2(typescript@6.0.3))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) '@standard-schema/spec': specifier: catalog:deps version: 1.1.0 @@ -833,9 +827,6 @@ importers: pathe: specifier: catalog:deps version: 2.0.3 - quansync: - specifier: catalog:deps - version: 0.2.11 ufo: specifier: catalog:deps version: 1.6.4 @@ -1425,18 +1416,15 @@ importers: plugins/inspect: dependencies: - '@standard-community/standard-json': + '@valibot/to-json-schema': specifier: catalog:deps - version: 0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3)))(quansync@0.2.11)(valibot@1.4.2(typescript@6.0.3))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3) + version: 1.7.1(valibot@1.4.2(typescript@6.0.3)) cac: specifier: catalog:deps version: 7.0.0 nostics: specifier: catalog:deps version: 1.2.0 - quansync: - specifier: catalog:deps - version: 0.2.11 devDependencies: '@antfu/design': specifier: catalog:frontend @@ -4297,38 +4285,6 @@ packages: '@speed-highlight/core@1.2.15': resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} - '@standard-community/standard-json@0.3.5': - resolution: {integrity: sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA==} - peerDependencies: - '@standard-schema/spec': ^1.0.0 - '@types/json-schema': ^7.0.15 - '@valibot/to-json-schema': ^1.3.0 - arktype: ^2.1.20 - effect: ^3.16.8 - quansync: ^0.2.11 - sury: ^10.0.0 - typebox: ^1.0.17 - valibot: ^1.1.0 - zod: ^3.25.0 || ^4.0.0 - zod-to-json-schema: ^3.24.5 - peerDependenciesMeta: - '@valibot/to-json-schema': - optional: true - arktype: - optional: true - effect: - optional: true - sury: - optional: true - typebox: - optional: true - valibot: - optional: true - zod: - optional: true - zod-to-json-schema: - optional: true - '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -9484,11 +9440,6 @@ packages: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} - zod-to-json-schema@3.25.2: - resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} - peerDependencies: - zod: ^3.25.28 || ^4 - zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -11837,17 +11788,6 @@ snapshots: '@speed-highlight/core@1.2.15': {} - '@standard-community/standard-json@0.3.5(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3)))(quansync@0.2.11)(valibot@1.4.2(typescript@6.0.3))(zod-to-json-schema@3.25.2(zod@4.4.3))(zod@4.4.3)': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/json-schema': 7.0.15 - quansync: 0.2.11 - optionalDependencies: - '@valibot/to-json-schema': 1.7.1(valibot@1.4.2(typescript@6.0.3)) - valibot: 1.4.2(typescript@6.0.3) - zod: 4.4.3 - zod-to-json-schema: 3.25.2(zod@4.4.3) - '@standard-schema/spec@1.1.0': {} '@storybook/addon-a11y@10.5.5(storybook@10.5.5(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))': @@ -12615,7 +12555,6 @@ snapshots: '@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3))': dependencies: valibot: 1.4.2(typescript@6.0.3) - optional: true '@vercel/nft@1.5.0(rollup@4.60.3)(supports-color@10.2.2)': dependencies: @@ -17981,11 +17920,6 @@ snapshots: compress-commons: 6.0.2 readable-stream: 4.7.0 - zod-to-json-schema@3.25.2(zod@4.4.3): - dependencies: - zod: 4.4.3 - optional: true - zod@4.4.3: {} zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d4c34628..22fa7cb4 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -74,8 +74,8 @@ catalogs: '@json-render/core': ^0.19.0 '@modelcontextprotocol/client': ^2.0.0 '@modelcontextprotocol/server': ^2.0.0 - '@standard-community/standard-json': ^0.3.5 '@standard-schema/spec': ^1.1.0 + '@valibot/to-json-schema': ^1.7.1 birpc: ^4.0.0 cac: ^7.0.0 chokidar: ^5.0.0 @@ -96,7 +96,6 @@ catalogs: parse5: ^8.0.1 pathe: ^2.0.3 perfect-debounce: ^2.1.0 - quansync: ^0.2.11 structured-clone-es: ^2.0.1 tinyexec: ^1.2.4 tinyglobby: ^0.2.17 diff --git a/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts index 0c20b598..a70426f4 100644 --- a/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts @@ -20,10 +20,10 @@ export declare const alwaysFunctions: readonly [{ args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe/rpc").RpcFunctionAgentOptions; + setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string) => void) | undefined; - dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -34,10 +34,10 @@ export declare const alwaysFunctions: readonly [{ args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe/rpc").RpcFunctionAgentOptions; + setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string) => void) | undefined; - dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -254,8 +254,8 @@ export declare const readFunctions: readonly [{ mtime: number; }[]>; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }[], import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + } | null, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap, import("devframe/utils/simple-schema").SimpleSchema]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe/rpc").RpcFunctionAgentOptions; + setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string, args_1: number | undefined) => string | null) | undefined; - dump?: import("devframe/rpc").RpcDump<[string, number | undefined], string | null, import("devframe").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string, number | undefined], string | null, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -360,8 +360,8 @@ export declare const readFunctions: readonly [{ uploadExtensions: string[] | "*"; }>; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable>) | undefined; @@ -372,7 +372,7 @@ export declare const readFunctions: readonly [{ dump?: import("devframe/rpc").RpcDump<[], { write: boolean; uploadExtensions: string[] | "*"; - }, import("devframe").DevframeNodeContext> | undefined; + }, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }[], import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + } | null, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap, import("devframe/utils/simple-schema").SimpleSchema]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe/rpc").RpcFunctionAgentOptions; + setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string, args_1: number | undefined) => string | null) | undefined; - dump?: import("devframe/rpc").RpcDump<[string, number | undefined], string | null, import("devframe").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string, number | undefined], string | null, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -659,8 +659,8 @@ export declare const serverFunctions: readonly [{ uploadExtensions: string[] | "*"; }>; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable>) | undefined; @@ -671,7 +671,7 @@ export declare const serverFunctions: readonly [{ dump?: import("devframe/rpc").RpcDump<[], { write: boolean; uploadExtensions: string[] | "*"; - }, import("devframe").DevframeNodeContext> | undefined; + }, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe/rpc").RpcFunctionAgentOptions; + setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string) => void) | undefined; - dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -702,10 +702,10 @@ export declare const serverFunctions: readonly [{ args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe/rpc").RpcFunctionAgentOptions; + setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string) => void) | undefined; - dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -724,8 +724,8 @@ export declare const serverFunctions: readonly [{ uploadId: string; }>; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: { @@ -890,7 +890,7 @@ export declare const serverFunctions: readonly [{ }) => void) | undefined; dump?: import("devframe/rpc").RpcDump<[{ path: string; - }], void, import("devframe").DevframeNodeContext> | undefined; + }], void, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe").RpcFunctionAgentOptions; - setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: { @@ -1124,7 +1124,7 @@ export declare const writeFunctions: readonly [{ }) => void) | undefined; dump?: import("devframe/rpc").RpcDump<[{ path: string; - }], void, import("devframe").DevframeNodeContext> | undefined; + }], void, import("devframe/types").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap Date: Mon, 3 Aug 2026 01:46:42 +0000 Subject: [PATCH 8/8] test: regenerate plugin-assets rpc api snapshot from a clean build The snapshot was previously updated from an incremental dist that rendered multi-entry types via subpath specifiers (devframe/rpc, devframe/types); a clean build (as CI does) canonicalizes them to the bare devframe entry. Regenerated to match CI. --- .../plugin-assets/rpc.snapshot.d.ts | 120 +++++++++--------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts index a70426f4..0c20b598 100644 --- a/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/@devframes/plugin-assets/rpc.snapshot.d.ts @@ -20,10 +20,10 @@ export declare const alwaysFunctions: readonly [{ args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe").RpcFunctionAgentOptions; + setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string) => void) | undefined; - dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe/types").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -34,10 +34,10 @@ export declare const alwaysFunctions: readonly [{ args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe").RpcFunctionAgentOptions; + setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string) => void) | undefined; - dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe/types").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -254,8 +254,8 @@ export declare const readFunctions: readonly [{ mtime: number; }[]>; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }[], import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + } | null, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap, import("devframe/utils/simple-schema").SimpleSchema]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe").RpcFunctionAgentOptions; + setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string, args_1: number | undefined) => string | null) | undefined; - dump?: import("devframe/rpc").RpcDump<[string, number | undefined], string | null, import("devframe/types").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string, number | undefined], string | null, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -360,8 +360,8 @@ export declare const readFunctions: readonly [{ uploadExtensions: string[] | "*"; }>; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable>) | undefined; @@ -372,7 +372,7 @@ export declare const readFunctions: readonly [{ dump?: import("devframe/rpc").RpcDump<[], { write: boolean; uploadExtensions: string[] | "*"; - }, import("devframe/types").DevframeNodeContext> | undefined; + }, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }[], import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + } | null, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap, import("devframe/utils/simple-schema").SimpleSchema]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe").RpcFunctionAgentOptions; + setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string, args_1: number | undefined) => string | null) | undefined; - dump?: import("devframe/rpc").RpcDump<[string, number | undefined], string | null, import("devframe/types").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string, number | undefined], string | null, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -659,8 +659,8 @@ export declare const serverFunctions: readonly [{ uploadExtensions: string[] | "*"; }>; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable>) | undefined; @@ -671,7 +671,7 @@ export declare const serverFunctions: readonly [{ dump?: import("devframe/rpc").RpcDump<[], { write: boolean; uploadExtensions: string[] | "*"; - }, import("devframe/types").DevframeNodeContext> | undefined; + }, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe").RpcFunctionAgentOptions; + setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string) => void) | undefined; - dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe/types").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -702,10 +702,10 @@ export declare const serverFunctions: readonly [{ args: readonly [import("devframe/utils/simple-schema").SimpleSchema]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; + agent?: import("devframe").RpcFunctionAgentOptions; + setup?: ((context: import("devframe").DevframeNodeContext) => import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: string) => void) | undefined; - dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe/types").DevframeNodeContext> | undefined; + dump?: import("devframe/rpc").RpcDump<[string], void, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap>> | undefined; __promise?: import("devframe/rpc").Thenable> | undefined; @@ -724,8 +724,8 @@ export declare const serverFunctions: readonly [{ uploadId: string; }>; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: { @@ -890,7 +890,7 @@ export declare const serverFunctions: readonly [{ }) => void) | undefined; dump?: import("devframe/rpc").RpcDump<[{ path: string; - }], void, import("devframe/types").DevframeNodeContext> | undefined; + }], void, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable | undefined; + }, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap]; returns: import("devframe/utils/simple-schema").SimpleSchema; jsonSerializable?: boolean; - agent?: import("devframe/rpc").RpcFunctionAgentOptions; - setup?: ((context: import("devframe/types").DevframeNodeContext) => import("devframe/rpc").Thenable import("devframe/rpc").Thenable>) | undefined; handler?: ((args_0: { @@ -1124,7 +1124,7 @@ export declare const writeFunctions: readonly [{ }) => void) | undefined; dump?: import("devframe/rpc").RpcDump<[{ path: string; - }], void, import("devframe/types").DevframeNodeContext> | undefined; + }], void, import("devframe").DevframeNodeContext> | undefined; snapshot?: boolean; __cache?: WeakMap