@bobtail.software/b-ssr 1.1.0 → 1.1.2
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 +77 -5
- package/dist/fastify-b-ssr-plugin.cjs +1 -1
- package/dist/fastify-b-ssr-plugin.d.cts +29 -3
- package/dist/fastify-b-ssr-plugin.d.ts +29 -3
- package/dist/fastify-b-ssr-plugin.js +1 -1
- package/dist/rpc-type-generator.cjs +4 -4
- package/dist/rpc-type-generator.js +4 -4
- package/dist/vite-rpc-plugin.cjs +40 -40
- package/dist/vite-rpc-plugin.js +37 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,6 +59,71 @@ await fastify.listen({ port: 3000 });
|
|
|
59
59
|
console.log('Server running on http://localhost:3000');
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
### Multi-entry SSR (opcional)
|
|
63
|
+
|
|
64
|
+
Puedes definir múltiples entry points de SSR y escogerlos por ruta o con un resolver.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
await fastify.register(bSsrPlugin, {
|
|
68
|
+
root: process.cwd(),
|
|
69
|
+
entries: [
|
|
70
|
+
{
|
|
71
|
+
name: 'app',
|
|
72
|
+
match: '/app',
|
|
73
|
+
devEntryFile: '/src/entry-app-server.tsx',
|
|
74
|
+
prodEntryFile: './dist/app/server/entry-server.mjs',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'admin',
|
|
78
|
+
match: /^\\/admin/,
|
|
79
|
+
devEntryFile: '/src/entry-admin-server.tsx',
|
|
80
|
+
prodEntryFile: './dist/admin/server/entry-server.mjs',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'fallback',
|
|
84
|
+
devEntryFile: '/src/entry-server.tsx',
|
|
85
|
+
prodEntryFile: './dist/server/entry-server.mjs',
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
// Solo se sirve un clientDistDir por defecto.
|
|
89
|
+
// Si necesitas varios, registra static manualmente.
|
|
90
|
+
clientDistDir: './dist/client',
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Selección de entry (orden de prioridad):
|
|
95
|
+
|
|
96
|
+
1. `entry` en `addRenderRoute` (override explícito)
|
|
97
|
+
2. `resolveEntry(req)` en opciones del plugin
|
|
98
|
+
3. `entries[].match` (string, RegExp, o función)
|
|
99
|
+
4. Fallback sin `match`
|
|
100
|
+
5. `devEntryFile` / `prodEntryFile` (single-entry legacy)
|
|
101
|
+
|
|
102
|
+
Resolver global:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
const entries = [
|
|
106
|
+
{
|
|
107
|
+
name: 'app',
|
|
108
|
+
devEntryFile: '/src/entry-app-server.tsx',
|
|
109
|
+
prodEntryFile: './dist/app/server/entry-server.mjs',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'admin',
|
|
113
|
+
devEntryFile: '/src/entry-admin-server.tsx',
|
|
114
|
+
prodEntryFile: './dist/admin/server/entry-server.mjs',
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
await fastify.register(bSsrPlugin, {
|
|
119
|
+
root: process.cwd(),
|
|
120
|
+
entries,
|
|
121
|
+
resolveEntry: (req) => (req.headers['x-entry'] === 'admin' ? entries[1] : null),
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Si `entries` no existe, el comportamiento es el mismo que antes (single-entry).
|
|
126
|
+
|
|
62
127
|
### 2. Configuración de Vite (`vite.config.ts`)
|
|
63
128
|
|
|
64
129
|
Necesitas el plugin `rpcGeneratorPlugin` para habilitar la magia de los tipos y la separación cliente/servidor.
|
|
@@ -342,6 +407,14 @@ fastify.addRenderRoute('/*', {
|
|
|
342
407
|
});
|
|
343
408
|
```
|
|
344
409
|
|
|
410
|
+
Puedes forzar un entry específico por ruta usando `entry` (aplica también a sub-rutas por el wildcard):
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
fastify.addRenderRoute('/admin', { entry: 'admin' });
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
`entry` debe coincidir con `entries[].name`.
|
|
417
|
+
|
|
345
418
|
---
|
|
346
419
|
|
|
347
420
|
## 📂 Estructura de Archivos Recomendada
|
|
@@ -392,13 +465,12 @@ pnpm test:coverage
|
|
|
392
465
|
|
|
393
466
|
### Cobertura de Tests
|
|
394
467
|
|
|
395
|
-
|
|
396
|
-
- **4 tests en skip** - Limitaciones conocidas del generador de tipos
|
|
468
|
+
La suite cubre unit tests e integration tests. Algunos tests están en `skip` por limitaciones conocidas del generador de tipos.
|
|
397
469
|
|
|
398
|
-
**Distribución:**
|
|
470
|
+
**Distribución (general):**
|
|
399
471
|
|
|
400
|
-
- Unit Tests:
|
|
401
|
-
- Integration Tests:
|
|
472
|
+
- Unit Tests: vite-rpc-plugin, type generator standalone, virtual modules, firewall
|
|
473
|
+
- Integration Tests: zod-validation, error-handling, ssr-hydration, rpc-client
|
|
402
474
|
|
|
403
475
|
## 🔒 Seguridad
|
|
404
476
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var b=Object.create;var v=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var G=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var k=(e,r)=>{for(var n in r)v(e,n,{get:r[n],enumerable:!0})},w=(e,r,n,f)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of I(r))!P.call(e,o)&&o!==n&&v(e,o,{get:()=>r[o],enumerable:!(f=O(r,o))||f.enumerable});return e};var R=(e,r,n)=>(n=e!=null?b(G(e)):{},w(r||!e||!e.__esModule?v(n,"default",{value:e,enumerable:!0}):n,e)),q=e=>w(v({},"__esModule",{value:!0}),e);var Z={};k(Z,{default:()=>H});module.exports=q(Z);var N=require("@fastify/multipart"),T=R(require("fastify-plugin"),1),E=R(require("path"),1);async function D(e=24678){let r=await import("net");return new Promise((n,f)=>{let o=r.createServer();o.listen(e,"127.0.0.1",()=>{let t=o.address(),s=typeof t=="object"&&t!==null?t.port:e;o.close(()=>n(s))}),o.on("error",t=>{t.code==="EADDRINUSE"?o.close(()=>n(D(e+1))):f(t)})})}function F(e){if(e)return e}function x(e){let r=e.raw?.url||e.url||"",n=r.indexOf("?");return n===-1?r:r.slice(0,n)}function A(e,r,n){return e.match?typeof e.match=="string"?n.startsWith(e.match):e.match instanceof RegExp?e.match.test(n):e.match(r):!1}function C(e,r){if(r.resolveEntry){let t=r.resolveEntry(e);if(t)return t}let n=r.entries||[];if(n.length===0)return;let f=x(e),o;for(let t of n){if(!t.match){o||(o=t);continue}if(A(t,e,f))return t}return o}function _(e,r){if(!(!r.entries||r.entries.length===0))return r.entries.find(n=>n.name===e)}var $=async(e,r)=>{if(e.body&&typeof e.body=="object"){let n=e.body,f={};for(let o of Object.keys(n)){let t=n[o];t&&typeof t=="object"?t.type==="field"?f[o]=t.value:t.type==="file"&&(f[o]={filename:t.filename,mimetype:t.mimetype,encoding:t.encoding,fieldname:t.fieldname,file:t.file}):f[o]=t}e.body=f}},M=async(e,r)=>{let n=t=>{if(r.errorHandler){let s=r.errorHandler(t);if(s)return s}return{message:"Internal Server Error",statusCode:500}};if(e.hasDecorator("viteInitDone")&&e.viteInitDone)return;if(!e.hasDecorator("multipartErrors"))try{let t=await import("@fastify/multipart");await e.register(t.default)}catch(t){if(t.code!=="FST_ERR_DEC_ALREADY_PRESENT")throw t}let f=process.env.NODE_ENV==="production";if(f){let t=new Set;r.clientDistDir&&t.add(r.clientDistDir);for(let d of r.entries||[])d.clientDistDir&&t.add(d.clientDistDir);t.size>1&&console.warn("\u26A0\uFE0F [Fastify-SSR] Multiple clientDistDir detected. Only the first will be served. Register additional static dirs manually if needed.");let[s]=t;if(s)try{let d=await import("@fastify/static"),h=E.default.resolve(r.root,s);await e.register(d.default,{root:h,wildcard:!1}),console.log(`\u{1F4C2} [Fastify-SSR] Serving static assets from: ${h}`)}catch(d){console.error("\u274C [Fastify-SSR] Error registering @fastify/static. Did you install it?",d)}}else{if(!e.hasDecorator("use"))try{let u=await import("@fastify/middie");await e.register(u.default,{hook:"onRequest"})}catch(u){if(u.code!=="FST_ERR_DEC_ALREADY_PRESENT")throw u}let t=await import("vite"),s=r.viteConfig||{},d;s.server?.hmr&&typeof s.server.hmr=="object"&&"port"in s.server.hmr?d=s.server.hmr.port:d=r.hmrPort??await D();let h=await t.createServer({root:r.root,appType:"custom",...s,server:{hmr:{port:d},...s.server,middlewareMode:!0}});e.use(h.middlewares),e.hasDecorator("viteServer")||e.decorate("viteServer",h),console.log("\u{1F680} [Fastify-SSR] Vite Dev Server Ready")}e.decorate("viteInitDone",!0);let o=async(t,s,d,h)=>{try{let u=t.raw.url,y=r.getGlobalSettings?await r.getGlobalSettings(t):void 0;if(E.default.extname(u)!==""){s.status(404).send(`File not found: ${u}`);return}let m="";!f&&e.viteServer&&(m=await e.viteServer.transformIndexHtml(u,"<html><head></head><body></body></html>"),m=m.substring(m.indexOf("<head>")+6,m.indexOf("</head>"))),m=(y?`<script>window.__GLOBAL_SETTINGS__ = ${JSON.stringify(y)}</script>`:"")+m;let a;if(h){if(a=_(h,r),!a)throw new Error(`Entry "${h}" not found. Make sure entries[] includes a matching name.`)}else a=C(t,r);let i=a?.devEntryFile??r.devEntryFile,l=a?.prodEntryFile??r.prodEntryFile;if(!i||!l)throw new Error(`No SSR entry resolved for request "${x(t)}". Provide devEntryFile/prodEntryFile or configure entries/resolveEntry.`);let c=f?await import(E.default.resolve(r.root,l)):await e.viteServer.ssrLoadModule(i),p=c.render||c.default;if(typeof p!="function")throw new Error(`Entry file ${f?l:i} must export a 'render' function.`);await p({req:t,reply:s,head:m,data:d,globalSettings:y})}catch(u){e.viteServer?.ssrFixStacktrace(u),console.error("[SSR Error]:",u),s.sent||s.status(500).send("Internal Server Error")}};e.decorate("addRpcRoute",function(t,s){let{handler:d,schema:h,...u}=s,y=`/rpc${t}`,m=F(h),S={...u,schema:m};s?.schema?.consumes?.includes("multipart/form-data")&&(S.preValidation=$),this.route({method:"POST",url:y,...S,handler:async(i,l)=>{try{let c=await d.call(this,i,l);return l.sent?void 0:c}catch(c){if(console.error(`[RPC Error] ${y}:`,c),!l.sent){let{statusCode:p,message:g}=n(c);l.status(p).send({error:{message:g}})}}}})}),e.decorate("addRenderRoute",function(t,s){let{handler:d,schema:h,entry:u,...y}=s||{},m=F(h),S=async(a,i,l)=>{try{let c=await d?.call(this,a,i);return l?c:o(a,i,c,u)}catch(c){if(console.error(`[Render Error] ${t}:`,c),!i.sent){let{statusCode:p,message:g}=n(c);if(l)i.status(p).send({error:{message:g}});else return o(a,i,{ssrError:{statusCode:500,message:"Internal Error"}})}}};if(this.route({method:"GET",url:t,schema:m,...y,handler:(a,i)=>S(a,i,!1)}),t!=="*"&&t!=="/*"){this.route({method:"GET",url:`/loader${t}`,schema:m,...y,handler:(l,c)=>S(l,c,!0)});let a=t.endsWith("/")?"*":"/*",i=`${t}${a}`;this.route({method:"GET",url:i,schema:m,...y,handler:(l,c)=>S(l,c,!1)})}}),e.decorate("addLoaderRoute",function(t,s){let{handler:d,schema:h,...u}=s,y=`/api${t}`,m=F(h);this.route({method:"GET",url:y,schema:m,...u,handler:async(S,a)=>{try{let i=await d.call(this,S,a);return a.sent?void 0:i}catch(i){if(console.error(`[Loader API Error] ${y}:`,i),!a.sent){let{statusCode:l,message:c}=n(i);a.status(l).send({error:{message:c}})}}}})})},H=(0,T.default)(M,{name:"fastify-b-ssr"});
|
|
@@ -13,6 +13,11 @@ type AddRpcRouteOptions<TRpcSchema extends FastifySchema> = BaseRouteOptions<Rou
|
|
|
13
13
|
type AddRenderRouteOptions<TSchema extends FastifySchema, TRpcSchema extends FastifySchema> = BaseRouteOptions<RouteGenericForSchema<TSchema>> & {
|
|
14
14
|
handler?: (this: FastifyInstance, req: FastifyRequest<RouteGenericForSchema<TSchema>>, reply: FastifyReply<RouteGenericForSchema<TSchema>>) => Promise<unknown> | unknown;
|
|
15
15
|
prefix?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Nombre del entry point a usar para esta ruta (y sus sub-rutas).
|
|
18
|
+
* Debe coincidir con `entries[].name` en las opciones del plugin.
|
|
19
|
+
*/
|
|
20
|
+
entry?: string;
|
|
16
21
|
};
|
|
17
22
|
type AddLoaderRouteOptions<TSchema extends FastifySchema> = BaseRouteOptions<RouteGenericForSchema<TSchema>> & {
|
|
18
23
|
handler: (this: FastifyInstance, req: FastifyRequest<RouteGenericForSchema<TSchema>>, reply: FastifyReply<RouteGenericForSchema<TSchema>>) => Promise<unknown> | unknown;
|
|
@@ -25,18 +30,39 @@ interface RouteGenericForSchema<TSchema extends FastifySchema> extends RouteGene
|
|
|
25
30
|
Params: InferZod<TSchema['params']>;
|
|
26
31
|
Headers: InferZod<TSchema['headers']>;
|
|
27
32
|
}
|
|
33
|
+
type SsrEntryMatch = string | RegExp | ((req: FastifyRequest) => boolean);
|
|
34
|
+
interface SsrEntry {
|
|
35
|
+
/** Nombre opcional para debugging */
|
|
36
|
+
name?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Regla para escoger el entry. Si no se provee, actúa como fallback
|
|
39
|
+
* cuando ningún match coincide.
|
|
40
|
+
*/
|
|
41
|
+
match?: SsrEntryMatch;
|
|
42
|
+
/** Ruta al archivo de entrada SSR en desarrollo (ej: '/src/entry-ssr-server.tsx') */
|
|
43
|
+
devEntryFile: string;
|
|
44
|
+
/** Ruta relativa al archivo compilado del servidor SSR en producción (ej: './dist-react/server/entry-server.mjs') */
|
|
45
|
+
prodEntryFile: string;
|
|
46
|
+
/** Ruta relativa a la carpeta de salida del cliente en producción */
|
|
47
|
+
clientDistDir?: string;
|
|
48
|
+
}
|
|
49
|
+
type SsrEntryResolver = (req: FastifyRequest) => SsrEntry | null | undefined;
|
|
28
50
|
interface FastifyReactSsrOptions {
|
|
29
51
|
/** Raíz del proyecto (donde está vite.config.ts). Generalmente `process.cwd()` o `__dirname` */
|
|
30
52
|
root: string;
|
|
31
53
|
/** Ruta al archivo de entrada SSR en desarrollo (ej: '/src/entry-ssr-server.tsx') */
|
|
32
|
-
devEntryFile
|
|
54
|
+
devEntryFile?: string;
|
|
33
55
|
/** Ruta relativa al archivo compilado del servidor SSR en producción (ej: './dist-react/server/entry-server.mjs') */
|
|
34
|
-
prodEntryFile
|
|
56
|
+
prodEntryFile?: string;
|
|
35
57
|
/**
|
|
36
58
|
* Ruta relativa a la carpeta de salida del cliente en producción (ej: './dist-react/client').
|
|
37
59
|
* Requerido para servir assets (CSS, JS) en producción.
|
|
38
60
|
*/
|
|
39
61
|
clientDistDir?: string;
|
|
62
|
+
/** Múltiples entry points para SSR */
|
|
63
|
+
entries?: SsrEntry[];
|
|
64
|
+
/** Resolver custom para elegir entry point en base al request */
|
|
65
|
+
resolveEntry?: SsrEntryResolver;
|
|
40
66
|
/** Configuración extra para Vite (opcional) */
|
|
41
67
|
viteConfig?: InlineConfig;
|
|
42
68
|
/** Manejador de errores personalizado */
|
|
@@ -62,4 +88,4 @@ declare module 'fastify' {
|
|
|
62
88
|
}
|
|
63
89
|
declare const _default: FastifyPluginAsyncZod<FastifyReactSsrOptions>;
|
|
64
90
|
|
|
65
|
-
export { type AddLoaderRouteOptions, type AddRenderRouteOptions, type AddRpcRouteOptions, type BaseRouteOptions, type FastifyReactSsrOptions, type RouteGenericForSchema, _default as default };
|
|
91
|
+
export { type AddLoaderRouteOptions, type AddRenderRouteOptions, type AddRpcRouteOptions, type BaseRouteOptions, type FastifyReactSsrOptions, type RouteGenericForSchema, type SsrEntry, type SsrEntryMatch, type SsrEntryResolver, _default as default };
|
|
@@ -13,6 +13,11 @@ type AddRpcRouteOptions<TRpcSchema extends FastifySchema> = BaseRouteOptions<Rou
|
|
|
13
13
|
type AddRenderRouteOptions<TSchema extends FastifySchema, TRpcSchema extends FastifySchema> = BaseRouteOptions<RouteGenericForSchema<TSchema>> & {
|
|
14
14
|
handler?: (this: FastifyInstance, req: FastifyRequest<RouteGenericForSchema<TSchema>>, reply: FastifyReply<RouteGenericForSchema<TSchema>>) => Promise<unknown> | unknown;
|
|
15
15
|
prefix?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Nombre del entry point a usar para esta ruta (y sus sub-rutas).
|
|
18
|
+
* Debe coincidir con `entries[].name` en las opciones del plugin.
|
|
19
|
+
*/
|
|
20
|
+
entry?: string;
|
|
16
21
|
};
|
|
17
22
|
type AddLoaderRouteOptions<TSchema extends FastifySchema> = BaseRouteOptions<RouteGenericForSchema<TSchema>> & {
|
|
18
23
|
handler: (this: FastifyInstance, req: FastifyRequest<RouteGenericForSchema<TSchema>>, reply: FastifyReply<RouteGenericForSchema<TSchema>>) => Promise<unknown> | unknown;
|
|
@@ -25,18 +30,39 @@ interface RouteGenericForSchema<TSchema extends FastifySchema> extends RouteGene
|
|
|
25
30
|
Params: InferZod<TSchema['params']>;
|
|
26
31
|
Headers: InferZod<TSchema['headers']>;
|
|
27
32
|
}
|
|
33
|
+
type SsrEntryMatch = string | RegExp | ((req: FastifyRequest) => boolean);
|
|
34
|
+
interface SsrEntry {
|
|
35
|
+
/** Nombre opcional para debugging */
|
|
36
|
+
name?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Regla para escoger el entry. Si no se provee, actúa como fallback
|
|
39
|
+
* cuando ningún match coincide.
|
|
40
|
+
*/
|
|
41
|
+
match?: SsrEntryMatch;
|
|
42
|
+
/** Ruta al archivo de entrada SSR en desarrollo (ej: '/src/entry-ssr-server.tsx') */
|
|
43
|
+
devEntryFile: string;
|
|
44
|
+
/** Ruta relativa al archivo compilado del servidor SSR en producción (ej: './dist-react/server/entry-server.mjs') */
|
|
45
|
+
prodEntryFile: string;
|
|
46
|
+
/** Ruta relativa a la carpeta de salida del cliente en producción */
|
|
47
|
+
clientDistDir?: string;
|
|
48
|
+
}
|
|
49
|
+
type SsrEntryResolver = (req: FastifyRequest) => SsrEntry | null | undefined;
|
|
28
50
|
interface FastifyReactSsrOptions {
|
|
29
51
|
/** Raíz del proyecto (donde está vite.config.ts). Generalmente `process.cwd()` o `__dirname` */
|
|
30
52
|
root: string;
|
|
31
53
|
/** Ruta al archivo de entrada SSR en desarrollo (ej: '/src/entry-ssr-server.tsx') */
|
|
32
|
-
devEntryFile
|
|
54
|
+
devEntryFile?: string;
|
|
33
55
|
/** Ruta relativa al archivo compilado del servidor SSR en producción (ej: './dist-react/server/entry-server.mjs') */
|
|
34
|
-
prodEntryFile
|
|
56
|
+
prodEntryFile?: string;
|
|
35
57
|
/**
|
|
36
58
|
* Ruta relativa a la carpeta de salida del cliente en producción (ej: './dist-react/client').
|
|
37
59
|
* Requerido para servir assets (CSS, JS) en producción.
|
|
38
60
|
*/
|
|
39
61
|
clientDistDir?: string;
|
|
62
|
+
/** Múltiples entry points para SSR */
|
|
63
|
+
entries?: SsrEntry[];
|
|
64
|
+
/** Resolver custom para elegir entry point en base al request */
|
|
65
|
+
resolveEntry?: SsrEntryResolver;
|
|
40
66
|
/** Configuración extra para Vite (opcional) */
|
|
41
67
|
viteConfig?: InlineConfig;
|
|
42
68
|
/** Manejador de errores personalizado */
|
|
@@ -62,4 +88,4 @@ declare module 'fastify' {
|
|
|
62
88
|
}
|
|
63
89
|
declare const _default: FastifyPluginAsyncZod<FastifyReactSsrOptions>;
|
|
64
90
|
|
|
65
|
-
export { type AddLoaderRouteOptions, type AddRenderRouteOptions, type AddRpcRouteOptions, type BaseRouteOptions, type FastifyReactSsrOptions, type RouteGenericForSchema, _default as default };
|
|
91
|
+
export { type AddLoaderRouteOptions, type AddRenderRouteOptions, type AddRpcRouteOptions, type BaseRouteOptions, type FastifyReactSsrOptions, type RouteGenericForSchema, type SsrEntry, type SsrEntryMatch, type SsrEntryResolver, _default as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"@fastify/multipart";import
|
|
1
|
+
import"@fastify/multipart";import w from"fastify-plugin";import v from"path";async function g(t=24678){let r=await import("net");return new Promise((s,h)=>{let d=r.createServer();d.listen(t,"127.0.0.1",()=>{let e=d.address(),n=typeof e=="object"&&e!==null?e.port:t;d.close(()=>s(n))}),d.on("error",e=>{e.code==="EADDRINUSE"?d.close(()=>s(g(t+1))):h(e)})})}function E(t){if(t)return t}function F(t){let r=t.raw?.url||t.url||"",s=r.indexOf("?");return s===-1?r:r.slice(0,s)}function T(t,r,s){return t.match?typeof t.match=="string"?s.startsWith(t.match):t.match instanceof RegExp?t.match.test(s):t.match(r):!1}function D(t,r){if(r.resolveEntry){let e=r.resolveEntry(t);if(e)return e}let s=r.entries||[];if(s.length===0)return;let h=F(t),d;for(let e of s){if(!e.match){d||(d=e);continue}if(T(e,t,h))return e}return d}function x(t,r){if(!(!r.entries||r.entries.length===0))return r.entries.find(s=>s.name===t)}var b=async(t,r)=>{if(t.body&&typeof t.body=="object"){let s=t.body,h={};for(let d of Object.keys(s)){let e=s[d];e&&typeof e=="object"?e.type==="field"?h[d]=e.value:e.type==="file"&&(h[d]={filename:e.filename,mimetype:e.mimetype,encoding:e.encoding,fieldname:e.fieldname,file:e.file}):h[d]=e}t.body=h}},O=async(t,r)=>{let s=e=>{if(r.errorHandler){let n=r.errorHandler(e);if(n)return n}return{message:"Internal Server Error",statusCode:500}};if(t.hasDecorator("viteInitDone")&&t.viteInitDone)return;if(!t.hasDecorator("multipartErrors"))try{let e=await import("@fastify/multipart");await t.register(e.default)}catch(e){if(e.code!=="FST_ERR_DEC_ALREADY_PRESENT")throw e}let h=process.env.NODE_ENV==="production";if(h){let e=new Set;r.clientDistDir&&e.add(r.clientDistDir);for(let c of r.entries||[])c.clientDistDir&&e.add(c.clientDistDir);e.size>1&&console.warn("\u26A0\uFE0F [Fastify-SSR] Multiple clientDistDir detected. Only the first will be served. Register additional static dirs manually if needed.");let[n]=e;if(n)try{let c=await import("@fastify/static"),m=v.resolve(r.root,n);await t.register(c.default,{root:m,wildcard:!1}),console.log(`\u{1F4C2} [Fastify-SSR] Serving static assets from: ${m}`)}catch(c){console.error("\u274C [Fastify-SSR] Error registering @fastify/static. Did you install it?",c)}}else{if(!t.hasDecorator("use"))try{let u=await import("@fastify/middie");await t.register(u.default,{hook:"onRequest"})}catch(u){if(u.code!=="FST_ERR_DEC_ALREADY_PRESENT")throw u}let e=await import("vite"),n=r.viteConfig||{},c;n.server?.hmr&&typeof n.server.hmr=="object"&&"port"in n.server.hmr?c=n.server.hmr.port:c=r.hmrPort??await g();let m=await e.createServer({root:r.root,appType:"custom",...n,server:{hmr:{port:c},...n.server,middlewareMode:!0}});t.use(m.middlewares),t.hasDecorator("viteServer")||t.decorate("viteServer",m),console.log("\u{1F680} [Fastify-SSR] Vite Dev Server Ready")}t.decorate("viteInitDone",!0);let d=async(e,n,c,m)=>{try{let u=e.raw.url,y=r.getGlobalSettings?await r.getGlobalSettings(e):void 0;if(v.extname(u)!==""){n.status(404).send(`File not found: ${u}`);return}let f="";!h&&t.viteServer&&(f=await t.viteServer.transformIndexHtml(u,"<html><head></head><body></body></html>"),f=f.substring(f.indexOf("<head>")+6,f.indexOf("</head>"))),f=(y?`<script>window.__GLOBAL_SETTINGS__ = ${JSON.stringify(y)}</script>`:"")+f;let i;if(m){if(i=x(m,r),!i)throw new Error(`Entry "${m}" not found. Make sure entries[] includes a matching name.`)}else i=D(e,r);let o=i?.devEntryFile??r.devEntryFile,l=i?.prodEntryFile??r.prodEntryFile;if(!o||!l)throw new Error(`No SSR entry resolved for request "${F(e)}". Provide devEntryFile/prodEntryFile or configure entries/resolveEntry.`);let a=h?await import(v.resolve(r.root,l)):await t.viteServer.ssrLoadModule(o),R=a.render||a.default;if(typeof R!="function")throw new Error(`Entry file ${h?l:o} must export a 'render' function.`);await R({req:e,reply:n,head:f,data:c,globalSettings:y})}catch(u){t.viteServer?.ssrFixStacktrace(u),console.error("[SSR Error]:",u),n.sent||n.status(500).send("Internal Server Error")}};t.decorate("addRpcRoute",function(e,n){let{handler:c,schema:m,...u}=n,y=`/rpc${e}`,f=E(m),S={...u,schema:f};n?.schema?.consumes?.includes("multipart/form-data")&&(S.preValidation=b),this.route({method:"POST",url:y,...S,handler:async(o,l)=>{try{let a=await c.call(this,o,l);return l.sent?void 0:a}catch(a){if(console.error(`[RPC Error] ${y}:`,a),!l.sent){let{statusCode:R,message:p}=s(a);l.status(R).send({error:{message:p}})}}}})}),t.decorate("addRenderRoute",function(e,n){let{handler:c,schema:m,entry:u,...y}=n||{},f=E(m),S=async(i,o,l)=>{try{let a=await c?.call(this,i,o);return l?a:d(i,o,a,u)}catch(a){if(console.error(`[Render Error] ${e}:`,a),!o.sent){let{statusCode:R,message:p}=s(a);if(l)o.status(R).send({error:{message:p}});else return d(i,o,{ssrError:{statusCode:500,message:"Internal Error"}})}}};if(this.route({method:"GET",url:e,schema:f,...y,handler:(i,o)=>S(i,o,!1)}),e!=="*"&&e!=="/*"){this.route({method:"GET",url:`/loader${e}`,schema:f,...y,handler:(l,a)=>S(l,a,!0)});let i=e.endsWith("/")?"*":"/*",o=`${e}${i}`;this.route({method:"GET",url:o,schema:f,...y,handler:(l,a)=>S(l,a,!1)})}}),t.decorate("addLoaderRoute",function(e,n){let{handler:c,schema:m,...u}=n,y=`/api${e}`,f=E(m);this.route({method:"GET",url:y,schema:f,...u,handler:async(S,i)=>{try{let o=await c.call(this,S,i);return i.sent?void 0:o}catch(o){if(console.error(`[Loader API Error] ${y}:`,o),!i.sent){let{statusCode:l,message:a}=s(o);i.status(l).send({error:{message:a}})}}}})})},k=w(O,{name:"fastify-b-ssr"});export{k as default};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var be=Object.create;var G=Object.defineProperty;var Fe=Object.getOwnPropertyDescriptor;var we=Object.getOwnPropertyNames;var Ce=Object.getPrototypeOf,Se=Object.prototype.hasOwnProperty;var Pe=(e,t)=>{for(var r in t)G(e,r,{get:t[r],enumerable:!0})},se=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of we(t))!Se.call(e,s)&&s!==r&&G(e,s,{get:()=>t[s],enumerable:!(n=Fe(t,s))||n.enumerable});return e};var X=(e,t,r)=>(r=e!=null?be(Ce(e)):{},se(t||!e||!e.__esModule?G(r,"default",{value:e,enumerable:!0}):r,e)),Ee=e=>se(G({},"__esModule",{value:!0}),e);var Ne={};Pe(Ne,{generateRpcTypes:()=>$e});module.exports=Ee(Ne);var te=X(require("fast-glob"),1),E=require("fs/promises"),P=X(require("path"),1),j=require("ts-morph");var v=require("fs/promises"),S=X(require("path"),1),q=require("prettier"),g=require("ts-morph"),ie=require("crypto");function Ae(e){let t=e.replace(/[^a-zA-Z0-9]+(.)?/g,(r,n)=>n?n.toUpperCase():"");return t.charAt(0).toLowerCase()+t.slice(1)}function J(e,t,r){let n=S.default.basename(r,S.default.extname(r))||"Index",s=t.replace(/:[a-zA-Z0-9_]+/g,"").replace(/\//g," "),p=`${e} ${s} ${n}`;return Ae(p)}function Re(e){let t=/'[^']+'/g,r=/`[^`]+`/g,n=[],s,p=/'[^']+'/g;for(;(s=p.exec(e))!==null;)n.push({start:s.index,end:s.index+s[0].length,value:s[0]});let o=/`[^`]+`/g;for(;(s=o.exec(e))!==null;)n.push({start:s.index,end:s.index+s[0].length,value:s[0]});if(n.length>1){let a=[...n].sort((u,w)=>{let F=u.value.replace(/['`]/g,""),l=w.value.replace(/['`]/g,"");return F.localeCompare(l)}),m="",f=0;for(let u=0;u<n.length;u++)m+=e.slice(f,n[u].start),m+=a[u].value,f=n[u].end;return m+=e.slice(f),m}return e}function ve(e){let t=e.match(/^\{([\s\S]*)\}$/);if(!t)return e;let r=t[1];if(!r.includes(":"))return e;let n=r.split(/[;,]/).map(o=>o.trim()).filter(o=>o),s=[];for(let o of n){let a=o.indexOf(":");if(a===-1)continue;let m=o.slice(0,a).trim(),f=o.slice(a+1).trim();s.push({name:m,value:f})}return s.length<=1?e:(s.sort((o,a)=>o.name.localeCompare(a.name)),`{ ${s.map(o=>`${o.name}: ${o.value}`).join("; ")} }`)}function Z(e){let t=Re(e);return t=ve(t),t}async function ee(e,t){try{if(await(0,v.readFile)(e,"utf-8")===t)return!1}catch{}let r=t;try{let n=await(0,q.resolveConfig)(e)||{};r=await(0,q.format)(t,{...n,parser:"typescript",filepath:e})}catch{}try{if(await(0,v.readFile)(e,"utf-8")===r)return!1}catch{}return await(0,v.writeFile)(e,r),!0}function oe(e){return(0,ie.createHash)("sha1").update(e).digest("hex")}var Y="v1",ae=S.default.join(process.cwd(),"node_modules",".cache","b-ssr"),ce=S.default.join(ae,"rpc-cache.json");async function le(){try{let e=await(0,v.readFile)(ce,"utf-8"),t=JSON.parse(e);return t.version!==Y?{version:Y,files:{}}:t}catch{return{version:Y,files:{}}}}async function pe(e){try{await(0,v.mkdir)(ae,{recursive:!0}),await(0,v.writeFile)(ce,JSON.stringify(e,null,2))}catch(t){console.error("\u26A0\uFE0F [B-SSR] Failed to save persistent cache:",t)}}function L(e,t,r,n,s,p){function o(l){return l.replace(/import\(['"](.*?)['"]\)/g,(y,i)=>{if(!S.default.isAbsolute(i))return y;let c=i;if(!S.default.extname(c)){let C=s.getSourceFile(N=>N.getFilePath().replace(/\.(mts|ts|tsx)$/,"")===i);C&&(c=C.getFilePath())}let d=S.default.relative(S.default.dirname(n),c).replace(/\\/g,"/");return d.startsWith(".")||(d="./"+d),`import("${d}")`})}if(e.getSymbol()?.getName()==="Promise"){let l=e.getAwaitedType();if(l)return`Promise<${L(l,t,r,n,s,p)}>`}let a=e.getAliasSymbol()??e.getSymbol();if(a?.getName()==="__object")return Z(o(e.getText(t,g.ts.TypeFormatFlags.NoTruncation|g.ts.TypeFormatFlags.UseFullyQualifiedType)));if(e.isObject()&&!a){let l=(y,i=0)=>{if(!(i>5)){if(y.isArray()){let c=y.getArrayElementType();c&&l(c,i);return}if(y.isObject()&&!y.getSymbol()&&!y.getAliasSymbol()){for(let c of y.getApparentProperties()){let d=c.getValueDeclaration();d&&l(c.getTypeAtLocation(d),i+1)}return}L(y,t,r,n,s,p)}};return l(e),Z(o(e.getText(t,g.ts.TypeFormatFlags.NoTruncation|g.ts.TypeFormatFlags.UseFullyQualifiedType)))}if(!a)return Z(o(e.getText(t,g.ts.TypeFormatFlags.NoTruncation)));let m=a.getDeclarations()[0];if(!m)return a.getName();if(g.Node.isImportSpecifier(m)||g.Node.isImportClause(m)){let l=m.getFirstAncestorByKind(g.SyntaxKind.ImportDeclaration);if(l){let y=l.getModuleSpecifierValue(),i=a.getName();return p.has(y)||p.set(y,new Set),p.get(y)?.add(i),i}}let f=m.getSourceFile();if(f.getFilePath()===r.getFilePath())return a.getName();if(f.isInNodeModules())return Z(o(e.getText(t,g.ts.TypeFormatFlags.NoTruncation)));let u=S.default.relative(S.default.dirname(n),f.getFilePath()).replace(/\\/g,"/"),w=u.startsWith(".")?u:`./${u}`,F=a.getName();return p.has(w)||p.set(w,new Set),p.get(w)?.add(F),F}function ge(e,t,r,n,s){let p;if(g.Node.isFunctionLikeDeclaration(e)||g.Node.isArrowFunction(e)){let o=e.getDescendantsOfKind(g.SyntaxKind.ReturnStatement);for(let a of o){let m=a.getExpression();if(m?.isKind(g.SyntaxKind.SatisfiesExpression)){p=m.getTypeNode()?.getType();break}}}if(p)return L(p,e,t,r,n,s);{let o=e.getType().getCallSignatures();if(o.length>0){let a=o[0]?.getReturnType();return L(a,e,t,r,n,s)}}return"unknown"}function ue(e,t,r,n,s){let p="unknown",o="unknown",a="unknown",m=!1;if(!e||!g.Node.isObjectLiteralExpression(e))return{paramsType:p,queryType:o,bodyType:a,isMultipart:m};let f=e.getProperty("schema");if(f?.isKind(g.SyntaxKind.PropertyAssignment)){let u=f.getInitializer();if(u?.isKind(g.SyntaxKind.Identifier)){let F=u.getSymbol()?.getValueDeclaration();if(F){let l=F.getFirstDescendantByKind(g.SyntaxKind.ObjectLiteralExpression);l&&(u=l)}}if(u&&g.Node.isObjectLiteralExpression(u)){let w=u.getProperty("consumes");if(w?.isKind(g.SyntaxKind.PropertyAssignment)){let l=w.getInitializer();g.Node.isArrayLiteralExpression(l)&&(m=l.getElements().some(y=>y.isKind(g.SyntaxKind.StringLiteral)&&y.getLiteralValue()==="multipart/form-data"))}let F=l=>{let y=u.getProperty(l);if(y?.isKind(g.SyntaxKind.PropertyAssignment)){let i=y.getInitializer();if(i){let c=i.getType(),d=c.getApparentType(),C=d.getProperty("_output");if(C)return L(C.getTypeAtLocation(i).getApparentType(),i,t,r,n,s);let N=c.getSymbol(),h=c.getAliasSymbol(),T=N?.getName(),b=h?.getName();if(T&&T!=="__type"&&T!=="object"||b&&b!=="__type"&&b!=="object")return L(c,i,t,r,n,s);let O=d.getProperties();if(O.length>0){let I=[];for(let K of O){let W=K.getTypeAtLocation(i);I.push(`${K.getName()}: ${W.getText(i)}`)}return`{ ${I.join("; ")} }`}return L(c,i,t,r,n,s)}}return"unknown"};if(p=F("params"),o=F("querystring"),a=F("body"),m){let l="{ file: File }";a=a!=="unknown"&&a.trim().startsWith("{")?`(${a} & ${l})`:l}}}return{paramsType:p,queryType:o,bodyType:a,isMultipart:m}}function je(e){let t=[];return e.paramsType!=="unknown"&&t.push(`params: ${e.paramsType}`),e.queryType!=="unknown"&&t.push(`query: ${e.queryType}`),e.bodyType!=="unknown"&&t.push(`body: ${e.bodyType}`),t.push("signal?: AbortSignal"),t}function fe(e){let t=je(e),r="";return t.length>0&&(r=`args: { ${t.join("; ")} }`),r||(r="args: { signal?: AbortSignal }"),`export declare const ${e.name}: (${r}, ssrContext?: { req?: FastifyRequest, reply?: FastifyReply }) => ${e.returnType};`}async function $e(e={}){let{routerPattern:t="src-ts/routers/**/*.mts",tsConfigFilePath:r="tsconfig.json",routerBaseDir:n="src-ts/routers",clean:s=!1}=e,p=[],o=[],a=[];try{let l=function(i,c){let d=[],C=[...c.entries()].sort((T,b)=>T[0].localeCompare(b[0]));for(let[T,b]of C)d.push(`import type { ${[...b].sort().join(", ")} } from "${T}";`);let N=[...i].sort((T,b)=>T.name.localeCompare(b.name)),h=[];return N.forEach(T=>{h.push(fe(T))}),`// AUTO-GENERATED by @bobtail.software/b-ssr. DO NOT EDIT.
|
|
2
2
|
/* eslint-disable */
|
|
3
3
|
|
|
4
|
-
`+(
|
|
4
|
+
`+(d.length>0?d.join(`
|
|
5
5
|
`)+`
|
|
6
6
|
|
|
7
|
-
`:"")+
|
|
7
|
+
`:"")+h.join(`
|
|
8
8
|
|
|
9
|
-
`);return(await ee(
|
|
9
|
+
`)};var m=l;let f=await le(),u=new j.Project({tsConfigFilePath:r,skipAddingFilesFromTsConfig:!0}),w=P.default.resolve(process.cwd(),n||".");async function F(i){try{let c=P.default.resolve(i),d=await(0,E.readFile)(i,"utf-8"),C=oe(d),N=P.default.extname(i),h=i.substring(0,i.length-N.length)+".universal.d.ts",T=f.files[c];if(T&&T.hash===C){let x=T.fnInfo,A=new Map;T.imports.forEach(([R,D])=>{A.set(R,new Set(D))});let B=l(x,A);return(await ee(h,B)||await(0,E.access)(h).then(()=>!0).catch(()=>!1))&&p.push(h),!0}let b=u.addSourceFileAtPath(i);await b.refreshFromFileSystem();let O=b.getDescendantsOfKind(j.SyntaxKind.CallExpression),I=O.filter(x=>x.getExpression().getText().endsWith(".addRpcRoute")),K=O.filter(x=>x.getExpression().getText().endsWith(".addRenderRoute")),W=O.filter(x=>x.getExpression().getText().endsWith(".addLoaderRoute"));if(I.length===0&&K.length===0&&W.length===0){if(delete f.files[c],s)try{await(0,E.unlink)(h),o.push(h)}catch{}return!1}let V=[],$=new Map;$.has("fastify")||$.set("fastify",new Set),$.get("fastify")?.add("FastifyRequest"),$.get("fastify")?.add("FastifyReply");let M=P.default.relative(w,P.default.dirname(i)).split(P.default.sep).join("/"),me=M==="."||!M?"":M.startsWith("/")?M:"/"+M,Q=(x,A)=>{let[B,_]=x.getArguments();if(!B?.isKind(j.SyntaxKind.StringLiteral))return;let R=B.getLiteralValue(),D="unknown",k=me,{paramsType:de,queryType:he,bodyType:Te,isMultipart:xe}=ue(_,b,h,u,$);if(_?.isKind(j.SyntaxKind.ObjectLiteralExpression)){let ne=_.getProperty("prefix");if(ne?.isKind(j.SyntaxKind.PropertyAssignment)){let H=ne.getInitializer();H?.isKind(j.SyntaxKind.StringLiteral)&&(k=H.getLiteralValue())}let re=_.getProperty("handler");if(re?.isKind(j.SyntaxKind.PropertyAssignment)){let H=re.getInitializer();H&&(D=ge(H,b,h,u,$),D.startsWith("Promise<")||(D=`Promise<${D}>`))}}let U="",z="";A==="rpc"?(U=P.default.join(k,"rpc",R).replace(/\\/g,"/"),z=J("action",k,R)):A==="loader"?(U=P.default.join(k,"loader",R).replace(/\\/g,"/"),z=J("loader",k,R)):A==="api"&&(U=P.default.join(k,"api",R).replace(/\\/g,"/"),z=J("get",k,R)),z&&V.push({type:A,name:z,returnType:D,url:R,rpcUrl:U,loaderUrl:U,paramsType:de,queryType:he,bodyType:Te,isMultipart:xe})};if(I.forEach(x=>Q(x,"rpc")),K.forEach(x=>Q(x,"loader")),W.forEach(x=>Q(x,"api")),V.length===0){if(delete f.files[c],s)try{await(0,E.unlink)(h),o.push(h)}catch{}return!1}let ye=l(V,$);return(await ee(h,ye)||await(0,E.access)(h).then(()=>!0).catch(()=>!1))&&p.push(h),f.files[c]={hash:C,fnInfo:V,imports:Array.from($.entries()).map(([x,A])=>[x,Array.from(A)])},!0}catch(c){return a.push(`Error processing ${i}: ${c instanceof Error?c.message:String(c)}`),!1}}let y=await(0,te.default)(t,{absolute:!0});if(await Promise.all(y.map(i=>F(i))),u.resolveSourceFileDependencies(),await pe(f),s){let i=await(0,te.default)(P.default.join(n,"**/*.universal.d.ts"),{absolute:!0});for(let c of i){let d=c.replace(".universal.d.ts",".mts").replace(".universal.d.ts",".ts");if(!await(0,E.access)(d).then(()=>!0).catch(()=>!1))try{await(0,E.unlink)(c),o.push(c)}catch{}}}}catch(f){a.push(`Fatal error: ${f instanceof Error?f.message:String(f)}`)}return{generated:p,cleaned:o,errors:a}}0&&(module.exports={generateRpcTypes});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ue from"fast-glob";import{access as Y,unlink as ee,readFile as Ae}from"fs/promises";import S from"path";import{Project as Re,SyntaxKind as O}from"ts-morph";import{writeFile as re,readFile as Q,mkdir as xe}from"fs/promises";import C from"path";import{format as be,resolveConfig as Fe}from"prettier";import{Node as N,SyntaxKind as A,ts as L}from"ts-morph";import{createHash as we}from"crypto";function Ce(e){let t=e.replace(/[^a-zA-Z0-9]+(.)?/g,(c,n)=>n?n.toUpperCase():"");return t.charAt(0).toLowerCase()+t.slice(1)}function Z(e,t,c){let n=C.basename(c,C.extname(c))||"Index",i=t.replace(/:[a-zA-Z0-9_]+/g,"").replace(/\//g," "),p=`${e} ${i} ${n}`;return Ce(p)}function Se(e){let t=/'[^']+'/g,c=/`[^`]+`/g,n=[],i,p=/'[^']+'/g;for(;(i=p.exec(e))!==null;)n.push({start:i.index,end:i.index+i[0].length,value:i[0]});let s=/`[^`]+`/g;for(;(i=s.exec(e))!==null;)n.push({start:i.index,end:i.index+i[0].length,value:i[0]});if(n.length>1){let o=[...n].sort((g,F)=>{let b=g.value.replace(/['`]/g,""),l=F.value.replace(/['`]/g,"");return b.localeCompare(l)}),f="",u=0;for(let g=0;g<n.length;g++)f+=e.slice(u,n[g].start),f+=o[g].value,u=n[g].end;return f+=e.slice(u),f}return e}function Pe(e){let t=e.match(/^\{([\s\S]*)\}$/);if(!t)return e;let c=t[1];if(!c.includes(":"))return e;let n=c.split(/[;,]/).map(s=>s.trim()).filter(s=>s),i=[];for(let s of n){let o=s.indexOf(":");if(o===-1)continue;let f=s.slice(0,o).trim(),u=s.slice(o+1).trim();i.push({name:f,value:u})}return i.length<=1?e:(i.sort((s,o)=>s.name.localeCompare(o.name)),`{ ${i.map(s=>`${s.name}: ${s.value}`).join("; ")} }`)}function G(e){let t=Se(e);return t=Pe(t),t}async function X(e,t){try{if(await Q(e,"utf-8")===t)return!1}catch{}let c=t;try{let n=await Fe(e)||{};c=await be(t,{...n,parser:"typescript",filepath:e})}catch{}try{if(await Q(e,"utf-8")===c)return!1}catch{}return await re(e,c),!0}function se(e){return we("sha1").update(e).digest("hex")}var J="v1",ie=C.join(process.cwd(),"node_modules",".cache","b-ssr"),oe=C.join(ie,"rpc-cache.json");async function ae(){try{let e=await Q(oe,"utf-8"),t=JSON.parse(e);return t.version!==J?{version:J,files:{}}:t}catch{return{version:J,files:{}}}}async function ce(e){try{await xe(ie,{recursive:!0}),await re(oe,JSON.stringify(e,null,2))}catch(t){console.error("\u26A0\uFE0F [B-SSR] Failed to save persistent cache:",t)}}function D(e,t,c,n,i,p){function s(l){return l.replace(/import\(['"](.*?)['"]\)/g,(m,r)=>{if(!C.isAbsolute(r))return m;let a=r;if(!C.extname(a)){let w=i.getSourceFile(v=>v.getFilePath().replace(/\.(mts|ts|tsx)$/,"")===r);w&&(a=w.getFilePath())}let y=C.relative(C.dirname(n),a).replace(/\\/g,"/");return y.startsWith(".")||(y="./"+y),`import("${y}")`})}if(e.getSymbol()?.getName()==="Promise"){let l=e.getAwaitedType();if(l)return`Promise<${D(l,t,c,n,i,p)}>`}let o=e.getAliasSymbol()??e.getSymbol();if(o?.getName()==="__object")return G(s(e.getText(t,L.TypeFormatFlags.NoTruncation|L.TypeFormatFlags.UseFullyQualifiedType)));if(e.isObject()&&!o){let l=(m,r=0)=>{if(!(r>5)){if(m.isArray()){let a=m.getArrayElementType();a&&l(a,r);return}if(m.isObject()&&!m.getSymbol()&&!m.getAliasSymbol()){for(let a of m.getApparentProperties()){let y=a.getValueDeclaration();y&&l(a.getTypeAtLocation(y),r+1)}return}D(m,t,c,n,i,p)}};return l(e),G(s(e.getText(t,L.TypeFormatFlags.NoTruncation|L.TypeFormatFlags.UseFullyQualifiedType)))}if(!o)return G(s(e.getText(t,L.TypeFormatFlags.NoTruncation)));let f=o.getDeclarations()[0];if(!f)return o.getName();if(N.isImportSpecifier(f)||N.isImportClause(f)){let l=f.getFirstAncestorByKind(A.ImportDeclaration);if(l){let m=l.getModuleSpecifierValue(),r=o.getName();return p.has(m)||p.set(m,new Set),p.get(m)?.add(r),r}}let u=f.getSourceFile();if(u.getFilePath()===c.getFilePath())return o.getName();if(u.isInNodeModules())return G(s(e.getText(t,L.TypeFormatFlags.NoTruncation)));let g=C.relative(C.dirname(n),u.getFilePath()).replace(/\\/g,"/"),F=g.startsWith(".")?g:`./${g}`,b=o.getName();return p.has(F)||p.set(F,new Set),p.get(F)?.add(b),b}function le(e,t,c,n,i){let p;if(N.isFunctionLikeDeclaration(e)||N.isArrowFunction(e)){let s=e.getDescendantsOfKind(A.ReturnStatement);for(let o of s){let f=o.getExpression();if(f?.isKind(A.SatisfiesExpression)){p=f.getTypeNode()?.getType();break}}}if(p)return D(p,e,t,c,n,i);{let s=e.getType().getCallSignatures();if(s.length>0){let o=s[0]?.getReturnType();return D(o,e,t,c,n,i)}}return"unknown"}function pe(e,t,c,n,i){let p="unknown",s="unknown",o="unknown",f=!1;if(!e||!N.isObjectLiteralExpression(e))return{paramsType:p,queryType:s,bodyType:o,isMultipart:f};let u=e.getProperty("schema");if(u?.isKind(A.PropertyAssignment)){let g=u.getInitializer();if(g?.isKind(A.Identifier)){let b=g.getSymbol()?.getValueDeclaration();if(b){let l=b.getFirstDescendantByKind(A.ObjectLiteralExpression);l&&(g=l)}}if(g&&N.isObjectLiteralExpression(g)){let F=g.getProperty("consumes");if(F?.isKind(A.PropertyAssignment)){let l=F.getInitializer();N.isArrayLiteralExpression(l)&&(f=l.getElements().some(m=>m.isKind(A.StringLiteral)&&m.getLiteralValue()==="multipart/form-data"))}let b=l=>{let m=g.getProperty(l);if(m?.isKind(A.PropertyAssignment)){let r=m.getInitializer();if(r){let a=r.getType(),y=a.getApparentType(),w=y.getProperty("_output");if(w)return D(w.getTypeAtLocation(r).getApparentType(),r,t,c,n,i);let v=a.getSymbol(),d=a.getAliasSymbol(),h=v?.getName(),x=d?.getName();if(h&&h!=="__type"&&h!=="object"||x&&x!=="__type"&&x!=="object")return D(a,r,t,c,n,i);let k=y.getProperties();if(k.length>0){let I=[];for(let K of k){let W=K.getTypeAtLocation(r);I.push(`${K.getName()}: ${W.getText(r)}`)}return`{ ${I.join("; ")} }`}return D(a,r,t,c,n,i)}}return"unknown"};if(p=b("params"),s=b("querystring"),o=b("body"),f){let l="{ file: File }";o=o!=="unknown"&&o.trim().startsWith("{")?`(${o} & ${l})`:l}}}return{paramsType:p,queryType:s,bodyType:o,isMultipart:f}}function Ee(e){let t=[];return e.paramsType!=="unknown"&&t.push(`params: ${e.paramsType}`),e.queryType!=="unknown"&&t.push(`query: ${e.queryType}`),e.bodyType!=="unknown"&&t.push(`body: ${e.bodyType}`),t.push("signal?: AbortSignal"),t}function ge(e){let t=Ee(e),c="";return t.length>0&&(c=`args: { ${t.join("; ")} }`),c||(c="args: { signal?: AbortSignal }"),`export declare const ${e.name}: (${c}, ssrContext?: { req?: FastifyRequest, reply?: FastifyReply }) => ${e.returnType};`}async function Ze(e={}){let{routerPattern:t="src-ts/routers/**/*.mts",tsConfigFilePath:c="tsconfig.json",routerBaseDir:n="src-ts/routers",clean:i=!1}=e,p=[],s=[],o=[];try{let l=function(r,a){let y=[],w=[...a.entries()].sort((h,x)=>h[0].localeCompare(x[0]));for(let[h,x]of w)y.push(`import type { ${[...x].sort().join(", ")} } from "${h}";`);let v=[...r].sort((h,x)=>h.name.localeCompare(x.name)),d=[];return v.forEach(h=>{d.push(ge(h))}),`// AUTO-GENERATED by @bobtail.software/b-ssr. DO NOT EDIT.
|
|
2
2
|
/* eslint-disable */
|
|
3
3
|
|
|
4
|
-
`+(
|
|
4
|
+
`+(y.length>0?y.join(`
|
|
5
5
|
`)+`
|
|
6
6
|
|
|
7
|
-
`:"")+
|
|
7
|
+
`:"")+d.join(`
|
|
8
8
|
|
|
9
|
-
`);
|
|
9
|
+
`)};var f=l;let u=await ae(),g=new Re({tsConfigFilePath:c,skipAddingFilesFromTsConfig:!0}),F=S.resolve(process.cwd(),n||".");async function b(r){try{let a=S.resolve(r),y=await Ae(r,"utf-8"),w=se(y),v=S.extname(r),d=r.substring(0,r.length-v.length)+".universal.d.ts",h=u.files[a];if(h&&h.hash===w){let T=h.fnInfo,P=new Map;h.imports.forEach(([E,j])=>{P.set(E,new Set(j))});let B=l(T,P);return(await X(d,B)||await Y(d).then(()=>!0).catch(()=>!1))&&p.push(d),!0}let x=g.addSourceFileAtPath(r);await x.refreshFromFileSystem();let k=x.getDescendantsOfKind(O.CallExpression),I=k.filter(T=>T.getExpression().getText().endsWith(".addRpcRoute")),K=k.filter(T=>T.getExpression().getText().endsWith(".addRenderRoute")),W=k.filter(T=>T.getExpression().getText().endsWith(".addLoaderRoute"));if(I.length===0&&K.length===0&&W.length===0){if(delete u.files[a],i)try{await ee(d),s.push(d)}catch{}return!1}let V=[],R=new Map;R.has("fastify")||R.set("fastify",new Set),R.get("fastify")?.add("FastifyRequest"),R.get("fastify")?.add("FastifyReply");let M=S.relative(F,S.dirname(r)).split(S.sep).join("/"),fe=M==="."||!M?"":M.startsWith("/")?M:"/"+M,q=(T,P)=>{let[B,_]=T.getArguments();if(!B?.isKind(O.StringLiteral))return;let E=B.getLiteralValue(),j="unknown",$=fe,{paramsType:ye,queryType:de,bodyType:he,isMultipart:Te}=pe(_,x,d,g,R);if(_?.isKind(O.ObjectLiteralExpression)){let te=_.getProperty("prefix");if(te?.isKind(O.PropertyAssignment)){let H=te.getInitializer();H?.isKind(O.StringLiteral)&&($=H.getLiteralValue())}let ne=_.getProperty("handler");if(ne?.isKind(O.PropertyAssignment)){let H=ne.getInitializer();H&&(j=le(H,x,d,g,R),j.startsWith("Promise<")||(j=`Promise<${j}>`))}}let U="",z="";P==="rpc"?(U=S.join($,"rpc",E).replace(/\\/g,"/"),z=Z("action",$,E)):P==="loader"?(U=S.join($,"loader",E).replace(/\\/g,"/"),z=Z("loader",$,E)):P==="api"&&(U=S.join($,"api",E).replace(/\\/g,"/"),z=Z("get",$,E)),z&&V.push({type:P,name:z,returnType:j,url:E,rpcUrl:U,loaderUrl:U,paramsType:ye,queryType:de,bodyType:he,isMultipart:Te})};if(I.forEach(T=>q(T,"rpc")),K.forEach(T=>q(T,"loader")),W.forEach(T=>q(T,"api")),V.length===0){if(delete u.files[a],i)try{await ee(d),s.push(d)}catch{}return!1}let me=l(V,R);return(await X(d,me)||await Y(d).then(()=>!0).catch(()=>!1))&&p.push(d),u.files[a]={hash:w,fnInfo:V,imports:Array.from(R.entries()).map(([T,P])=>[T,Array.from(P)])},!0}catch(a){return o.push(`Error processing ${r}: ${a instanceof Error?a.message:String(a)}`),!1}}let m=await ue(t,{absolute:!0});if(await Promise.all(m.map(r=>b(r))),g.resolveSourceFileDependencies(),await ce(u),i){let r=await ue(S.join(n,"**/*.universal.d.ts"),{absolute:!0});for(let a of r){let y=a.replace(".universal.d.ts",".mts").replace(".universal.d.ts",".ts");if(!await Y(y).then(()=>!0).catch(()=>!1))try{await ee(a),s.push(a)}catch{}}}}catch(u){o.push(`Fatal error: ${u instanceof Error?u.message:String(u)}`)}return{generated:p,cleaned:s,errors:o}}export{Ze as generateRpcTypes};
|
package/dist/vite-rpc-plugin.cjs
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var je=Object.create;var ue=Object.defineProperty;var Me=Object.getOwnPropertyDescriptor;var Oe=Object.getOwnPropertyNames;var Ie=Object.getPrototypeOf,De=Object.prototype.hasOwnProperty;var Le=(e,r)=>{for(var p in r)ue(e,p,{get:r[p],enumerable:!0})},Fe=(e,r,p,c)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of Oe(r))!De.call(e,a)&&a!==p&&ue(e,a,{get:()=>r[a],enumerable:!(c=Me(r,a))||c.enumerable});return e};var pe=(e,r,p)=>(p=e!=null?je(Ie(e)):{},Fe(r||!e||!e.__esModule?ue(p,"default",{value:e,enumerable:!0}):p,e)),ke=e=>Fe(ue({},"__esModule",{value:!0}),e);var Be={};Le(Be,{generateRpcTypes:()=>Ae,rpcGeneratorPlugin:()=>We});module.exports=ke(Be);var $e=pe(require("fast-glob"),1),ee=require("fs/promises"),E=pe(require("path"),1),H=require("ts-morph");var B=require("fs/promises"),k=pe(require("path"),1),fe=require("prettier"),T=require("ts-morph"),Pe=require("crypto");function Ne(e){let r=e.replace(/[^a-zA-Z0-9]+(.)?/g,(p,c)=>c?c.toUpperCase():"");return r.charAt(0).toLowerCase()+r.slice(1)}function Z(e,r,p){let c=k.default.basename(p,k.default.extname(p))||"Index",a=r.replace(/:[a-zA-Z0-9_]+/g,"").replace(/\//g," "),m=`${e} ${a} ${c}`;return Ne(m)}function qe(e){let r=/'[^']+'/g,p=/`[^`]+`/g,c=[],a,m=/'[^']+'/g;for(;(a=m.exec(e))!==null;)c.push({start:a.index,end:a.index+a[0].length,value:a[0]});let u=/`[^`]+`/g;for(;(a=u.exec(e))!==null;)c.push({start:a.index,end:a.index+a[0].length,value:a[0]});if(c.length>1){let d=[...c].sort((x,$)=>{let v=x.value.replace(/['`]/g,""),h=$.value.replace(/['`]/g,"");return v.localeCompare(h)}),b="",y=0;for(let x=0;x<c.length;x++)b+=e.slice(y,c[x].start),b+=d[x].value,y=c[x].end;return b+=e.slice(y),b}return e}function Ue(e){let r=e.match(/^\{([\s\S]*)\}$/);if(!r)return e;let p=r[1];if(!p.includes(":"))return e;let c=p.split(/[;,]/).map(u=>u.trim()).filter(u=>u),a=[];for(let u of c){let d=u.indexOf(":");if(d===-1)continue;let b=u.slice(0,d).trim(),y=u.slice(d+1).trim();a.push({name:b,value:y})}return a.length<=1?e:(a.sort((u,d)=>u.name.localeCompare(d.name)),`{ ${a.map(u=>`${u.name}: ${u.value}`).join("; ")} }`)}function ge(e){let r=qe(e);return r=Ue(r),r}async function ne(e,r){try{if(await(0,B.readFile)(e,"utf-8")===r)return!1}catch{}let p=r;try{let c=await(0,fe.resolveConfig)(e)||{};p=await(0,fe.format)(r,{...c,parser:"typescript",filepath:e})}catch{}try{if(await(0,B.readFile)(e,"utf-8")===p)return!1}catch{}return await(0,B.writeFile)(e,p),!0}function de(e){return(0,Pe.createHash)("sha1").update(e).digest("hex")}var we="v1",Ee=k.default.join(process.cwd(),"node_modules",".cache","b-ssr"),ve=k.default.join(Ee,"rpc-cache.json");async function me(){try{let e=await(0,B.readFile)(ve,"utf-8"),r=JSON.parse(e);return r.version!==we?{version:we,files:{}}:r}catch{return{version:we,files:{}}}}async function he(e){try{await(0,B.mkdir)(Ee,{recursive:!0}),await(0,B.writeFile)(ve,JSON.stringify(e,null,2))}catch(r){console.error("\u26A0\uFE0F [B-SSR] Failed to save persistent cache:",r)}}function Y(e,r,p,c,a,m){function u(h){return h.replace(/import\(['"](.*?)['"]\)/g,(C,f)=>{if(!k.default.isAbsolute(f))return C;let t=f;if(!k.default.extname(t)){let s=a.getSourceFile(l=>l.getFilePath().replace(/\.(mts|ts|tsx)$/,"")===f);s&&(t=s.getFilePath())}let n=k.default.relative(k.default.dirname(c),t).replace(/\\/g,"/");return n.startsWith(".")||(n="./"+n),`import("${n}")`})}if(e.getSymbol()?.getName()==="Promise"){let h=e.getAwaitedType();if(h)return`Promise<${Y(h,r,p,c,a,m)}>`}let d=e.getAliasSymbol()??e.getSymbol();if(d?.getName()==="__object")return ge(u(e.getText(r,T.ts.TypeFormatFlags.NoTruncation|T.ts.TypeFormatFlags.UseFullyQualifiedType)));if(e.isObject()&&!d){let h=(C,f=0)=>{if(!(f>5)){if(C.isArray()){let t=C.getArrayElementType();t&&h(t,f);return}if(C.isObject()&&!C.getSymbol()&&!C.getAliasSymbol()){for(let t of C.getApparentProperties()){let n=t.getValueDeclaration();n&&h(t.getTypeAtLocation(n),f+1)}return}Y(C,r,p,c,a,m)}};return h(e),ge(u(e.getText(r,T.ts.TypeFormatFlags.NoTruncation|T.ts.TypeFormatFlags.UseFullyQualifiedType)))}if(!d)return ge(u(e.getText(r,T.ts.TypeFormatFlags.NoTruncation)));let b=d.getDeclarations()[0];if(!b)return d.getName();if(T.Node.isImportSpecifier(b)||T.Node.isImportClause(b)){let h=b.getFirstAncestorByKind(T.SyntaxKind.ImportDeclaration);if(h){let C=h.getModuleSpecifierValue(),f=d.getName();return m.has(C)||m.set(C,new Set),m.get(C)?.add(f),f}}let y=b.getSourceFile();if(y.getFilePath()===p.getFilePath())return d.getName();if(y.isInNodeModules())return ge(u(e.getText(r,T.ts.TypeFormatFlags.NoTruncation)));let x=k.default.relative(k.default.dirname(c),y.getFilePath()).replace(/\\/g,"/"),$=x.startsWith(".")?x:`./${x}`,v=d.getName();return m.has($)||m.set($,new Set),m.get($)?.add(v),v}function ye(e,r,p,c,a){let m;if(T.Node.isFunctionLikeDeclaration(e)||T.Node.isArrowFunction(e)){let u=e.getDescendantsOfKind(T.SyntaxKind.ReturnStatement);for(let d of u){let b=d.getExpression();if(b?.isKind(T.SyntaxKind.SatisfiesExpression)){m=b.getTypeNode()?.getType();break}}}if(m)return Y(m,e,r,p,c,a);{let u=e.getType().getCallSignatures();if(u.length>0){let d=u[0]?.getReturnType();return Y(d,e,r,p,c,a)}}return"unknown"}function xe(e,r,p,c,a){let m="unknown",u="unknown",d="unknown",b=!1;if(!e||!T.Node.isObjectLiteralExpression(e))return{paramsType:m,queryType:u,bodyType:d,isMultipart:b};let y=e.getProperty("schema");if(y?.isKind(T.SyntaxKind.PropertyAssignment)){let x=y.getInitializer();if(x?.isKind(T.SyntaxKind.Identifier)){let v=x.getSymbol()?.getValueDeclaration();if(v){let h=v.getFirstDescendantByKind(T.SyntaxKind.ObjectLiteralExpression);h&&(x=h)}}if(x&&T.Node.isObjectLiteralExpression(x)){let $=x.getProperty("consumes");if($?.isKind(T.SyntaxKind.PropertyAssignment)){let h=$.getInitializer();T.Node.isArrayLiteralExpression(h)&&(b=h.getElements().some(C=>C.isKind(T.SyntaxKind.StringLiteral)&&C.getLiteralValue()==="multipart/form-data"))}let v=h=>{let C=x.getProperty(h);if(C?.isKind(T.SyntaxKind.PropertyAssignment)){let f=C.getInitializer();if(f){let t=f.getType(),n=t.getApparentType(),s=n.getProperty("_output");if(s)return Y(s.getTypeAtLocation(f).getApparentType(),f,r,p,c,a);let l=t.getSymbol(),g=t.getAliasSymbol(),o=l?.getName(),i=g?.getName();if(o&&o!=="__type"&&o!=="object"||i&&i!=="__type"&&i!=="object")return Y(t,f,r,p,c,a);let w=n.getProperties();if(w.length>0){let S=[];for(let j of w){let te=j.getTypeAtLocation(f);S.push(`${j.getName()}: ${te.getText(f)}`)}return`{ ${S.join("; ")} }`}return Y(t,f,r,p,c,a)}}return"unknown"};if(m=v("params"),u=v("querystring"),d=v("body"),b){let h="{ file: File }";d=d!=="unknown"&&d.trim().startsWith("{")?`(${d} & ${h})`:h}}}return{paramsType:m,queryType:u,bodyType:d,isMultipart:b}}function Ke(e){let r=[];return e.paramsType!=="unknown"&&r.push(`params: ${e.paramsType}`),e.queryType!=="unknown"&&r.push(`query: ${e.queryType}`),e.bodyType!=="unknown"&&r.push(`body: ${e.bodyType}`),r.push("signal?: AbortSignal"),r}function be(e){let r=Ke(e),p="";return r.length>0&&(p=`args: { ${r.join("; ")} }`),p||(p="args: { signal?: AbortSignal }"),`export declare const ${e.name}: (${p}, ssrContext?: { req?: FastifyRequest, reply?: FastifyReply }) => ${e.returnType};`}var Ce=require("perf_hooks");var Te=pe(require("fast-glob"),1),U=require("fs/promises"),N=pe(require("path"),1),_=require("ts-morph");async function Ae(e={}){let{routerPattern:r="src-ts/routers/**/*.mts",tsConfigFilePath:p="tsconfig.json",routerBaseDir:c="src-ts/routers",clean:a=!1}=e,m=[],u=[],d=[];try{let h=function(f,t){let n=[],s=[...t.entries()].sort((o,i)=>o[0].localeCompare(i[0]));for(let[o,i]of s)n.push(`import type { ${[...i].sort().join(", ")} } from "${o}";`);let l=[...f].sort((o,i)=>o.name.localeCompare(i.name)),g=[];return l.forEach(o=>{g.push(be(o))}),`// AUTO-GENERATED by @bobtail.software/b-ssr. DO NOT EDIT.
|
|
2
2
|
/* eslint-disable */
|
|
3
3
|
|
|
4
|
-
`+(
|
|
4
|
+
`+(n.length>0?n.join(`
|
|
5
5
|
`)+`
|
|
6
6
|
|
|
7
|
-
`:"")+
|
|
7
|
+
`:"")+g.join(`
|
|
8
8
|
|
|
9
|
-
`);return(await
|
|
9
|
+
`)};var b=h;let y=await me(),x=new _.Project({tsConfigFilePath:p,skipAddingFilesFromTsConfig:!0}),$=N.default.resolve(process.cwd(),c||".");async function v(f){try{let t=N.default.resolve(f),n=await(0,U.readFile)(f,"utf-8"),s=de(n),l=N.default.extname(f),g=f.substring(0,f.length-l.length)+".universal.d.ts",o=y.files[t];if(o&&o.hash===s){let F=o.fnInfo,D=new Map;o.imports.forEach(([M,O])=>{D.set(M,new Set(O))});let R=h(F,D);return(await ne(g,R)||await(0,U.access)(g).then(()=>!0).catch(()=>!1))&&m.push(g),!0}let i=x.addSourceFileAtPath(f);await i.refreshFromFileSystem();let w=i.getDescendantsOfKind(_.SyntaxKind.CallExpression),S=w.filter(F=>F.getExpression().getText().endsWith(".addRpcRoute")),j=w.filter(F=>F.getExpression().getText().endsWith(".addRenderRoute")),te=w.filter(F=>F.getExpression().getText().endsWith(".addLoaderRoute"));if(S.length===0&&j.length===0&&te.length===0){if(delete y.files[t],a)try{await(0,U.unlink)(g),u.push(g)}catch{}return!1}let q=[],I=new Map;I.has("fastify")||I.set("fastify",new Set),I.get("fastify")?.add("FastifyRequest"),I.get("fastify")?.add("FastifyReply");let re=N.default.relative($,N.default.dirname(f)).split(N.default.sep).join("/"),J=re==="."||!re?"":re.startsWith("/")?re:"/"+re,oe=(F,D)=>{let[R,A]=F.getArguments();if(!R?.isKind(_.SyntaxKind.StringLiteral))return;let M=R.getLiteralValue(),O="unknown",P=J,{paramsType:W,queryType:L,bodyType:ae,isMultipart:ce}=xe(A,i,g,x,I);if(A?.isKind(_.SyntaxKind.ObjectLiteralExpression)){let V=A.getProperty("prefix");if(V?.isKind(_.SyntaxKind.PropertyAssignment)){let G=V.getInitializer();G?.isKind(_.SyntaxKind.StringLiteral)&&(P=G.getLiteralValue())}let z=A.getProperty("handler");if(z?.isKind(_.SyntaxKind.PropertyAssignment)){let G=z.getInitializer();G&&(O=ye(G,i,g,x,I),O.startsWith("Promise<")||(O=`Promise<${O}>`))}}let Q="",X="";D==="rpc"?(Q=N.default.join(P,"rpc",M).replace(/\\/g,"/"),X=Z("action",P,M)):D==="loader"?(Q=N.default.join(P,"loader",M).replace(/\\/g,"/"),X=Z("loader",P,M)):D==="api"&&(Q=N.default.join(P,"api",M).replace(/\\/g,"/"),X=Z("get",P,M)),X&&q.push({type:D,name:X,returnType:O,url:M,rpcUrl:Q,loaderUrl:Q,paramsType:W,queryType:L,bodyType:ae,isMultipart:ce})};if(S.forEach(F=>oe(F,"rpc")),j.forEach(F=>oe(F,"loader")),te.forEach(F=>oe(F,"api")),q.length===0){if(delete y.files[t],a)try{await(0,U.unlink)(g),u.push(g)}catch{}return!1}let ie=h(q,I);return(await ne(g,ie)||await(0,U.access)(g).then(()=>!0).catch(()=>!1))&&m.push(g),y.files[t]={hash:s,fnInfo:q,imports:Array.from(I.entries()).map(([F,D])=>[F,Array.from(D)])},!0}catch(t){return d.push(`Error processing ${f}: ${t instanceof Error?t.message:String(t)}`),!1}}let C=await(0,Te.default)(r,{absolute:!0});if(await Promise.all(C.map(f=>v(f))),x.resolveSourceFileDependencies(),await he(y),a){let f=await(0,Te.default)(N.default.join(c,"**/*.universal.d.ts"),{absolute:!0});for(let t of f){let n=t.replace(".universal.d.ts",".mts").replace(".universal.d.ts",".ts");if(!await(0,U.access)(n).then(()=>!0).catch(()=>!1))try{await(0,U.unlink)(t),u.push(t)}catch{}}}}catch(y){d.push(`Fatal error: ${y instanceof Error?y.message:String(y)}`)}return{generated:m,cleaned:u,errors:d}}var se="virtual:b-ssr-rpc-universal:";function We(e={}){let{routerPattern:r="src-ts/routers/**/*.mts",tsConfigFilePath:p="tsconfig.json",routerBaseDir:c="src-ts/routers"}=e,a=new Map,m=new Set,u=new H.Project({tsConfigFilePath:p,skipAddingFilesFromTsConfig:!0}),d=E.default.resolve(process.cwd(),c||"."),b=!1,y={version:"v1",files:{}};async function x(t){try{let n=E.default.resolve(t),s=await(0,ee.readFile)(t,"utf-8"),l=de(s),g=y.files[n];if(g&&g.hash===l){let R=g.fnInfo,A=new Map;g.imports.forEach(([ae,ce])=>{A.set(ae,new Set(ce))}),m.add(n);let{client:M,server:O}=v(R,t);a.set(n,{client:M,server:O});let P=E.default.extname(t),W=t.substring(0,t.length-P.length)+".universal.d.ts",L=$(R,A);await ne(W,L);return}let o=u.addSourceFileAtPath(t);await o.refreshFromFileSystem(),b||u.resolveSourceFileDependencies();let i=o.getDescendantsOfKind(H.SyntaxKind.CallExpression),w=i.filter(R=>R.getExpression().getText().endsWith(".addRpcRoute")),S=i.filter(R=>R.getExpression().getText().endsWith(".addRenderRoute")),j=i.filter(R=>R.getExpression().getText().endsWith(".addLoaderRoute")),te=E.default.extname(t),q=t.substring(0,t.length-te.length)+".universal.d.ts";if(w.length===0&&S.length===0&&j.length===0){a.delete(n),m.delete(n),delete y.files[n],q!==t&&await(0,ee.unlink)(q).catch(()=>{});return}m.add(n);let I=[],K=new Map;K.has("fastify")||K.set("fastify",new Set),K.get("fastify")?.add("FastifyRequest"),K.get("fastify")?.add("FastifyReply");let J=E.default.relative(d,E.default.dirname(t)).split(E.default.sep).join("/"),oe=J==="."||!J?"":J.startsWith("/")?J:"/"+J,ie=(R,A)=>{let[M,O]=R.getArguments();if(!M?.isKind(H.SyntaxKind.StringLiteral))return;let P=M.getLiteralValue(),W="unknown",L=oe,{paramsType:ae,queryType:ce,bodyType:Q,isMultipart:X}=xe(O,o,q,u,K);if(O?.isKind(H.SyntaxKind.ObjectLiteralExpression)){let G=O.getProperty("prefix");if(G?.isKind(H.SyntaxKind.PropertyAssignment)){let le=G.getInitializer();le?.isKind(H.SyntaxKind.StringLiteral)&&(L=le.getLiteralValue())}let Se=O.getProperty("handler");if(Se?.isKind(H.SyntaxKind.PropertyAssignment)){let le=Se.getInitializer();le&&(W=ye(le,o,q,u,K),W.startsWith("Promise<")||(W=`Promise<${W}>`))}}let V="",z="";A==="rpc"?(V=E.default.join(L,"rpc",P).replace(/\\/g,"/"),z=Z("action",L,P)):A==="loader"?(V=E.default.join(L,"loader",P).replace(/\\/g,"/"),z=Z("loader",L,P)):A==="api"&&(V=E.default.join(L,"api",P).replace(/\\/g,"/"),z=Z("get",L,P)),z&&I.push({type:A,name:z,returnType:W,url:P,rpcUrl:V,loaderUrl:V,paramsType:ae,queryType:ce,bodyType:Q,isMultipart:X})};w.forEach(R=>ie(R,"rpc")),S.forEach(R=>ie(R,"loader")),j.forEach(R=>ie(R,"api"));let Re=$(I,K);await ne(q,Re);let{client:F,server:D}=v(I,t);a.set(n,{client:F,server:D}),y.files[n]={hash:l,fnInfo:I,imports:Array.from(K.entries()).map(([R,A])=>[R,Array.from(A)])}}catch(n){console.error(`[rpc-generator] Error al procesar ${t}:`,{error:n})}}function $(t,n){let s=[],l=[...n.entries()].sort((i,w)=>i[0].localeCompare(w[0]));for(let[i,w]of l)s.push(`import type { ${[...w].sort().join(", ")} } from "${i}";`);let g=[...t].sort((i,w)=>i.name.localeCompare(w.name)),o=[];return g.forEach(i=>{o.push(be(i))}),`// AUTO-GENERATED by @bobtail.software/b-ssr. DO NOT EDIT.
|
|
10
10
|
/* eslint-disable */
|
|
11
11
|
|
|
12
|
-
`+(
|
|
12
|
+
`+(s.length>0?s.join(`
|
|
13
13
|
`)+`
|
|
14
14
|
|
|
15
|
-
`:"")+
|
|
15
|
+
`:"")+o.join(`
|
|
16
16
|
|
|
17
|
-
`)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
`)}function v(t,n){let s=[],l=[];t.forEach(i=>{let S=i.type==="rpc"?"POST":"GET",j=i.type==="loader"?i.loaderUrl:i.rpcUrl;l.push({name:i.name,rpcUrl:j,method:S,isMultipart:!!i.isMultipart}),s.push({name:i.name,url:i.url,type:i.type,requiresArgs:!0})});let g=`
|
|
18
|
+
import { createClientRpc } from '@bobtail.software/b-ssr/client';
|
|
19
|
+
|
|
20
|
+
if (import.meta.env.DEV) {
|
|
21
|
+
console.debug('\u{1F6E1}\uFE0F [B-SSR Security] Loaded SAFE Client-Stub for: ${E.default.basename(n)}');
|
|
22
|
+
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
${l.map(i=>`
|
|
25
|
+
export const ${i.name} = createClientRpc({
|
|
26
|
+
url: '${i.rpcUrl}',
|
|
27
|
+
method: '${i.method}',
|
|
28
|
+
isMultipart: ${i.isMultipart}
|
|
29
|
+
});
|
|
30
|
+
`).join(`
|
|
31
31
|
`)}
|
|
32
|
-
|
|
32
|
+
`,o=C(s,n);return{client:g,server:o}}let h=(t,n,s)=>{if(n.includes("node_modules")||n.startsWith(se))return null;if(s?.ssr===!0&&/\.[cm]?[jt]sx?$/.test(n)){let l=/\brequire\s*\(/.test(t),g=/\bmodule\.exports\b/.test(t),o=/\bexports\./.test(t);if(l||g||o)return"import { createRequire } from 'module';"+`
|
|
33
33
|
const require = createRequire(import.meta.url);
|
|
34
34
|
const module = { exports: {} };
|
|
35
35
|
const exports = module.exports;
|
|
36
36
|
`+t+`
|
|
37
|
-
export default module.exports;`}return null};function
|
|
37
|
+
export default module.exports;`}return null};function C(t,n){if(t.length===0)return"";let s=process.cwd(),l=E.default.relative(s,n).replace(/\\/g,"/");return`
|
|
38
38
|
import path from 'path';
|
|
39
39
|
import { pathToFileURL } from 'url';
|
|
40
40
|
|
|
41
41
|
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
42
|
-
throw new Error('\u{1F6A8} [B-SSR SECURITY ALERT] Server-side code leaked to browser: ${`./${
|
|
42
|
+
throw new Error('\u{1F6A8} [B-SSR SECURITY ALERT] Server-side code leaked to browser: ${`./${l}`}');
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
let initPromise;
|
|
@@ -53,7 +53,7 @@ export default module.exports;`}return null};function E(t,s){if(t.length===0)ret
|
|
|
53
53
|
renderOptionsMap = new Map();
|
|
54
54
|
loaderApiOptionsMap = new Map();
|
|
55
55
|
const projectRoot = process.cwd();
|
|
56
|
-
const absolutePath = path.resolve(projectRoot, '${
|
|
56
|
+
const absolutePath = path.resolve(projectRoot, '${l}');
|
|
57
57
|
const serverModuleUrl = pathToFileURL(absolutePath).href;
|
|
58
58
|
|
|
59
59
|
if (typeof globalThis.require === 'undefined') {
|
|
@@ -101,46 +101,46 @@ export default module.exports;`}return null};function E(t,s){if(t.length===0)ret
|
|
|
101
101
|
return initPromise;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
${t.map(
|
|
104
|
+
${t.map(o=>{let i=[];o.requiresArgs&&i.push("args"),i.push("ssrContext");let w=o.requiresArgs?"args":"{}",S="";return o.type==="rpc"?S=`
|
|
105
105
|
if (fn.isMultipart) throw new Error('RPC multipart no soportado en SSR.');
|
|
106
106
|
await getOptionsMaps();
|
|
107
|
-
const options = rpcOptionsMap.get('${
|
|
108
|
-
if (!options?.handler) throw new Error('Handler no encontrado para RPC: ${
|
|
109
|
-
const augmentedReq = Object.assign(Object.create(ssrContext.req), ${
|
|
107
|
+
const options = rpcOptionsMap.get('${o.url}');
|
|
108
|
+
if (!options?.handler) throw new Error('Handler no encontrado para RPC: ${o.name}');
|
|
109
|
+
const augmentedReq = Object.assign(Object.create(ssrContext.req), ${w});
|
|
110
110
|
return await options.handler.call(ssrContext.req.server, augmentedReq, ssrContext.reply);
|
|
111
|
-
`:
|
|
111
|
+
`:o.type==="api"?S=`
|
|
112
112
|
await getOptionsMaps();
|
|
113
|
-
const options = loaderApiOptionsMap.get('${
|
|
113
|
+
const options = loaderApiOptionsMap.get('${o.url}');
|
|
114
114
|
if (!options?.handler) {
|
|
115
|
-
console.error('\u26A0\uFE0F [B-SSR Warning] Handler API no encontrado:', '${
|
|
115
|
+
console.error('\u26A0\uFE0F [B-SSR Warning] Handler API no encontrado:', '${o.name}', 'URL:', '${o.url}');
|
|
116
116
|
return null;
|
|
117
117
|
}
|
|
118
|
-
const augmentedReq = Object.assign(Object.create(ssrContext.req), ${
|
|
118
|
+
const augmentedReq = Object.assign(Object.create(ssrContext.req), ${w});
|
|
119
119
|
return await options.handler.call(ssrContext.req.server, augmentedReq, ssrContext.reply);
|
|
120
|
-
`:
|
|
120
|
+
`:S=`
|
|
121
121
|
await getOptionsMaps();
|
|
122
|
-
const options = renderOptionsMap.get('${
|
|
122
|
+
const options = renderOptionsMap.get('${o.url}');
|
|
123
123
|
if (!options) return {};
|
|
124
|
-
const augmentedReq = Object.assign(Object.create(ssrContext.req), ${
|
|
124
|
+
const augmentedReq = Object.assign(Object.create(ssrContext.req), ${w});
|
|
125
125
|
let customData = {};
|
|
126
126
|
try {
|
|
127
127
|
if (options.handler) {
|
|
128
128
|
customData = (await options.handler.call(ssrContext.req.server, augmentedReq, ssrContext.reply)) || {};
|
|
129
129
|
}
|
|
130
130
|
} catch (handlerErr) {
|
|
131
|
-
console.error('\u274C [B-SSR Handler Error] ${
|
|
131
|
+
console.error('\u274C [B-SSR Handler Error] ${o.name}:', handlerErr);
|
|
132
132
|
return {};
|
|
133
133
|
}
|
|
134
134
|
if (ssrContext.reply.sent) return;
|
|
135
135
|
return { ...customData };
|
|
136
|
-
`,`export const ${
|
|
136
|
+
`,`export const ${o.name} = async (${i.join(", ")}) => {
|
|
137
137
|
try {
|
|
138
|
-
if (!ssrContext?.req) throw new Error('ssrContext requerido en ${
|
|
139
|
-
${
|
|
138
|
+
if (!ssrContext?.req) throw new Error('ssrContext requerido en ${o.name} (SSR)');
|
|
139
|
+
${S}
|
|
140
140
|
} catch (error) {
|
|
141
|
-
console.error('\u274C [B-SSR Error] ${
|
|
141
|
+
console.error('\u274C [B-SSR Error] ${o.name}:', error);
|
|
142
142
|
throw error;
|
|
143
143
|
}
|
|
144
144
|
};`}).join(`
|
|
145
145
|
`)}
|
|
146
|
-
`}async function
|
|
146
|
+
`}async function f(){b=!0;let t=Ce.performance.now();y=await me();let n=await(0,$e.default)(r,{absolute:!0});await Promise.all(n.map(l=>x(l))),u.resolveSourceFileDependencies(),await he(y);let s=Ce.performance.now();console.log(`\u{1F680} [B-SSR] RPC Generation completed in ${Math.round(s-t)}ms (${n.length} files)`),b=!1}return{name:"b-ssr-vite-plugin-rpc-universal-generator",enforce:"pre",async buildStart(){await f()},configureServer(t){let n=s=>s.endsWith(".mts")||s.endsWith(".ts");t.watcher.on("add",s=>n(s)&&x(s)),t.watcher.on("change",s=>n(s)&&x(s)),t.watcher.on("unlink",s=>{if(n(s)){let l=E.default.resolve(s);a.delete(l),m.delete(l),delete y.files[l];let g=E.default.extname(s),o=s.substring(0,s.length-g.length)+".universal.d.ts";(0,ee.unlink)(o).catch(()=>{})}})},async resolveId(t,n,s){if(t.startsWith(se))return null;if(t.includes(".universal")){let l=t.split("?")[0],g=l;if(l.endsWith(".universal"))g=l;else if(l.endsWith(".universal.ts"))g=l.slice(0,-3);else if(l.endsWith(".universal.mts"))g=l.slice(0,-4);else if(l.endsWith(".universal.js"))g=l.slice(0,-3);else return null;let o=[".mts",".ts"];for(let i of o){let w=g.slice(0,-10)+i,S=await this.resolve(w,n,{skipSelf:!0});if(S){let j=s?.ssr?"?mode=ssr":"?mode=client";return se+S.id+j}try{await(0,ee.access)(w);let j=s?.ssr?"?mode=ssr":"?mode=client";return se+w+j}catch{}}}return null},async load(t,n){if(t.startsWith(se)){let s=t.slice(se.length).split("?")[0],l=a.get(s);return l||(await x(s),l=a.get(s)),l?n?.ssr===!0?l.server:l.client:null}if(n?.ssr!==!0){let s=E.default.resolve(t);if(m.has(s))return`throw new Error("\u{1F6A8} [B-SSR FIREWALL] BLOCKED: Backend File imported in Client: ${E.default.basename(t)}");`}return null},transform:h}}0&&(module.exports={generateRpcTypes,rpcGeneratorPlugin});
|
package/dist/vite-rpc-plugin.js
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import
|
|
1
|
+
import qe from"fast-glob";import{access as Ue,unlink as Pe,readFile as Ke}from"fs/promises";import E from"path";import{Project as We,SyntaxKind as re}from"ts-morph";import{writeFile as Ce,readFile as ye,mkdir as ve}from"fs/promises";import k from"path";import{format as Ae,resolveConfig as $e}from"prettier";import{Node as Z,SyntaxKind as K,ts as Y}from"ts-morph";import{createHash as je}from"crypto";function Me(t){let s=t.replace(/[^a-zA-Z0-9]+(.)?/g,(d,c)=>c?c.toUpperCase():"");return s.charAt(0).toLowerCase()+s.slice(1)}function H(t,s,d){let c=k.basename(d,k.extname(d))||"Index",l=s.replace(/:[a-zA-Z0-9_]+/g,"").replace(/\//g," "),m=`${t} ${l} ${c}`;return Me(m)}function Oe(t){let s=/'[^']+'/g,d=/`[^`]+`/g,c=[],l,m=/'[^']+'/g;for(;(l=m.exec(t))!==null;)c.push({start:l.index,end:l.index+l[0].length,value:l[0]});let p=/`[^`]+`/g;for(;(l=p.exec(t))!==null;)c.push({start:l.index,end:l.index+l[0].length,value:l[0]});if(c.length>1){let f=[...c].sort((x,A)=>{let P=x.value.replace(/['`]/g,""),h=A.value.replace(/['`]/g,"");return P.localeCompare(h)}),b="",y=0;for(let x=0;x<c.length;x++)b+=t.slice(y,c[x].start),b+=f[x].value,y=c[x].end;return b+=t.slice(y),b}return t}function Ie(t){let s=t.match(/^\{([\s\S]*)\}$/);if(!s)return t;let d=s[1];if(!d.includes(":"))return t;let c=d.split(/[;,]/).map(p=>p.trim()).filter(p=>p),l=[];for(let p of c){let f=p.indexOf(":");if(f===-1)continue;let b=p.slice(0,f).trim(),y=p.slice(f+1).trim();l.push({name:b,value:y})}return l.length<=1?t:(l.sort((p,f)=>p.name.localeCompare(f.name)),`{ ${l.map(p=>`${p.name}: ${p.value}`).join("; ")} }`)}function le(t){let s=Oe(t);return s=Ie(s),s}async function ee(t,s){try{if(await ye(t,"utf-8")===s)return!1}catch{}let d=s;try{let c=await $e(t)||{};d=await Ae(s,{...c,parser:"typescript",filepath:t})}catch{}try{if(await ye(t,"utf-8")===d)return!1}catch{}return await Ce(t,d),!0}function pe(t){return je("sha1").update(t).digest("hex")}var he="v1",Re=k.join(process.cwd(),"node_modules",".cache","b-ssr"),Se=k.join(Re,"rpc-cache.json");async function ue(){try{let t=await ye(Se,"utf-8"),s=JSON.parse(t);return s.version!==he?{version:he,files:{}}:s}catch{return{version:he,files:{}}}}async function ge(t){try{await ve(Re,{recursive:!0}),await Ce(Se,JSON.stringify(t,null,2))}catch(s){console.error("\u26A0\uFE0F [B-SSR] Failed to save persistent cache:",s)}}function J(t,s,d,c,l,m){function p(h){return h.replace(/import\(['"](.*?)['"]\)/g,(T,g)=>{if(!k.isAbsolute(g))return T;let e=g;if(!k.extname(e)){let n=l.getSourceFile(a=>a.getFilePath().replace(/\.(mts|ts|tsx)$/,"")===g);n&&(e=n.getFilePath())}let r=k.relative(k.dirname(c),e).replace(/\\/g,"/");return r.startsWith(".")||(r="./"+r),`import("${r}")`})}if(t.getSymbol()?.getName()==="Promise"){let h=t.getAwaitedType();if(h)return`Promise<${J(h,s,d,c,l,m)}>`}let f=t.getAliasSymbol()??t.getSymbol();if(f?.getName()==="__object")return le(p(t.getText(s,Y.TypeFormatFlags.NoTruncation|Y.TypeFormatFlags.UseFullyQualifiedType)));if(t.isObject()&&!f){let h=(T,g=0)=>{if(!(g>5)){if(T.isArray()){let e=T.getArrayElementType();e&&h(e,g);return}if(T.isObject()&&!T.getSymbol()&&!T.getAliasSymbol()){for(let e of T.getApparentProperties()){let r=e.getValueDeclaration();r&&h(e.getTypeAtLocation(r),g+1)}return}J(T,s,d,c,l,m)}};return h(t),le(p(t.getText(s,Y.TypeFormatFlags.NoTruncation|Y.TypeFormatFlags.UseFullyQualifiedType)))}if(!f)return le(p(t.getText(s,Y.TypeFormatFlags.NoTruncation)));let b=f.getDeclarations()[0];if(!b)return f.getName();if(Z.isImportSpecifier(b)||Z.isImportClause(b)){let h=b.getFirstAncestorByKind(K.ImportDeclaration);if(h){let T=h.getModuleSpecifierValue(),g=f.getName();return m.has(T)||m.set(T,new Set),m.get(T)?.add(g),g}}let y=b.getSourceFile();if(y.getFilePath()===d.getFilePath())return f.getName();if(y.isInNodeModules())return le(p(t.getText(s,Y.TypeFormatFlags.NoTruncation)));let x=k.relative(k.dirname(c),y.getFilePath()).replace(/\\/g,"/"),A=x.startsWith(".")?x:`./${x}`,P=f.getName();return m.has(A)||m.set(A,new Set),m.get(A)?.add(P),P}function fe(t,s,d,c,l){let m;if(Z.isFunctionLikeDeclaration(t)||Z.isArrowFunction(t)){let p=t.getDescendantsOfKind(K.ReturnStatement);for(let f of p){let b=f.getExpression();if(b?.isKind(K.SatisfiesExpression)){m=b.getTypeNode()?.getType();break}}}if(m)return J(m,t,s,d,c,l);{let p=t.getType().getCallSignatures();if(p.length>0){let f=p[0]?.getReturnType();return J(f,t,s,d,c,l)}}return"unknown"}function de(t,s,d,c,l){let m="unknown",p="unknown",f="unknown",b=!1;if(!t||!Z.isObjectLiteralExpression(t))return{paramsType:m,queryType:p,bodyType:f,isMultipart:b};let y=t.getProperty("schema");if(y?.isKind(K.PropertyAssignment)){let x=y.getInitializer();if(x?.isKind(K.Identifier)){let P=x.getSymbol()?.getValueDeclaration();if(P){let h=P.getFirstDescendantByKind(K.ObjectLiteralExpression);h&&(x=h)}}if(x&&Z.isObjectLiteralExpression(x)){let A=x.getProperty("consumes");if(A?.isKind(K.PropertyAssignment)){let h=A.getInitializer();Z.isArrayLiteralExpression(h)&&(b=h.getElements().some(T=>T.isKind(K.StringLiteral)&&T.getLiteralValue()==="multipart/form-data"))}let P=h=>{let T=x.getProperty(h);if(T?.isKind(K.PropertyAssignment)){let g=T.getInitializer();if(g){let e=g.getType(),r=e.getApparentType(),n=r.getProperty("_output");if(n)return J(n.getTypeAtLocation(g).getApparentType(),g,s,d,c,l);let a=e.getSymbol(),u=e.getAliasSymbol(),o=a?.getName(),i=u?.getName();if(o&&o!=="__type"&&o!=="object"||i&&i!=="__type"&&i!=="object")return J(e,g,s,d,c,l);let w=r.getProperties();if(w.length>0){let R=[];for(let $ of w){let Q=$.getTypeAtLocation(g);R.push(`${$.getName()}: ${Q.getText(g)}`)}return`{ ${R.join("; ")} }`}return J(e,g,s,d,c,l)}}return"unknown"};if(m=P("params"),p=P("querystring"),f=P("body"),b){let h="{ file: File }";f=f!=="unknown"&&f.trim().startsWith("{")?`(${f} & ${h})`:h}}}return{paramsType:m,queryType:p,bodyType:f,isMultipart:b}}function De(t){let s=[];return t.paramsType!=="unknown"&&s.push(`params: ${t.paramsType}`),t.queryType!=="unknown"&&s.push(`query: ${t.queryType}`),t.bodyType!=="unknown"&&s.push(`body: ${t.bodyType}`),s.push("signal?: AbortSignal"),s}function me(t){let s=De(t),d="";return s.length>0&&(d=`args: { ${s.join("; ")} }`),d||(d="args: { signal?: AbortSignal }"),`export declare const ${t.name}: (${d}, ssrContext?: { req?: FastifyRequest, reply?: FastifyReply }) => ${t.returnType};`}import{performance as Ee}from"perf_hooks";import Fe from"fast-glob";import{access as xe,unlink as be,readFile as Le}from"fs/promises";import N from"path";import{Project as ke,SyntaxKind as te}from"ts-morph";async function Ne(t={}){let{routerPattern:s="src-ts/routers/**/*.mts",tsConfigFilePath:d="tsconfig.json",routerBaseDir:c="src-ts/routers",clean:l=!1}=t,m=[],p=[],f=[];try{let h=function(g,e){let r=[],n=[...e.entries()].sort((o,i)=>o[0].localeCompare(i[0]));for(let[o,i]of n)r.push(`import type { ${[...i].sort().join(", ")} } from "${o}";`);let a=[...g].sort((o,i)=>o.name.localeCompare(i.name)),u=[];return a.forEach(o=>{u.push(me(o))}),`// AUTO-GENERATED by @bobtail.software/b-ssr. DO NOT EDIT.
|
|
2
2
|
/* eslint-disable */
|
|
3
3
|
|
|
4
|
-
`+(
|
|
4
|
+
`+(r.length>0?r.join(`
|
|
5
5
|
`)+`
|
|
6
6
|
|
|
7
|
-
`:"")+
|
|
7
|
+
`:"")+u.join(`
|
|
8
8
|
|
|
9
|
-
`);return(await ae(
|
|
9
|
+
`)};var b=h;let y=await ue(),x=new ke({tsConfigFilePath:d,skipAddingFilesFromTsConfig:!0}),A=N.resolve(process.cwd(),c||".");async function P(g){try{let e=N.resolve(g),r=await Le(g,"utf-8"),n=pe(r),a=N.extname(g),u=g.substring(0,g.length-a.length)+".universal.d.ts",o=y.files[e];if(o&&o.hash===n){let S=o.fnInfo,I=new Map;o.imports.forEach(([j,M])=>{I.set(j,new Set(M))});let C=h(S,I);return(await ee(u,C)||await xe(u).then(()=>!0).catch(()=>!1))&&m.push(u),!0}let i=x.addSourceFileAtPath(g);await i.refreshFromFileSystem();let w=i.getDescendantsOfKind(te.CallExpression),R=w.filter(S=>S.getExpression().getText().endsWith(".addRpcRoute")),$=w.filter(S=>S.getExpression().getText().endsWith(".addRenderRoute")),Q=w.filter(S=>S.getExpression().getText().endsWith(".addLoaderRoute"));if(R.length===0&&$.length===0&&Q.length===0){if(delete y.files[e],l)try{await be(u),p.push(u)}catch{}return!1}let L=[],O=new Map;O.has("fastify")||O.set("fastify",new Set),O.get("fastify")?.add("FastifyRequest"),O.get("fastify")?.add("FastifyReply");let X=N.relative(A,N.dirname(g)).split(N.sep).join("/"),V=X==="."||!X?"":X.startsWith("/")?X:"/"+X,se=(S,I)=>{let[C,v]=S.getArguments();if(!C?.isKind(te.StringLiteral))return;let j=C.getLiteralValue(),M="unknown",F=V,{paramsType:U,queryType:D,bodyType:ie,isMultipart:ae}=de(v,i,u,x,O);if(v?.isKind(te.ObjectLiteralExpression)){let W=v.getProperty("prefix");if(W?.isKind(te.PropertyAssignment)){let _=W.getInitializer();_?.isKind(te.StringLiteral)&&(F=_.getLiteralValue())}let B=v.getProperty("handler");if(B?.isKind(te.PropertyAssignment)){let _=B.getInitializer();_&&(M=fe(_,i,u,x,O),M.startsWith("Promise<")||(M=`Promise<${M}>`))}}let z="",G="";I==="rpc"?(z=N.join(F,"rpc",j).replace(/\\/g,"/"),G=H("action",F,j)):I==="loader"?(z=N.join(F,"loader",j).replace(/\\/g,"/"),G=H("loader",F,j)):I==="api"&&(z=N.join(F,"api",j).replace(/\\/g,"/"),G=H("get",F,j)),G&&L.push({type:I,name:G,returnType:M,url:j,rpcUrl:z,loaderUrl:z,paramsType:U,queryType:D,bodyType:ie,isMultipart:ae})};if(R.forEach(S=>se(S,"rpc")),$.forEach(S=>se(S,"loader")),Q.forEach(S=>se(S,"api")),L.length===0){if(delete y.files[e],l)try{await be(u),p.push(u)}catch{}return!1}let oe=h(L,O);return(await ee(u,oe)||await xe(u).then(()=>!0).catch(()=>!1))&&m.push(u),y.files[e]={hash:n,fnInfo:L,imports:Array.from(O.entries()).map(([S,I])=>[S,Array.from(I)])},!0}catch(e){return f.push(`Error processing ${g}: ${e instanceof Error?e.message:String(e)}`),!1}}let T=await Fe(s,{absolute:!0});if(await Promise.all(T.map(g=>P(g))),x.resolveSourceFileDependencies(),await ge(y),l){let g=await Fe(N.join(c,"**/*.universal.d.ts"),{absolute:!0});for(let e of g){let r=e.replace(".universal.d.ts",".mts").replace(".universal.d.ts",".ts");if(!await xe(r).then(()=>!0).catch(()=>!1))try{await be(e),p.push(e)}catch{}}}}catch(y){f.push(`Fatal error: ${y instanceof Error?y.message:String(y)}`)}return{generated:m,cleaned:p,errors:f}}var ne="virtual:b-ssr-rpc-universal:";function mt(t={}){let{routerPattern:s="src-ts/routers/**/*.mts",tsConfigFilePath:d="tsconfig.json",routerBaseDir:c="src-ts/routers"}=t,l=new Map,m=new Set,p=new We({tsConfigFilePath:d,skipAddingFilesFromTsConfig:!0}),f=E.resolve(process.cwd(),c||"."),b=!1,y={version:"v1",files:{}};async function x(e){try{let r=E.resolve(e),n=await Ke(e,"utf-8"),a=pe(n),u=y.files[r];if(u&&u.hash===a){let C=u.fnInfo,v=new Map;u.imports.forEach(([ie,ae])=>{v.set(ie,new Set(ae))}),m.add(r);let{client:j,server:M}=P(C,e);l.set(r,{client:j,server:M});let F=E.extname(e),U=e.substring(0,e.length-F.length)+".universal.d.ts",D=A(C,v);await ee(U,D);return}let o=p.addSourceFileAtPath(e);await o.refreshFromFileSystem(),b||p.resolveSourceFileDependencies();let i=o.getDescendantsOfKind(re.CallExpression),w=i.filter(C=>C.getExpression().getText().endsWith(".addRpcRoute")),R=i.filter(C=>C.getExpression().getText().endsWith(".addRenderRoute")),$=i.filter(C=>C.getExpression().getText().endsWith(".addLoaderRoute")),Q=E.extname(e),L=e.substring(0,e.length-Q.length)+".universal.d.ts";if(w.length===0&&R.length===0&&$.length===0){l.delete(r),m.delete(r),delete y.files[r],L!==e&&await Pe(L).catch(()=>{});return}m.add(r);let O=[],q=new Map;q.has("fastify")||q.set("fastify",new Set),q.get("fastify")?.add("FastifyRequest"),q.get("fastify")?.add("FastifyReply");let V=E.relative(f,E.dirname(e)).split(E.sep).join("/"),se=V==="."||!V?"":V.startsWith("/")?V:"/"+V,oe=(C,v)=>{let[j,M]=C.getArguments();if(!j?.isKind(re.StringLiteral))return;let F=j.getLiteralValue(),U="unknown",D=se,{paramsType:ie,queryType:ae,bodyType:z,isMultipart:G}=de(M,o,L,p,q);if(M?.isKind(re.ObjectLiteralExpression)){let _=M.getProperty("prefix");if(_?.isKind(re.PropertyAssignment)){let ce=_.getInitializer();ce?.isKind(re.StringLiteral)&&(D=ce.getLiteralValue())}let Te=M.getProperty("handler");if(Te?.isKind(re.PropertyAssignment)){let ce=Te.getInitializer();ce&&(U=fe(ce,o,L,p,q),U.startsWith("Promise<")||(U=`Promise<${U}>`))}}let W="",B="";v==="rpc"?(W=E.join(D,"rpc",F).replace(/\\/g,"/"),B=H("action",D,F)):v==="loader"?(W=E.join(D,"loader",F).replace(/\\/g,"/"),B=H("loader",D,F)):v==="api"&&(W=E.join(D,"api",F).replace(/\\/g,"/"),B=H("get",D,F)),B&&O.push({type:v,name:B,returnType:U,url:F,rpcUrl:W,loaderUrl:W,paramsType:ie,queryType:ae,bodyType:z,isMultipart:G})};w.forEach(C=>oe(C,"rpc")),R.forEach(C=>oe(C,"loader")),$.forEach(C=>oe(C,"api"));let we=A(O,q);await ee(L,we);let{client:S,server:I}=P(O,e);l.set(r,{client:S,server:I}),y.files[r]={hash:a,fnInfo:O,imports:Array.from(q.entries()).map(([C,v])=>[C,Array.from(v)])}}catch(r){console.error(`[rpc-generator] Error al procesar ${e}:`,{error:r})}}function A(e,r){let n=[],a=[...r.entries()].sort((i,w)=>i[0].localeCompare(w[0]));for(let[i,w]of a)n.push(`import type { ${[...w].sort().join(", ")} } from "${i}";`);let u=[...e].sort((i,w)=>i.name.localeCompare(w.name)),o=[];return u.forEach(i=>{o.push(me(i))}),`// AUTO-GENERATED by @bobtail.software/b-ssr. DO NOT EDIT.
|
|
10
10
|
/* eslint-disable */
|
|
11
11
|
|
|
12
|
-
`+(
|
|
12
|
+
`+(n.length>0?n.join(`
|
|
13
13
|
`)+`
|
|
14
14
|
|
|
15
|
-
`:"")+
|
|
15
|
+
`:"")+o.join(`
|
|
16
16
|
|
|
17
|
-
`)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
`)}function P(e,r){let n=[],a=[];e.forEach(i=>{let R=i.type==="rpc"?"POST":"GET",$=i.type==="loader"?i.loaderUrl:i.rpcUrl;a.push({name:i.name,rpcUrl:$,method:R,isMultipart:!!i.isMultipart}),n.push({name:i.name,url:i.url,type:i.type,requiresArgs:!0})});let u=`
|
|
18
|
+
import { createClientRpc } from '@bobtail.software/b-ssr/client';
|
|
19
|
+
|
|
20
|
+
if (import.meta.env.DEV) {
|
|
21
|
+
console.debug('\u{1F6E1}\uFE0F [B-SSR Security] Loaded SAFE Client-Stub for: ${E.basename(r)}');
|
|
22
|
+
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
${a.map(i=>`
|
|
25
|
+
export const ${i.name} = createClientRpc({
|
|
26
|
+
url: '${i.rpcUrl}',
|
|
27
|
+
method: '${i.method}',
|
|
28
|
+
isMultipart: ${i.isMultipart}
|
|
29
|
+
});
|
|
30
|
+
`).join(`
|
|
31
31
|
`)}
|
|
32
|
-
|
|
32
|
+
`,o=T(n,r);return{client:u,server:o}}let h=(e,r,n)=>{if(r.includes("node_modules")||r.startsWith(ne))return null;if(n?.ssr===!0&&/\.[cm]?[jt]sx?$/.test(r)){let a=/\brequire\s*\(/.test(e),u=/\bmodule\.exports\b/.test(e),o=/\bexports\./.test(e);if(a||u||o)return"import { createRequire } from 'module';"+`
|
|
33
33
|
const require = createRequire(import.meta.url);
|
|
34
34
|
const module = { exports: {} };
|
|
35
35
|
const exports = module.exports;
|
|
36
36
|
`+e+`
|
|
37
|
-
export default module.exports;`}return null};function
|
|
37
|
+
export default module.exports;`}return null};function T(e,r){if(e.length===0)return"";let n=process.cwd(),a=E.relative(n,r).replace(/\\/g,"/");return`
|
|
38
38
|
import path from 'path';
|
|
39
39
|
import { pathToFileURL } from 'url';
|
|
40
40
|
|
|
41
41
|
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
42
|
-
throw new Error('\u{1F6A8} [B-SSR SECURITY ALERT] Server-side code leaked to browser: ${`./${
|
|
42
|
+
throw new Error('\u{1F6A8} [B-SSR SECURITY ALERT] Server-side code leaked to browser: ${`./${a}`}');
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
let initPromise;
|
|
@@ -53,7 +53,7 @@ export default module.exports;`}return null};function C(e,n){if(e.length===0)ret
|
|
|
53
53
|
renderOptionsMap = new Map();
|
|
54
54
|
loaderApiOptionsMap = new Map();
|
|
55
55
|
const projectRoot = process.cwd();
|
|
56
|
-
const absolutePath = path.resolve(projectRoot, '${
|
|
56
|
+
const absolutePath = path.resolve(projectRoot, '${a}');
|
|
57
57
|
const serverModuleUrl = pathToFileURL(absolutePath).href;
|
|
58
58
|
|
|
59
59
|
if (typeof globalThis.require === 'undefined') {
|
|
@@ -101,25 +101,25 @@ export default module.exports;`}return null};function C(e,n){if(e.length===0)ret
|
|
|
101
101
|
return initPromise;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
${e.map(
|
|
104
|
+
${e.map(o=>{let i=[];o.requiresArgs&&i.push("args"),i.push("ssrContext");let w=o.requiresArgs?"args":"{}",R="";return o.type==="rpc"?R=`
|
|
105
105
|
if (fn.isMultipart) throw new Error('RPC multipart no soportado en SSR.');
|
|
106
106
|
await getOptionsMaps();
|
|
107
|
-
const options = rpcOptionsMap.get('${
|
|
108
|
-
if (!options?.handler) throw new Error('Handler no encontrado para RPC: ${
|
|
107
|
+
const options = rpcOptionsMap.get('${o.url}');
|
|
108
|
+
if (!options?.handler) throw new Error('Handler no encontrado para RPC: ${o.name}');
|
|
109
109
|
const augmentedReq = Object.assign(Object.create(ssrContext.req), ${w});
|
|
110
110
|
return await options.handler.call(ssrContext.req.server, augmentedReq, ssrContext.reply);
|
|
111
|
-
`:
|
|
111
|
+
`:o.type==="api"?R=`
|
|
112
112
|
await getOptionsMaps();
|
|
113
|
-
const options = loaderApiOptionsMap.get('${
|
|
113
|
+
const options = loaderApiOptionsMap.get('${o.url}');
|
|
114
114
|
if (!options?.handler) {
|
|
115
|
-
console.error('\u26A0\uFE0F [B-SSR Warning] Handler API no encontrado:', '${
|
|
115
|
+
console.error('\u26A0\uFE0F [B-SSR Warning] Handler API no encontrado:', '${o.name}', 'URL:', '${o.url}');
|
|
116
116
|
return null;
|
|
117
117
|
}
|
|
118
118
|
const augmentedReq = Object.assign(Object.create(ssrContext.req), ${w});
|
|
119
119
|
return await options.handler.call(ssrContext.req.server, augmentedReq, ssrContext.reply);
|
|
120
|
-
`:
|
|
120
|
+
`:R=`
|
|
121
121
|
await getOptionsMaps();
|
|
122
|
-
const options = renderOptionsMap.get('${
|
|
122
|
+
const options = renderOptionsMap.get('${o.url}');
|
|
123
123
|
if (!options) return {};
|
|
124
124
|
const augmentedReq = Object.assign(Object.create(ssrContext.req), ${w});
|
|
125
125
|
let customData = {};
|
|
@@ -128,19 +128,19 @@ export default module.exports;`}return null};function C(e,n){if(e.length===0)ret
|
|
|
128
128
|
customData = (await options.handler.call(ssrContext.req.server, augmentedReq, ssrContext.reply)) || {};
|
|
129
129
|
}
|
|
130
130
|
} catch (handlerErr) {
|
|
131
|
-
console.error('\u274C [B-SSR Handler Error] ${
|
|
131
|
+
console.error('\u274C [B-SSR Handler Error] ${o.name}:', handlerErr);
|
|
132
132
|
return {};
|
|
133
133
|
}
|
|
134
134
|
if (ssrContext.reply.sent) return;
|
|
135
135
|
return { ...customData };
|
|
136
|
-
`,`export const ${
|
|
136
|
+
`,`export const ${o.name} = async (${i.join(", ")}) => {
|
|
137
137
|
try {
|
|
138
|
-
if (!ssrContext?.req) throw new Error('ssrContext requerido en ${
|
|
139
|
-
${
|
|
138
|
+
if (!ssrContext?.req) throw new Error('ssrContext requerido en ${o.name} (SSR)');
|
|
139
|
+
${R}
|
|
140
140
|
} catch (error) {
|
|
141
|
-
console.error('\u274C [B-SSR Error] ${
|
|
141
|
+
console.error('\u274C [B-SSR Error] ${o.name}:', error);
|
|
142
142
|
throw error;
|
|
143
143
|
}
|
|
144
144
|
};`}).join(`
|
|
145
145
|
`)}
|
|
146
|
-
`}async function
|
|
146
|
+
`}async function g(){b=!0;let e=Ee.now();y=await ue();let r=await qe(s,{absolute:!0});await Promise.all(r.map(a=>x(a))),p.resolveSourceFileDependencies(),await ge(y);let n=Ee.now();console.log(`\u{1F680} [B-SSR] RPC Generation completed in ${Math.round(n-e)}ms (${r.length} files)`),b=!1}return{name:"b-ssr-vite-plugin-rpc-universal-generator",enforce:"pre",async buildStart(){await g()},configureServer(e){let r=n=>n.endsWith(".mts")||n.endsWith(".ts");e.watcher.on("add",n=>r(n)&&x(n)),e.watcher.on("change",n=>r(n)&&x(n)),e.watcher.on("unlink",n=>{if(r(n)){let a=E.resolve(n);l.delete(a),m.delete(a),delete y.files[a];let u=E.extname(n),o=n.substring(0,n.length-u.length)+".universal.d.ts";Pe(o).catch(()=>{})}})},async resolveId(e,r,n){if(e.startsWith(ne))return null;if(e.includes(".universal")){let a=e.split("?")[0],u=a;if(a.endsWith(".universal"))u=a;else if(a.endsWith(".universal.ts"))u=a.slice(0,-3);else if(a.endsWith(".universal.mts"))u=a.slice(0,-4);else if(a.endsWith(".universal.js"))u=a.slice(0,-3);else return null;let o=[".mts",".ts"];for(let i of o){let w=u.slice(0,-10)+i,R=await this.resolve(w,r,{skipSelf:!0});if(R){let $=n?.ssr?"?mode=ssr":"?mode=client";return ne+R.id+$}try{await Ue(w);let $=n?.ssr?"?mode=ssr":"?mode=client";return ne+w+$}catch{}}}return null},async load(e,r){if(e.startsWith(ne)){let n=e.slice(ne.length).split("?")[0],a=l.get(n);return a||(await x(n),a=l.get(n)),a?r?.ssr===!0?a.server:a.client:null}if(r?.ssr!==!0){let n=E.resolve(e);if(m.has(n))return`throw new Error("\u{1F6A8} [B-SSR FIREWALL] BLOCKED: Backend File imported in Client: ${E.basename(e)}");`}return null},transform:h}}export{Ne as generateRpcTypes,mt as rpcGeneratorPlugin};
|
package/package.json
CHANGED