pasika 0.10.55 → 0.10.57

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.
@@ -0,0 +1,20 @@
1
+ import { ZodType, z } from 'zod';
2
+ import { HttpMethod } from './http-method.js';
3
+
4
+ declare const apiErrorResponseSchema: z.ZodObject<{
5
+ message: z.ZodString;
6
+ }, z.core.$strip>;
7
+ interface ApiContract<TResponseSchema extends ZodType = ZodType, TRequestSchema extends ZodType | undefined = undefined> {
8
+ readonly method: HttpMethod;
9
+ readonly path: string;
10
+ readonly responseSchema: TResponseSchema;
11
+ readonly requestSchema?: TRequestSchema;
12
+ }
13
+ /**
14
+ * Declares one schema-validated JSON endpoint. Route, client, and mock import
15
+ * this same value so method, path, request validation, and response validation
16
+ * have one source.
17
+ */
18
+ declare function defineApiContract<TResponseSchema extends ZodType, TRequestSchema extends ZodType | undefined = undefined>(contract: ApiContract<TResponseSchema, TRequestSchema>): Readonly<ApiContract<TResponseSchema, TRequestSchema>>;
19
+
20
+ export { type ApiContract, apiErrorResponseSchema, defineApiContract };
@@ -0,0 +1,8 @@
1
+ import {
2
+ apiErrorResponseSchema,
3
+ defineApiContract
4
+ } from "../chunk-Z3C5YVO4.js";
5
+ export {
6
+ apiErrorResponseSchema,
7
+ defineApiContract
8
+ };
@@ -1,7 +1,8 @@
1
1
  import { NextResponse } from 'next/server';
2
2
  import { z } from 'zod';
3
+ import { ApiContract } from './api-contract.js';
4
+ import './http-method.js';
3
5
 
4
- /** Whatever a handler's own response needs to carry; a failure never inherits it. */
5
6
  type ResponseHeaders = Headers | Record<string, string>;
6
7
  type HandlerResult<TData> = {
7
8
  data: TData;
@@ -13,14 +14,12 @@ type HandlerResult<TData> = {
13
14
  headers?: ResponseHeaders;
14
15
  };
15
16
  /**
16
- * Turns a handler's result, or a failure thrown under it, into a response: the data
17
- * goes through the response schema, an `HttpError` becomes `{ success: false, data: null, message }`
18
- * at its own status and is never cached, and anything else stays an error.
19
- *
20
- * A handler whose own response is not JSON calls it with the handler alone and returns
21
- * `{ body, status, headers }`, so a relayed body passes through unread.
17
+ * Turns a handler result into the raw HTTP response described by one API contract.
18
+ * Successful JSON data is validated and returned as the body directly. An HttpError
19
+ * becomes the standard { message } error body at its own status and is never cached.
20
+ * Anything else remains an error.
22
21
  */
23
- declare function withResponse<TSchema extends z.ZodType, Args extends unknown[]>(responseSchema: TSchema, handler?: (...args: Args) => Promise<HandlerResult<z.input<TSchema>>>): (...args: Args) => Promise<NextResponse>;
22
+ declare function withResponse<TSchema extends z.ZodType, Args extends unknown[]>(contract: ApiContract<TSchema, z.ZodType | undefined>, handler?: (...args: Args) => Promise<HandlerResult<z.input<TSchema>>>): (...args: Args) => Promise<NextResponse>;
24
23
  declare function withResponse<Args extends unknown[]>(handler: (...args: Args) => Promise<HandlerResult<ReadableStream<Uint8Array>>>): (...args: Args) => Promise<NextResponse>;
25
24
 
26
25
  export { type HandlerResult, type ResponseHeaders, withResponse };
@@ -1,3 +1,6 @@
1
+ import {
2
+ apiErrorResponseSchema
3
+ } from "../chunk-Z3C5YVO4.js";
1
4
  import {
2
5
  HttpError
3
6
  } from "../chunk-7JI2V3HF.js";
@@ -7,9 +10,9 @@ import { NextResponse } from "next/server";
7
10
  import { z } from "zod";
8
11
  var streamBody = z.custom((value) => value instanceof ReadableStream);
9
12
  function missingHandler() {
10
- throw new HttpError("withResponse requires a response schema and a handler.", 500);
13
+ throw new HttpError("withResponse requires an API contract and a handler.", 500);
11
14
  }
12
- function withResponse(responseSchemaOrHandler, handler) {
15
+ function withResponse(contractOrHandler, handler) {
13
16
  const respond = (schema, wrapped = missingHandler) => async (...requestArgs) => {
14
17
  try {
15
18
  const result = await wrapped(...requestArgs);
@@ -18,18 +21,18 @@ function withResponse(responseSchemaOrHandler, handler) {
18
21
  return new NextResponse(streamBody.parse(body), { status: status2, headers: headers2 });
19
22
  }
20
23
  const { data, status, headers } = result;
21
- return NextResponse.json({ success: true, data: schema.parse(data) }, { status, headers });
24
+ return NextResponse.json(schema.parse(data), { status, headers });
22
25
  } catch (error) {
23
26
  if (error instanceof HttpError) {
24
- return NextResponse.json(
25
- { success: false, data: null, message: error.message },
26
- { status: error.status, headers: { "Cache-Control": "no-store" } }
27
- );
27
+ return NextResponse.json(apiErrorResponseSchema.parse({ message: error.message }), {
28
+ status: error.status,
29
+ headers: { "Cache-Control": "no-store" }
30
+ });
28
31
  }
29
32
  throw error;
30
33
  }
31
34
  };
32
- return typeof responseSchemaOrHandler === "function" ? respond(streamBody, responseSchemaOrHandler) : respond(responseSchemaOrHandler, handler);
35
+ return typeof contractOrHandler === "function" ? respond(streamBody, contractOrHandler) : respond(contractOrHandler.responseSchema, handler);
33
36
  }
34
37
  export {
35
38
  withResponse
@@ -1,30 +1,28 @@
1
1
  import { ZodType, z } from 'zod';
2
+ import { ApiContract } from './api-contract.js';
3
+ import './http-method.js';
2
4
 
3
- interface ZodFetchOptions<TSchema extends ZodType = never, TRequestSchema extends ZodType = ZodType> {
5
+ interface ApiContractFetchOptions<TResponseSchema extends ZodType, TRequestSchema extends ZodType | undefined = undefined> {
6
+ contract: ApiContract<TResponseSchema, TRequestSchema>;
7
+ url?: string | URL;
8
+ init?: Omit<RequestInit, "method">;
9
+ }
10
+ interface ZodFetchRelayedOptions {
4
11
  url: string | URL;
5
12
  init?: RequestInit;
6
- requestSchema?: TRequestSchema;
7
- responseSchema?: TSchema;
8
13
  }
9
- /** The response itself, for a caller whose own response relays a body nothing has read. */
10
14
  interface ZodFetchRelayedResponse {
11
15
  body: ReadableStream<Uint8Array>;
12
16
  status: number;
13
17
  headers: Headers;
14
18
  }
15
19
  /**
16
- * The only module in a repository that calls `fetch`. It hands a JSON body back as
17
- * data the response schema accepted, and a failure back as an `HttpError` carrying
18
- * the status the upstream reported, the message for a client, and what the upstream
19
- * answered with. A call that names no response schema gets the response itself.
20
- *
21
- * A success that carries no body is the schema's call: one that allows its data to
22
- * be absent, such as `z.undefined()` for an endpoint answering `204`, accepts it, and
23
- * one that does not makes it a failure of this gateway's own, a 502.
20
+ * Makes one schema-validated JSON request through an API contract, or relays a
21
+ * response body when no contract is supplied. Contract calls reuse the endpoint's
22
+ * method, request schema, and response schema; url only overrides the concrete
23
+ * address for dynamic or external endpoints.
24
24
  */
25
- declare function zodFetch<TSchema extends ZodType>(options: ZodFetchOptions<TSchema> & {
26
- responseSchema: TSchema;
27
- }): Promise<z.output<TSchema>>;
28
- declare function zodFetch(options: ZodFetchOptions): Promise<ZodFetchRelayedResponse>;
25
+ declare function zodFetch<TResponseSchema extends ZodType, TRequestSchema extends ZodType | undefined>(options: ApiContractFetchOptions<TResponseSchema, TRequestSchema>): Promise<z.output<TResponseSchema>>;
26
+ declare function zodFetch(options: ZodFetchRelayedOptions): Promise<ZodFetchRelayedResponse>;
29
27
 
30
- export { type ZodFetchOptions, type ZodFetchRelayedResponse, zodFetch };
28
+ export { type ApiContractFetchOptions, type ZodFetchRelayedOptions, type ZodFetchRelayedResponse, zodFetch };
@@ -1,3 +1,6 @@
1
+ import {
2
+ apiErrorResponseSchema
3
+ } from "../chunk-Z3C5YVO4.js";
1
4
  import {
2
5
  HttpError
3
6
  } from "../chunk-7JI2V3HF.js";
@@ -14,23 +17,28 @@ function decodeFailureBody(body) {
14
17
  return body;
15
18
  }
16
19
  }
20
+ function failureMessage(status, statusText, body) {
21
+ const parsed = apiErrorResponseSchema.safeParse(body);
22
+ if (parsed.success) return parsed.data.message;
23
+ const suffix = statusText === "" ? "" : ` ${statusText}`;
24
+ return `Request failed with status ${String(status)}${suffix}`;
25
+ }
26
+ function parseRequestBody(schema, body) {
27
+ if (schema === void 0) return;
28
+ const decoded = typeof body === "string" ? JSON.parse(body) : body;
29
+ schema.parse(decoded);
30
+ }
17
31
  async function zodFetch(options) {
18
- if (options.requestSchema !== void 0) {
19
- const rawBody = options.init?.body;
20
- const body2 = typeof rawBody === "string" ? JSON.parse(rawBody) : rawBody;
21
- options.requestSchema.parse(body2);
22
- }
23
- const response = await fetch(options.url, options.init);
32
+ const contract = "contract" in options ? options.contract : void 0;
33
+ const url = "contract" in options ? options.url ?? options.contract.path : options.url;
34
+ const init = contract ? { ...options.init, method: contract.method } : options.init;
35
+ if (contract) parseRequestBody(contract.requestSchema, init?.body);
36
+ const response = await fetch(url, init);
24
37
  if (!response.ok) {
25
- const body2 = await response.text();
26
- const statusText = response.statusText === "" ? "" : ` ${response.statusText}`;
27
- throw new HttpError(
28
- `Request failed with status ${String(response.status)}${statusText}`,
29
- response.status,
30
- decodeFailureBody(body2)
31
- );
38
+ const body2 = decodeFailureBody(await response.text());
39
+ throw new HttpError(failureMessage(response.status, response.statusText, body2), response.status, body2);
32
40
  }
33
- if (options.responseSchema === void 0) {
41
+ if (!contract) {
34
42
  const streamed = streamBody.safeParse(response.body);
35
43
  if (!streamed.success) {
36
44
  throw new HttpError("The upstream answered without a body to relay.", BAD_GATEWAY);
@@ -39,12 +47,12 @@ async function zodFetch(options) {
39
47
  }
40
48
  const body = await response.text();
41
49
  if (body === "") {
42
- const absent = options.responseSchema.safeParse(void 0);
50
+ const absent = contract.responseSchema.safeParse(void 0);
43
51
  if (absent.success) return absent.data;
44
52
  throw new HttpError("The upstream answered without a body to decode.", BAD_GATEWAY);
45
53
  }
46
54
  const decoded = JSON.parse(body);
47
- return options.responseSchema.parse(decoded);
55
+ return contract.responseSchema.parse(decoded);
48
56
  }
49
57
  export {
50
58
  zodFetch
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.10.55",
3
+ "version": "0.10.57",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,6 +13,10 @@
13
13
  "types": "./dist/eslint/pasika/index.d.ts",
14
14
  "import": "./dist/eslint/pasika/index.js"
15
15
  },
16
+ "./api-contract": {
17
+ "types": "./dist/helpers/api-contract.d.ts",
18
+ "import": "./dist/helpers/api-contract.js"
19
+ },
16
20
  "./cn": {
17
21
  "types": "./dist/helpers/cn.d.ts",
18
22
  "import": "./dist/helpers/cn.js"
@@ -25,10 +29,6 @@
25
29
  "types": "./dist/helpers/http-method.d.ts",
26
30
  "import": "./dist/helpers/http-method.js"
27
31
  },
28
- "./response-envelope": {
29
- "types": "./dist/helpers/response-envelope.d.ts",
30
- "import": "./dist/helpers/response-envelope.js"
31
- },
32
32
  "./zod-fetch": {
33
33
  "types": "./dist/helpers/zod-fetch.d.ts",
34
34
  "import": "./dist/helpers/zod-fetch.js"
@@ -1,20 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- declare function responseEnvelope<TSchema extends z.ZodType>(dataSchema: TSchema): z.ZodType<{
4
- success: true;
5
- data: z.output<TSchema>;
6
- } | {
7
- success: false;
8
- data: null;
9
- message: string;
10
- }, {
11
- success: true;
12
- data: z.input<TSchema>;
13
- } | {
14
- success: false;
15
- data: null;
16
- message: string;
17
- }>;
18
- declare function responseEnvelope(dataSchema: z.ZodType): z.ZodType;
19
-
20
- export { responseEnvelope };
@@ -1,18 +0,0 @@
1
- // helpers/response-envelope.ts
2
- import { z } from "zod";
3
- function responseEnvelope(dataSchema) {
4
- return z.discriminatedUnion("success", [
5
- z.object({
6
- success: z.literal(true),
7
- data: dataSchema
8
- }),
9
- z.object({
10
- success: z.literal(false),
11
- data: z.null(),
12
- message: z.string()
13
- })
14
- ]);
15
- }
16
- export {
17
- responseEnvelope
18
- };