@forinda/kickjs-http 0.3.0

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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/dist/application.d.ts +89 -0
  3. package/dist/application.js +10 -0
  4. package/dist/application.js.map +1 -0
  5. package/dist/bootstrap.d.ts +27 -0
  6. package/dist/bootstrap.js +11 -0
  7. package/dist/bootstrap.js.map +1 -0
  8. package/dist/chunk-75Z5FSZN.js +88 -0
  9. package/dist/chunk-75Z5FSZN.js.map +1 -0
  10. package/dist/chunk-7QVYU63E.js +7 -0
  11. package/dist/chunk-7QVYU63E.js.map +1 -0
  12. package/dist/chunk-BNWCVQQH.js +54 -0
  13. package/dist/chunk-BNWCVQQH.js.map +1 -0
  14. package/dist/chunk-I6UNTOQD.js +52 -0
  15. package/dist/chunk-I6UNTOQD.js.map +1 -0
  16. package/dist/chunk-JD2RKDKH.js +61 -0
  17. package/dist/chunk-JD2RKDKH.js.map +1 -0
  18. package/dist/chunk-JM7X7SAD.js +133 -0
  19. package/dist/chunk-JM7X7SAD.js.map +1 -0
  20. package/dist/chunk-KAWXFLFS.js +50 -0
  21. package/dist/chunk-KAWXFLFS.js.map +1 -0
  22. package/dist/chunk-P3YCN5LK.js +196 -0
  23. package/dist/chunk-P3YCN5LK.js.map +1 -0
  24. package/dist/chunk-RZUH6NBM.js +110 -0
  25. package/dist/chunk-RZUH6NBM.js.map +1 -0
  26. package/dist/chunk-U2JYL2NW.js +62 -0
  27. package/dist/chunk-U2JYL2NW.js.map +1 -0
  28. package/dist/chunk-ZI52TGQ4.js +22 -0
  29. package/dist/chunk-ZI52TGQ4.js.map +1 -0
  30. package/dist/context.d.ts +55 -0
  31. package/dist/context.js +9 -0
  32. package/dist/context.js.map +1 -0
  33. package/dist/index.d.ts +16 -0
  34. package/dist/index.js +64 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/middleware/csrf.d.ts +49 -0
  37. package/dist/middleware/csrf.js +8 -0
  38. package/dist/middleware/csrf.js.map +1 -0
  39. package/dist/middleware/error-handler.d.ts +8 -0
  40. package/dist/middleware/error-handler.js +10 -0
  41. package/dist/middleware/error-handler.js.map +1 -0
  42. package/dist/middleware/request-id.d.ts +7 -0
  43. package/dist/middleware/request-id.js +10 -0
  44. package/dist/middleware/request-id.js.map +1 -0
  45. package/dist/middleware/upload.d.ts +57 -0
  46. package/dist/middleware/upload.js +10 -0
  47. package/dist/middleware/upload.js.map +1 -0
  48. package/dist/middleware/validate.d.ts +15 -0
  49. package/dist/middleware/validate.js +8 -0
  50. package/dist/middleware/validate.js.map +1 -0
  51. package/dist/query/index.d.ts +47 -0
  52. package/dist/query/index.js +20 -0
  53. package/dist/query/index.js.map +1 -0
  54. package/dist/router-builder.d.ts +12 -0
  55. package/dist/router-builder.js +13 -0
  56. package/dist/router-builder.js.map +1 -0
  57. package/dist/types-Doz6f3AB.d.ts +72 -0
  58. package/package.json +94 -0
package/dist/index.js ADDED
@@ -0,0 +1,64 @@
1
+ import {
2
+ cleanupFiles,
3
+ upload
4
+ } from "./chunk-75Z5FSZN.js";
5
+ import {
6
+ bootstrap
7
+ } from "./chunk-KAWXFLFS.js";
8
+ import {
9
+ Application
10
+ } from "./chunk-P3YCN5LK.js";
11
+ import {
12
+ buildRoutes,
13
+ getControllerPath
14
+ } from "./chunk-U2JYL2NW.js";
15
+ import {
16
+ validate
17
+ } from "./chunk-JD2RKDKH.js";
18
+ import {
19
+ RequestContext
20
+ } from "./chunk-RZUH6NBM.js";
21
+ import {
22
+ FILTER_OPERATORS,
23
+ buildQueryParams,
24
+ parseFilters,
25
+ parsePagination,
26
+ parseQuery,
27
+ parseSearchQuery,
28
+ parseSort
29
+ } from "./chunk-JM7X7SAD.js";
30
+ import {
31
+ csrf
32
+ } from "./chunk-I6UNTOQD.js";
33
+ import {
34
+ errorHandler,
35
+ notFoundHandler
36
+ } from "./chunk-BNWCVQQH.js";
37
+ import {
38
+ REQUEST_ID_HEADER,
39
+ requestId
40
+ } from "./chunk-ZI52TGQ4.js";
41
+ import "./chunk-7QVYU63E.js";
42
+ export {
43
+ Application,
44
+ FILTER_OPERATORS,
45
+ REQUEST_ID_HEADER,
46
+ RequestContext,
47
+ bootstrap,
48
+ buildQueryParams,
49
+ buildRoutes,
50
+ cleanupFiles,
51
+ csrf,
52
+ errorHandler,
53
+ getControllerPath,
54
+ notFoundHandler,
55
+ parseFilters,
56
+ parsePagination,
57
+ parseQuery,
58
+ parseSearchQuery,
59
+ parseSort,
60
+ requestId,
61
+ upload,
62
+ validate
63
+ };
64
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,49 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+
3
+ interface CsrfOptions {
4
+ /** Cookie name for the CSRF token (default: '_csrf') */
5
+ cookie?: string;
6
+ /** Header name to check for the token (default: 'x-csrf-token') */
7
+ header?: string;
8
+ /** HTTP methods that require CSRF validation (default: POST, PUT, PATCH, DELETE) */
9
+ methods?: string[];
10
+ /** Paths to exclude from CSRF checks (e.g. webhooks) */
11
+ ignorePaths?: string[];
12
+ /** Token byte length before hex encoding (default: 32 = 64 hex chars) */
13
+ tokenLength?: number;
14
+ /** Cookie options */
15
+ cookieOptions?: {
16
+ httpOnly?: boolean;
17
+ sameSite?: 'strict' | 'lax' | 'none';
18
+ secure?: boolean;
19
+ path?: string;
20
+ };
21
+ }
22
+ /**
23
+ * Double-submit cookie CSRF protection middleware.
24
+ *
25
+ * On every request, sets a CSRF token cookie. For state-changing methods
26
+ * (POST, PUT, PATCH, DELETE), validates that the request header matches
27
+ * the cookie value.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import { csrf } from '@forinda/kickjs-http'
32
+ *
33
+ * bootstrap({
34
+ * modules,
35
+ * middleware: [
36
+ * cookieParser(),
37
+ * csrf(),
38
+ * // ... other middleware
39
+ * ],
40
+ * })
41
+ * ```
42
+ *
43
+ * Client usage:
44
+ * 1. Read the `_csrf` cookie value
45
+ * 2. Send it in the `x-csrf-token` header on every mutating request
46
+ */
47
+ declare function csrf(options?: CsrfOptions): (req: Request, res: Response, next: NextFunction) => void | Response<any, Record<string, any>>;
48
+
49
+ export { type CsrfOptions, csrf };
@@ -0,0 +1,8 @@
1
+ import {
2
+ csrf
3
+ } from "../chunk-I6UNTOQD.js";
4
+ import "../chunk-7QVYU63E.js";
5
+ export {
6
+ csrf
7
+ };
8
+ //# sourceMappingURL=csrf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,8 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+
3
+ /** Catch-all for unmatched routes */
4
+ declare function notFoundHandler(): (_req: Request, res: Response, _next: NextFunction) => void;
5
+ /** Global error handler */
6
+ declare function errorHandler(): (err: any, req: Request, res: Response, _next: NextFunction) => Response<any, Record<string, any>> | undefined;
7
+
8
+ export { errorHandler, notFoundHandler };
@@ -0,0 +1,10 @@
1
+ import {
2
+ errorHandler,
3
+ notFoundHandler
4
+ } from "../chunk-BNWCVQQH.js";
5
+ import "../chunk-7QVYU63E.js";
6
+ export {
7
+ errorHandler,
8
+ notFoundHandler
9
+ };
10
+ //# sourceMappingURL=error-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,7 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+
3
+ declare const REQUEST_ID_HEADER = "x-request-id";
4
+ /** Middleware that generates or propagates a unique request ID */
5
+ declare function requestId(): (req: Request, res: Response, next: NextFunction) => void;
6
+
7
+ export { REQUEST_ID_HEADER, requestId };
@@ -0,0 +1,10 @@
1
+ import {
2
+ REQUEST_ID_HEADER,
3
+ requestId
4
+ } from "../chunk-ZI52TGQ4.js";
5
+ import "../chunk-7QVYU63E.js";
6
+ export {
7
+ REQUEST_ID_HEADER,
8
+ requestId
9
+ };
10
+ //# sourceMappingURL=request-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,57 @@
1
+ import { Request, Response, NextFunction, RequestHandler } from 'express';
2
+ import { Options } from 'multer';
3
+
4
+ interface UploadOptions {
5
+ /** Max file size in bytes (default: 5MB) */
6
+ maxSize?: number;
7
+ /** Allowed MIME types (default: all) */
8
+ allowedTypes?: string[];
9
+ /** Multer storage config (default: memory storage) */
10
+ storage?: Options['storage'];
11
+ /** Multer dest for disk storage shorthand */
12
+ dest?: string;
13
+ }
14
+ /**
15
+ * Single file upload middleware. Attaches the file to `req.file`.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * @Post('/avatar')
20
+ * @Middleware(upload.single('avatar', { maxSize: 2 * 1024 * 1024, allowedTypes: ['image/*'] }))
21
+ * async uploadAvatar(ctx: RequestContext) {
22
+ * ctx.json({ filename: ctx.file.originalname })
23
+ * }
24
+ * ```
25
+ */
26
+ declare function single(fieldName: string, options?: UploadOptions): RequestHandler;
27
+ /**
28
+ * Multiple file upload middleware. Attaches files to `req.files`.
29
+ */
30
+ declare function array(fieldName: string, maxCount?: number, options?: UploadOptions): RequestHandler;
31
+ /**
32
+ * No file upload — just parse multipart form data without file fields.
33
+ */
34
+ declare function none(options?: UploadOptions): RequestHandler;
35
+ /**
36
+ * Middleware that automatically cleans up uploaded files after the response
37
+ * is sent. Attach this AFTER your upload middleware.
38
+ *
39
+ * Only cleans up disk-stored files (files with a `path` property).
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * middleware: [
44
+ * upload.single('file', { dest: '/tmp/uploads' }),
45
+ * cleanupFiles(),
46
+ * ]
47
+ * ```
48
+ */
49
+ declare function cleanupFiles(): (req: Request, res: Response, next: NextFunction) => void;
50
+ /** Upload middleware factory with `.single()`, `.array()`, `.none()` methods */
51
+ declare const upload: {
52
+ single: typeof single;
53
+ array: typeof array;
54
+ none: typeof none;
55
+ };
56
+
57
+ export { type UploadOptions, cleanupFiles, upload };
@@ -0,0 +1,10 @@
1
+ import {
2
+ cleanupFiles,
3
+ upload
4
+ } from "../chunk-75Z5FSZN.js";
5
+ import "../chunk-7QVYU63E.js";
6
+ export {
7
+ cleanupFiles,
8
+ upload
9
+ };
10
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,15 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+
3
+ interface ValidationSchema {
4
+ body?: any;
5
+ query?: any;
6
+ params?: any;
7
+ }
8
+ /**
9
+ * Express middleware that validates request body/query/params against schemas.
10
+ * Works with any validation library that exposes `.safeParse(data)` returning
11
+ * `{ success: true, data }` or `{ success: false, error: { issues } }`.
12
+ */
13
+ declare function validate(schema: ValidationSchema): (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
14
+
15
+ export { type ValidationSchema, validate };
@@ -0,0 +1,8 @@
1
+ import {
2
+ validate
3
+ } from "../chunk-JD2RKDKH.js";
4
+ import "../chunk-7QVYU63E.js";
5
+ export {
6
+ validate
7
+ };
8
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,47 @@
1
+ import { c as ParsedQuery, a as FilterItem, P as PaginationParams, d as QueryFieldConfig, S as SortItem } from '../types-Doz6f3AB.js';
2
+ export { F as FILTER_OPERATORS, b as FilterOperator, Q as QueryBuilderAdapter } from '../types-Doz6f3AB.js';
3
+
4
+ /** Parse filter strings like "status:eq:active" into structured objects */
5
+ declare function parseFilters(filterParam: string | string[] | undefined, allowedFields?: string[]): FilterItem[];
6
+ /** Parse sort strings like "firstName:asc" into structured objects */
7
+ declare function parseSort(sortParam: string | string[] | undefined, allowedFields?: string[]): SortItem[];
8
+ /** Parse page/limit into pagination with computed offset */
9
+ declare function parsePagination(params: {
10
+ page?: string | number;
11
+ limit?: string | number;
12
+ }): PaginationParams;
13
+ /** Sanitize and truncate search query */
14
+ declare function parseSearchQuery(q: string | undefined): string;
15
+ /**
16
+ * Parse a raw Express query object into a structured, ORM-agnostic ParsedQuery.
17
+ *
18
+ * @param query - Raw query string object from `req.query` or Zod-validated object
19
+ * @param fieldConfig - Optional field restrictions (whitelist filterable/sortable/searchable)
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * // In a controller
24
+ * @Get('/')
25
+ * async list(ctx: RequestContext) {
26
+ * const parsed = parseQuery(ctx.query, {
27
+ * filterable: ['status', 'priority'],
28
+ * sortable: ['createdAt', 'title'],
29
+ * })
30
+ * // Pass to your ORM query builder adapter
31
+ * const q = drizzleAdapter.build(parsed, { columns, searchColumns })
32
+ * }
33
+ * ```
34
+ *
35
+ * Query string format:
36
+ * - Filters: `?filter=field:operator:value` (repeatable)
37
+ * - Sort: `?sort=field:asc|desc` (repeatable)
38
+ * - Pagination: `?page=1&limit=20`
39
+ * - Search: `?q=search+term`
40
+ *
41
+ * Filter operators: eq, neq, gt, gte, lt, lte, between, in, contains, starts, ends
42
+ */
43
+ declare function parseQuery(query: Record<string, any>, fieldConfig?: QueryFieldConfig): ParsedQuery;
44
+ /** Convert ParsedQuery back into query string parameters */
45
+ declare function buildQueryParams(parsed: Partial<ParsedQuery>): Record<string, string | string[] | number>;
46
+
47
+ export { FilterItem, PaginationParams, ParsedQuery, QueryFieldConfig, SortItem, buildQueryParams, parseFilters, parsePagination, parseQuery, parseSearchQuery, parseSort };
@@ -0,0 +1,20 @@
1
+ import {
2
+ FILTER_OPERATORS,
3
+ buildQueryParams,
4
+ parseFilters,
5
+ parsePagination,
6
+ parseQuery,
7
+ parseSearchQuery,
8
+ parseSort
9
+ } from "../chunk-JM7X7SAD.js";
10
+ import "../chunk-7QVYU63E.js";
11
+ export {
12
+ FILTER_OPERATORS,
13
+ buildQueryParams,
14
+ parseFilters,
15
+ parsePagination,
16
+ parseQuery,
17
+ parseSearchQuery,
18
+ parseSort
19
+ };
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,12 @@
1
+ import { Router } from 'express';
2
+
3
+ /** Get the controller path set by @Controller('/path') */
4
+ declare function getControllerPath(controllerClass: any): string;
5
+ /**
6
+ * Build an Express Router from a controller class decorated with @Get, @Post, etc.
7
+ * Resolves the controller from the DI container, wraps handlers in RequestContext,
8
+ * and applies class-level and method-level middleware.
9
+ */
10
+ declare function buildRoutes(controllerClass: any): Router;
11
+
12
+ export { buildRoutes, getControllerPath };
@@ -0,0 +1,13 @@
1
+ import {
2
+ buildRoutes,
3
+ getControllerPath
4
+ } from "./chunk-U2JYL2NW.js";
5
+ import "./chunk-JD2RKDKH.js";
6
+ import "./chunk-RZUH6NBM.js";
7
+ import "./chunk-JM7X7SAD.js";
8
+ import "./chunk-7QVYU63E.js";
9
+ export {
10
+ buildRoutes,
11
+ getControllerPath
12
+ };
13
+ //# sourceMappingURL=router-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,72 @@
1
+ /** Supported filter operators for query string parsing */
2
+ type FilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'in' | 'contains' | 'starts' | 'ends';
3
+ declare const FILTER_OPERATORS: Set<string>;
4
+ interface FilterItem {
5
+ field: string;
6
+ operator: FilterOperator;
7
+ value: string;
8
+ }
9
+ interface SortItem {
10
+ field: string;
11
+ direction: 'asc' | 'desc';
12
+ }
13
+ interface PaginationParams {
14
+ page: number;
15
+ limit: number;
16
+ offset: number;
17
+ }
18
+ /**
19
+ * The result of parsing a query string. ORM-agnostic — pass this to
20
+ * a query builder adapter (Drizzle, Prisma, Sequelize, etc.) to produce
21
+ * database-specific query objects.
22
+ */
23
+ interface ParsedQuery {
24
+ filters: FilterItem[];
25
+ sort: SortItem[];
26
+ pagination: PaginationParams;
27
+ search: string;
28
+ }
29
+ /**
30
+ * Restrict which fields can be filtered, sorted, or searched.
31
+ * Fields not in the allow-list are silently ignored.
32
+ */
33
+ interface QueryFieldConfig {
34
+ filterable?: string[];
35
+ sortable?: string[];
36
+ searchable?: string[];
37
+ }
38
+ /**
39
+ * Interface for ORM-specific query builder adapters.
40
+ * Implement this to translate a `ParsedQuery` into your ORM's query format.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * // Drizzle adapter
45
+ * class DrizzleQueryAdapter implements QueryBuilderAdapter<DrizzleQueryResult> {
46
+ * build(parsed: ParsedQuery, config: DrizzleConfig): DrizzleQueryResult {
47
+ * // Convert filters → Drizzle SQL conditions
48
+ * // Convert sort → Drizzle orderBy
49
+ * return { where, orderBy, limit, offset }
50
+ * }
51
+ * }
52
+ *
53
+ * // Prisma adapter
54
+ * class PrismaQueryAdapter implements QueryBuilderAdapter<PrismaQueryResult> {
55
+ * build(parsed: ParsedQuery, config: PrismaConfig): PrismaQueryResult {
56
+ * return { where, orderBy, skip, take }
57
+ * }
58
+ * }
59
+ * ```
60
+ */
61
+ interface QueryBuilderAdapter<TResult = any, TConfig = any> {
62
+ /** Human-readable name for debugging */
63
+ readonly name: string;
64
+ /**
65
+ * Convert a ParsedQuery into an ORM-specific query object.
66
+ * @param parsed - The ORM-agnostic parsed query
67
+ * @param config - ORM-specific configuration (column maps, search columns, etc.)
68
+ */
69
+ build(parsed: ParsedQuery, config: TConfig): TResult;
70
+ }
71
+
72
+ export { FILTER_OPERATORS as F, type PaginationParams as P, type QueryBuilderAdapter as Q, type SortItem as S, type FilterItem as a, type FilterOperator as b, type ParsedQuery as c, type QueryFieldConfig as d };
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@forinda/kickjs-http",
3
+ "version": "0.3.0",
4
+ "description": "Express 5 integration, router builder, RequestContext, and middleware for KickJS",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./application": {
14
+ "import": "./dist/application.js",
15
+ "types": "./dist/application.d.ts"
16
+ },
17
+ "./bootstrap": {
18
+ "import": "./dist/bootstrap.js",
19
+ "types": "./dist/bootstrap.d.ts"
20
+ },
21
+ "./context": {
22
+ "import": "./dist/context.js",
23
+ "types": "./dist/context.d.ts"
24
+ },
25
+ "./router": {
26
+ "import": "./dist/router-builder.js",
27
+ "types": "./dist/router-builder.d.ts"
28
+ },
29
+ "./middleware/csrf": {
30
+ "import": "./dist/middleware/csrf.js",
31
+ "types": "./dist/middleware/csrf.d.ts"
32
+ },
33
+ "./middleware/upload": {
34
+ "import": "./dist/middleware/upload.js",
35
+ "types": "./dist/middleware/upload.d.ts"
36
+ },
37
+ "./middleware/validate": {
38
+ "import": "./dist/middleware/validate.js",
39
+ "types": "./dist/middleware/validate.d.ts"
40
+ },
41
+ "./middleware/request-id": {
42
+ "import": "./dist/middleware/request-id.js",
43
+ "types": "./dist/middleware/request-id.d.ts"
44
+ },
45
+ "./middleware/error-handler": {
46
+ "import": "./dist/middleware/error-handler.js",
47
+ "types": "./dist/middleware/error-handler.d.ts"
48
+ },
49
+ "./query": {
50
+ "import": "./dist/query/index.js",
51
+ "types": "./dist/query/index.d.ts"
52
+ }
53
+ },
54
+ "files": [
55
+ "dist"
56
+ ],
57
+ "dependencies": {
58
+ "cookie-parser": "^1.4.7",
59
+ "multer": "^2.1.1",
60
+ "reflect-metadata": "^0.2.2",
61
+ "@forinda/kickjs-core": "0.3.0"
62
+ },
63
+ "peerDependencies": {
64
+ "express": "^5.1.0"
65
+ },
66
+ "devDependencies": {
67
+ "@swc/core": "^1.7.28",
68
+ "@types/cookie-parser": "^1.4.10",
69
+ "@types/express": "^5.0.6",
70
+ "@types/multer": "^2.1.0",
71
+ "@types/node": "^24.5.2",
72
+ "express": "^5.1.0",
73
+ "tsup": "^8.5.0",
74
+ "typescript": "^5.9.2",
75
+ "vitest": "^3.2.4"
76
+ },
77
+ "publishConfig": {
78
+ "access": "public"
79
+ },
80
+ "license": "MIT",
81
+ "author": "Felix Orinda",
82
+ "engines": {
83
+ "node": ">=20.0"
84
+ },
85
+ "scripts": {
86
+ "build": "tsup",
87
+ "dev": "tsup --watch",
88
+ "test": "vitest run",
89
+ "test:watch": "vitest",
90
+ "typecheck": "tsc --noEmit",
91
+ "clean": "rm -rf dist .turbo",
92
+ "lint": "tsc --noEmit"
93
+ }
94
+ }