@capixjs/transport-rest 0.1.0-alpha.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Capix Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # @capixjs/transport-rest
2
+
3
+ HTTP/1.1 REST transport for Capix. Mounts your capabilities as REST endpoints with automatic route inference.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @capixjs/core @capixjs/transport-rest zod
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { createServer } from '@capixjs/core';
15
+ import { restTransport } from '@capixjs/transport-rest';
16
+
17
+ createServer({
18
+ capabilities: {
19
+ users: { getUser, listUsers, createUser, updateUser, deleteUser },
20
+ },
21
+ transports: [restTransport({ port: 3000 })],
22
+ }).start();
23
+ ```
24
+
25
+ ## Route inference
26
+
27
+ Routes are inferred from capability names — no decorators or annotations needed:
28
+
29
+ | Capability | Group | Method | Path |
30
+ |---|---|---|---|
31
+ | `getUser` (with `id` field) | `users` | GET | `/users/:id` |
32
+ | `listUsers` | `users` | GET | `/users` |
33
+ | `getUsers` | `users` | GET | `/users` |
34
+ | `getMe` | `users` | GET | `/users/me` |
35
+ | `getStats` | `users` | GET | `/users/stats` |
36
+ | `findByEmail` | `users` | GET | `/users` |
37
+ | `searchUsers` | `users` | GET | `/users` |
38
+ | `createUser` | `users` | POST | `/users` |
39
+ | `addUser` | `users` | POST | `/users` |
40
+ | `updateUser` | `users` | PATCH | `/users/:id` |
41
+ | `replaceUser` | `users` | PUT | `/users/:id` |
42
+ | `deleteUser` | `users` | DELETE | `/users/:id` |
43
+ | `removeUser` | `users` | DELETE | `/users/:id` |
44
+ | `unfollow` | `users` | DELETE | `/users/:id/follow` |
45
+ | `bulkStatus` | `users` | POST | `/users/bulk-status` |
46
+ | `register` | `auth` | POST | `/auth/register` |
47
+
48
+ Rules:
49
+ - The path prefix comes from the capability's group key (`users`, `auth`, etc.)
50
+ - `get*` with an `id` field in the input schema → `GET /group/:id`
51
+ - `list*`, `find*`, `fetch*`, `read*`, `search*`, `filter*`, `all*` → `GET /group`
52
+ - `get*` without `id` field → collection (`GET /group`) only when the remainder matches the group name (e.g. `getUsers` in `users`); otherwise named endpoint (`GET /group/remainder`)
53
+ - `create*`, `add*`, `new*` → `POST /group`
54
+ - `un*` → `DELETE /group/:id/verb` (inverse sub-resource)
55
+ - `update*`, `edit*`, `patch*`, `modify*` → `PATCH /group/:id`
56
+ - `replace*`, `set*`, `put*` → `PUT /group/:id`
57
+ - `delete*`, `remove*`, `destroy*`, `cancel*` → `DELETE /group/:id`
58
+ - Anything else with `query` intent → `GET /group/key`
59
+ - Anything else with `mutation` intent → `POST /group/key`
60
+
61
+ Capability keys are converted to kebab-case by default: `bulkStatus` → `bulk-status`. Override with `urlCase: 'camel' | 'snake'`.
62
+
63
+ For nested resource routes (`/projects/:projectId/tasks`), use `overrides`:
64
+
65
+ ```ts
66
+ restTransport({
67
+ port: 3000,
68
+ overrides: {
69
+ 'tasks.listTasks': { method: 'GET', path: '/projects/:projectId/tasks' },
70
+ 'tasks.createTask': { method: 'POST', path: '/projects/:projectId/tasks' },
71
+ },
72
+ })
73
+ ```
74
+
75
+ ## HTTP override
76
+
77
+ Override the inferred route when needed:
78
+
79
+ ```ts
80
+ const importUsers = capability(
81
+ z.object({ file: z.string() }),
82
+ handler,
83
+ { http: { method: 'POST', path: '/admin/import' } },
84
+ );
85
+ ```
86
+
87
+ ## CORS
88
+
89
+ ```ts
90
+ restTransport({
91
+ port: 3000,
92
+ cors: {
93
+ origin: (origin) => origin.endsWith('.example.com'),
94
+ methods: ['GET', 'POST', 'PATCH', 'DELETE'],
95
+ headers: ['Content-Type', 'Authorization'],
96
+ credentials: true,
97
+ },
98
+ })
99
+ ```
100
+
101
+ `origin` can be a string, string array, or `(origin: string) => boolean` function.
102
+
103
+ ## Request hooks
104
+
105
+ ```ts
106
+ restTransport({
107
+ port: 3000,
108
+ onRequest: (req, res) => {
109
+ res.setHeader('X-Request-Id', crypto.randomUUID());
110
+ },
111
+ })
112
+ ```
113
+
114
+ ## File uploads
115
+
116
+ Use `UploadedFile` from the transport for typed file handling:
117
+
118
+ ```ts
119
+ import type { UploadedFile } from '@capixjs/transport-rest';
120
+ import { z } from 'zod';
121
+
122
+ const uploadAvatar = capability(
123
+ z.object({
124
+ file: z.custom<UploadedFile>(),
125
+ userId: z.string(),
126
+ }),
127
+ async ({ file, userId }) => {
128
+ await storage.save(`avatars/${userId}`, file.buffer);
129
+ return { url: `/avatars/${userId}` };
130
+ },
131
+ );
132
+ ```
133
+
134
+ Send multipart/form-data. Non-file fields are merged alongside file fields in the parsed input.
135
+
136
+ ## Options
137
+
138
+ ```ts
139
+ restTransport({
140
+ port: 3000, // required
141
+ host: '0.0.0.0', // default: '0.0.0.0'
142
+ maxBodySize: 1_048_576, // bytes, default: 1 MiB
143
+ urlCase: 'kebab', // 'kebab' (default) | 'camel' | 'snake'
144
+ cors: { ... },
145
+ onRequest: (req, res) => void,
146
+ multipart: { maxFileSize, maxFiles, allowedMimeTypes },
147
+ })
148
+ ```
149
+
150
+ ## Per-transport capabilities
151
+
152
+ Pass `capabilities` directly to the transport to expose only a subset on REST, independent of other transports:
153
+
154
+ ```ts
155
+ const publicAPI = { items: { list: listItems, get: getItem } };
156
+ const memberAPI = { items: { create: createItem, update: updateItem } };
157
+
158
+ createServer({
159
+ context: buildContext,
160
+ transports: [
161
+ restTransport({ port: 3000, capabilities: { ...publicAPI, ...memberAPI } }),
162
+ ],
163
+ });
164
+ ```
165
+
166
+ ## Exports
167
+
168
+ | Export | Description |
169
+ |---|---|
170
+ | `restTransport(opts)` | Creates an HTTP/REST transport |
171
+ | `generateRoutes(registry, opts?)` | Returns route definitions for a registry |
172
+ | `compileRouter(routes)` | Compiles routes into a radix-tree router |
173
+ | `uploadedFile()` | Zod schema for file upload fields |
174
+ | `UploadedFile` | Type for uploaded file objects |
175
+ | `RestTransportOptions` | Options type for `restTransport` |
176
+ | `GenerateRoutesOptions` | Options type for `generateRoutes` |
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,10 @@
1
+ /**
2
+ * index.ts — public API for capix-transport-rest
3
+ */
4
+ export { restTransport } from './transport.js';
5
+ export type { RestTransportOptions } from './transport.js';
6
+ export { compileRouter, generateRoutes } from './router.js';
7
+ export type { RouteDefinition, RouterMatch, Router, GenerateRoutesOptions, HttpOverride } from './router.js';
8
+ export { uploadedFile } from './multipart.js';
9
+ export type { UploadedFile, MultipartOptions } from './multipart.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC7G,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * index.ts — public API for capix-transport-rest
3
+ */
4
+ export { restTransport } from './transport.js';
5
+ export { compileRouter, generateRoutes } from './router.js';
6
+ export { uploadedFile } from './multipart.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * multipart-parser.ts — busboy-based multipart/form-data parser
3
+ * Depends on: busboy, multipart.ts
4
+ */
5
+ import type { IncomingHttpHeaders } from 'node:http';
6
+ import type { UploadedFile, MultipartOptions } from './multipart.js';
7
+ export type ParsedMultipart = {
8
+ fields: Record<string, string>;
9
+ files: Record<string, UploadedFile>;
10
+ };
11
+ /**
12
+ * Parses a multipart/form-data body from an already-read Buffer.
13
+ * Fields are returned as strings; files as UploadedFile objects.
14
+ * Rejects with an error that has a `.status` property on limit violations.
15
+ */
16
+ export declare function parseMultipart(headers: IncomingHttpHeaders, body: Buffer, options?: MultipartOptions): Promise<ParsedMultipart>;
17
+ //# sourceMappingURL=multipart-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multipart-parser.d.ts","sourceRoot":"","sources":["../src/multipart-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAErE,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACrC,CAAC;AAQF;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAgE1B"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * multipart-parser.ts — busboy-based multipart/form-data parser
3
+ * Depends on: busboy, multipart.ts
4
+ */
5
+ import Busboy from 'busboy';
6
+ function parseError(message, status) {
7
+ return Object.assign(new Error(message), { status });
8
+ }
9
+ /**
10
+ * Parses a multipart/form-data body from an already-read Buffer.
11
+ * Fields are returned as strings; files as UploadedFile objects.
12
+ * Rejects with an error that has a `.status` property on limit violations.
13
+ */
14
+ export function parseMultipart(headers, body, options = {}) {
15
+ const maxFileSize = options.maxFileSize ?? 5 * 1024 * 1024;
16
+ const maxFiles = options.maxFiles ?? 1;
17
+ return new Promise((resolve, reject) => {
18
+ const fields = {};
19
+ const files = {};
20
+ let settled = false;
21
+ function fail(err) {
22
+ if (settled)
23
+ return;
24
+ settled = true;
25
+ reject(err);
26
+ }
27
+ const busboy = Busboy({
28
+ headers,
29
+ limits: { fileSize: maxFileSize, files: maxFiles },
30
+ });
31
+ busboy.on('field', (name, value) => {
32
+ fields[name] = value;
33
+ });
34
+ // Fired when the files limit is reached — busboy stops emitting 'file' events beyond this.
35
+ busboy.on('filesLimit', () => {
36
+ fail(parseError(`Too many files. Maximum is ${maxFiles}.`, 400));
37
+ });
38
+ busboy.on('file', (fieldName, stream, info) => {
39
+ const chunks = [];
40
+ let truncated = false;
41
+ stream.on('data', (chunk) => { chunks.push(chunk); });
42
+ stream.on('limit', () => { truncated = true; });
43
+ stream.on('end', () => {
44
+ if (truncated) {
45
+ fail(parseError(`File '${info.filename || fieldName}' exceeds ${maxFileSize} bytes.`, 413));
46
+ return;
47
+ }
48
+ const buffer = Buffer.concat(chunks);
49
+ files[fieldName] = {
50
+ filename: info.filename || fieldName,
51
+ contentType: info.mimeType || 'application/octet-stream',
52
+ sizeBytes: buffer.byteLength,
53
+ buffer,
54
+ };
55
+ });
56
+ stream.on('error', (err) => fail(Object.assign(err, { status: 400 })));
57
+ });
58
+ busboy.on('finish', () => {
59
+ if (!settled) {
60
+ settled = true;
61
+ resolve({ fields, files });
62
+ }
63
+ });
64
+ busboy.on('error', (err) => fail(Object.assign(err, { status: 400 })));
65
+ busboy.write(body);
66
+ busboy.end();
67
+ });
68
+ }
69
+ //# sourceMappingURL=multipart-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multipart-parser.js","sourceRoot":"","sources":["../src/multipart-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * multipart.ts — UploadedFile type and Zod schema factory
3
+ * Depends on: zod
4
+ */
5
+ import type { ZodType } from 'zod';
6
+ export type UploadedFile = {
7
+ filename: string;
8
+ contentType: string;
9
+ sizeBytes: number;
10
+ buffer: Buffer;
11
+ };
12
+ export type MultipartOptions = {
13
+ /** Max bytes per file. Default 5MB. */
14
+ maxFileSize?: number;
15
+ /** Max number of files per request. Default 1. */
16
+ maxFiles?: number;
17
+ };
18
+ type UploadedFileOptions = {
19
+ /** Max bytes for this specific field. Default 5MB. */
20
+ maxSize?: number;
21
+ /** Allowed MIME types. If omitted, all types are accepted. */
22
+ accept?: string[];
23
+ };
24
+ /**
25
+ * Zod schema for a file uploaded via multipart/form-data.
26
+ * Use inside z.object() alongside regular fields.
27
+ *
28
+ * @example
29
+ * z.object({
30
+ * file: uploadedFile({ maxSize: 2 * 1024 * 1024, accept: ['image/jpeg', 'image/png'] }),
31
+ * title: z.string(),
32
+ * })
33
+ */
34
+ export declare function uploadedFile(options?: UploadedFileOptions): ZodType<UploadedFile>;
35
+ export {};
36
+ //# sourceMappingURL=multipart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multipart.d.ts","sourceRoot":"","sources":["../src/multipart.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAEnC,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAK,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAI,MAAM,CAAC;IACpB,MAAM,EAAO,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,YAAY,CAAC,CA4BrF"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * multipart.ts — UploadedFile type and Zod schema factory
3
+ * Depends on: zod
4
+ */
5
+ import { z } from 'zod';
6
+ /**
7
+ * Zod schema for a file uploaded via multipart/form-data.
8
+ * Use inside z.object() alongside regular fields.
9
+ *
10
+ * @example
11
+ * z.object({
12
+ * file: uploadedFile({ maxSize: 2 * 1024 * 1024, accept: ['image/jpeg', 'image/png'] }),
13
+ * title: z.string(),
14
+ * })
15
+ */
16
+ export function uploadedFile(options = {}) {
17
+ const maxSize = options.maxSize ?? 5 * 1024 * 1024;
18
+ return z
19
+ .custom((val) => typeof val === 'object' &&
20
+ val !== null &&
21
+ typeof val.filename === 'string' &&
22
+ typeof val.contentType === 'string' &&
23
+ typeof val.sizeBytes === 'number' &&
24
+ Buffer.isBuffer(val.buffer), { message: 'Expected an uploaded file' })
25
+ .superRefine((file, ctx) => {
26
+ if (file.sizeBytes > maxSize) {
27
+ ctx.addIssue({
28
+ code: z.ZodIssueCode.custom,
29
+ message: `File size ${file.sizeBytes} bytes exceeds maximum ${maxSize} bytes`,
30
+ });
31
+ }
32
+ if (options.accept !== undefined && !options.accept.includes(file.contentType)) {
33
+ ctx.addIssue({
34
+ code: z.ZodIssueCode.custom,
35
+ message: `File type '${file.contentType}' not accepted. Allowed: ${options.accept.join(', ')}`,
36
+ });
37
+ }
38
+ });
39
+ }
40
+ //# sourceMappingURL=multipart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multipart.js","sourceRoot":"","sources":["../src/multipart.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAwBxB;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,UAA+B,EAAE;IAC5D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;IAEnD,OAAO,CAAC;SACL,MAAM,CACL,CAAC,GAAY,EAAuB,EAAE,CACpC,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,KAAK,IAAI;QACZ,OAAQ,GAAoB,CAAC,QAAQ,KAAK,QAAQ;QAClD,OAAQ,GAAoB,CAAC,WAAW,KAAK,QAAQ;QACrD,OAAQ,GAAoB,CAAC,SAAS,KAAK,QAAQ;QACnD,MAAM,CAAC,QAAQ,CAAE,GAAoB,CAAC,MAAM,CAAC,EAC/C,EAAE,OAAO,EAAE,2BAA2B,EAAE,CACzC;SACA,WAAW,CAAC,CAAC,IAAkB,EAAE,GAAoB,EAAE,EAAE;QACxD,IAAI,IAAI,CAAC,SAAS,GAAG,OAAO,EAAE,CAAC;YAC7B,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM;gBAC3B,OAAO,EAAE,aAAa,IAAI,CAAC,SAAS,0BAA0B,OAAO,QAAQ;aAC9E,CAAC,CAAC;QACL,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/E,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM;gBAC3B,OAAO,EAAE,cAAc,IAAI,CAAC,WAAW,4BAA4B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;aAC/F,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * router.ts — radix-tree router for the REST transport
3
+ * No core dependency except types (CapabilityRegistry, AnyCapability).
4
+ */
5
+ import type { CapabilityRegistry } from '@capixjs/core';
6
+ export type RouteDefinition = {
7
+ readonly method: string;
8
+ readonly path: string;
9
+ readonly capability: string;
10
+ };
11
+ export type RouterMatch = {
12
+ readonly found: true;
13
+ readonly capability: string;
14
+ readonly params: Record<string, string> | null;
15
+ } | {
16
+ readonly found: false;
17
+ readonly allowedMethods?: string[];
18
+ };
19
+ export type Router = {
20
+ match(method: string, path: string): RouterMatch;
21
+ readonly routes: ReadonlyArray<RouteDefinition>;
22
+ };
23
+ export type HttpOverride = {
24
+ readonly method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
25
+ readonly path: string;
26
+ };
27
+ export type GenerateRoutesOptions = {
28
+ /** Case style for URL path segments. Default: 'kebab' (bulkStatus → bulk-status). */
29
+ urlCase?: 'kebab' | 'camel' | 'snake';
30
+ /** Per-capability HTTP route overrides keyed by dot-path. Takes full precedence over URL inference. */
31
+ overrides?: Record<string, HttpOverride>;
32
+ };
33
+ /** Builds a Router from route definitions. Throws on duplicate routes. */
34
+ export declare function compileRouter(routes: RouteDefinition[]): Router;
35
+ /**
36
+ * Generates route definitions from a compiled capability registry.
37
+ * Transport-level overrides take full precedence over URL inference.
38
+ */
39
+ export declare function generateRoutes(registry: CapabilityRegistry, options?: GenerateRoutesOptions): RouteDefinition[];
40
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAiB,MAAM,eAAe,CAAC;AAGvE,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;CAAE,GACrG;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAElE,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,qFAAqF;IACrF,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IACtC,uGAAuG;IACvG,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC1C,CAAC;AA0IF,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,CAmB/D;AA0BD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,GAAE,qBAA0B,GAClC,eAAe,EAAE,CAqBnB"}
package/dist/router.js ADDED
@@ -0,0 +1,255 @@
1
+ /**
2
+ * router.ts — radix-tree router for the REST transport
3
+ * No core dependency except types (CapabilityRegistry, AnyCapability).
4
+ */
5
+ import { inferIntent } from '@capixjs/core';
6
+ function newNode() {
7
+ return { handlers: new Map(), staticChildren: new Map() };
8
+ }
9
+ /** Splits a URL path into segments in a single pass (no intermediate array from filter). */
10
+ function splitPath(path) {
11
+ const segments = [];
12
+ let start = path.charCodeAt(0) === 47 ? 1 : 0; // skip leading /
13
+ for (let i = start; i <= path.length; i++) {
14
+ if (i === path.length || path.charCodeAt(i) === 47) { // 47 = '/'
15
+ if (i > start)
16
+ segments.push(path.slice(start, i));
17
+ start = i + 1;
18
+ }
19
+ }
20
+ return segments;
21
+ }
22
+ function insertRoute(root, method, path, capability) {
23
+ const segments = splitPath(path);
24
+ // method is already uppercase — compileRouter calls toUpperCase() before insertRoute
25
+ const upperMethod = method;
26
+ let node = root;
27
+ for (const seg of segments) {
28
+ if (seg.startsWith(':')) {
29
+ const paramName = seg.slice(1);
30
+ if (!node.paramChild) {
31
+ node.paramChild = { node: newNode(), methodNames: new Map([[upperMethod, paramName]]) };
32
+ }
33
+ else {
34
+ // Allow different param names per method — only error on same method with conflicting names
35
+ const existing = node.paramChild.methodNames.get(upperMethod);
36
+ if (existing !== undefined && existing !== paramName) {
37
+ throw new Error(`[capix] Router conflict: param name mismatch for ${upperMethod} at same level ` +
38
+ `(':${existing}' vs ':${paramName}') for capability '${capability}'`);
39
+ }
40
+ node.paramChild.methodNames.set(upperMethod, paramName);
41
+ }
42
+ node = node.paramChild.node;
43
+ }
44
+ else {
45
+ if (!node.staticChildren.has(seg)) {
46
+ node.staticChildren.set(seg, newNode());
47
+ }
48
+ node = node.staticChildren.get(seg);
49
+ }
50
+ }
51
+ if (node.handlers.has(upperMethod)) {
52
+ throw new Error(`[capix] Duplicate route: ${upperMethod} ${path} (capability: ${capability})`);
53
+ }
54
+ node.handlers.set(upperMethod, capability);
55
+ }
56
+ function matchRoute(root, method, segments, index, params) {
57
+ if (index === segments.length) {
58
+ if (node_hasAnyHandler(root)) {
59
+ const cap = root.handlers.get(method);
60
+ if (cap !== undefined) {
61
+ // null params means no path params were encountered — skip allocation.
62
+ return { found: true, capability: cap, params };
63
+ }
64
+ return { found: false, allowedMethods: [...root.handlers.keys()] };
65
+ }
66
+ return { found: false };
67
+ }
68
+ const seg = segments[index];
69
+ if (seg === undefined)
70
+ return { found: false };
71
+ // Static match preferred over param match
72
+ const staticChild = root.staticChildren.get(seg);
73
+ if (staticChild !== undefined) {
74
+ const result = matchRoute(staticChild, method, segments, index + 1, params);
75
+ if (result.found)
76
+ return result;
77
+ // If static matched path but not method, return that result
78
+ if (!result.found && result.allowedMethods !== undefined)
79
+ return result;
80
+ }
81
+ // Param match — look up the param name for the current method
82
+ if (root.paramChild !== undefined) {
83
+ // Conditional decode: skip when there are no percent-encoded chars
84
+ const decoded = seg.includes('%') ? decodeURIComponent(seg) : seg;
85
+ const paramName = root.paramChild.methodNames.get(method) ??
86
+ root.paramChild.methodNames.values().next().value ??
87
+ 'id';
88
+ // Lazily allocate params object (only when first param encountered).
89
+ // Mutate in place and restore on backtrack.
90
+ const actualParams = params ?? {};
91
+ const prev = actualParams[paramName];
92
+ actualParams[paramName] = decoded;
93
+ const result = matchRoute(root.paramChild.node, method, segments, index + 1, actualParams);
94
+ if (!result.found) {
95
+ if (prev === undefined)
96
+ delete actualParams[paramName];
97
+ else
98
+ actualParams[paramName] = prev;
99
+ }
100
+ return result;
101
+ }
102
+ return { found: false };
103
+ }
104
+ function node_hasAnyHandler(node) {
105
+ return node.handlers.size > 0;
106
+ }
107
+ // ---------------------------------------------------------------------------
108
+ // compileRouter
109
+ // ---------------------------------------------------------------------------
110
+ /** Builds a Router from route definitions. Throws on duplicate routes. */
111
+ export function compileRouter(routes) {
112
+ const root = newNode();
113
+ for (const route of routes) {
114
+ insertRoute(root, route.method.toUpperCase(), route.path, route.capability);
115
+ }
116
+ return {
117
+ routes,
118
+ // Callers must pass method in uppercase — transport guarantees this via req.method.
119
+ match(method, rawPath) {
120
+ // Strip query string without creating an intermediate array.
121
+ const qIdx = rawPath.indexOf('?');
122
+ const pathOnly = qIdx !== -1 ? rawPath.slice(0, qIdx) : rawPath;
123
+ const segments = splitPath(pathOnly);
124
+ // Start with null params; allocate only if a route param is matched.
125
+ return matchRoute(root, method, segments, 0, null);
126
+ },
127
+ };
128
+ }
129
+ // ---------------------------------------------------------------------------
130
+ // URL case conversion
131
+ // ---------------------------------------------------------------------------
132
+ function toKebabCase(s) {
133
+ return s.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
134
+ }
135
+ function toSnakeCase(s) {
136
+ return s.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase();
137
+ }
138
+ function applyUrlCase(s, urlCase) {
139
+ switch (urlCase) {
140
+ case 'kebab': return toKebabCase(s);
141
+ case 'snake': return toSnakeCase(s);
142
+ case 'camel': return s;
143
+ }
144
+ }
145
+ // ---------------------------------------------------------------------------
146
+ // generateRoutes
147
+ // ---------------------------------------------------------------------------
148
+ /**
149
+ * Generates route definitions from a compiled capability registry.
150
+ * Transport-level overrides take full precedence over URL inference.
151
+ */
152
+ export function generateRoutes(registry, options = {}) {
153
+ const urlCase = options.urlCase ?? 'kebab';
154
+ const overrides = options.overrides ?? {};
155
+ const routes = [];
156
+ for (const key of Object.keys(overrides)) {
157
+ if (!registry.has(key)) {
158
+ console.warn(`[capix] REST transport: override for '${key}' does not match any registered capability.`);
159
+ }
160
+ }
161
+ for (const [dotPath, cap] of registry) {
162
+ const override = overrides[dotPath];
163
+ if (override !== undefined) {
164
+ routes.push({ method: override.method, path: override.path, capability: dotPath });
165
+ continue;
166
+ }
167
+ routes.push(...inferRoutes(dotPath, cap, urlCase));
168
+ }
169
+ return routes;
170
+ }
171
+ function inferRoutes(dotPath, cap, urlCase) {
172
+ const segments = dotPath.split('.');
173
+ const key = segments[segments.length - 1] ?? dotPath;
174
+ // Group path = all segments except the last one, with case conversion applied
175
+ const groupSegments = segments.slice(0, -1).map((s) => applyUrlCase(s, urlCase));
176
+ const groupPath = '/' + groupSegments.join('/');
177
+ // Use cap.intent when the user set it explicitly; otherwise infer from key name
178
+ const intent = cap._intentExplicit ? cap.intent : inferIntent(key);
179
+ const hasIdField = cap.inputSchema
180
+ ? 'shape' in cap.inputSchema &&
181
+ typeof cap.inputSchema.shape === 'object' &&
182
+ cap.inputSchema.shape !== null &&
183
+ 'id' in cap.inputSchema.shape
184
+ : false;
185
+ // Key after case conversion (used when key appears in the URL)
186
+ const urlKey = applyUrlCase(key, urlCase);
187
+ switch (intent) {
188
+ case 'query':
189
+ if (hasIdField) {
190
+ // get* with id → GET /group/:id
191
+ return [{ method: 'GET', path: groupPath + '/:id', capability: dotPath }];
192
+ }
193
+ // list*, find*, fetch*, read*, search*, filter*, all* → always collection → GET /group
194
+ if (/^(list|find|fetch|read|search|filter|all)/i.test(key)) {
195
+ return [{ method: 'GET', path: groupPath || '/', capability: dotPath }];
196
+ }
197
+ {
198
+ // get* without id field: collection only when the remainder matches the parent
199
+ // group name. getMe → GET /group/me, getStats → GET /group/stats,
200
+ // getUsers (in users group) → GET /group.
201
+ const getMatch = /^get([A-Z].*)?/.exec(key);
202
+ if (getMatch) {
203
+ const remainder = getMatch[1] ?? '';
204
+ const lastGroup = groupSegments[groupSegments.length - 1] ?? '';
205
+ const rLow = remainder.toLowerCase();
206
+ const gLow = lastGroup.toLowerCase();
207
+ const matchesGroup = !remainder ||
208
+ rLow === gLow ||
209
+ rLow === gLow.replace(/s$/, '') || // "User" matches "users"
210
+ gLow === rLow.replace(/s$/, ''); // "Users" matches "user"
211
+ if (matchesGroup) {
212
+ return [{ method: 'GET', path: groupPath || '/', capability: dotPath }];
213
+ }
214
+ // Named: getMe → /group/me, getStats → /group/stats
215
+ const namedKey = applyUrlCase(remainder || key, urlCase);
216
+ const namedPath = groupSegments.length > 0
217
+ ? '/' + [...groupSegments, namedKey].join('/')
218
+ : remainder
219
+ ? '/' + namedKey
220
+ : '/' + applyUrlCase(key, urlCase);
221
+ return [{ method: 'GET', path: namedPath, capability: dotPath }];
222
+ }
223
+ }
224
+ // Named query (me, status, health, etc.) — no collection prefix → GET /group/key
225
+ {
226
+ const namedPath = groupSegments.length > 0 ? '/' + [...groupSegments, urlKey].join('/') : '/' + urlKey;
227
+ return [{ method: 'GET', path: namedPath, capability: dotPath }];
228
+ }
229
+ case 'mutation': {
230
+ // create* → POST /group (drop capability key from path)
231
+ // 'register' intentionally excluded — it's a named action (POST /auth/register), not a resource creation
232
+ const isCreate = /^(create|add|new)/i.test(key);
233
+ if (isCreate) {
234
+ return [{ method: 'POST', path: groupPath || '/', capability: dotPath }];
235
+ }
236
+ // un* → DELETE /group/:id/verb (inverse sub-resource action)
237
+ const unMatch = /^un([A-Za-z].*)/.exec(key);
238
+ if (unMatch) {
239
+ const verb = applyUrlCase(unMatch[1], urlCase);
240
+ const basePath = groupSegments.length > 0 ? groupPath : '';
241
+ return [{ method: 'DELETE', path: basePath + '/:id/' + verb, capability: dotPath }];
242
+ }
243
+ // Named action → POST /group/key
244
+ const actionPath = groupSegments.length > 0 ? '/' + [...groupSegments, urlKey].join('/') : '/' + urlKey;
245
+ return [{ method: 'POST', path: actionPath, capability: dotPath }];
246
+ }
247
+ case 'update':
248
+ return [{ method: 'PATCH', path: groupPath + '/:id', capability: dotPath }];
249
+ case 'replace':
250
+ return [{ method: 'PUT', path: groupPath + '/:id', capability: dotPath }];
251
+ case 'delete':
252
+ return [{ method: 'DELETE', path: groupPath + '/:id', capability: dotPath }];
253
+ }
254
+ }
255
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * serializer.ts — compile fast-json-stringify serializers from Zod output schemas.
3
+ *
4
+ * Replaces the generic `JSON.stringify` call in the response path with a schema-compiled
5
+ * serializer for capabilities that declare an outputSchema. Compiled at mount time; zero
6
+ * per-request setup cost.
7
+ */
8
+ import type { CapabilityRegistry } from '@capixjs/core';
9
+ /** Pre-compiled response serializer: data → '{"data":<json>}' */
10
+ export type ResponseSerializer = (data: unknown) => string;
11
+ declare const DEFAULT: ResponseSerializer;
12
+ export { DEFAULT as defaultSerializer };
13
+ /**
14
+ * Builds per-capability serializers from compiled output schemas.
15
+ * Falls back to JSON.stringify for capabilities without an outputSchema or
16
+ * if zod-to-json-schema cannot convert the schema.
17
+ */
18
+ export declare function buildSerializers(registry: CapabilityRegistry): Map<string, ResponseSerializer>;
19
+ //# sourceMappingURL=serializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,iEAAiE;AACjE,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,CAAC;AAE3D,QAAA,MAAM,OAAO,EAAE,kBAAsE,CAAC;AAEtF,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,CAAC;AAExC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAwB9F"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * serializer.ts — compile fast-json-stringify serializers from Zod output schemas.
3
+ *
4
+ * Replaces the generic `JSON.stringify` call in the response path with a schema-compiled
5
+ * serializer for capabilities that declare an outputSchema. Compiled at mount time; zero
6
+ * per-request setup cost.
7
+ */
8
+ import fastJsonStringify from 'fast-json-stringify';
9
+ import { zodToJsonSchema } from 'zod-to-json-schema';
10
+ const DEFAULT = (data) => '{"data":' + JSON.stringify(data) + '}';
11
+ export { DEFAULT as defaultSerializer };
12
+ /**
13
+ * Builds per-capability serializers from compiled output schemas.
14
+ * Falls back to JSON.stringify for capabilities without an outputSchema or
15
+ * if zod-to-json-schema cannot convert the schema.
16
+ */
17
+ export function buildSerializers(registry) {
18
+ const map = new Map();
19
+ for (const [dotPath, cap] of registry) {
20
+ if (cap.outputSchema === null)
21
+ continue;
22
+ try {
23
+ const jsonSchema = zodToJsonSchema(cap.outputSchema, {
24
+ target: 'jsonSchema7',
25
+ $refStrategy: 'none',
26
+ });
27
+ // Remove the $schema meta field — fjs doesn't need it
28
+ if (typeof jsonSchema === 'object' && jsonSchema !== null) {
29
+ delete jsonSchema['$schema'];
30
+ }
31
+ const serializeData = fastJsonStringify(jsonSchema);
32
+ map.set(dotPath, (data) => '{"data":' + serializeData(data) + '}');
33
+ }
34
+ catch {
35
+ // Schema type not supported by fjs (e.g. z.union with discriminants); fall back to JSON.stringify.
36
+ }
37
+ }
38
+ return map;
39
+ }
40
+ //# sourceMappingURL=serializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serializer.js","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAMrD,MAAM,OAAO,GAAuB,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;AAEtF,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,CAAC;AAExC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAA4B;IAC3D,MAAM,GAAG,GAAG,IAAI,GAAG,EAA8B,CAAC;IAElD,KAAK,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtC,IAAI,GAAG,CAAC,YAAY,KAAK,IAAI;YAAE,SAAS;QAExC,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,YAAY,EAAE;gBACnD,MAAM,EAAE,aAAa;gBACrB,YAAY,EAAE,MAAM;aACrB,CAAC,CAAC;YACH,sDAAsD;YACtD,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;gBAC1D,OAAQ,UAAsC,CAAC,SAAS,CAAC,CAAC;YAC5D,CAAC;YAED,MAAM,aAAa,GAAG,iBAAiB,CAAC,UAAqD,CAAC,CAAC;YAC/F,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC;YACP,mGAAmG;QACrG,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * transport.ts — HTTP/REST transport
3
+ *
4
+ * Uses node:http for HTTP/1.1. node:http2 requires TLS for HTTP/2 browser support
5
+ * (ALPN negotiation); plain h2c is not usable by curl or browsers. For production
6
+ * HTTP/2, wrap with http2.createSecureServer and pass your TLS certificates.
7
+ *
8
+ * Depends on: router.ts, capix core
9
+ */
10
+ import type { IncomingMessage, ServerResponse } from 'node:http';
11
+ import type { GroupTree, TransportWithCapabilities } from '@capixjs/core';
12
+ import type { HttpOverride } from './router.js';
13
+ import type { MultipartOptions } from './multipart.js';
14
+ export type RestTransportHooks = {
15
+ /** Called on every response before headers are written. Use to inject additional headers. */
16
+ readonly onRequest?: (req: IncomingMessage, res: ServerResponse) => void;
17
+ };
18
+ export type RestTransportOptions = {
19
+ readonly port: number;
20
+ readonly host?: string;
21
+ readonly cors?: {
22
+ readonly origin?: string | ((origin: string) => boolean);
23
+ readonly methods?: string;
24
+ readonly headers?: string;
25
+ };
26
+ readonly maxBodySize?: number;
27
+ readonly hooks?: RestTransportHooks;
28
+ /** Enable multipart/form-data parsing. Pass true or an options object. */
29
+ readonly multipart?: boolean | MultipartOptions;
30
+ /** Case style for inferred URL segments. Default: 'kebab' (bulkStatus → bulk-status). */
31
+ readonly urlCase?: 'kebab' | 'camel' | 'snake';
32
+ /**
33
+ * Per-request timeout in milliseconds. Default: 30_000 (30 seconds).
34
+ *
35
+ * Set to `false` to disable timeouts entirely. **Only use this in benchmarks
36
+ * or tests — never in production.** A hung capability will hold its connection
37
+ * and resources indefinitely, causing connection exhaustion under load.
38
+ */
39
+ readonly timeout?: number | false;
40
+ /**
41
+ * HTTP route overrides keyed by capability dot-path.
42
+ * Use for nested resource routes that URL inference cannot produce.
43
+ *
44
+ * @example
45
+ * overrides: {
46
+ * 'tasks.listProjectTasks': { method: 'GET', path: '/v1/projects/:projectId/tasks' },
47
+ * 'tasks.createProjectTask': { method: 'POST', path: '/v1/projects/:projectId/tasks' },
48
+ * }
49
+ */
50
+ readonly overrides?: Record<string, HttpOverride>;
51
+ /**
52
+ * Capability registry for this transport only.
53
+ * When set, overrides the server-level `capabilities` default for REST routes.
54
+ * Use to prevent non-REST capabilities from getting HTTP endpoints.
55
+ */
56
+ readonly capabilities?: GroupTree;
57
+ };
58
+ /** Creates a REST transport using node:http. */
59
+ export declare function restTransport(options: RestTransportOptions): TransportWithCapabilities;
60
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,KAAK,EAAyD,SAAS,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAEjI,OAAO,KAAK,EAAU,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAOvD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,6FAA6F;IAC7F,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;CAC1E,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE;QACd,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;QACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,CAAC;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IAChD,yFAAyF;IACzF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC/C;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAClC;;;;;;;;;OASG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAClD;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC;CACnC,CAAC;AAEF,gDAAgD;AAChD,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,yBAAyB,CAsStF"}
@@ -0,0 +1,303 @@
1
+ /**
2
+ * transport.ts — HTTP/REST transport
3
+ *
4
+ * Uses node:http for HTTP/1.1. node:http2 requires TLS for HTTP/2 browser support
5
+ * (ALPN negotiation); plain h2c is not usable by curl or browsers. For production
6
+ * HTTP/2, wrap with http2.createSecureServer and pass your TLS certificates.
7
+ *
8
+ * Depends on: router.ts, capix core
9
+ */
10
+ import * as http from 'node:http';
11
+ import { compileRouter, generateRoutes } from './router.js';
12
+ import { parseMultipart } from './multipart-parser.js';
13
+ import { buildSerializers, defaultSerializer } from './serializer.js';
14
+ const DEFAULT_MAX_BODY_SIZE = 1024 * 1024; // 1MB
15
+ const EMPTY_INPUT = {};
16
+ /** Creates a REST transport using node:http. */
17
+ export function restTransport(options) {
18
+ let server = null;
19
+ let router = null;
20
+ let invokeFn = null;
21
+ let serializers = null;
22
+ const corsOriginOpt = options.cors?.origin ?? '*';
23
+ const maxBodySize = options.maxBodySize ?? DEFAULT_MAX_BODY_SIZE;
24
+ const corsMethodsValue = options.cors?.methods ?? 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
25
+ const corsHeadersValue = options.cors?.headers ?? 'Content-Type, Authorization';
26
+ const hasDynamicOrigin = typeof corsOriginOpt === 'function';
27
+ const requestTimeout = options.timeout === undefined ? 30_000 : options.timeout;
28
+ // When timeout: false — share one never-aborted signal per transport instance.
29
+ const _noTimeoutSignal = requestTimeout === false ? new AbortController().signal : null;
30
+ if (requestTimeout === false) {
31
+ console.warn('[capix] WARNING: timeout: false disables request timeouts. ' +
32
+ 'Hung capabilities will hold resources indefinitely. ' +
33
+ 'Do not use this in production.');
34
+ }
35
+ // Pre-built headers for the fast path (static CORS origin).
36
+ // When origin is a function, fall back to per-request setCorsHeaders.
37
+ let _jsonHeaders200 = null;
38
+ let _jsonHeadersErr = null;
39
+ let _preflightHeaders = null;
40
+ if (!hasDynamicOrigin) {
41
+ const corsBase = {
42
+ 'Access-Control-Allow-Methods': corsMethodsValue,
43
+ 'Access-Control-Allow-Headers': corsHeadersValue,
44
+ };
45
+ if (corsOriginOpt)
46
+ corsBase['Access-Control-Allow-Origin'] = corsOriginOpt;
47
+ _jsonHeaders200 = { 'Content-Type': 'application/json', ...corsBase };
48
+ _jsonHeadersErr = { 'Content-Type': 'application/json', ...corsBase };
49
+ _preflightHeaders = { ...corsBase };
50
+ }
51
+ function setCorsHeaders(req, res) {
52
+ const reqOrigin = req.headers['origin'] ?? '';
53
+ const allowOrigin = corsOriginOpt(reqOrigin) ? reqOrigin : '';
54
+ if (allowOrigin)
55
+ res.setHeader('Access-Control-Allow-Origin', allowOrigin);
56
+ res.setHeader('Access-Control-Allow-Methods', corsMethodsValue);
57
+ res.setHeader('Access-Control-Allow-Headers', corsHeadersValue);
58
+ }
59
+ async function readBody(req) {
60
+ return new Promise((resolve, reject) => {
61
+ const chunks = [];
62
+ let size = 0;
63
+ req.on('data', (chunk) => {
64
+ size += chunk.byteLength;
65
+ if (size > maxBodySize) {
66
+ req.resume(); // drain remaining data without closing socket so we can still respond
67
+ reject(new Error('PAYLOAD_TOO_LARGE'));
68
+ return;
69
+ }
70
+ chunks.push(chunk);
71
+ });
72
+ req.on('end', () => resolve(Buffer.concat(chunks)));
73
+ req.on('error', reject);
74
+ });
75
+ }
76
+ function coerceQueryValue(val) {
77
+ if (val === 'true')
78
+ return true;
79
+ if (val === 'false')
80
+ return false;
81
+ if (val !== '' && !isNaN(Number(val)))
82
+ return Number(val);
83
+ return val;
84
+ }
85
+ function parseQueryString(url) {
86
+ const idx = url.indexOf('?');
87
+ if (idx === -1)
88
+ return null;
89
+ const qs = url.slice(idx + 1);
90
+ const result = {};
91
+ for (const part of qs.split('&')) {
92
+ const eqIdx = part.indexOf('=');
93
+ if (eqIdx === -1)
94
+ continue;
95
+ const key = decodeURIComponent(part.slice(0, eqIdx));
96
+ const val = decodeURIComponent(part.slice(eqIdx + 1));
97
+ result[key] = coerceQueryValue(val);
98
+ }
99
+ return result;
100
+ }
101
+ function sendJson(res, status, body) {
102
+ const json = JSON.stringify(body);
103
+ res.writeHead(status, _jsonHeadersErr ?? { 'Content-Type': 'application/json' });
104
+ res.end(json);
105
+ }
106
+ function handler(req, res) {
107
+ // Fast path: pre-built static headers avoid per-request setHeader calls.
108
+ // Dynamic origin functions still use setCorsHeaders.
109
+ if (hasDynamicOrigin)
110
+ setCorsHeaders(req, res);
111
+ options.hooks?.onRequest?.(req, res);
112
+ // CORS preflight
113
+ if (req.method === 'OPTIONS') {
114
+ if (_preflightHeaders) {
115
+ res.writeHead(204, _preflightHeaders);
116
+ }
117
+ else {
118
+ res.writeHead(204);
119
+ }
120
+ res.end();
121
+ return;
122
+ }
123
+ if (!router || !invokeFn) {
124
+ sendJson(res, 503, { error: 'ServiceUnavailable', message: 'Server not ready' });
125
+ return;
126
+ }
127
+ const url = req.url ?? '/';
128
+ // Node.js provides uppercase methods per RFC 7230; explicit toUpperCase() documents the contract.
129
+ const method = (req.method ?? 'GET').toUpperCase();
130
+ const match = router.match(method, url);
131
+ if (!match.found) {
132
+ if (match.allowedMethods !== undefined) {
133
+ res.setHeader('Allow', match.allowedMethods.join(', '));
134
+ sendJson(res, 405, { error: 'MethodNotAllowed', message: 'Method not allowed' });
135
+ }
136
+ else {
137
+ sendJson(res, 404, { error: 'NotFound', message: 'Not found' });
138
+ }
139
+ return;
140
+ }
141
+ const queryParams = parseQueryString(url);
142
+ const pathParams = match.params;
143
+ const noBody = method === 'GET' || method === 'HEAD' || method === 'DELETE';
144
+ // Async continuation to keep handler signature synchronous (avoids unhandled rejection)
145
+ const finish = async () => {
146
+ let bodyParams = {};
147
+ let rawBodyForContext;
148
+ if (!noBody) {
149
+ let rawBody;
150
+ try {
151
+ rawBody = await readBody(req);
152
+ }
153
+ catch (err) {
154
+ if (err instanceof Error && err.message === 'PAYLOAD_TOO_LARGE') {
155
+ sendJson(res, 413, { error: 'PayloadTooLarge', message: 'Request body exceeds size limit' });
156
+ return;
157
+ }
158
+ sendJson(res, 400, { error: 'BadRequest', message: 'Failed to read request body' });
159
+ return;
160
+ }
161
+ if (rawBody.length > 0) {
162
+ rawBodyForContext = rawBody;
163
+ const contentType = req.headers['content-type'] ?? '';
164
+ if (contentType.includes('multipart/form-data') && options.multipart) {
165
+ const multipartOpts = typeof options.multipart === 'object' ? options.multipart : {};
166
+ try {
167
+ const parsed = await parseMultipart(req.headers, rawBody, multipartOpts);
168
+ for (const [k, v] of Object.entries(parsed.fields))
169
+ bodyParams[k] = coerceQueryValue(v);
170
+ for (const [k, f] of Object.entries(parsed.files))
171
+ bodyParams[k] = f;
172
+ }
173
+ catch (err) {
174
+ const status = err.status ?? 400;
175
+ const message = err instanceof Error ? err.message : 'Failed to parse multipart body';
176
+ sendJson(res, status, { error: status === 413 ? 'PayloadTooLarge' : 'BadRequest', message });
177
+ return;
178
+ }
179
+ }
180
+ else if (contentType.includes('application/json')) {
181
+ try {
182
+ bodyParams = JSON.parse(rawBody.toString('utf8'));
183
+ }
184
+ catch {
185
+ sendJson(res, 400, { error: 'BadRequest', message: 'Invalid JSON body' });
186
+ return;
187
+ }
188
+ }
189
+ }
190
+ }
191
+ // Merge: query params < body params < path params (path wins).
192
+ // Router returns null for params when no path params exist (avoids allocation).
193
+ // For the common case (GET with no query and no path params), reuse EMPTY_INPUT.
194
+ let input;
195
+ if (pathParams === null && noBody && queryParams === null) {
196
+ input = EMPTY_INPUT;
197
+ }
198
+ else {
199
+ input = {};
200
+ if (queryParams !== null)
201
+ Object.assign(input, queryParams);
202
+ Object.assign(input, bodyParams);
203
+ if (pathParams !== null)
204
+ Object.assign(input, pathParams);
205
+ }
206
+ // Build flat headers map using for..in (avoids Object.entries array allocation).
207
+ const headers = {};
208
+ for (const k in req.headers) {
209
+ const v = req.headers[k];
210
+ if (v !== undefined)
211
+ headers[k] = Array.isArray(v) ? v.join(', ') : v;
212
+ }
213
+ const signal = _noTimeoutSignal ?? AbortSignal.timeout(requestTimeout);
214
+ const invocation = invokeFn({
215
+ capability: match.capability,
216
+ input,
217
+ headers,
218
+ signal,
219
+ ...(rawBodyForContext !== undefined ? { rawBody: rawBodyForContext } : {}),
220
+ });
221
+ // Race invocation against the abort signal so hung capabilities don't hold resources.
222
+ // When timeout: false the signal never fires, so we await directly.
223
+ const response = requestTimeout === false
224
+ ? await invocation
225
+ : await Promise.race([
226
+ invocation,
227
+ new Promise((resolve) => {
228
+ signal.addEventListener('abort', () => resolve({
229
+ ok: false,
230
+ error: { status: 504, error: 'GatewayTimeout', message: 'Request timed out' },
231
+ }), { once: true });
232
+ }),
233
+ ]);
234
+ if (response.ok) {
235
+ if (response.data === null || response.data === undefined) {
236
+ res.writeHead(204, _preflightHeaders ?? undefined);
237
+ res.end();
238
+ }
239
+ else {
240
+ const serialize = serializers?.get(match.capability) ?? defaultSerializer;
241
+ const json = serialize(response.data);
242
+ res.writeHead(200, _jsonHeaders200 ?? { 'Content-Type': 'application/json' });
243
+ res.end(json);
244
+ }
245
+ }
246
+ else {
247
+ const { status, error, message, meta } = response.error;
248
+ const body = { error, message };
249
+ if (meta !== undefined)
250
+ body['meta'] = meta;
251
+ // Add Retry-After header for rate limit responses per RFC 6585
252
+ if (status === 429 && typeof meta?.retryAfter === 'number') {
253
+ res.setHeader('Retry-After', String(meta.retryAfter));
254
+ }
255
+ sendJson(res, status, body);
256
+ }
257
+ };
258
+ finish().catch((err) => {
259
+ console.error('[capix:rest] Unhandled error in handler:', err);
260
+ if (!res.headersSent) {
261
+ sendJson(res, 500, { error: 'Internal', message: 'Internal server error' });
262
+ }
263
+ });
264
+ }
265
+ return {
266
+ ...(options.capabilities !== undefined ? { _capabilities: options.capabilities } : {}),
267
+ async mount(invoke, mountOptions) {
268
+ invokeFn = invoke;
269
+ const routeOpts = {
270
+ ...(options.urlCase !== undefined ? { urlCase: options.urlCase } : {}),
271
+ ...(options.overrides !== undefined ? { overrides: options.overrides } : {}),
272
+ };
273
+ const routes = generateRoutes(mountOptions.registry, routeOpts);
274
+ router = compileRouter(routes);
275
+ serializers = buildSerializers(mountOptions.registry);
276
+ console.log('\nCapix REST transport starting...');
277
+ for (const route of routes) {
278
+ console.log(` ✓ ${route.method.padEnd(7)} ${route.path}`);
279
+ }
280
+ return new Promise((resolve, reject) => {
281
+ server = http.createServer(handler);
282
+ server.on('error', reject);
283
+ server.listen(options.port, options.host ?? '0.0.0.0', () => {
284
+ console.log(`\n Listening on http://localhost:${options.port}\n`);
285
+ resolve();
286
+ });
287
+ });
288
+ },
289
+ async unmount() {
290
+ return new Promise((resolve, reject) => {
291
+ if (!server)
292
+ return resolve();
293
+ server.close((err) => {
294
+ if (err)
295
+ reject(err);
296
+ else
297
+ resolve();
298
+ });
299
+ });
300
+ },
301
+ };
302
+ }
303
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGtE,MAAM,qBAAqB,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,MAAM;AACjD,MAAM,WAAW,GAA4B,EAAE,CAAC;AAgDhD,gDAAgD;AAChD,MAAM,UAAU,aAAa,CAAC,OAA6B;IACzD,IAAI,MAAM,GAAuB,IAAI,CAAC;IACtC,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,QAAQ,GAAoB,IAAI,CAAC;IACrC,IAAI,WAAW,GAA2C,IAAI,CAAC;IAE/D,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,IAAI,GAAG,CAAC;IAClD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,qBAAqB,CAAC;IACjE,MAAM,gBAAgB,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,wCAAwC,CAAC;IAC3F,MAAM,gBAAgB,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,6BAA6B,CAAC;IAChF,MAAM,gBAAgB,GAAG,OAAO,aAAa,KAAK,UAAU,CAAC;IAC7D,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IAChF,+EAA+E;IAC/E,MAAM,gBAAgB,GAAG,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,eAAe,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACxF,IAAI,cAAc,KAAK,KAAK,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,CACV,6DAA6D;YAC7D,sDAAsD;YACtD,gCAAgC,CACjC,CAAC;IACJ,CAAC;IAED,4DAA4D;IAC5D,sEAAsE;IACtE,IAAI,eAAe,GAAkC,IAAI,CAAC;IAC1D,IAAI,eAAe,GAAkC,IAAI,CAAC;IAC1D,IAAI,iBAAiB,GAAkC,IAAI,CAAC;IAE5D,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,QAAQ,GAA2B;YACvC,8BAA8B,EAAE,gBAAgB;YAChD,8BAA8B,EAAE,gBAAgB;SACjD,CAAC;QACF,IAAI,aAAa;YAAE,QAAQ,CAAC,6BAA6B,CAAC,GAAG,aAAa,CAAC;QAC3E,eAAe,GAAI,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,QAAQ,EAAE,CAAC;QACvE,eAAe,GAAI,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,QAAQ,EAAE,CAAC;QACvE,iBAAiB,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;IACtC,CAAC;IAED,SAAS,cAAc,CAAC,GAAoB,EAAE,GAAmB;QAC/D,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC9C,MAAM,WAAW,GAAI,aAAwC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1F,IAAI,WAAW;YAAE,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,WAAW,CAAC,CAAC;QAC3E,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,gBAAgB,CAAC,CAAC;QAChE,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,gBAAgB,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,UAAU,QAAQ,CAAC,GAAoB;QAC1C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;YAEb,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBAC/B,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC;gBACzB,IAAI,IAAI,GAAG,WAAW,EAAE,CAAC;oBACvB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,sEAAsE;oBACpF,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;oBACvC,OAAO;gBACT,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACpD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS,gBAAgB,CAAC,GAAW;QACnC,IAAI,GAAG,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QAChC,IAAI,GAAG,KAAK,OAAO;YAAE,OAAO,KAAK,CAAC;QAClC,IAAI,GAAG,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1D,OAAO,GAAG,CAAC;IACb,CAAC;IAED,SAAS,gBAAgB,CAAC,GAAW;QACnC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC5B,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAC9B,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,SAAS;YAC3B,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;YACrD,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;YACtD,MAAM,CAAC,GAAG,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,SAAS,QAAQ,CAAC,GAAmB,EAAE,MAAc,EAAE,IAAa;QAClE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAClC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,eAAe,IAAI,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;QACjF,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,SAAS,OAAO,CAAC,GAAoB,EAAE,GAAmB;QACxD,yEAAyE;QACzE,qDAAqD;QACrD,IAAI,gBAAgB;YAAE,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC/C,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAErC,iBAAiB;QACjB,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,iBAAiB,EAAE,CAAC;gBACtB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;YACD,GAAG,CAAC,GAAG,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QAED,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACzB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAC;YACjF,OAAO;QACT,CAAC;QAED,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC;QAC3B,kGAAkG;QAClG,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QAEnD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAExC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;gBACvC,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBACxD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAC;YACnF,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;YAClE,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,QAAQ,CAAC;QAE5E,wFAAwF;QACxF,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;YACvC,IAAI,UAAU,GAA4B,EAAE,CAAC;YAC7C,IAAI,iBAAqC,CAAC;YAE1C,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,IAAI,OAAe,CAAC;gBACpB,IAAI,CAAC;oBACH,OAAO,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAChC,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,KAAK,mBAAmB,EAAE,CAAC;wBAChE,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC,CAAC;wBAC7F,OAAO;oBACT,CAAC;oBACD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC,CAAC;oBACpF,OAAO;gBACT,CAAC;gBAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvB,iBAAiB,GAAG,OAAO,CAAC;oBAC5B,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;oBACtD,IAAI,WAAW,CAAC,QAAQ,CAAC,qBAAqB,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;wBACrE,MAAM,aAAa,GACjB,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;wBACjE,IAAI,CAAC;4BACH,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;4BACzE,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;gCAAE,UAAU,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;4BACxF,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;gCAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;wBACvE,CAAC;wBAAC,OAAO,GAAG,EAAE,CAAC;4BACb,MAAM,MAAM,GAAI,GAA2B,CAAC,MAAM,IAAI,GAAG,CAAC;4BAC1D,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,gCAAgC,CAAC;4BACtF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;4BAC7F,OAAO;wBACT,CAAC;oBACH,CAAC;yBAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;wBACpD,IAAI,CAAC;4BACH,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA4B,CAAC;wBAC/E,CAAC;wBAAC,MAAM,CAAC;4BACP,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAC;4BAC1E,OAAO;wBACT,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAED,+DAA+D;YAC/D,gFAAgF;YAChF,iFAAiF;YACjF,IAAI,KAA8B,CAAC;YACnC,IAAI,UAAU,KAAK,IAAI,IAAI,MAAM,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBAC1D,KAAK,GAAG,WAAW,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,KAAK,GAAG,EAAE,CAAC;gBACX,IAAI,WAAW,KAAK,IAAI;oBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;gBAC5D,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;gBACjC,IAAI,UAAU,KAAK,IAAI;oBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;YAC5D,CAAC;YAED,iFAAiF;YACjF,MAAM,OAAO,GAA2B,EAAE,CAAC;YAC3C,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;gBAC5B,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBACzB,IAAI,CAAC,KAAK,SAAS;oBAAE,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACxE,CAAC;YAED,MAAM,MAAM,GAAG,gBAAgB,IAAI,WAAW,CAAC,OAAO,CAAC,cAAwB,CAAC,CAAC;YAEjF,MAAM,UAAU,GAAG,QAAS,CAAC;gBAC3B,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,KAAK;gBACL,OAAO;gBACP,MAAM;gBACN,GAAG,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3E,CAAC,CAAC;YAEH,sFAAsF;YACtF,oEAAoE;YACpE,MAAM,QAAQ,GAAuB,cAAc,KAAK,KAAK;gBAC3D,CAAC,CAAC,MAAM,UAAU;gBAClB,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC;oBACjB,UAAU;oBACV,IAAI,OAAO,CAAqB,CAAC,OAAO,EAAE,EAAE;wBAC1C,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC;4BAC7C,EAAE,EAAE,KAAK;4BACT,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,mBAAmB,EAAE;yBAC9E,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;oBACtB,CAAC,CAAC;iBACH,CAAC,CAAC;YAEP,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC1D,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,iBAAiB,IAAI,SAAS,CAAC,CAAC;oBACnD,GAAG,CAAC,GAAG,EAAE,CAAC;gBACZ,CAAC;qBAAM,CAAC;oBACN,MAAM,SAAS,GAAG,WAAW,EAAE,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,iBAAiB,CAAC;oBAC1E,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;oBACtC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,eAAe,IAAI,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAC9E,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC;gBACxD,MAAM,IAAI,GAA4B,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;gBACzD,IAAI,IAAI,KAAK,SAAS;oBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;gBAC5C,+DAA+D;gBAC/D,IAAI,MAAM,KAAK,GAAG,IAAI,OAAQ,IAA6C,EAAE,UAAU,KAAK,QAAQ,EAAE,CAAC;oBACrG,GAAG,CAAC,SAAS,CAAC,aAAa,EAAE,MAAM,CAAE,IAA+B,CAAC,UAAU,CAAC,CAAC,CAAC;gBACpF,CAAC;gBACD,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACrB,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,GAAG,CAAC,CAAC;YAC/D,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAEtF,KAAK,CAAC,KAAK,CAAC,MAAgB,EAAE,YAA0B;YACtD,QAAQ,GAAG,MAAM,CAAC;YAClB,MAAM,SAAS,GAAG;gBAChB,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtE,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC7E,CAAC;YACF,MAAM,MAAM,GAAG,cAAc,CAAC,YAAY,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAChE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YAC/B,WAAW,GAAG,gBAAgB,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YAEtD,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAClD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAC7D,CAAC;YAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;gBACpC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC3B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,SAAS,EAAE,GAAG,EAAE;oBAC1D,OAAO,CAAC,GAAG,CAAC,qCAAqC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;oBACnE,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,OAAO;YACX,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,IAAI,CAAC,MAAM;oBAAE,OAAO,OAAO,EAAE,CAAC;gBAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;oBACnB,IAAI,GAAG;wBAAE,MAAM,CAAC,GAAG,CAAC,CAAC;;wBAChB,OAAO,EAAE,CAAC;gBACjB,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@capixjs/transport-rest",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Capix HTTP/REST transport",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "!dist/**/*.test.*",
18
+ "LICENSE"
19
+ ],
20
+ "keywords": [
21
+ "capix",
22
+ "rest",
23
+ "http",
24
+ "transport",
25
+ "router",
26
+ "multipart",
27
+ "file-upload",
28
+ "typescript"
29
+ ],
30
+ "engines": {
31
+ "node": ">=20"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "test": "vitest run",
36
+ "typecheck": "tsc --noEmit"
37
+ },
38
+ "dependencies": {
39
+ "busboy": "^1.6.0",
40
+ "fast-json-stringify": "^6.4.0",
41
+ "zod": "^3.23.0",
42
+ "zod-to-json-schema": "^3.25.2"
43
+ },
44
+ "peerDependencies": {
45
+ "@capixjs/core": ">=0.1.0-0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/busboy": "^1.5.0",
49
+ "@types/form-data": "^2.5.2",
50
+ "@types/node": "^20.0.0",
51
+ "form-data": "^4.0.5",
52
+ "typescript": "^5.5.0",
53
+ "vitest": "^1.6.0",
54
+ "@capixjs/core": "workspace:*"
55
+ }
56
+ }