vitek-plugin 0.1.2-beta.5 → 0.2.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -4
- package/dist/adapters/vite/dev-server-middleware.d.ts +8 -0
- package/dist/adapters/vite/dev-server-middleware.d.ts.map +1 -0
- package/dist/adapters/vite/dev-server-middleware.js +30 -0
- package/dist/adapters/vite/dev-server-state.d.ts +41 -0
- package/dist/adapters/vite/dev-server-state.d.ts.map +1 -0
- package/dist/adapters/vite/dev-server-state.js +191 -0
- package/dist/adapters/vite/dev-server.d.ts +2 -21
- package/dist/adapters/vite/dev-server.d.ts.map +1 -1
- package/dist/adapters/vite/dev-server.js +7 -379
- package/dist/adapters/vite/path-utils.d.ts +20 -0
- package/dist/adapters/vite/path-utils.d.ts.map +1 -0
- package/dist/adapters/vite/path-utils.js +46 -0
- package/dist/adapters/vite/path-utils.test.d.ts +2 -0
- package/dist/adapters/vite/path-utils.test.d.ts.map +1 -0
- package/dist/adapters/vite/path-utils.test.js +79 -0
- package/dist/build/build-api-bundle.d.ts +1 -0
- package/dist/build/build-api-bundle.d.ts.map +1 -1
- package/dist/build/build-api-bundle.js +38 -3
- package/dist/build/build-api-bundle.test.d.ts +2 -0
- package/dist/build/build-api-bundle.test.d.ts.map +1 -0
- package/dist/build/build-api-bundle.test.js +50 -0
- package/dist/build/build-sockets-bundle.test.d.ts +2 -0
- package/dist/build/build-sockets-bundle.test.d.ts.map +1 -0
- package/dist/build/build-sockets-bundle.test.js +49 -0
- package/dist/cli/cli.d.ts +8 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +25 -0
- package/dist/cli/fixtures/serve-config/vitek.config.d.mts +6 -0
- package/dist/cli/fixtures/serve-config/vitek.config.d.mts.map +1 -0
- package/dist/cli/fixtures/serve-config/vitek.config.mjs +19 -0
- package/dist/cli/init.d.ts +15 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +99 -0
- package/dist/cli/init.test.d.ts +2 -0
- package/dist/cli/init.test.d.ts.map +1 -0
- package/dist/cli/init.test.js +117 -0
- package/dist/cli/mcp-project-config.d.ts +8 -0
- package/dist/cli/mcp-project-config.d.ts.map +1 -0
- package/dist/cli/mcp-project-config.js +26 -0
- package/dist/cli/mcp-project.d.ts +2 -0
- package/dist/cli/mcp-project.d.ts.map +1 -0
- package/dist/cli/mcp-project.js +101 -0
- package/dist/cli/serve.d.ts +27 -1
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +85 -10
- package/dist/cli/serve.test.d.ts +2 -0
- package/dist/cli/serve.test.d.ts.map +1 -0
- package/dist/cli/serve.test.js +108 -0
- package/dist/core/asyncapi/generate.d.ts +5 -3
- package/dist/core/asyncapi/generate.d.ts.map +1 -1
- package/dist/core/asyncapi/generate.test.d.ts +2 -0
- package/dist/core/asyncapi/generate.test.d.ts.map +1 -0
- package/dist/core/asyncapi/generate.test.js +120 -0
- package/dist/core/context/create-context.d.ts +2 -0
- package/dist/core/context/create-context.d.ts.map +1 -1
- package/dist/core/file-system/extract-type-from-file.d.ts +4 -0
- package/dist/core/file-system/extract-type-from-file.d.ts.map +1 -0
- package/dist/core/file-system/extract-type-from-file.js +77 -0
- package/dist/core/file-system/extract-type-from-file.test.d.ts +2 -0
- package/dist/core/file-system/extract-type-from-file.test.d.ts.map +1 -0
- package/dist/core/file-system/extract-type-from-file.test.js +75 -0
- package/dist/core/file-system/watch-api-dir.d.ts +4 -1
- package/dist/core/file-system/watch-api-dir.d.ts.map +1 -1
- package/dist/core/file-system/watch-api-dir.js +31 -6
- package/dist/core/file-system/watch-api-dir.test.d.ts +2 -0
- package/dist/core/file-system/watch-api-dir.test.d.ts.map +1 -0
- package/dist/core/file-system/watch-api-dir.test.js +38 -0
- package/dist/core/generation/run-file-generation.d.ts +24 -0
- package/dist/core/generation/run-file-generation.d.ts.map +1 -0
- package/dist/core/generation/run-file-generation.js +90 -0
- package/dist/core/generation/run-file-generation.test.d.ts +2 -0
- package/dist/core/generation/run-file-generation.test.d.ts.map +1 -0
- package/dist/core/generation/run-file-generation.test.js +151 -0
- package/dist/core/introspection/manifest.d.ts +24 -0
- package/dist/core/introspection/manifest.d.ts.map +1 -0
- package/dist/core/introspection/manifest.js +41 -0
- package/dist/core/introspection/manifest.test.d.ts +2 -0
- package/dist/core/introspection/manifest.test.d.ts.map +1 -0
- package/dist/core/introspection/manifest.test.js +62 -0
- package/dist/core/middleware/get-applicable-middlewares.d.ts +7 -0
- package/dist/core/middleware/get-applicable-middlewares.d.ts.map +1 -1
- package/dist/core/middleware/get-applicable-middlewares.js +23 -15
- package/dist/core/middleware/get-applicable-middlewares.test.js +36 -1
- package/dist/core/openapi/generate.d.ts +5 -74
- package/dist/core/openapi/generate.d.ts.map +1 -1
- package/dist/core/openapi/generate.js +4 -419
- package/dist/core/openapi/generate.test.d.ts +2 -0
- package/dist/core/openapi/generate.test.d.ts.map +1 -0
- package/dist/core/openapi/generate.test.js +184 -0
- package/dist/core/openapi/jsdoc.d.ts +3 -0
- package/dist/core/openapi/jsdoc.d.ts.map +1 -0
- package/dist/core/openapi/jsdoc.js +68 -0
- package/dist/core/openapi/jsdoc.test.d.ts +2 -0
- package/dist/core/openapi/jsdoc.test.d.ts.map +1 -0
- package/dist/core/openapi/jsdoc.test.js +111 -0
- package/dist/core/openapi/spec-builder.d.ts +4 -0
- package/dist/core/openapi/spec-builder.d.ts.map +1 -0
- package/dist/core/openapi/spec-builder.js +257 -0
- package/dist/core/openapi/spec-builder.test.d.ts +2 -0
- package/dist/core/openapi/spec-builder.test.d.ts.map +1 -0
- package/dist/core/openapi/spec-builder.test.js +93 -0
- package/dist/core/openapi/types.d.ts +42 -0
- package/dist/core/openapi/types.d.ts.map +1 -0
- package/dist/core/openapi/types.js +5 -0
- package/dist/core/server/cors.d.ts +29 -0
- package/dist/core/server/cors.d.ts.map +1 -0
- package/dist/core/server/cors.js +55 -0
- package/dist/core/server/cors.test.d.ts +2 -0
- package/dist/core/server/cors.test.d.ts.map +1 -0
- package/dist/core/server/cors.test.js +49 -0
- package/dist/core/server/proxy.d.ts +16 -0
- package/dist/core/server/proxy.d.ts.map +1 -0
- package/dist/core/server/proxy.js +20 -0
- package/dist/core/server/proxy.test.d.ts +2 -0
- package/dist/core/server/proxy.test.d.ts.map +1 -0
- package/dist/core/server/proxy.test.js +53 -0
- package/dist/core/server/request-handler.d.ts +17 -3
- package/dist/core/server/request-handler.d.ts.map +1 -1
- package/dist/core/server/request-handler.js +192 -84
- package/dist/core/server/request-handler.test.js +287 -22
- package/dist/core/socket/socket-handler.test.d.ts +2 -0
- package/dist/core/socket/socket-handler.test.d.ts.map +1 -0
- package/dist/core/socket/socket-handler.test.js +107 -0
- package/dist/core/types/schema.test.d.ts +2 -0
- package/dist/core/types/schema.test.d.ts.map +1 -0
- package/dist/core/types/schema.test.js +41 -0
- package/dist/core/validation/types.d.ts +2 -1
- package/dist/core/validation/types.d.ts.map +1 -1
- package/dist/core/validation/validator.d.ts +4 -16
- package/dist/core/validation/validator.d.ts.map +1 -1
- package/dist/core/validation/validator.js +4 -16
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/plugin/context.d.ts +15 -0
- package/dist/plugin/context.d.ts.map +1 -0
- package/dist/plugin/context.js +12 -0
- package/dist/plugin/options.d.ts +46 -0
- package/dist/plugin/options.d.ts.map +1 -0
- package/dist/plugin/options.js +1 -0
- package/dist/plugin/plugin-api.d.ts +49 -0
- package/dist/plugin/plugin-api.d.ts.map +1 -0
- package/dist/plugin/plugin-api.js +5 -0
- package/dist/plugin/vitek-build.d.ts +7 -0
- package/dist/plugin/vitek-build.d.ts.map +1 -0
- package/dist/plugin/vitek-build.js +104 -0
- package/dist/plugin/vitek-config.d.ts +4 -0
- package/dist/plugin/vitek-config.d.ts.map +1 -0
- package/dist/plugin/vitek-config.js +51 -0
- package/dist/plugin/vitek-config.test.d.ts +2 -0
- package/dist/plugin/vitek-config.test.d.ts.map +1 -0
- package/dist/plugin/vitek-config.test.js +62 -0
- package/dist/plugin/vitek-dev.d.ts +7 -0
- package/dist/plugin/vitek-dev.d.ts.map +1 -0
- package/dist/plugin/vitek-dev.js +71 -0
- package/dist/plugin/vitek-preview.d.ts +7 -0
- package/dist/plugin/vitek-preview.d.ts.map +1 -0
- package/dist/plugin/vitek-preview.js +107 -0
- package/dist/plugin/vitek-resolve.d.ts +7 -0
- package/dist/plugin/vitek-resolve.d.ts.map +1 -0
- package/dist/plugin/vitek-resolve.js +25 -0
- package/dist/plugin/vitek-transform.d.ts +7 -0
- package/dist/plugin/vitek-transform.d.ts.map +1 -0
- package/dist/plugin/vitek-transform.js +55 -0
- package/dist/plugin/vitek.d.ts +10 -0
- package/dist/plugin/vitek.d.ts.map +1 -0
- package/dist/plugin/vitek.js +27 -0
- package/dist/plugin.d.ts +3 -32
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +2 -246
- package/dist/plugin.test.js +114 -28
- package/dist/shared/response-helpers.d.ts +21 -0
- package/dist/shared/response-helpers.d.ts.map +1 -1
- package/dist/shared/response-helpers.js +41 -0
- package/dist/shared/response-helpers.test.js +54 -1
- package/package.json +19 -4
package/README.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
**File-based HTTP API generation for Vite**
|
|
7
7
|
|
|
8
|
-
[](https://github.com/martinsbicudo/vitek-plugin)
|
|
9
9
|
[](LICENSE)
|
|
10
|
-
[](https://vitejs.dev/)
|
|
10
|
+
[](https://vitejs.dev/)
|
|
11
11
|
|
|
12
12
|
> **Beta Version**: This project is currently in beta. APIs may change in future releases.
|
|
13
13
|
</div>
|
|
@@ -20,12 +20,25 @@ Vitek is a Vite plugin that turns a folder of files into an HTTP API.
|
|
|
20
20
|
|
|
21
21
|
**Full documentation:** [docs/](./docs/) · [View online](https://martinsbicudo.github.io/vitek-plugin/) (VitePress — run `npm run docs:dev` or `pnpm docs:dev` to view locally).
|
|
22
22
|
|
|
23
|
-
**
|
|
23
|
+
**MCP:** Expose your project API to AI assistants (Cursor, Claude) with `vitek mcp` — [MCP – API do projeto](docs/guide/mcp-project.md).
|
|
24
|
+
|
|
25
|
+
**Examples:** [examples/](./examples/) — `basic-js`, `js-react`, `typescript-react`, `import-external`, `socket-only`, `api-docs`, `prisma`, and `docker`.
|
|
24
26
|
|
|
25
27
|
---
|
|
26
28
|
|
|
27
29
|
## Quick Start
|
|
28
30
|
|
|
31
|
+
**Option A — scaffold with init (recommended for new projects):**
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install vitek-plugin
|
|
35
|
+
npx vitek init
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Then `npm run dev` and open `http://localhost:5173/api/health`. The init command creates `src/api/health.get.ts` and adds vitek to your Vite config.
|
|
39
|
+
|
|
40
|
+
**Option B — manual setup:**
|
|
41
|
+
|
|
29
42
|
```bash
|
|
30
43
|
npm install vitek-plugin
|
|
31
44
|
```
|
|
@@ -86,11 +99,29 @@ Then open `http://localhost:5173/api-docs.html` for interactive API documentatio
|
|
|
86
99
|
|
|
87
100
|
---
|
|
88
101
|
|
|
102
|
+
## Security
|
|
103
|
+
|
|
104
|
+
- **Body size**: Use `maxBodySize` (bytes) in plugin options or in `vitek.config.mjs` for production to reject oversized bodies with 413 and avoid unbounded memory use.
|
|
105
|
+
- **CORS**: Configure `cors` (e.g. specific `origin`) in production; avoid `*` with credentials.
|
|
106
|
+
- **Trust proxy**: Set `trustProxy: true` only when behind a reverse proxy; do not trust client-sent `X-Forwarded-*` without it.
|
|
107
|
+
- **Response headers**: Header values set from handler responses are sanitized (CRLF removed) to reduce response-splitting risk.
|
|
108
|
+
- **Validation**: `ValidationRule.pattern` (string) is compiled with `new RegExp`. Avoid complex or user-supplied patterns (ReDoS); prefer allowlists or simple character classes.
|
|
109
|
+
- **Dependencies**: Run `pnpm audit` (or `npm audit`) and keep `connect`, `serve-static`, `ws`, and other dependencies updated.
|
|
110
|
+
- **Logging**: Avoid logging full request body or headers in production.
|
|
111
|
+
|
|
112
|
+
[Security (full) →](./docs/guide/security.md) · [Configuration →](./docs/guide/configuration.md) · [Production →](./docs/guide/production-deploy.md)
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
89
116
|
## Links
|
|
90
117
|
|
|
91
118
|
- [Documentation](./docs/) — [view online](https://martinsbicudo.github.io/vitek-plugin/) · guides, API reference, configuration, examples
|
|
92
119
|
- [OpenAPI / Swagger + AsyncAPI](./docs/guide/openapi.md) — Auto-generate API docs (REST + WebSockets)
|
|
93
|
-
- [
|
|
120
|
+
- [Plugin API](./docs/guide/plugin-api.md) — Extend Vitek with `afterTypesGenerated` and `beforeApiRequest` hooks
|
|
121
|
+
- [Alias](./docs/guide/alias.md) — Use `resolve.alias` for stable import paths
|
|
122
|
+
- [Introspection](./docs/guide/introspection.md) — Programmatic API: `getRoutes`, `getSockets`, `vitek-manifest.json`
|
|
123
|
+
- [Bundle size](./docs/guide/bundle-size.md) — Plugin and generated bundles (vitek-api.mjs); tree-shaking, heavy imports
|
|
124
|
+
- [Examples](./examples/) — socket-only, basic-js, js-react, typescript-react, import-external, api-docs, prisma, docker
|
|
94
125
|
- [GitHub](https://github.com/martinsbicudo/vitek-plugin)
|
|
95
126
|
- [NPM](https://www.npmjs.com/package/vitek-plugin)
|
|
96
127
|
- [License](LICENSE)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { VitekApp } from '../../core/shared/vitek-app.js';
|
|
2
|
+
import type { DevServerState } from './dev-server-state.js';
|
|
3
|
+
import type { ViteDevServerOptions } from './dev-server-state.js';
|
|
4
|
+
export declare function createApiMiddleware(state: DevServerState, options: ViteDevServerOptions, shared: VitekApp): (req: import("http").IncomingMessage, res: import("http").ServerResponse, next: import("vite").Connect.NextFunction) => Promise<void>;
|
|
5
|
+
export declare function createSocketSetup(state: DevServerState, options: ViteDevServerOptions, shared: VitekApp): (httpServer: {
|
|
6
|
+
on(event: "upgrade", listener: (req: import("http").IncomingMessage, socket: import("stream").Duplex, head: Buffer) => void): void;
|
|
7
|
+
}) => void;
|
|
8
|
+
//# sourceMappingURL=dev-server-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-server-middleware.d.ts","sourceRoot":"","sources":["../../../src/adapters/vite/dev-server-middleware.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,oBAAoB,EAC7B,MAAM,EAAE,QAAQ,yIAajB;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,oBAAoB,EAC7B,MAAM,EAAE,QAAQ,IAER,YAAY;IAAE,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAA;CAAE,UAY3J"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createRequestHandler } from '../../core/server/request-handler.js';
|
|
2
|
+
import { createSocketHandler } from '../../core/socket/socket-handler.js';
|
|
3
|
+
import { SOCKET_BASE_PATH } from '../../shared/constants.js';
|
|
4
|
+
export function createApiMiddleware(state, options, shared) {
|
|
5
|
+
return createRequestHandler({
|
|
6
|
+
routes: state.routes,
|
|
7
|
+
middlewares: state.middlewares,
|
|
8
|
+
beforeApiRequest: options.beforeApiRequest,
|
|
9
|
+
cors: options.cors,
|
|
10
|
+
trustProxy: options.trustProxy,
|
|
11
|
+
maxBodySize: options.maxBodySize,
|
|
12
|
+
onError: options.onError,
|
|
13
|
+
logger: options.logger,
|
|
14
|
+
shared,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export function createSocketSetup(state, options, shared) {
|
|
18
|
+
return (httpServer) => {
|
|
19
|
+
if (options.sockets !== false && state.sockets.length > 0) {
|
|
20
|
+
const socketBasePath = options.socketBasePath ?? SOCKET_BASE_PATH;
|
|
21
|
+
const handler = createSocketHandler({
|
|
22
|
+
sockets: state.sockets,
|
|
23
|
+
socketBasePath,
|
|
24
|
+
shared,
|
|
25
|
+
logger: options.logger,
|
|
26
|
+
});
|
|
27
|
+
httpServer.on('upgrade', handler);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ViteDevServer } from 'vite';
|
|
2
|
+
import { type ApiWatcher } from '../../core/file-system/watch-api-dir.js';
|
|
3
|
+
import { type SocketEntry } from '../../core/socket/socket-handler.js';
|
|
4
|
+
import type { OpenApiOptions } from '../../core/openapi/generate.js';
|
|
5
|
+
import type { Route } from '../../core/routing/route-types.js';
|
|
6
|
+
import type { LoadedMiddleware } from '../../core/middleware/get-applicable-middlewares.js';
|
|
7
|
+
import type { BeforeApiRequestHook } from '../../core/server/request-handler.js';
|
|
8
|
+
import type { AfterTypesGeneratedContext } from '../../plugin/plugin-api.js';
|
|
9
|
+
import type { VitekLogger } from './logger.js';
|
|
10
|
+
export interface ViteDevServerOptions {
|
|
11
|
+
root: string;
|
|
12
|
+
apiDir: string;
|
|
13
|
+
logger: VitekLogger;
|
|
14
|
+
viteServer: ViteDevServer;
|
|
15
|
+
enableValidation?: boolean;
|
|
16
|
+
openApi?: OpenApiOptions | boolean;
|
|
17
|
+
sockets?: boolean;
|
|
18
|
+
socketBasePath?: string;
|
|
19
|
+
onGenerationError?: (error: Error) => void;
|
|
20
|
+
beforeApiRequest?: BeforeApiRequestHook[];
|
|
21
|
+
afterTypesGenerated?: ((ctx: AfterTypesGeneratedContext) => void | Promise<void>)[];
|
|
22
|
+
apiBasePath?: string;
|
|
23
|
+
cors?: boolean | import('../../core/server/cors.js').CorsOptions;
|
|
24
|
+
trustProxy?: boolean;
|
|
25
|
+
maxBodySize?: number;
|
|
26
|
+
onError?: (err: Error, req: import('http').IncomingMessage, res: import('http').ServerResponse) => void | Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export declare class DevServerState {
|
|
29
|
+
private options;
|
|
30
|
+
routes: Route[];
|
|
31
|
+
middlewares: LoadedMiddleware[];
|
|
32
|
+
sockets: SocketEntry[];
|
|
33
|
+
watcher: ApiWatcher | null;
|
|
34
|
+
constructor(options: ViteDevServerOptions);
|
|
35
|
+
initialize(): Promise<void>;
|
|
36
|
+
reload(showReloadLog?: boolean): Promise<void>;
|
|
37
|
+
setupWatcher(): void;
|
|
38
|
+
generateTypes(): Promise<void>;
|
|
39
|
+
cleanup(): void;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=dev-server-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-server-state.d.ts","sourceRoot":"","sources":["../../../src/adapters/vite/dev-server-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAK7F,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAG5F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,KAAK,EAA4B,MAAM,mCAAmC,CAAC;AACzF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qDAAqD,CAAC;AAC5F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAqB/C,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,aAAa,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAC3C,gBAAgB,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAC1C,mBAAmB,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,0BAA0B,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IACpF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,2BAA2B,EAAE,WAAW,CAAC;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,OAAO,MAAM,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzH;AAED,qBAAa,cAAc;IAMb,OAAO,CAAC,OAAO;IAL3B,MAAM,EAAE,KAAK,EAAE,CAAM;IACrB,WAAW,EAAE,gBAAgB,EAAE,CAAM;IACrC,OAAO,EAAE,WAAW,EAAE,CAAM;IAC5B,OAAO,EAAE,UAAU,GAAG,IAAI,CAAQ;gBAEd,OAAO,EAAE,oBAAoB;IAE3C,UAAU;IAKV,MAAM,CAAC,aAAa,UAAO;IAwHjC,YAAY;IAWN,aAAa;IAuCnB,OAAO;CAMR"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { scanApiDirectory } from '../../core/file-system/scan-api-dir.js';
|
|
3
|
+
import { watchApiDirectory } from '../../core/file-system/watch-api-dir.js';
|
|
4
|
+
import { createRoute } from '../../core/routing/route-parser.js';
|
|
5
|
+
import { routesToSchema } from '../../core/types/schema.js';
|
|
6
|
+
import { runFileGeneration } from '../../core/generation/run-file-generation.js';
|
|
7
|
+
import { patternToRegex } from '../../core/normalize/normalize-path.js';
|
|
8
|
+
import { extractBodyTypeFromFile, extractQueryTypeFromFile } from '../../core/file-system/extract-type-from-file.js';
|
|
9
|
+
import { API_BASE_PATH, SOCKET_BASE_PATH } from '../../shared/constants.js';
|
|
10
|
+
function deduplicateRoutesByKey(routes) {
|
|
11
|
+
const seen = new Set();
|
|
12
|
+
return routes.filter((r) => {
|
|
13
|
+
const key = `${r.method}:${r.pattern}`;
|
|
14
|
+
if (seen.has(key))
|
|
15
|
+
return false;
|
|
16
|
+
seen.add(key);
|
|
17
|
+
return true;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function deduplicateSocketsByPattern(sockets) {
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
return sockets.filter((s) => {
|
|
23
|
+
if (seen.has(s.pattern))
|
|
24
|
+
return false;
|
|
25
|
+
seen.add(s.pattern);
|
|
26
|
+
return true;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export class DevServerState {
|
|
30
|
+
options;
|
|
31
|
+
routes = [];
|
|
32
|
+
middlewares = [];
|
|
33
|
+
sockets = [];
|
|
34
|
+
watcher = null;
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.options = options;
|
|
37
|
+
}
|
|
38
|
+
async initialize() {
|
|
39
|
+
await this.reload(false);
|
|
40
|
+
this.setupWatcher();
|
|
41
|
+
}
|
|
42
|
+
async reload(showReloadLog = true) {
|
|
43
|
+
if (showReloadLog) {
|
|
44
|
+
this.options.logger.info('Reloading API routes...');
|
|
45
|
+
}
|
|
46
|
+
const scanResult = scanApiDirectory(this.options.apiDir);
|
|
47
|
+
this.middlewares.length = 0;
|
|
48
|
+
for (const middlewareInfo of scanResult.middlewares) {
|
|
49
|
+
try {
|
|
50
|
+
const relativePath = path.relative(this.options.root, middlewareInfo.path);
|
|
51
|
+
const vitePath = `/${relativePath.replace(/\\/g, '/')}`;
|
|
52
|
+
const middlewareModule = await this.options.viteServer.ssrLoadModule(vitePath);
|
|
53
|
+
const middleware = middlewareModule.default || middlewareModule.middleware;
|
|
54
|
+
let middlewareArray = [];
|
|
55
|
+
if (Array.isArray(middleware)) {
|
|
56
|
+
middlewareArray = middleware;
|
|
57
|
+
}
|
|
58
|
+
else if (typeof middleware === 'function') {
|
|
59
|
+
middlewareArray = [middleware];
|
|
60
|
+
}
|
|
61
|
+
if (middlewareArray.length > 0) {
|
|
62
|
+
let pathPatterns;
|
|
63
|
+
if (middlewareInfo.basePattern === '' && middlewareModule.config?.path) {
|
|
64
|
+
const raw = Array.isArray(middlewareModule.config.path) ? middlewareModule.config.path : [middlewareModule.config.path];
|
|
65
|
+
pathPatterns = raw.map((p) => String(p).replace(/^\/api\/?/, '').replace(/^\/+|\/+$/g, '')).filter(Boolean);
|
|
66
|
+
}
|
|
67
|
+
this.middlewares.push({
|
|
68
|
+
middleware: middlewareArray,
|
|
69
|
+
basePattern: middlewareInfo.basePattern,
|
|
70
|
+
pathPatterns,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
this.options.logger.warn(`Failed to load middleware ${middlewareInfo.path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const totalMiddlewareCount = this.middlewares.reduce((sum, m) => sum + m.middleware.length, 0);
|
|
79
|
+
this.options.logger.middlewareLoaded(totalMiddlewareCount);
|
|
80
|
+
this.routes.length = 0;
|
|
81
|
+
for (const parsedRoute of scanResult.routes) {
|
|
82
|
+
try {
|
|
83
|
+
const relativePath = path.relative(this.options.root, parsedRoute.file);
|
|
84
|
+
const vitePath = `/${relativePath.replace(/\\/g, '/')}`;
|
|
85
|
+
const handlerModule = await this.options.viteServer.ssrLoadModule(vitePath);
|
|
86
|
+
const handler = handlerModule.default || handlerModule.handler || handlerModule[parsedRoute.method];
|
|
87
|
+
if (typeof handler !== 'function') {
|
|
88
|
+
this.options.logger.warn(`Route file ${parsedRoute.file} does not export a handler function`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const bodyType = extractBodyTypeFromFile(parsedRoute.file);
|
|
92
|
+
const queryType = extractQueryTypeFromFile(parsedRoute.file);
|
|
93
|
+
const route = createRoute(parsedRoute, handler, bodyType, queryType);
|
|
94
|
+
this.routes.push(route);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
this.options.logger.error(`Failed to load route ${parsedRoute.file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
this.routes = deduplicateRoutesByKey(this.routes);
|
|
101
|
+
const routesInfo = this.routes.map((r) => ({
|
|
102
|
+
method: r.method,
|
|
103
|
+
pattern: r.pattern,
|
|
104
|
+
}));
|
|
105
|
+
this.options.logger.routesRegistered(routesInfo, API_BASE_PATH);
|
|
106
|
+
this.sockets.length = 0;
|
|
107
|
+
const socketsEnabled = this.options.sockets !== false;
|
|
108
|
+
if (socketsEnabled) {
|
|
109
|
+
for (const parsedSocket of scanResult.sockets) {
|
|
110
|
+
try {
|
|
111
|
+
const relativePath = path.relative(this.options.root, parsedSocket.file);
|
|
112
|
+
const vitePath = `/${relativePath.replace(/\\/g, '/')}`;
|
|
113
|
+
const handlerModule = await this.options.viteServer.ssrLoadModule(vitePath);
|
|
114
|
+
const handler = handlerModule.default ?? handlerModule.handler;
|
|
115
|
+
if (typeof handler !== 'function') {
|
|
116
|
+
this.options.logger.warn(`Socket file ${parsedSocket.file} does not export a handler function`);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const regex = patternToRegex(parsedSocket.pattern);
|
|
120
|
+
this.sockets.push({
|
|
121
|
+
pattern: parsedSocket.pattern,
|
|
122
|
+
params: parsedSocket.params,
|
|
123
|
+
file: parsedSocket.file,
|
|
124
|
+
regex,
|
|
125
|
+
handler,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
this.options.logger.error(`Failed to load socket ${parsedSocket.file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
this.sockets = deduplicateSocketsByPattern(this.sockets);
|
|
134
|
+
const socketBasePath = this.options.socketBasePath ?? SOCKET_BASE_PATH;
|
|
135
|
+
this.options.logger.socketsRegistered(this.sockets.map((s) => ({ pattern: s.pattern })), socketBasePath);
|
|
136
|
+
await this.generateTypes();
|
|
137
|
+
}
|
|
138
|
+
setupWatcher() {
|
|
139
|
+
if (this.watcher) {
|
|
140
|
+
this.watcher.close();
|
|
141
|
+
}
|
|
142
|
+
this.watcher = watchApiDirectory(this.options.apiDir, async (event, filePath) => {
|
|
143
|
+
this.options.logger.info(`API file ${event}: ${filePath}`);
|
|
144
|
+
await this.reload();
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async generateTypes() {
|
|
148
|
+
try {
|
|
149
|
+
const schema = routesToSchema(this.routes);
|
|
150
|
+
const socketBasePath = this.options.socketBasePath ?? SOCKET_BASE_PATH;
|
|
151
|
+
const port = this.options.viteServer.config.server?.port || 5173;
|
|
152
|
+
await runFileGeneration({
|
|
153
|
+
root: this.options.root,
|
|
154
|
+
schema,
|
|
155
|
+
sockets: this.sockets,
|
|
156
|
+
apiBasePath: API_BASE_PATH,
|
|
157
|
+
socketBasePath,
|
|
158
|
+
openApi: this.options.openApi,
|
|
159
|
+
serverPort: port,
|
|
160
|
+
logger: {
|
|
161
|
+
typesGenerated: (p) => this.options.logger.typesGenerated(p),
|
|
162
|
+
servicesGenerated: (p) => this.options.logger.servicesGenerated(p),
|
|
163
|
+
info: (m) => this.options.logger.info(m),
|
|
164
|
+
warn: (m) => this.options.logger.warn(m),
|
|
165
|
+
},
|
|
166
|
+
onGenerationError: this.options.onGenerationError,
|
|
167
|
+
});
|
|
168
|
+
const apiBasePath = this.options.apiBasePath ?? API_BASE_PATH;
|
|
169
|
+
for (const hook of this.options.afterTypesGenerated ?? []) {
|
|
170
|
+
await hook({
|
|
171
|
+
root: this.options.root,
|
|
172
|
+
schema,
|
|
173
|
+
sockets: this.sockets.map((s) => ({ pattern: s.pattern, params: s.params, file: s.file })),
|
|
174
|
+
apiBasePath,
|
|
175
|
+
socketBasePath,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
181
|
+
this.options.logger.error(`Failed to generate types: ${err.message}`);
|
|
182
|
+
this.options.onGenerationError?.(err);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
cleanup() {
|
|
186
|
+
if (this.watcher) {
|
|
187
|
+
this.watcher.close();
|
|
188
|
+
this.watcher = null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -1,24 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Thin layer that connects core → Vite
|
|
4
|
-
*/
|
|
5
|
-
import type { ViteDevServer } from 'vite';
|
|
6
|
-
import type { OpenApiOptions } from '../../core/openapi/generate.js';
|
|
7
|
-
import type { VitekLogger } from './logger.js';
|
|
8
|
-
export interface ViteDevServerOptions {
|
|
9
|
-
root: string;
|
|
10
|
-
apiDir: string;
|
|
11
|
-
logger: VitekLogger;
|
|
12
|
-
viteServer: ViteDevServer;
|
|
13
|
-
enableValidation?: boolean;
|
|
14
|
-
openApi?: OpenApiOptions | boolean;
|
|
15
|
-
sockets?: boolean;
|
|
16
|
-
/** Base path for WebSocket endpoints (e.g. /api/ws). Default from constants. */
|
|
17
|
-
socketBasePath?: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Creates middleware for Vite development server
|
|
21
|
-
*/
|
|
1
|
+
import { type ViteDevServerOptions } from './dev-server-state.js';
|
|
2
|
+
export type { ViteDevServerOptions } from './dev-server-state.js';
|
|
22
3
|
export declare function createViteDevServerMiddleware(options: ViteDevServerOptions): {
|
|
23
4
|
ready: Promise<void>;
|
|
24
5
|
middleware: (req: import("http").IncomingMessage, res: import("http").ServerResponse, next: import("vite").Connect.NextFunction) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../../src/adapters/vite/dev-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../../src/adapters/vite/dev-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAOlF,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,oBAAoB;;;;;;;;EA4C1E"}
|