wexts 4.1.5 → 4.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.md +49 -49
  2. package/bin/wexts.cjs +2 -2
  3. package/dist/chunk-27NEYQD2.mjs +222 -0
  4. package/dist/chunk-27NEYQD2.mjs.map +1 -0
  5. package/dist/chunk-2LJVUMXW.js +228 -0
  6. package/dist/chunk-2LJVUMXW.js.map +1 -0
  7. package/dist/chunk-5DBFN65W.mjs +183 -0
  8. package/dist/chunk-5DBFN65W.mjs.map +1 -0
  9. package/dist/chunk-75HBI2XG.js +504 -0
  10. package/dist/chunk-75HBI2XG.js.map +1 -0
  11. package/dist/chunk-AVMQJWYD.js +95 -0
  12. package/dist/chunk-AVMQJWYD.js.map +1 -0
  13. package/dist/chunk-C2AG7Q3C.mjs +65 -0
  14. package/dist/chunk-C2AG7Q3C.mjs.map +1 -0
  15. package/dist/chunk-DS5KVVWJ.mjs +95 -0
  16. package/dist/chunk-DS5KVVWJ.mjs.map +1 -0
  17. package/dist/chunk-FUPLZMSR.mjs +228 -0
  18. package/dist/chunk-FUPLZMSR.mjs.map +1 -0
  19. package/dist/chunk-HLT3IHJY.js +183 -0
  20. package/dist/chunk-HLT3IHJY.js.map +1 -0
  21. package/dist/chunk-L77ANWWT.mjs +22 -0
  22. package/dist/chunk-L77ANWWT.mjs.map +1 -0
  23. package/dist/chunk-P3Q4I5FD.js +222 -0
  24. package/dist/chunk-P3Q4I5FD.js.map +1 -0
  25. package/dist/chunk-VBIQGUDI.mjs +504 -0
  26. package/dist/chunk-VBIQGUDI.mjs.map +1 -0
  27. package/dist/chunk-WCKSKU3C.js +65 -0
  28. package/dist/chunk-WCKSKU3C.js.map +1 -0
  29. package/dist/chunk-XE4OXN2W.js +12 -0
  30. package/dist/chunk-XE4OXN2W.js.map +1 -0
  31. package/dist/cli/index.d.mts +23 -0
  32. package/dist/cli/index.d.ts +23 -0
  33. package/dist/cli/index.js +745 -0
  34. package/dist/cli/index.js.map +1 -0
  35. package/dist/cli/index.mjs +747 -0
  36. package/dist/cli/index.mjs.map +1 -0
  37. package/dist/client/index.d.mts +43 -0
  38. package/dist/client/index.d.ts +43 -0
  39. package/dist/client/index.js +187 -0
  40. package/dist/client/index.js.map +1 -0
  41. package/dist/client/index.mjs +166 -0
  42. package/dist/client/index.mjs.map +1 -0
  43. package/dist/codegen/index.d.mts +2 -0
  44. package/dist/codegen/index.d.ts +2 -0
  45. package/dist/codegen/index.js +16 -0
  46. package/dist/codegen/index.js.map +1 -0
  47. package/dist/codegen/index.mjs +16 -0
  48. package/dist/codegen/index.mjs.map +1 -0
  49. package/dist/decorators-BiQtOHuF.d.ts +60 -0
  50. package/dist/decorators-vBopODtr.d.mts +60 -0
  51. package/dist/dev-server/index.d.mts +1 -0
  52. package/dist/dev-server/index.d.ts +1 -0
  53. package/dist/dev-server/index.js +13 -0
  54. package/dist/dev-server/index.js.map +1 -0
  55. package/dist/dev-server/index.mjs +13 -0
  56. package/dist/dev-server/index.mjs.map +1 -0
  57. package/dist/errors-D545UvXf.d.mts +37 -0
  58. package/dist/errors-D545UvXf.d.ts +37 -0
  59. package/dist/index-7RvU-jGE.d.mts +66 -0
  60. package/dist/index-7RvU-jGE.d.ts +66 -0
  61. package/dist/index-CJKs4wtN.d.ts +92 -0
  62. package/dist/index-CUR6CnLk.d.ts +27 -0
  63. package/dist/index-h66Vq9dK.d.mts +92 -0
  64. package/dist/index-tqmYsTUn.d.mts +27 -0
  65. package/dist/index.d.mts +289 -0
  66. package/dist/index.d.ts +289 -0
  67. package/dist/index.js +544 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/index.mjs +544 -0
  70. package/dist/index.mjs.map +1 -0
  71. package/dist/nest/index.d.mts +2 -0
  72. package/dist/nest/index.d.ts +2 -0
  73. package/dist/nest/index.js +36 -0
  74. package/dist/nest/index.js.map +1 -0
  75. package/dist/nest/index.mjs +36 -0
  76. package/dist/nest/index.mjs.map +1 -0
  77. package/dist/next/index.d.mts +66 -0
  78. package/dist/next/index.d.ts +66 -0
  79. package/dist/next/index.js +282 -0
  80. package/dist/next/index.js.map +1 -0
  81. package/dist/next/index.mjs +251 -0
  82. package/dist/next/index.mjs.map +1 -0
  83. package/dist/rpc/index.d.mts +32 -0
  84. package/dist/rpc/index.d.ts +32 -0
  85. package/dist/rpc/index.js +19 -0
  86. package/dist/rpc/index.js.map +1 -0
  87. package/dist/rpc/index.mjs +1 -0
  88. package/dist/rpc/index.mjs.map +1 -0
  89. package/dist/runtime/index.d.mts +55 -0
  90. package/dist/runtime/index.d.ts +55 -0
  91. package/dist/runtime/index.js +15 -0
  92. package/dist/runtime/index.js.map +1 -0
  93. package/dist/runtime/index.mjs +15 -0
  94. package/dist/runtime/index.mjs.map +1 -0
  95. package/dist/server/index.d.mts +7 -0
  96. package/dist/server/index.d.ts +7 -0
  97. package/dist/server/index.js +58 -0
  98. package/dist/server/index.js.map +1 -0
  99. package/dist/server/index.mjs +58 -0
  100. package/dist/server/index.mjs.map +1 -0
  101. package/dist/types/index.d.mts +12 -0
  102. package/dist/types/index.d.ts +12 -0
  103. package/dist/types/index.js +19 -0
  104. package/dist/types/index.js.map +1 -0
  105. package/dist/types/index.mjs +1 -0
  106. package/dist/types/index.mjs.map +1 -0
  107. package/dist/types-CQ_aexOX.d.mts +32 -0
  108. package/dist/types-CQ_aexOX.d.ts +32 -0
  109. package/dist/vercel-builder/index.d.mts +58 -0
  110. package/dist/vercel-builder/index.d.ts +58 -0
  111. package/dist/vercel-builder/index.js +330 -0
  112. package/dist/vercel-builder/index.js.map +1 -0
  113. package/dist/vercel-builder/index.mjs +330 -0
  114. package/dist/vercel-builder/index.mjs.map +1 -0
  115. package/package.json +153 -153
  116. package/templates/.dockerignore +43 -43
  117. package/templates/.env.example +17 -17
  118. package/templates/Dockerfile +60 -60
  119. package/templates/Procfile +1 -1
  120. package/templates/README.md +67 -67
  121. package/templates/api-sdk.ts +115 -115
  122. package/templates/docker-compose.yml +34 -34
  123. package/templates/nestjs-api/.env.example +3 -3
  124. package/templates/nestjs-api/README.md +87 -87
  125. package/templates/nestjs-api/nest-cli.json +6 -6
  126. package/templates/nestjs-api/package.json +40 -40
  127. package/templates/nestjs-api/prisma/migrations/20251123205437_init/migration.sql +24 -24
  128. package/templates/nestjs-api/prisma/migrations/migration_lock.toml +3 -3
  129. package/templates/nestjs-api/prisma/schema.prisma +29 -29
  130. package/templates/nestjs-api/src/app.module.ts +17 -17
  131. package/templates/nestjs-api/src/auth/auth.controller.ts +27 -27
  132. package/templates/nestjs-api/src/auth/auth.module.ts +37 -37
  133. package/templates/nestjs-api/src/auth/auth.service.ts +86 -86
  134. package/templates/nestjs-api/src/auth/dto/auth.dto.ts +22 -22
  135. package/templates/nestjs-api/src/auth/guards/jwt-auth.guard.ts +5 -5
  136. package/templates/nestjs-api/src/auth/strategies/jwt.strategy.ts +27 -27
  137. package/templates/nestjs-api/src/main.ts +32 -32
  138. package/templates/nestjs-api/src/prisma/prisma.module.ts +9 -9
  139. package/templates/nestjs-api/src/prisma/prisma.service.ts +14 -14
  140. package/templates/nestjs-api/src/todos/dto/todo.dto.ts +24 -24
  141. package/templates/nestjs-api/src/todos/todos.controller.ts +39 -39
  142. package/templates/nestjs-api/src/todos/todos.module.ts +11 -11
  143. package/templates/nestjs-api/src/todos/todos.service.ts +53 -53
  144. package/templates/nestjs-api/src/users/users.controller.ts +14 -14
  145. package/templates/nestjs-api/src/users/users.module.ts +12 -12
  146. package/templates/nestjs-api/src/users/users.service.ts +19 -19
  147. package/templates/nestjs-api/tsconfig.json +39 -39
  148. package/templates/nextjs-web/README.md +76 -76
  149. package/templates/nextjs-web/app/actions/auth.ts +108 -108
  150. package/templates/nextjs-web/app/dashboard/error.tsx +39 -39
  151. package/templates/nextjs-web/app/dashboard/loading.tsx +14 -14
  152. package/templates/nextjs-web/app/dashboard/page.tsx +5 -5
  153. package/templates/nextjs-web/app/globals.css +93 -93
  154. package/templates/nextjs-web/app/layout.tsx +29 -29
  155. package/templates/nextjs-web/app/login/page.tsx +5 -5
  156. package/templates/nextjs-web/app/page.tsx +28 -28
  157. package/templates/nextjs-web/app/register/page.tsx +5 -5
  158. package/templates/nextjs-web/components/ui/button.tsx +56 -56
  159. package/templates/nextjs-web/components/ui/card.tsx +79 -79
  160. package/templates/nextjs-web/components/ui/input.tsx +25 -25
  161. package/templates/nextjs-web/components/ui/label.tsx +24 -24
  162. package/templates/nextjs-web/features/auth/LoginForm.tsx +140 -140
  163. package/templates/nextjs-web/features/auth/RegisterForm.tsx +159 -159
  164. package/templates/nextjs-web/features/auth/api.ts +35 -35
  165. package/templates/nextjs-web/features/auth/index.ts +3 -3
  166. package/templates/nextjs-web/features/dashboard/DashboardView.tsx +204 -204
  167. package/templates/nextjs-web/features/dashboard/api.ts +9 -9
  168. package/templates/nextjs-web/features/dashboard/components.tsx +74 -74
  169. package/templates/nextjs-web/features/dashboard/index.ts +3 -3
  170. package/templates/nextjs-web/hooks/index.ts +4 -4
  171. package/templates/nextjs-web/lib/api-client.ts +89 -89
  172. package/templates/nextjs-web/lib/api.ts +115 -115
  173. package/templates/nextjs-web/lib/axios-global-config.ts +17 -17
  174. package/templates/nextjs-web/lib/utils.ts +6 -6
  175. package/templates/nextjs-web/lib/wexts-client.ts +4 -4
  176. package/templates/nextjs-web/next-env.d.ts +6 -6
  177. package/templates/nextjs-web/next.config.ts +20 -20
  178. package/templates/nextjs-web/package.json +37 -37
  179. package/templates/nextjs-web/postcss.config.js +6 -6
  180. package/templates/nextjs-web/tailwind.config.ts +69 -69
  181. package/templates/nextjs-web/tsconfig.json +41 -41
  182. package/templates/nixpacks.toml +11 -11
  183. package/templates/root-package.json +31 -31
  184. package/templates/server.ts +66 -66
  185. package/templates/tsconfig.json +30 -30
package/README.md CHANGED
@@ -1,49 +1,49 @@
1
- # Wexts Package
2
-
3
- `wexts` is a production-focused toolkit for running Next.js + NestJS behind one Fastify production runtime with generated typed RPC.
4
-
5
- ## Stable In This Patch
6
-
7
- - `wexts generate` creates a deterministic RPC manifest and typed client.
8
- - `wexts start` starts the Fastify production runtime.
9
- - `wexts doctor` and `wexts doctor --security` validate common release blockers.
10
- - `wexts/next` exposes `FusionProvider`, `useFusion`, and `useWexts<T>()`.
11
- - `wexts/client` exposes `createWextsRpcClient()`.
12
- - `wexts/rpc` exposes RPC metadata types/decorators.
13
- - `wexts/runtime` exposes the production runtime helpers.
14
-
15
- ## Canonical Example
16
-
17
- Use `examples/hello-rpc` from the repository as the release-verified path.
18
-
19
- ```bash
20
- pnpm --filter wexts-example-hello-rpc generate
21
- pnpm --filter wexts-example-hello-rpc build
22
- pnpm --filter wexts-example-hello-rpc run doctor
23
- pnpm --filter wexts-example-hello-rpc run doctor:security
24
- ```
25
-
26
- ## Production Runtime
27
-
28
- Production runtime is single-port:
29
-
30
- - `/health`
31
- - `/api/health`
32
- - `/rpc/:service/:method`
33
- - optional Nest under `/api`
34
- - optional Next frontend routes
35
-
36
- Development mode currently runs separate web/API processes. Use `wexts start` for the supported single-port runtime path.
37
-
38
- ## Security
39
-
40
- Use `@wexts/security` / Wexts Shield before runtime routes. It provides application-layer controls. It does not replace Cloudflare/WAF/provider DDoS protection, and its default memory rate limit store is single-process only.
41
-
42
- ## Deprecated Compatibility Paths
43
-
44
- - `demo/`
45
- - `packages/templates/nestjs-api`
46
- - `packages/templates/nextjs-web`
47
- - legacy Fusion controller codegen paths
48
-
49
- These are retained for compatibility and should not be marketed as the recommended production structure.
1
+ # Wexts Package
2
+
3
+ `wexts` is a production-focused toolkit for running Next.js + NestJS behind one Fastify production runtime with generated typed RPC.
4
+
5
+ ## Stable In This Patch
6
+
7
+ - `wexts generate` creates a deterministic RPC manifest and typed client.
8
+ - `wexts start` starts the Fastify production runtime.
9
+ - `wexts doctor` and `wexts doctor --security` validate common release blockers.
10
+ - `wexts/next` exposes `FusionProvider`, `useFusion`, and `useWexts<T>()`.
11
+ - `wexts/client` exposes `createWextsRpcClient()`.
12
+ - `wexts/rpc` exposes RPC metadata types/decorators.
13
+ - `wexts/runtime` exposes the production runtime helpers.
14
+
15
+ ## Canonical Example
16
+
17
+ Use `examples/hello-rpc` from the repository as the release-verified path.
18
+
19
+ ```bash
20
+ pnpm --filter wexts-example-hello-rpc generate
21
+ pnpm --filter wexts-example-hello-rpc build
22
+ pnpm --filter wexts-example-hello-rpc run doctor
23
+ pnpm --filter wexts-example-hello-rpc run doctor:security
24
+ ```
25
+
26
+ ## Production Runtime
27
+
28
+ Production runtime is single-port:
29
+
30
+ - `/health`
31
+ - `/api/health`
32
+ - `/rpc/:service/:method`
33
+ - optional Nest under `/api`
34
+ - optional Next frontend routes
35
+
36
+ Development mode currently runs separate web/API processes. Use `wexts start` for the supported single-port runtime path.
37
+
38
+ ## Security
39
+
40
+ Use `@wexts/security` / Wexts Shield before runtime routes. It provides application-layer controls. It does not replace Cloudflare/WAF/provider DDoS protection, and its default memory rate limit store is single-process only.
41
+
42
+ ## Deprecated Compatibility Paths
43
+
44
+ - `demo/`
45
+ - `packages/templates/nestjs-api`
46
+ - `packages/templates/nextjs-web`
47
+ - legacy Fusion controller codegen paths
48
+
49
+ These are retained for compatibility and should not be marketed as the recommended production structure.
package/bin/wexts.cjs CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env node
2
- require("../dist/cli/index.js");
1
+ #!/usr/bin/env node
2
+ require("../dist/cli/index.js");
@@ -0,0 +1,222 @@
1
+ import {
2
+ WextsRuntimeError
3
+ } from "./chunk-DS5KVVWJ.mjs";
4
+ import {
5
+ __name
6
+ } from "./chunk-L77ANWWT.mjs";
7
+
8
+ // src/runtime/rpc-router.ts
9
+ async function registerRpcRoutes(fastify, options) {
10
+ const serviceMap = new Map(options.manifest.services.map((service) => [
11
+ service.name,
12
+ service
13
+ ]));
14
+ fastify.post("/rpc/:service/:method", async (request, reply) => {
15
+ const service = serviceMap.get(request.params.service);
16
+ if (!service) {
17
+ reply.status(404);
18
+ return {
19
+ error: "WEXTS_RPC_SERVICE_NOT_FOUND"
20
+ };
21
+ }
22
+ const method = service.methods.find((candidate) => candidate.name === request.params.method);
23
+ if (!method) {
24
+ reply.status(404);
25
+ return {
26
+ error: "WEXTS_RPC_METHOD_NOT_FOUND"
27
+ };
28
+ }
29
+ if (method.requireAuth || service.requireAuth) {
30
+ const authorized = await options.authorize?.(request, service, method.name);
31
+ if (!authorized) {
32
+ reply.status(401);
33
+ return {
34
+ error: "WEXTS_RPC_AUTH_REQUIRED"
35
+ };
36
+ }
37
+ }
38
+ const instance = options.services[service.name];
39
+ const handler = instance?.[method.handlerName];
40
+ if (!handler) {
41
+ reply.status(500);
42
+ return {
43
+ error: "WEXTS_RPC_HANDLER_NOT_BOUND"
44
+ };
45
+ }
46
+ const args = Array.isArray(request.body?.args) ? request.body.args : [];
47
+ const data = await handler.apply(instance, args);
48
+ return {
49
+ data
50
+ };
51
+ });
52
+ }
53
+ __name(registerRpcRoutes, "registerRpcRoutes");
54
+
55
+ // src/runtime/server.ts
56
+ import * as fs from "fs";
57
+ import * as path from "path";
58
+ import Fastify from "fastify";
59
+ import { registerWextsShield } from "@wexts/security";
60
+ async function createWextsRuntimeServer(config = {}) {
61
+ const rootDir = path.resolve(config.rootDir ?? process.cwd());
62
+ const fastify = Fastify({
63
+ logger: config.logger ?? true,
64
+ bodyLimit: config.security?.bodyLimitBytes ?? 1048576,
65
+ requestTimeout: config.security?.requestTimeoutMs ?? 3e4
66
+ });
67
+ const manifest = config.rpcManifest ?? loadJson(rootDir, config.rpcManifestPath);
68
+ const routePolicies = [
69
+ ...config.security?.routePolicies ?? [],
70
+ ...rpcPoliciesFromManifest(manifest)
71
+ ];
72
+ await registerWextsShield(fastify, {
73
+ ...config.security,
74
+ routePolicies
75
+ });
76
+ fastify.get("/health", async () => ({
77
+ ok: true,
78
+ runtime: "wexts"
79
+ }));
80
+ fastify.get("/api/health", async () => ({
81
+ ok: true,
82
+ runtime: "wexts",
83
+ scope: "api"
84
+ }));
85
+ if (manifest && config.rpcServices) {
86
+ await registerRpcRoutes(fastify, {
87
+ manifest,
88
+ services: config.rpcServices,
89
+ authorize: /* @__PURE__ */ __name((request) => Boolean(request.headers.authorization || request.headers.cookie), "authorize")
90
+ });
91
+ }
92
+ if (config.nestAppModule || config.nestAppModulePath) {
93
+ await mountNest(fastify, rootDir, config);
94
+ }
95
+ if (config.nextDir) {
96
+ await mountNext(fastify, rootDir, config);
97
+ }
98
+ const start = /* @__PURE__ */ __name(async () => {
99
+ await fastify.listen({
100
+ port: config.port ?? Number(process.env.PORT ?? 3e3),
101
+ host: config.host ?? "0.0.0.0"
102
+ });
103
+ }, "start");
104
+ const close = /* @__PURE__ */ __name(async () => {
105
+ await fastify.close();
106
+ }, "close");
107
+ registerShutdown(close);
108
+ return {
109
+ fastify,
110
+ start,
111
+ close
112
+ };
113
+ }
114
+ __name(createWextsRuntimeServer, "createWextsRuntimeServer");
115
+ async function startWextsRuntime(config = {}) {
116
+ const server = await createWextsRuntimeServer(config);
117
+ await server.start();
118
+ return server;
119
+ }
120
+ __name(startWextsRuntime, "startWextsRuntime");
121
+ function rpcPoliciesFromManifest(manifest) {
122
+ if (!manifest) return [];
123
+ return manifest.services.flatMap((service) => service.methods.map((method) => ({
124
+ path: `/rpc/${service.name}/${method.name}`,
125
+ methods: [
126
+ "POST"
127
+ ],
128
+ mode: service.requireAuth || method.requireAuth ? "requireAuth" : "public"
129
+ })));
130
+ }
131
+ __name(rpcPoliciesFromManifest, "rpcPoliciesFromManifest");
132
+ function loadJson(rootDir, filePath) {
133
+ if (!filePath) return void 0;
134
+ const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(rootDir, filePath);
135
+ if (!fs.existsSync(absolutePath)) return void 0;
136
+ return JSON.parse(fs.readFileSync(absolutePath, "utf8"));
137
+ }
138
+ __name(loadJson, "loadJson");
139
+ async function mountNest(fastify, rootDir, config) {
140
+ const [{ NestFactory }, { FastifyAdapter }] = await Promise.all([
141
+ import("@nestjs/core"),
142
+ import("@nestjs/platform-fastify")
143
+ ]);
144
+ const AppModule = config.nestAppModule ?? await importModule(rootDir, config.nestAppModulePath);
145
+ const moduleValue = AppModule.AppModule ?? AppModule;
146
+ const nestApp = await NestFactory.create(moduleValue, new FastifyAdapter(fastify), {
147
+ logger: config.dev ? [
148
+ "log",
149
+ "error",
150
+ "warn"
151
+ ] : [
152
+ "error",
153
+ "warn"
154
+ ]
155
+ });
156
+ nestApp.setGlobalPrefix("api");
157
+ await nestApp.init();
158
+ }
159
+ __name(mountNest, "mountNest");
160
+ async function mountNext(fastify, rootDir, config) {
161
+ const nextModule = await import("next");
162
+ const next = nextModule.default;
163
+ if (!next) {
164
+ throw new WextsRuntimeError({
165
+ code: "WEXTS_RUNTIME_NEXT_MISSING",
166
+ message: "Next.js could not be loaded. Install next or omit nextDir.",
167
+ suggestedFix: "Install `next` in the application or remove `nextDir` from wexts.runtime.js.",
168
+ docsSlug: "runtime"
169
+ });
170
+ }
171
+ const nextApp = next({
172
+ dev: config.dev ?? process.env.NODE_ENV !== "production",
173
+ dir: path.isAbsolute(config.nextDir) ? config.nextDir : path.join(rootDir, config.nextDir)
174
+ });
175
+ await nextApp.prepare();
176
+ const handler = nextApp.getRequestHandler();
177
+ fastify.all("/*", async (request, reply) => {
178
+ await handler(request.raw, reply.raw);
179
+ reply.hijack();
180
+ });
181
+ }
182
+ __name(mountNext, "mountNext");
183
+ async function importModule(rootDir, modulePath) {
184
+ const absolutePath = path.isAbsolute(modulePath) ? modulePath : path.join(rootDir, modulePath);
185
+ return import(pathToFileUrl(absolutePath));
186
+ }
187
+ __name(importModule, "importModule");
188
+ function pathToFileUrl(filePath) {
189
+ return `file://${filePath}`;
190
+ }
191
+ __name(pathToFileUrl, "pathToFileUrl");
192
+ function registerShutdown(close) {
193
+ const handler = /* @__PURE__ */ __name(async () => {
194
+ await close();
195
+ process.exit(0);
196
+ }, "handler");
197
+ process.once("SIGINT", handler);
198
+ process.once("SIGTERM", handler);
199
+ }
200
+ __name(registerShutdown, "registerShutdown");
201
+
202
+ // src/runtime/vercel-handler.ts
203
+ async function createWextsHandler(config = {}) {
204
+ const server = await createWextsRuntimeServer({
205
+ ...config,
206
+ // Serverless does not use long-lived logging
207
+ logger: config.logger ?? false
208
+ });
209
+ await server.fastify.ready();
210
+ return (req, res) => {
211
+ server.fastify.server.emit("request", req, res);
212
+ };
213
+ }
214
+ __name(createWextsHandler, "createWextsHandler");
215
+
216
+ export {
217
+ registerRpcRoutes,
218
+ createWextsRuntimeServer,
219
+ startWextsRuntime,
220
+ createWextsHandler
221
+ };
222
+ //# sourceMappingURL=chunk-27NEYQD2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/rpc-router.ts","../src/runtime/server.ts","../src/runtime/vercel-handler.ts"],"sourcesContent":["import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';\nimport type { RpcManifest, RpcServiceManifest } from '../rpc/types';\n\nexport type RpcServiceInstances = Record<string, Record<string, (...args: unknown[]) => unknown | Promise<unknown>>>;\n\nexport interface RegisterRpcRoutesOptions {\n manifest: RpcManifest;\n services: RpcServiceInstances;\n authorize?: (request: FastifyRequest, service: RpcServiceManifest, methodName: string) => boolean | Promise<boolean>;\n}\n\nexport async function registerRpcRoutes(fastify: FastifyInstance, options: RegisterRpcRoutesOptions): Promise<void> {\n const serviceMap = new Map(options.manifest.services.map((service) => [service.name, service]));\n\n fastify.post('/rpc/:service/:method', async (request: FastifyRequest<{\n Params: { service: string; method: string };\n Body: { args?: unknown[] };\n }>, reply: FastifyReply) => {\n const service = serviceMap.get(request.params.service);\n if (!service) {\n reply.status(404);\n return { error: 'WEXTS_RPC_SERVICE_NOT_FOUND' };\n }\n\n const method = service.methods.find((candidate) => candidate.name === request.params.method);\n if (!method) {\n reply.status(404);\n return { error: 'WEXTS_RPC_METHOD_NOT_FOUND' };\n }\n\n if (method.requireAuth || service.requireAuth) {\n const authorized = await options.authorize?.(request, service, method.name);\n if (!authorized) {\n reply.status(401);\n return { error: 'WEXTS_RPC_AUTH_REQUIRED' };\n }\n }\n\n const instance = options.services[service.name];\n const handler = instance?.[method.handlerName];\n if (!handler) {\n reply.status(500);\n return { error: 'WEXTS_RPC_HANDLER_NOT_BOUND' };\n }\n\n const args = Array.isArray(request.body?.args) ? request.body.args : [];\n const data = await handler.apply(instance, args);\n return { data };\n });\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport Fastify, { FastifyInstance, FastifyServerOptions } from 'fastify';\nimport { registerWextsShield, type WextsShieldConfig, type WextsShieldRoutePolicy } from '@wexts/security';\nimport type { RpcManifest } from '../rpc/types';\nimport { registerRpcRoutes, type RpcServiceInstances } from './rpc-router';\nimport { WextsRuntimeError } from '../errors';\n\nexport interface WextsRuntimeConfig {\n rootDir?: string;\n port?: number;\n host?: string;\n dev?: boolean;\n nextDir?: string;\n nestAppModule?: unknown;\n nestAppModulePath?: string;\n rpcManifest?: RpcManifest;\n rpcManifestPath?: string;\n rpcServices?: RpcServiceInstances;\n security?: WextsShieldConfig;\n logger?: FastifyServerOptions['logger'];\n}\n\nexport interface WextsRuntimeServer {\n fastify: FastifyInstance;\n start: () => Promise<void>;\n close: () => Promise<void>;\n}\n\nexport async function createWextsRuntimeServer(config: WextsRuntimeConfig = {}): Promise<WextsRuntimeServer> {\n const rootDir = path.resolve(config.rootDir ?? process.cwd());\n const fastify = Fastify({\n logger: config.logger ?? true,\n bodyLimit: config.security?.bodyLimitBytes ?? 1_048_576,\n requestTimeout: config.security?.requestTimeoutMs ?? 30_000,\n });\n\n const manifest = config.rpcManifest ?? loadJson<RpcManifest>(rootDir, config.rpcManifestPath);\n const routePolicies = [\n ...(config.security?.routePolicies ?? []),\n ...rpcPoliciesFromManifest(manifest),\n ];\n\n await registerWextsShield(fastify, {\n ...config.security,\n routePolicies,\n });\n\n fastify.get('/health', async () => ({\n ok: true,\n runtime: 'wexts',\n }));\n\n fastify.get('/api/health', async () => ({\n ok: true,\n runtime: 'wexts',\n scope: 'api',\n }));\n\n if (manifest && config.rpcServices) {\n await registerRpcRoutes(fastify, {\n manifest,\n services: config.rpcServices,\n authorize: (request) => Boolean(request.headers.authorization || request.headers.cookie),\n });\n }\n\n if (config.nestAppModule || config.nestAppModulePath) {\n await mountNest(fastify, rootDir, config);\n }\n\n if (config.nextDir) {\n await mountNext(fastify, rootDir, config);\n }\n\n const start = async () => {\n await fastify.listen({\n port: config.port ?? Number(process.env.PORT ?? 3000),\n host: config.host ?? '0.0.0.0',\n });\n };\n\n const close = async () => {\n await fastify.close();\n };\n\n registerShutdown(close);\n\n return {\n fastify,\n start,\n close,\n };\n}\n\nexport async function startWextsRuntime(config: WextsRuntimeConfig = {}): Promise<WextsRuntimeServer> {\n const server = await createWextsRuntimeServer(config);\n await server.start();\n return server;\n}\n\nfunction rpcPoliciesFromManifest(manifest?: RpcManifest): WextsShieldRoutePolicy[] {\n if (!manifest) return [];\n\n return manifest.services.flatMap((service) => service.methods.map((method) => ({\n path: `/rpc/${service.name}/${method.name}`,\n methods: ['POST'],\n mode: service.requireAuth || method.requireAuth ? 'requireAuth' : 'public',\n })));\n}\n\nfunction loadJson<T>(rootDir: string, filePath?: string): T | undefined {\n if (!filePath) return undefined;\n\n const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(rootDir, filePath);\n if (!fs.existsSync(absolutePath)) return undefined;\n return JSON.parse(fs.readFileSync(absolutePath, 'utf8')) as T;\n}\n\nasync function mountNest(fastify: FastifyInstance, rootDir: string, config: WextsRuntimeConfig): Promise<void> {\n const [{ NestFactory }, { FastifyAdapter }] = await Promise.all([\n import('@nestjs/core'),\n import('@nestjs/platform-fastify'),\n ]);\n const AppModule = config.nestAppModule ?? await importModule(rootDir, config.nestAppModulePath!);\n const moduleValue = (AppModule as { AppModule?: unknown }).AppModule ?? AppModule;\n const nestApp = await NestFactory.create(moduleValue as never, new FastifyAdapter(fastify as never), {\n logger: config.dev ? ['log', 'error', 'warn'] : ['error', 'warn'],\n });\n nestApp.setGlobalPrefix('api');\n await nestApp.init();\n}\n\nasync function mountNext(fastify: FastifyInstance, rootDir: string, config: WextsRuntimeConfig): Promise<void> {\n const nextModule = await import('next') as unknown as { default?: (options: unknown) => { prepare: () => Promise<void>; getRequestHandler: () => (req: unknown, res: unknown) => Promise<void> } };\n const next = nextModule.default;\n if (!next) {\n throw new WextsRuntimeError({\n code: 'WEXTS_RUNTIME_NEXT_MISSING',\n message: 'Next.js could not be loaded. Install next or omit nextDir.',\n suggestedFix: 'Install `next` in the application or remove `nextDir` from wexts.runtime.js.',\n docsSlug: 'runtime',\n });\n }\n const nextApp = next({\n dev: config.dev ?? process.env.NODE_ENV !== 'production',\n dir: path.isAbsolute(config.nextDir!) ? config.nextDir : path.join(rootDir, config.nextDir!),\n });\n await nextApp.prepare();\n const handler = nextApp.getRequestHandler();\n\n fastify.all('/*', async (request, reply) => {\n await handler(request.raw, reply.raw);\n reply.hijack();\n });\n}\n\nasync function importModule(rootDir: string, modulePath: string): Promise<unknown> {\n const absolutePath = path.isAbsolute(modulePath) ? modulePath : path.join(rootDir, modulePath);\n return import(pathToFileUrl(absolutePath));\n}\n\nfunction pathToFileUrl(filePath: string): string {\n return `file://${filePath}`;\n}\n\nfunction registerShutdown(close: () => Promise<void>): void {\n const handler = async () => {\n await close();\n process.exit(0);\n };\n\n process.once('SIGINT', handler);\n process.once('SIGTERM', handler);\n}\n","/**\n * Vercel / serverless handler adapter for the Wexts runtime.\n *\n * Creates a standard Node.js (req, res) handler that delegates to the\n * Fastify-backed Wexts runtime **without** calling fastify.listen().\n *\n * Usage:\n * const handler = await createWextsHandler({ ... });\n * export default handler; // Vercel function entry\n */\n\nimport type { IncomingMessage, ServerResponse } from 'http';\nimport type { WextsRuntimeConfig } from './server';\nimport { createWextsRuntimeServer } from './server';\n\nexport type WextsHandler = (req: IncomingMessage, res: ServerResponse) => void;\n\n/**\n * Build a serverless-compatible handler from the Wexts runtime.\n * The returned function accepts Node http (req, res) and passes them\n * into the Fastify instance without ever calling listen().\n */\nexport async function createWextsHandler(\n config: WextsRuntimeConfig = {},\n): Promise<WextsHandler> {\n const server = await createWextsRuntimeServer({\n ...config,\n // Serverless does not use long-lived logging\n logger: config.logger ?? false,\n });\n\n // Fastify exposes a raw Node handler via server.server (the http.Server)\n // But we need to call .ready() first so all plugins are loaded.\n await server.fastify.ready();\n\n return (req: IncomingMessage, res: ServerResponse) => {\n server.fastify.server.emit('request', req, res);\n };\n}\n"],"mappings":"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","names":["registerRpcRoutes","fastify","options","serviceMap","Map","manifest","services","map","service","name","post","request","reply","get","params","status","error","method","methods","find","candidate","requireAuth","authorized","authorize","instance","handler","handlerName","args","Array","isArray","body","data","apply","fs","path","Fastify","registerWextsShield","createWextsRuntimeServer","config","rootDir","resolve","process","cwd","fastify","Fastify","logger","bodyLimit","security","bodyLimitBytes","requestTimeout","requestTimeoutMs","manifest","rpcManifest","loadJson","rpcManifestPath","routePolicies","rpcPoliciesFromManifest","registerWextsShield","get","ok","runtime","scope","rpcServices","registerRpcRoutes","services","authorize","request","Boolean","headers","authorization","cookie","nestAppModule","nestAppModulePath","mountNest","nextDir","mountNext","start","listen","port","Number","env","PORT","host","close","registerShutdown","startWextsRuntime","server","flatMap","service","methods","map","method","path","name","mode","requireAuth","filePath","undefined","absolutePath","isAbsolute","join","existsSync","JSON","parse","readFileSync","NestFactory","FastifyAdapter","Promise","all","AppModule","importModule","moduleValue","nestApp","create","dev","setGlobalPrefix","init","nextModule","next","default","WextsRuntimeError","code","message","suggestedFix","docsSlug","nextApp","NODE_ENV","dir","prepare","handler","getRequestHandler","reply","raw","hijack","modulePath","pathToFileUrl","exit","once","createWextsHandler","config","server","createWextsRuntimeServer","logger","fastify","ready","req","res","emit"]}
@@ -0,0 +1,228 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
2
+
3
+ var _chunkWCKSKU3Cjs = require('./chunk-WCKSKU3C.js');
4
+
5
+
6
+
7
+ var _chunkXE4OXN2Wjs = require('./chunk-XE4OXN2W.js');
8
+
9
+ // src/dev-server/index.ts
10
+ var dev_server_exports = {};
11
+ _chunkXE4OXN2Wjs.__export.call(void 0, dev_server_exports, {
12
+ FusionDevServer: () => FusionDevServer,
13
+ ProcessRunner: () => ProcessRunner,
14
+ ProxyServer: () => ProxyServer
15
+ });
16
+
17
+ // src/dev-server/process-runner.ts
18
+ var _child_process = require('child_process');
19
+ var _picocolors = require('picocolors'); var pc = _interopRequireWildcard(_picocolors); var pc2 = _interopRequireWildcard(_picocolors);
20
+ var ProcessRunner = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this); }
21
+ static {
22
+ _chunkXE4OXN2Wjs.__name.call(void 0, this, "ProcessRunner");
23
+ }
24
+ __init() {this.processes = /* @__PURE__ */ new Map()}
25
+ __init2() {this.colors = {
26
+ cyan: pc.cyan,
27
+ green: pc.green,
28
+ yellow: pc.yellow,
29
+ magenta: pc.magenta,
30
+ blue: pc.blue
31
+ }}
32
+ async run(configs) {
33
+ _chunkWCKSKU3Cjs.logger.info("\u{1F680} Starting development servers...\n");
34
+ for (const config of configs) {
35
+ this.startProcess(config);
36
+ }
37
+ process.on("SIGINT", () => this.stopAll());
38
+ process.on("SIGTERM", () => this.stopAll());
39
+ }
40
+ startProcess(config) {
41
+ const { name, command, args, cwd, color, env } = config;
42
+ const colorFn = this.colors[color];
43
+ const prefix = colorFn(`[${name}]`);
44
+ _chunkWCKSKU3Cjs.logger.info(`${prefix} Starting...`);
45
+ const proc = _child_process.spawn.call(void 0, command, args, {
46
+ cwd,
47
+ stdio: "pipe",
48
+ shell: true,
49
+ env: {
50
+ ...process.env,
51
+ ...env
52
+ }
53
+ });
54
+ this.processes.set(name, proc);
55
+ _optionalChain([proc, 'access', _ => _.stdout, 'optionalAccess', _2 => _2.on, 'call', _3 => _3("data", (data) => {
56
+ const lines = data.toString().split("\n").filter((l) => l.trim());
57
+ lines.forEach((line) => {
58
+ console.log(`${prefix} ${line}`);
59
+ });
60
+ })]);
61
+ _optionalChain([proc, 'access', _4 => _4.stderr, 'optionalAccess', _5 => _5.on, 'call', _6 => _6("data", (data) => {
62
+ const lines = data.toString().split("\n").filter((l) => l.trim());
63
+ lines.forEach((line) => {
64
+ console.error(`${prefix} ${pc.red(line)}`);
65
+ });
66
+ })]);
67
+ proc.on("exit", (code) => {
68
+ if (code !== 0 && code !== null) {
69
+ _chunkWCKSKU3Cjs.logger.error(`${prefix} Exited with code ${code}`);
70
+ }
71
+ this.processes.delete(name);
72
+ });
73
+ proc.on("error", (error) => {
74
+ _chunkWCKSKU3Cjs.logger.error(`${prefix} Error:`, error.message);
75
+ });
76
+ }
77
+ stopAll() {
78
+ _chunkWCKSKU3Cjs.logger.info("\n\u{1F6D1} Stopping all processes...");
79
+ for (const [name, proc] of this.processes.entries()) {
80
+ _chunkWCKSKU3Cjs.logger.info(`Stopping ${name}...`);
81
+ proc.kill("SIGTERM");
82
+ }
83
+ setTimeout(() => {
84
+ process.exit(0);
85
+ }, 1e3);
86
+ }
87
+ isRunning(name) {
88
+ return this.processes.has(name);
89
+ }
90
+ }, _class);
91
+
92
+ // src/dev-server/dev-server.ts
93
+ var _path = require('path'); var path = _interopRequireWildcard(_path);
94
+ var _fs = require('fs'); var fs = _interopRequireWildcard(_fs);
95
+ var FusionDevServer = class {
96
+ static {
97
+ _chunkXE4OXN2Wjs.__name.call(void 0, this, "FusionDevServer");
98
+ }
99
+
100
+ constructor() {
101
+ this.processRunner = new ProcessRunner();
102
+ }
103
+ async start(config) {
104
+ const { apiPath, webPath, webPort = 3e3, apiPort = 5050, useProxy = false } = config;
105
+ if (useProxy) {
106
+ throw new Error("The legacy dev proxy is disabled because it conflicts with the Next.js port. Use the production runtime for single-port serving.");
107
+ }
108
+ if (!fs.existsSync(apiPath)) {
109
+ throw new Error(`API path not found: ${apiPath}`);
110
+ }
111
+ if (!fs.existsSync(webPath)) {
112
+ throw new Error(`Web path not found: ${webPath}`);
113
+ }
114
+ const processes = [];
115
+ processes.push({
116
+ name: "API",
117
+ command: "npm",
118
+ args: [
119
+ "run",
120
+ "start:dev"
121
+ ],
122
+ cwd: path.resolve(apiPath),
123
+ color: "cyan",
124
+ env: {
125
+ PORT: apiPort.toString()
126
+ }
127
+ });
128
+ processes.push({
129
+ name: "Web",
130
+ command: "npm",
131
+ args: [
132
+ "run",
133
+ "dev",
134
+ "--",
135
+ "-p",
136
+ webPort.toString()
137
+ ],
138
+ cwd: path.resolve(webPath),
139
+ color: "green",
140
+ env: {
141
+ NEXT_PUBLIC_API_URL: `http://localhost:${apiPort}`
142
+ }
143
+ });
144
+ await this.processRunner.run(processes);
145
+ _chunkWCKSKU3Cjs.logger.info("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
146
+ _chunkWCKSKU3Cjs.logger.info("\u2551 Fusion Development Server Ready \u2551");
147
+ _chunkWCKSKU3Cjs.logger.info("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\n");
148
+ _chunkWCKSKU3Cjs.logger.info(`\u{1F310} Web: http://localhost:${webPort}`);
149
+ _chunkWCKSKU3Cjs.logger.info(`\u{1F50C} API: http://localhost:${apiPort}`);
150
+ _chunkWCKSKU3Cjs.logger.info("\n");
151
+ }
152
+ stop() {
153
+ this.processRunner.stopAll();
154
+ }
155
+ };
156
+
157
+ // src/dev-server/proxy.ts
158
+ var _http = require('http'); var _http2 = _interopRequireDefault(_http);
159
+ var _httpproxy = require('http-proxy'); var _httpproxy2 = _interopRequireDefault(_httpproxy);
160
+
161
+ var ProxyServer = (_class2 = class {constructor() { _class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this); }
162
+ static {
163
+ _chunkXE4OXN2Wjs.__name.call(void 0, this, "ProxyServer");
164
+ }
165
+ __init3() {this.server = null}
166
+ __init4() {this.proxy = null}
167
+ async start(config) {
168
+ const { port, apiTarget, apiPrefix } = config;
169
+ this.proxy = _httpproxy2.default.createProxyServer({
170
+ target: apiTarget,
171
+ changeOrigin: true,
172
+ ws: true
173
+ });
174
+ this.proxy.on("error", (err, req, res) => {
175
+ _chunkWCKSKU3Cjs.logger.error("Proxy error:", err.message);
176
+ if (res && "headersSent" in res && !res.headersSent) {
177
+ res.writeHead(502, {
178
+ "Content-Type": "text/plain"
179
+ });
180
+ res.end("Bad Gateway - API server unavailable");
181
+ }
182
+ });
183
+ this.server = _http2.default.createServer((req, res) => {
184
+ if (_optionalChain([req, 'access', _7 => _7.url, 'optionalAccess', _8 => _8.startsWith, 'call', _9 => _9(apiPrefix)])) {
185
+ const newUrl = req.url.substring(apiPrefix.length) || "/";
186
+ req.url = newUrl;
187
+ _chunkWCKSKU3Cjs.logger.info(pc2.gray(`\u2192 ${req.method} ${apiPrefix}${newUrl}`));
188
+ this.proxy.web(req, res);
189
+ } else {
190
+ res.writeHead(404);
191
+ res.end("Not Found");
192
+ }
193
+ });
194
+ this.server.on("upgrade", (req, socket, head) => {
195
+ if (_optionalChain([req, 'access', _10 => _10.url, 'optionalAccess', _11 => _11.startsWith, 'call', _12 => _12(apiPrefix)])) {
196
+ const newUrl = req.url.substring(apiPrefix.length) || "/";
197
+ req.url = newUrl;
198
+ this.proxy.ws(req, socket, head);
199
+ }
200
+ });
201
+ return new Promise((resolve2) => {
202
+ this.server.listen(port, () => {
203
+ _chunkWCKSKU3Cjs.logger.success(`\u2705 Proxy server running on port ${port}`);
204
+ _chunkWCKSKU3Cjs.logger.info(` Forwarding ${pc2.cyan(apiPrefix + "/*")} \u2192 ${pc2.cyan(apiTarget)}
205
+ `);
206
+ resolve2();
207
+ });
208
+ });
209
+ }
210
+ stop() {
211
+ if (this.server) {
212
+ this.server.close();
213
+ this.server = null;
214
+ }
215
+ if (this.proxy) {
216
+ this.proxy.close();
217
+ this.proxy = null;
218
+ }
219
+ }
220
+ }, _class2);
221
+
222
+
223
+
224
+
225
+
226
+
227
+ exports.ProcessRunner = ProcessRunner; exports.FusionDevServer = FusionDevServer; exports.ProxyServer = ProxyServer; exports.dev_server_exports = dev_server_exports;
228
+ //# sourceMappingURL=chunk-2LJVUMXW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Volumes/Projects/wexts/packages/dist/chunk-2LJVUMXW.js","../src/dev-server/index.ts","../src/dev-server/process-runner.ts","../src/dev-server/dev-server.ts","../src/dev-server/proxy.ts"],"names":["ProcessRunner","processes","Map","colors","cyan","green","yellow","magenta","blue","run","configs","logger","info","config","startProcess","process","on","stopAll","name","command","args","cwd","color","env","colorFn","prefix","spawn","lines","forEach","log","error","code","exit","path","fs","FusionDevServer","processRunner","apiPath","useProxy","Error","existsSync","push","resolve","apiPort","toString","NEXT_PUBLIC_API_URL","httpProxy","pc","ProxyServer","port","apiTarget","changeOrigin","writeHead","server","url","newUrl","proxy","Promise","success","close"],"mappings":"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","file":"/Volumes/Projects/wexts/packages/dist/chunk-2LJVUMXW.js","sourcesContent":[null,"export * from './dev-server';\nexport * from './process-runner';\nexport * from './proxy';\n","import { spawn, ChildProcess } from 'child_process';\nimport { logger } from '../core/logger';\nimport * as pc from 'picocolors';\n\nexport interface ProcessConfig {\n name: string;\n command: string;\n args: string[];\n cwd: string;\n color: 'cyan' | 'green' | 'yellow' | 'magenta' | 'blue';\n env?: Record<string, string>;\n}\n\n/**\n * Run multiple processes concurrently with colored output\n */\nexport class ProcessRunner {\n private processes: Map<string, ChildProcess> = new Map();\n private colors = {\n cyan: pc.cyan,\n green: pc.green,\n yellow: pc.yellow,\n magenta: pc.magenta,\n blue: pc.blue,\n };\n\n async run(configs: ProcessConfig[]): Promise<void> {\n logger.info('šŸš€ Starting development servers...\\n');\n\n for (const config of configs) {\n this.startProcess(config);\n }\n\n // Handle graceful shutdown\n process.on('SIGINT', () => this.stopAll());\n process.on('SIGTERM', () => this.stopAll());\n }\n\n private startProcess(config: ProcessConfig): void {\n const { name, command, args, cwd, color, env } = config;\n\n const colorFn = this.colors[color];\n const prefix = colorFn(`[${name}]`);\n\n logger.info(`${prefix} Starting...`);\n\n const proc = spawn(command, args, {\n cwd,\n stdio: 'pipe',\n shell: true,\n env: { ...process.env, ...env },\n });\n\n this.processes.set(name, proc);\n\n // Handle stdout\n proc.stdout?.on('data', (data) => {\n const lines = data.toString().split('\\n').filter((l: string) => l.trim());\n lines.forEach((line: string) => {\n console.log(`${prefix} ${line}`);\n });\n });\n\n // Handle stderr\n proc.stderr?.on('data', (data) => {\n const lines = data.toString().split('\\n').filter((l: string) => l.trim());\n lines.forEach((line: string) => {\n console.error(`${prefix} ${pc.red(line)}`);\n });\n });\n\n // Handle exit\n proc.on('exit', (code) => {\n if (code !== 0 && code !== null) {\n logger.error(`${prefix} Exited with code ${code}`);\n }\n this.processes.delete(name);\n });\n\n // Handle errors\n proc.on('error', (error) => {\n logger.error(`${prefix} Error:`, error.message);\n });\n }\n\n stopAll(): void {\n logger.info('\\nšŸ›‘ Stopping all processes...');\n\n for (const [name, proc] of this.processes.entries()) {\n logger.info(`Stopping ${name}...`);\n proc.kill('SIGTERM');\n }\n\n setTimeout(() => {\n process.exit(0);\n }, 1000);\n }\n\n isRunning(name: string): boolean {\n return this.processes.has(name);\n }\n}\n","import { ProcessRunner, ProcessConfig } from './process-runner';\nimport { logger } from '../core/logger';\nimport * as path from 'path';\nimport * as fs from 'fs';\n\nexport interface DevServerConfig {\n apiPath: string;\n webPath: string;\n webPort?: number;\n apiPort?: number;\n useProxy?: boolean;\n}\n\n/**\n * Unified development server for Fusion projects\n */\nexport class FusionDevServer {\n private processRunner: ProcessRunner;\n\n constructor() {\n this.processRunner = new ProcessRunner();\n }\n\n async start(config: DevServerConfig): Promise<void> {\n const {\n apiPath,\n webPath,\n webPort = 3000,\n apiPort = 5050,\n useProxy = false,\n } = config;\n\n if (useProxy) {\n throw new Error('The legacy dev proxy is disabled because it conflicts with the Next.js port. Use the production runtime for single-port serving.');\n }\n\n // Validate paths\n if (!fs.existsSync(apiPath)) {\n throw new Error(`API path not found: ${apiPath}`);\n }\n if (!fs.existsSync(webPath)) {\n throw new Error(`Web path not found: ${webPath}`);\n }\n\n const processes: ProcessConfig[] = [];\n\n // Add API server\n processes.push({\n name: 'API',\n command: 'npm',\n args: ['run', 'start:dev'],\n cwd: path.resolve(apiPath),\n color: 'cyan',\n env: {\n PORT: apiPort.toString(),\n },\n });\n\n // Add Web server\n processes.push({\n name: 'Web',\n command: 'npm',\n args: ['run', 'dev', '--', '-p', webPort.toString()],\n cwd: path.resolve(webPath),\n color: 'green',\n env: {\n NEXT_PUBLIC_API_URL: `http://localhost:${apiPort}`,\n },\n });\n\n // Start processes\n await this.processRunner.run(processes);\n\n // Log info\n logger.info('╔═══════════════════════════════════════╗');\n logger.info('ā•‘ Fusion Development Server Ready ā•‘');\n logger.info('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\\n');\n logger.info(`🌐 Web: http://localhost:${webPort}`);\n logger.info(`šŸ”Œ API: http://localhost:${apiPort}`);\n logger.info('\\n');\n }\n\n stop(): void {\n this.processRunner.stopAll();\n }\n}\n","import http from 'http';\nimport httpProxy from 'http-proxy';\nimport { logger } from '../core/logger';\nimport * as pc from 'picocolors';\n\nexport interface ProxyConfig {\n port: number;\n apiTarget: string;\n apiPrefix: string;\n}\n\n/**\n * Proxy server to forward API requests from Next.js to NestJS\n */\nexport class ProxyServer {\n private server: http.Server | null = null;\n private proxy: httpProxy | null = null;\n\n async start(config: ProxyConfig): Promise<void> {\n const { port, apiTarget, apiPrefix } = config;\n\n this.proxy = httpProxy.createProxyServer({\n target: apiTarget,\n changeOrigin: true,\n ws: true, // WebSocket support\n });\n\n // Handle proxy errors\n this.proxy.on('error', (err, req, res) => {\n logger.error('Proxy error:', err.message);\n if (res && 'headersSent' in res && !(res as any).headersSent) {\n (res as http.ServerResponse).writeHead(502, { 'Content-Type': 'text/plain' });\n (res as http.ServerResponse).end('Bad Gateway - API server unavailable');\n }\n });\n\n this.server = http.createServer((req, res) => {\n // Check if request is for API\n if (req.url?.startsWith(apiPrefix)) {\n // Remove prefix before forwarding\n const newUrl = req.url.substring(apiPrefix.length) || '/';\n req.url = newUrl;\n\n logger.info(pc.gray(`→ ${req.method} ${apiPrefix}${newUrl}`));\n this.proxy!.web(req, res);\n } else {\n // Not an API request - should not happen\n res.writeHead(404);\n res.end('Not Found');\n }\n });\n\n // Handle WebSocket upgrade\n this.server.on('upgrade', (req, socket, head) => {\n if (req.url?.startsWith(apiPrefix)) {\n const newUrl = req.url.substring(apiPrefix.length) || '/';\n req.url = newUrl;\n this.proxy!.ws(req, socket, head);\n }\n });\n\n return new Promise((resolve) => {\n this.server!.listen(port, () => {\n logger.success(`āœ… Proxy server running on port ${port}`);\n logger.info(` Forwarding ${pc.cyan(apiPrefix + '/*')} → ${pc.cyan(apiTarget)}\\n`);\n resolve();\n });\n });\n }\n\n stop(): void {\n if (this.server) {\n this.server.close();\n this.server = null;\n }\n if (this.proxy) {\n this.proxy.close();\n this.proxy = null;\n }\n }\n}\n"]}