@next-ai-ready/actions 0.1.0-alpha.5 → 0.1.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @next-ai-ready/actions
2
2
 
3
- defineAction registry, Zod validation, schema emission.
3
+ Capability plane: define, register, and invoke typed actions.
4
4
 
5
- Part of [next-ai-ready](../../README.md). 🚧 Pre-alpha.
5
+ - `defineAction()` / `defineActions()` Zod v4 schemas + handler
6
+ - `invokeAction()` — HTTP/MCP entry with auth + validation (ADR-010)
7
+ - `buildActionsManifest()` — strip handlers for static JSON
8
+
9
+ Part of [next-ai-ready](../../README.md). Pre-alpha.
package/dist/index.d.ts CHANGED
@@ -43,6 +43,12 @@ declare function clearRegistry(): void;
43
43
  */
44
44
  declare function defineActions<A extends ReadonlyArray<AnyAction>>(actions: A): A;
45
45
 
46
+ /**
47
+ * Returns true when `schema` looks like a Zod v4 schema (C-50).
48
+ * Uses structural checks instead of relying solely on `_def`.
49
+ * Zod v3 has `_def`, Zod v4 has `_zod`.
50
+ */
51
+ declare function isZodSchema(schema: SchemaLike): boolean;
46
52
  /**
47
53
  * Convert a Zod schema to JSON Schema 2020-12.
48
54
  *
@@ -105,4 +111,4 @@ type InvokeResult<O = unknown> = InvokeResultOk<O> | InvokeResultErr;
105
111
  */
106
112
  declare function invokeAction(name: string, rawInput: unknown, ctxOrRequest: ActionContext | Request): Promise<InvokeResult>;
107
113
 
108
- export { type AnyAction, type InvokeResult, type InvokeResultErr, type InvokeResultOk, buildActionContext, buildActionsManifest, clearRegistry, defineAction, defineActions, getAction, invokeAction, listActions, registerActions, schemaToJsonSchema };
114
+ export { type AnyAction, type InvokeResult, type InvokeResultErr, type InvokeResultOk, buildActionContext, buildActionsManifest, clearRegistry, defineAction, defineActions, getAction, invokeAction, isZodSchema, listActions, registerActions, schemaToJsonSchema };
package/dist/index.js CHANGED
@@ -34,19 +34,21 @@ function defineActions(actions) {
34
34
 
35
35
  // src/schema.ts
36
36
  import { z } from "zod";
37
+ function isZodSchema(schema) {
38
+ if (typeof schema !== "object" || schema === null) return false;
39
+ if (typeof schema.safeParse !== "function" || typeof schema.parse !== "function") return false;
40
+ return "_zod" in schema;
41
+ }
37
42
  function schemaToJsonSchema(schema) {
38
43
  if (!isZodSchema(schema)) {
39
44
  throw new Error(
40
- "[next-ai-ready] Only Zod schemas are currently supported for action input/output. If you need another validator, please open an issue."
45
+ "[next-ai-ready] Only Zod v4 schemas are supported for action input/output. Install `zod@^4` and define actions with `z.object(...)` etc. Zod v3 is not supported."
41
46
  );
42
47
  }
43
48
  const json = z.toJSONSchema(schema);
44
49
  if ("$schema" in json) delete json.$schema;
45
50
  return json;
46
51
  }
47
- function isZodSchema(s) {
48
- return typeof s === "object" && s !== null && "_def" in s;
49
- }
50
52
 
51
53
  // src/manifest.ts
52
54
  function buildActionsManifest() {
@@ -68,6 +70,7 @@ function toEntry(def) {
68
70
  }
69
71
 
70
72
  // src/invoke.ts
73
+ import { z as z2 } from "zod";
71
74
  function buildActionContext(request, extras = {}) {
72
75
  const headers = request.headers;
73
76
  return {
@@ -132,6 +135,7 @@ async function invokeAction(name, rawInput, ctxOrRequest) {
132
135
  }
133
136
  }
134
137
  function extractZodIssues(err) {
138
+ if (err instanceof z2.ZodError) return err.issues;
135
139
  if (err && typeof err === "object" && "issues" in err) {
136
140
  return err.issues;
137
141
  }
@@ -145,6 +149,7 @@ export {
145
149
  defineActions,
146
150
  getAction,
147
151
  invokeAction,
152
+ isZodSchema,
148
153
  listActions,
149
154
  registerActions,
150
155
  schemaToJsonSchema
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/define-action.ts","../src/registry.ts","../src/schema.ts","../src/manifest.ts","../src/invoke.ts"],"sourcesContent":["import type { ActionDefinition, SchemaLike } from \"@next-ai-ready/core\";\n\n/**\n * Author an action.\n *\n * ```ts\n * import { defineAction } from \"@next-ai-ready/actions\"\n * import { z } from \"zod\"\n *\n * export const search = defineAction({\n * name: \"search_products\",\n * description: \"Full-text search across the product catalogue.\",\n * whenToUse: \"When the user asks to find a product by name or feature.\",\n * public: true,\n * input: z.object({ query: z.string().min(1), limit: z.number().int().max(50).optional() }),\n * output: z.object({ items: z.array(z.object({ id: z.string(), title: z.string() })) }),\n * handler: async ({ query, limit = 10 }) => ({ items: await search(query, limit) }),\n * })\n * ```\n *\n * Identity-typed: validation and registry insertion happen in `registry.ts`\n * (called by `defineActions()`), not here. This keeps `defineAction` a\n * compile-time-only construct so it tree-shakes cleanly.\n */\nexport function defineAction<I, O>(def: ActionDefinition<I, O>): ActionDefinition<I, O> {\n if (!/^[a-z][a-z0-9_]*$/.test(def.name)) {\n throw new Error(\n `[next-ai-ready] Action name \"${def.name}\" must be snake_case (lowercase letters, digits, underscores).`,\n );\n }\n return def;\n}\n\n/** Re-export for downstream packages that don't want to depend on core directly. */\nexport type { ActionDefinition, SchemaLike };\n","import type { ActionDefinition } from \"@next-ai-ready/core\";\n\n/**\n * Type-erased action — the storage type used everywhere we no longer have\n * the user's input/output generics in scope. `unknown` is wrong here because\n * the handler is contravariant in its input parameter; `any` is the only\n * sound erasure that lets `ActionDefinition<X, Y>` widen on insertion.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyAction = ActionDefinition<any, any>;\n\n/**\n * Process-global action registry.\n *\n * The user's `actions/index.ts` is imported once per process (by the build\n * CLI at build time, and by each `/api/actions/<name>` route at runtime).\n * It calls `registerActions(...)` to populate this singleton.\n *\n * We do NOT auto-register from `defineAction` itself — that would couple\n * authoring to a singleton and break tree-shaking. Authors collect their\n * actions into an array and pass them here exactly once.\n */\nconst registry = new Map<string, AnyAction>();\n\nexport function registerActions(actions: ReadonlyArray<AnyAction>): void {\n for (const action of actions) {\n if (registry.has(action.name)) {\n throw new Error(`[next-ai-ready] Duplicate action name: \"${action.name}\"`);\n }\n registry.set(action.name, action);\n }\n}\n\nexport function getAction(name: string): AnyAction | undefined {\n return registry.get(name);\n}\n\nexport function listActions(): AnyAction[] {\n return [...registry.values()].sort((a, b) => a.name.localeCompare(b.name));\n}\n\nexport function clearRegistry(): void {\n registry.clear();\n}\n\n/**\n * Convenience for users: `defineActions([...])` registers and returns the\n * array, so the same module can be both imported for side-effects (runtime)\n * and inspected (build CLI dynamic import).\n */\nexport function defineActions<A extends ReadonlyArray<AnyAction>>(actions: A): A {\n registerActions(actions);\n return actions;\n}\n","import { z } from \"zod\";\nimport type { SchemaLike } from \"@next-ai-ready/core\";\n\n/**\n * Convert a Zod schema to JSON Schema 2020-12.\n *\n * Implementation note: we use Zod v4's built-in `z.toJSONSchema()`. We do\n * NOT depend on the older `zod-to-json-schema` package — it targets Zod v3\n * and silently emits empty objects when fed Zod v4 schemas. If a user passes\n * a non-Zod `SchemaLike`, we fail loudly so build artifacts can never be\n * silently empty.\n *\n * The output is OpenAPI 3.1 / JSON Schema 2020-12 compatible. We strip the\n * `$schema` header since each action gets inlined into a larger document.\n */\nexport function schemaToJsonSchema(schema: SchemaLike): Record<string, unknown> {\n if (!isZodSchema(schema)) {\n throw new Error(\n \"[next-ai-ready] Only Zod schemas are currently supported for action input/output. \" +\n \"If you need another validator, please open an issue.\",\n );\n }\n const json = z.toJSONSchema(schema as unknown as Parameters<typeof z.toJSONSchema>[0]);\n if (\"$schema\" in json) delete (json as Record<string, unknown>).$schema;\n return json as Record<string, unknown>;\n}\n\nfunction isZodSchema(s: SchemaLike): boolean {\n return typeof s === \"object\" && s !== null && \"_def\" in s;\n}\n","import type {\n ActionDefinition,\n ActionManifestEntry,\n ActionsManifest,\n} from \"@next-ai-ready/core\";\nimport { schemaToJsonSchema } from \"./schema.js\";\nimport { listActions } from \"./registry.js\";\n\n/**\n * Serialize the current registry into an `ActionsManifest`.\n *\n * The manifest is the contract between build-time and emission-time tools\n * (OpenAPI, MCP, llms.txt action callouts). It contains *no functions* —\n * just metadata + JSON Schemas — so it's safe to bundle into a serverless\n * function or ship to a CDN.\n */\nexport function buildActionsManifest(): ActionsManifest {\n const entries = listActions()\n .map(toEntry)\n .sort((a, b) => a.name.localeCompare(b.name));\n return { actions: entries, generatedAt: new Date().toISOString() };\n}\n\nfunction toEntry(def: ActionDefinition<unknown, unknown>): ActionManifestEntry {\n return {\n name: def.name,\n description: def.description,\n whenToUse: def.whenToUse,\n whenNotToUse: def.whenNotToUse,\n tags: def.tags,\n public: def.public === true,\n inputSchema: schemaToJsonSchema(def.input),\n outputSchema: def.output ? schemaToJsonSchema(def.output) : undefined,\n examples: def.examples,\n };\n}\n","import type { ActionContext } from \"@next-ai-ready/core\";\nimport { getAction } from \"./registry.js\";\n\n/**\n * Build a full `ActionContext` from a Fetch API `Request`. Exported because\n * the Next route handler also needs to construct one before calling into\n * userland — keeping the construction logic in one place avoids drift.\n */\nexport function buildActionContext(request: Request, extras: { caller?: string } = {}): ActionContext {\n const headers = request.headers;\n return {\n request,\n headers,\n cookies: {\n get(name: string) {\n const raw = headers.get(\"cookie\") ?? \"\";\n for (const part of raw.split(/;\\s*/)) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n if (decodeURIComponent(part.slice(0, eq)) === name) {\n return { value: decodeURIComponent(part.slice(eq + 1)) };\n }\n }\n return undefined;\n },\n },\n caller: extras.caller,\n };\n}\n\nexport interface InvokeResultOk<O = unknown> {\n ok: true;\n data: O;\n action: string;\n latencyMs: number;\n}\nexport interface InvokeResultErr {\n ok: false;\n action: string;\n latencyMs: number;\n status: number;\n code: \"not_found\" | \"not_public\" | \"unauthorized\" | \"invalid_input\" | \"handler_error\";\n message: string;\n details?: unknown;\n}\nexport type InvokeResult<O = unknown> = InvokeResultOk<O> | InvokeResultErr;\n\n/**\n * Resolve + validate + call one action by name.\n *\n * This is the heart of the Capability plane. The runtime route handler in\n * `@next-ai-ready/next` is a thin wrapper around this — it parses the\n * Request body and turns `InvokeResult` into a `Response`.\n *\n * Security rules (ADR-010):\n * • Action MUST have `public: true` to be reachable.\n * • If `auth` is defined, it MUST return truthy.\n * • Input MUST pass `safeParse`. We never throw raw zod errors back.\n */\nexport async function invokeAction(\n name: string,\n rawInput: unknown,\n ctxOrRequest: ActionContext | Request,\n): Promise<InvokeResult> {\n const ctx: ActionContext = ctxOrRequest instanceof Request ? buildActionContext(ctxOrRequest) : ctxOrRequest;\n const t0 = Date.now();\n const action = getAction(name);\n if (!action) {\n return { ok: false, action: name, latencyMs: 0, status: 404, code: \"not_found\", message: `Unknown action: ${name}` };\n }\n if (!action.public) {\n return { ok: false, action: name, latencyMs: 0, status: 404, code: \"not_public\", message: `Action \"${name}\" is not exposed.` };\n }\n if (action.auth) {\n const allowed = await action.auth(ctx.request);\n if (!allowed) {\n return { ok: false, action: name, latencyMs: Date.now() - t0, status: 401, code: \"unauthorized\", message: \"Unauthorized.\" };\n }\n }\n const parsed = action.input.safeParse(rawInput);\n if (!parsed.success) {\n return {\n ok: false,\n action: name,\n latencyMs: Date.now() - t0,\n status: 400,\n code: \"invalid_input\",\n message: \"Invalid input.\",\n details: extractZodIssues(parsed.error),\n };\n }\n try {\n const data = await action.handler(parsed.data, ctx);\n return { ok: true, action: name, latencyMs: Date.now() - t0, data };\n } catch (err) {\n return {\n ok: false,\n action: name,\n latencyMs: Date.now() - t0,\n status: 500,\n code: \"handler_error\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nfunction extractZodIssues(err: unknown): unknown {\n if (err && typeof err === \"object\" && \"issues\" in err) {\n return (err as { issues: unknown }).issues;\n }\n return undefined;\n}\n"],"mappings":";AAwBO,SAAS,aAAmB,KAAqD;AACtF,MAAI,CAAC,oBAAoB,KAAK,IAAI,IAAI,GAAG;AACvC,UAAM,IAAI;AAAA,MACR,gCAAgC,IAAI,IAAI;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AACT;;;ACTA,IAAM,WAAW,oBAAI,IAAuB;AAErC,SAAS,gBAAgB,SAAyC;AACvE,aAAW,UAAU,SAAS;AAC5B,QAAI,SAAS,IAAI,OAAO,IAAI,GAAG;AAC7B,YAAM,IAAI,MAAM,2CAA2C,OAAO,IAAI,GAAG;AAAA,IAC3E;AACA,aAAS,IAAI,OAAO,MAAM,MAAM;AAAA,EAClC;AACF;AAEO,SAAS,UAAU,MAAqC;AAC7D,SAAO,SAAS,IAAI,IAAI;AAC1B;AAEO,SAAS,cAA2B;AACzC,SAAO,CAAC,GAAG,SAAS,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAC3E;AAEO,SAAS,gBAAsB;AACpC,WAAS,MAAM;AACjB;AAOO,SAAS,cAAkD,SAAe;AAC/E,kBAAgB,OAAO;AACvB,SAAO;AACT;;;ACrDA,SAAS,SAAS;AAeX,SAAS,mBAAmB,QAA6C;AAC9E,MAAI,CAAC,YAAY,MAAM,GAAG;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,QAAM,OAAO,EAAE,aAAa,MAAyD;AACrF,MAAI,aAAa,KAAM,QAAQ,KAAiC;AAChE,SAAO;AACT;AAEA,SAAS,YAAY,GAAwB;AAC3C,SAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,UAAU;AAC1D;;;ACbO,SAAS,uBAAwC;AACtD,QAAM,UAAU,YAAY,EACzB,IAAI,OAAO,EACX,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAC9C,SAAO,EAAE,SAAS,SAAS,cAAa,oBAAI,KAAK,GAAE,YAAY,EAAE;AACnE;AAEA,SAAS,QAAQ,KAA8D;AAC7E,SAAO;AAAA,IACL,MAAM,IAAI;AAAA,IACV,aAAa,IAAI;AAAA,IACjB,WAAW,IAAI;AAAA,IACf,cAAc,IAAI;AAAA,IAClB,MAAM,IAAI;AAAA,IACV,QAAQ,IAAI,WAAW;AAAA,IACvB,aAAa,mBAAmB,IAAI,KAAK;AAAA,IACzC,cAAc,IAAI,SAAS,mBAAmB,IAAI,MAAM,IAAI;AAAA,IAC5D,UAAU,IAAI;AAAA,EAChB;AACF;;;AC3BO,SAAS,mBAAmB,SAAkB,SAA8B,CAAC,GAAkB;AACpG,QAAM,UAAU,QAAQ;AACxB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS;AAAA,MACP,IAAI,MAAc;AAChB,cAAM,MAAM,QAAQ,IAAI,QAAQ,KAAK;AACrC,mBAAW,QAAQ,IAAI,MAAM,MAAM,GAAG;AACpC,gBAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,cAAI,KAAK,EAAG;AACZ,cAAI,mBAAmB,KAAK,MAAM,GAAG,EAAE,CAAC,MAAM,MAAM;AAClD,mBAAO,EAAE,OAAO,mBAAmB,KAAK,MAAM,KAAK,CAAC,CAAC,EAAE;AAAA,UACzD;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,QAAQ,OAAO;AAAA,EACjB;AACF;AA+BA,eAAsB,aACpB,MACA,UACA,cACuB;AACvB,QAAM,MAAqB,wBAAwB,UAAU,mBAAmB,YAAY,IAAI;AAChG,QAAM,KAAK,KAAK,IAAI;AACpB,QAAM,SAAS,UAAU,IAAI;AAC7B,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,IAAI,OAAO,QAAQ,MAAM,WAAW,GAAG,QAAQ,KAAK,MAAM,aAAa,SAAS,mBAAmB,IAAI,GAAG;AAAA,EACrH;AACA,MAAI,CAAC,OAAO,QAAQ;AAClB,WAAO,EAAE,IAAI,OAAO,QAAQ,MAAM,WAAW,GAAG,QAAQ,KAAK,MAAM,cAAc,SAAS,WAAW,IAAI,oBAAoB;AAAA,EAC/H;AACA,MAAI,OAAO,MAAM;AACf,UAAM,UAAU,MAAM,OAAO,KAAK,IAAI,OAAO;AAC7C,QAAI,CAAC,SAAS;AACZ,aAAO,EAAE,IAAI,OAAO,QAAQ,MAAM,WAAW,KAAK,IAAI,IAAI,IAAI,QAAQ,KAAK,MAAM,gBAAgB,SAAS,gBAAgB;AAAA,IAC5H;AAAA,EACF;AACA,QAAM,SAAS,OAAO,MAAM,UAAU,QAAQ;AAC9C,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW,KAAK,IAAI,IAAI;AAAA,MACxB,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS,iBAAiB,OAAO,KAAK;AAAA,IACxC;AAAA,EACF;AACA,MAAI;AACF,UAAM,OAAO,MAAM,OAAO,QAAQ,OAAO,MAAM,GAAG;AAClD,WAAO,EAAE,IAAI,MAAM,QAAQ,MAAM,WAAW,KAAK,IAAI,IAAI,IAAI,KAAK;AAAA,EACpE,SAAS,KAAK;AACZ,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW,KAAK,IAAI,IAAI;AAAA,MACxB,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IAC1D;AAAA,EACF;AACF;AAEA,SAAS,iBAAiB,KAAuB;AAC/C,MAAI,OAAO,OAAO,QAAQ,YAAY,YAAY,KAAK;AACrD,WAAQ,IAA4B;AAAA,EACtC;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/define-action.ts","../src/registry.ts","../src/schema.ts","../src/manifest.ts","../src/invoke.ts"],"sourcesContent":["import type { ActionDefinition, SchemaLike } from \"@next-ai-ready/core\";\n\n/**\n * Author an action.\n *\n * ```ts\n * import { defineAction } from \"@next-ai-ready/actions\"\n * import { z } from \"zod\"\n *\n * export const search = defineAction({\n * name: \"search_products\",\n * description: \"Full-text search across the product catalogue.\",\n * whenToUse: \"When the user asks to find a product by name or feature.\",\n * public: true,\n * input: z.object({ query: z.string().min(1), limit: z.number().int().max(50).optional() }),\n * output: z.object({ items: z.array(z.object({ id: z.string(), title: z.string() })) }),\n * handler: async ({ query, limit = 10 }) => ({ items: await search(query, limit) }),\n * })\n * ```\n *\n * Identity-typed: validation and registry insertion happen in `registry.ts`\n * (called by `defineActions()`), not here. This keeps `defineAction` a\n * compile-time-only construct so it tree-shakes cleanly.\n */\nexport function defineAction<I, O>(def: ActionDefinition<I, O>): ActionDefinition<I, O> {\n if (!/^[a-z][a-z0-9_]*$/.test(def.name)) {\n throw new Error(\n `[next-ai-ready] Action name \"${def.name}\" must be snake_case (lowercase letters, digits, underscores).`,\n );\n }\n return def;\n}\n\n/** Re-export for downstream packages that don't want to depend on core directly. */\nexport type { ActionDefinition, SchemaLike };\n","import type { ActionDefinition } from \"@next-ai-ready/core\";\n\n/**\n * Type-erased action — the storage type used everywhere we no longer have\n * the user's input/output generics in scope. `unknown` is wrong here because\n * the handler is contravariant in its input parameter; `any` is the only\n * sound erasure that lets `ActionDefinition<X, Y>` widen on insertion.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyAction = ActionDefinition<any, any>;\n\n/**\n * Process-global action registry.\n *\n * The user's `actions/index.ts` is imported once per process (by the build\n * CLI at build time, and by each `/api/actions/<name>` route at runtime).\n * It calls `registerActions(...)` to populate this singleton.\n *\n * We do NOT auto-register from `defineAction` itself — that would couple\n * authoring to a singleton and break tree-shaking. Authors collect their\n * actions into an array and pass them here exactly once.\n */\nconst registry = new Map<string, AnyAction>();\n\nexport function registerActions(actions: ReadonlyArray<AnyAction>): void {\n for (const action of actions) {\n if (registry.has(action.name)) {\n throw new Error(`[next-ai-ready] Duplicate action name: \"${action.name}\"`);\n }\n registry.set(action.name, action);\n }\n}\n\nexport function getAction(name: string): AnyAction | undefined {\n return registry.get(name);\n}\n\nexport function listActions(): AnyAction[] {\n return [...registry.values()].sort((a, b) => a.name.localeCompare(b.name));\n}\n\nexport function clearRegistry(): void {\n registry.clear();\n}\n\n/**\n * Convenience for users: `defineActions([...])` registers and returns the\n * array, so the same module can be both imported for side-effects (runtime)\n * and inspected (build CLI dynamic import).\n */\nexport function defineActions<A extends ReadonlyArray<AnyAction>>(actions: A): A {\n registerActions(actions);\n return actions;\n}\n","import { z } from \"zod\";\nimport type { SchemaLike } from \"@next-ai-ready/core\";\n\n/**\n * Returns true when `schema` looks like a Zod v4 schema (C-50).\n * Uses structural checks instead of relying solely on `_def`.\n * Zod v3 has `_def`, Zod v4 has `_zod`.\n */\nexport function isZodSchema(schema: SchemaLike): boolean {\n if (typeof schema !== \"object\" || schema === null) return false;\n if (typeof schema.safeParse !== \"function\" || typeof schema.parse !== \"function\") return false;\n // Zod v4 uses `_zod`, v3 uses `_def` — only v4 is supported.\n return \"_zod\" in schema;\n}\n\n/**\n * Convert a Zod schema to JSON Schema 2020-12.\n *\n * Implementation note: we use Zod v4's built-in `z.toJSONSchema()`. We do\n * NOT depend on the older `zod-to-json-schema` package — it targets Zod v3\n * and silently emits empty objects when fed Zod v4 schemas. If a user passes\n * a non-Zod `SchemaLike`, we fail loudly so build artifacts can never be\n * silently empty.\n *\n * The output is OpenAPI 3.1 / JSON Schema 2020-12 compatible. We strip the\n * `$schema` header since each action gets inlined into a larger document.\n */\nexport function schemaToJsonSchema(schema: SchemaLike): Record<string, unknown> {\n if (!isZodSchema(schema)) {\n throw new Error(\n \"[next-ai-ready] Only Zod v4 schemas are supported for action input/output. \" +\n \"Install `zod@^4` and define actions with `z.object(...)` etc. Zod v3 is not supported.\",\n );\n }\n const json = z.toJSONSchema(schema as unknown as Parameters<typeof z.toJSONSchema>[0]);\n if (\"$schema\" in json) delete (json as Record<string, unknown>).$schema;\n return json as Record<string, unknown>;\n}\n","import type {\n ActionDefinition,\n ActionManifestEntry,\n ActionsManifest,\n} from \"@next-ai-ready/core\";\nimport { schemaToJsonSchema } from \"./schema.js\";\nimport { listActions } from \"./registry.js\";\n\n/**\n * Serialize the current registry into an `ActionsManifest`.\n *\n * The manifest is the contract between build-time and emission-time tools\n * (OpenAPI, MCP, llms.txt action callouts). It contains *no functions* —\n * just metadata + JSON Schemas — so it's safe to bundle into a serverless\n * function or ship to a CDN.\n */\nexport function buildActionsManifest(): ActionsManifest {\n const entries = listActions()\n .map(toEntry)\n .sort((a, b) => a.name.localeCompare(b.name));\n return { actions: entries, generatedAt: new Date().toISOString() };\n}\n\nfunction toEntry(def: ActionDefinition<unknown, unknown>): ActionManifestEntry {\n return {\n name: def.name,\n description: def.description,\n whenToUse: def.whenToUse,\n whenNotToUse: def.whenNotToUse,\n tags: def.tags,\n public: def.public === true,\n inputSchema: schemaToJsonSchema(def.input),\n outputSchema: def.output ? schemaToJsonSchema(def.output) : undefined,\n examples: def.examples,\n };\n}\n","import { z } from \"zod\";\nimport type { ActionContext } from \"@next-ai-ready/core\";\nimport { getAction } from \"./registry.js\";\n\n/**\n * Build a full `ActionContext` from a Fetch API `Request`. Exported because\n * the Next route handler also needs to construct one before calling into\n * userland — keeping the construction logic in one place avoids drift.\n */\nexport function buildActionContext(request: Request, extras: { caller?: string } = {}): ActionContext {\n const headers = request.headers;\n return {\n request,\n headers,\n cookies: {\n get(name: string) {\n const raw = headers.get(\"cookie\") ?? \"\";\n for (const part of raw.split(/;\\s*/)) {\n const eq = part.indexOf(\"=\");\n if (eq < 0) continue;\n if (decodeURIComponent(part.slice(0, eq)) === name) {\n return { value: decodeURIComponent(part.slice(eq + 1)) };\n }\n }\n return undefined;\n },\n },\n caller: extras.caller,\n };\n}\n\nexport interface InvokeResultOk<O = unknown> {\n ok: true;\n data: O;\n action: string;\n latencyMs: number;\n}\nexport interface InvokeResultErr {\n ok: false;\n action: string;\n latencyMs: number;\n status: number;\n code: \"not_found\" | \"not_public\" | \"unauthorized\" | \"invalid_input\" | \"handler_error\";\n message: string;\n details?: unknown;\n}\nexport type InvokeResult<O = unknown> = InvokeResultOk<O> | InvokeResultErr;\n\n/**\n * Resolve + validate + call one action by name.\n *\n * This is the heart of the Capability plane. The runtime route handler in\n * `@next-ai-ready/next` is a thin wrapper around this — it parses the\n * Request body and turns `InvokeResult` into a `Response`.\n *\n * Security rules (ADR-010):\n * • Action MUST have `public: true` to be reachable.\n * • If `auth` is defined, it MUST return truthy.\n * • Input MUST pass `safeParse`. We never throw raw zod errors back.\n */\nexport async function invokeAction(\n name: string,\n rawInput: unknown,\n ctxOrRequest: ActionContext | Request,\n): Promise<InvokeResult> {\n const ctx: ActionContext = ctxOrRequest instanceof Request ? buildActionContext(ctxOrRequest) : ctxOrRequest;\n const t0 = Date.now();\n const action = getAction(name);\n if (!action) {\n return { ok: false, action: name, latencyMs: 0, status: 404, code: \"not_found\", message: `Unknown action: ${name}` };\n }\n if (!action.public) {\n return { ok: false, action: name, latencyMs: 0, status: 404, code: \"not_public\", message: `Action \"${name}\" is not exposed.` };\n }\n if (action.auth) {\n const allowed = await action.auth(ctx.request);\n if (!allowed) {\n return { ok: false, action: name, latencyMs: Date.now() - t0, status: 401, code: \"unauthorized\", message: \"Unauthorized.\" };\n }\n }\n const parsed = action.input.safeParse(rawInput);\n if (!parsed.success) {\n return {\n ok: false,\n action: name,\n latencyMs: Date.now() - t0,\n status: 400,\n code: \"invalid_input\",\n message: \"Invalid input.\",\n details: extractZodIssues(parsed.error),\n };\n }\n try {\n const data = await action.handler(parsed.data, ctx);\n return { ok: true, action: name, latencyMs: Date.now() - t0, data };\n } catch (err) {\n return {\n ok: false,\n action: name,\n latencyMs: Date.now() - t0,\n status: 500,\n code: \"handler_error\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nfunction extractZodIssues(err: unknown): unknown {\n if (err instanceof z.ZodError) return err.issues;\n if (err && typeof err === \"object\" && \"issues\" in err) {\n return (err as { issues: unknown }).issues;\n }\n return undefined;\n}\n"],"mappings":"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","names":["z","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@next-ai-ready/actions",
3
- "version": "0.1.0-alpha.5",
3
+ "version": "0.1.0-alpha.7",
4
4
  "description": "defineAction registry, Zod validation, schema emission.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "zod": "^4.4.3",
22
- "@next-ai-ready/core": "0.1.0-alpha.5"
22
+ "@next-ai-ready/core": "0.1.0-alpha.7"
23
23
  },
24
24
  "devDependencies": {
25
25
  "tsup": "^8.3.0",