pasika 0.10.2 → 0.10.3

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,10 @@
1
+ import { ClassValue } from 'clsx';
2
+
3
+ /**
4
+ * Combines conditional classes and resolves conflicting Tailwind utilities, so a
5
+ * later class wins within one utility group and the same variant, and a wider
6
+ * class later in the list replaces the narrower ones it covers.
7
+ */
8
+ declare function cn(...inputs: ClassValue[]): string;
9
+
10
+ export { cn };
@@ -0,0 +1,9 @@
1
+ // helpers/cn.ts
2
+ import { clsx } from "clsx";
3
+ import { twMerge } from "tailwind-merge";
4
+ function cn(...inputs) {
5
+ return twMerge(clsx(inputs));
6
+ }
7
+ export {
8
+ cn
9
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The failure a route answers with: the status the response carries, the message
3
+ * a client reads, and what a failed upstream call answered with, so a module that
4
+ * knows that upstream can name the reason and a log line can keep the body.
5
+ */
6
+ declare class HttpError extends Error {
7
+ readonly status: number;
8
+ readonly data: unknown;
9
+ constructor(message: string, status: number, data?: unknown);
10
+ }
11
+
12
+ export { HttpError };
@@ -0,0 +1,6 @@
1
+ import {
2
+ HttpError
3
+ } from "../chunk-7JI2V3HF.js";
4
+ export {
5
+ HttpError
6
+ };
@@ -0,0 +1,27 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { z } from 'zod';
3
+
4
+ /** Whatever a handler's own response needs to carry; a failure never inherits it. */
5
+ type ResponseHeaders = Headers | Record<string, string>;
6
+ type HandlerResult<TData> = {
7
+ message: string;
8
+ data: TData;
9
+ status?: number;
10
+ headers?: ResponseHeaders;
11
+ } | {
12
+ body: ReadableStream<Uint8Array>;
13
+ status: number;
14
+ headers?: ResponseHeaders;
15
+ };
16
+ /**
17
+ * Turns a handler's result, or a failure thrown under it, into a response: the data
18
+ * goes through the response schema, an `HttpError` becomes `{ data: null, message }`
19
+ * at its own status and is never cached, and anything else stays an error.
20
+ *
21
+ * A handler whose own response is not JSON calls it with the handler alone and returns
22
+ * `{ body, status, headers }`, so a relayed body passes through unread.
23
+ */
24
+ declare function withResponse<TSchema extends z.ZodType, Args extends unknown[]>(responseSchema: TSchema, handler?: (...args: Args) => Promise<HandlerResult<z.input<TSchema>>>): (...args: Args) => Promise<NextResponse>;
25
+ declare function withResponse<Args extends unknown[]>(handler: (...args: Args) => Promise<HandlerResult<ReadableStream<Uint8Array>>>): (...args: Args) => Promise<NextResponse>;
26
+
27
+ export { type HandlerResult, type ResponseHeaders, withResponse };
@@ -0,0 +1,36 @@
1
+ import {
2
+ HttpError
3
+ } from "../chunk-7JI2V3HF.js";
4
+
5
+ // helpers/with-response.ts
6
+ import { NextResponse } from "next/server";
7
+ import { z } from "zod";
8
+ var streamBody = z.custom((value) => value instanceof ReadableStream);
9
+ function missingHandler() {
10
+ throw new HttpError("withResponse requires a response schema and a handler.", 500);
11
+ }
12
+ function withResponse(responseSchemaOrHandler, handler) {
13
+ const respond = (schema, wrapped = missingHandler) => async (...requestArgs) => {
14
+ try {
15
+ const result = await wrapped(...requestArgs);
16
+ if ("body" in result) {
17
+ const { body, status: status2, headers: headers2 } = result;
18
+ return new NextResponse(streamBody.parse(body), { status: status2, headers: headers2 });
19
+ }
20
+ const { message, data, status, headers } = result;
21
+ return NextResponse.json({ data: schema.parse(data), message }, { status, headers });
22
+ } catch (error) {
23
+ if (error instanceof HttpError) {
24
+ return NextResponse.json(
25
+ { data: null, message: error.message },
26
+ { status: error.status, headers: { "Cache-Control": "no-store" } }
27
+ );
28
+ }
29
+ throw error;
30
+ }
31
+ };
32
+ return typeof responseSchemaOrHandler === "function" ? respond(streamBody, responseSchemaOrHandler) : respond(responseSchemaOrHandler, handler);
33
+ }
34
+ export {
35
+ withResponse
36
+ };
@@ -0,0 +1,25 @@
1
+ import { ZodType, z } from 'zod';
2
+
3
+ interface ZodFetchOptions<TSchema extends ZodType = never> {
4
+ url: string | URL;
5
+ init?: RequestInit;
6
+ responseSchema?: TSchema;
7
+ }
8
+ /** The response itself, for a caller whose own response relays a body nothing has read. */
9
+ interface ZodFetchRelayedResponse {
10
+ body: ReadableStream<Uint8Array>;
11
+ status: number;
12
+ headers: Headers;
13
+ }
14
+ /**
15
+ * The only module in a repository that calls `fetch`. It hands a JSON body back as
16
+ * data the response schema accepted, and a failure back as an `HttpError` carrying
17
+ * the status the upstream reported, the message for a client, and what the upstream
18
+ * answered with. A call that names no response schema gets the response itself.
19
+ */
20
+ declare function zodFetch<TSchema extends ZodType>(options: ZodFetchOptions<TSchema> & {
21
+ responseSchema: TSchema;
22
+ }): Promise<z.output<TSchema>>;
23
+ declare function zodFetch(options: ZodFetchOptions): Promise<ZodFetchRelayedResponse>;
24
+
25
+ export { type ZodFetchOptions, type ZodFetchRelayedResponse, zodFetch };
@@ -0,0 +1,43 @@
1
+ import {
2
+ HttpError
3
+ } from "../chunk-7JI2V3HF.js";
4
+
5
+ // helpers/zod-fetch.ts
6
+ import { z } from "zod";
7
+ var streamBody = z.custom((value) => value instanceof ReadableStream);
8
+ function decodeFailureBody(body) {
9
+ try {
10
+ const parsed = JSON.parse(body);
11
+ return parsed;
12
+ } catch {
13
+ return body;
14
+ }
15
+ }
16
+ async function zodFetch(options) {
17
+ const response = await fetch(options.url, options.init);
18
+ if (!response.ok) {
19
+ const body2 = await response.text();
20
+ const statusText = response.statusText === "" ? "" : ` ${response.statusText}`;
21
+ throw new HttpError(
22
+ `Request failed with status ${String(response.status)}${statusText}`,
23
+ response.status,
24
+ decodeFailureBody(body2)
25
+ );
26
+ }
27
+ if (options.responseSchema === void 0) {
28
+ const streamed = streamBody.safeParse(response.body);
29
+ if (!streamed.success) {
30
+ throw new HttpError("The upstream answered without a body to relay.", response.status);
31
+ }
32
+ return { body: streamed.data, status: response.status, headers: response.headers };
33
+ }
34
+ const body = await response.text();
35
+ if (body === "") {
36
+ throw new HttpError("The upstream answered without a body to decode.", response.status);
37
+ }
38
+ const decoded = JSON.parse(body);
39
+ return options.responseSchema.parse(decoded);
40
+ }
41
+ export {
42
+ zodFetch
43
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,6 +12,22 @@
12
12
  "./eslint": {
13
13
  "types": "./dist/eslint/pasika/index.d.ts",
14
14
  "import": "./dist/eslint/pasika/index.js"
15
+ },
16
+ "./cn": {
17
+ "types": "./dist/helpers/cn.d.ts",
18
+ "import": "./dist/helpers/cn.js"
19
+ },
20
+ "./http-error": {
21
+ "types": "./dist/helpers/http-error.d.ts",
22
+ "import": "./dist/helpers/http-error.js"
23
+ },
24
+ "./zod-fetch": {
25
+ "types": "./dist/helpers/zod-fetch.d.ts",
26
+ "import": "./dist/helpers/zod-fetch.js"
27
+ },
28
+ "./with-response": {
29
+ "types": "./dist/helpers/with-response.d.ts",
30
+ "import": "./dist/helpers/with-response.js"
15
31
  }
16
32
  },
17
33
  "files": [
@@ -54,10 +70,13 @@
54
70
  "@types/mdast": "4.0.4",
55
71
  "@types/node": "26.4.0",
56
72
  "@vitest/coverage-v8": "5.0.0",
73
+ "clsx": "2.1.1",
57
74
  "eslint": "10.9.1",
58
75
  "husky": "9.1.7",
59
76
  "lint-staged": "17.4.1",
77
+ "next": "16.3.5",
60
78
  "prettier": "3.8.1",
79
+ "tailwind-merge": "3.7.0",
61
80
  "tsup": "8.5.1",
62
81
  "tsx": "4.23.12",
63
82
  "typescript": "6.0.3",
@@ -68,7 +87,25 @@
68
87
  "peerDependencies": {
69
88
  "@eslint/css": ">=1.4.0",
70
89
  "@eslint/json": ">=2.0.1",
71
- "@eslint/markdown": ">=8.0.3"
90
+ "@eslint/markdown": ">=8.0.3",
91
+ "clsx": ">=2.0.0",
92
+ "next": ">=15.0.0",
93
+ "tailwind-merge": ">=3.0.0",
94
+ "zod": ">=4.0.0"
95
+ },
96
+ "peerDependenciesMeta": {
97
+ "clsx": {
98
+ "optional": true
99
+ },
100
+ "next": {
101
+ "optional": true
102
+ },
103
+ "tailwind-merge": {
104
+ "optional": true
105
+ },
106
+ "zod": {
107
+ "optional": true
108
+ }
72
109
  },
73
110
  "engines": {
74
111
  "node": ">=22"