wexts 2.0.9 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +241 -235
- package/dist/chunk-2KAQYLVN.js +1 -0
- package/dist/chunk-2KAQYLVN.js.map +1 -0
- package/dist/chunk-3OM7CHCA.js +65 -0
- package/dist/chunk-3OM7CHCA.js.map +1 -0
- package/dist/chunk-667BQCEM.js +375 -0
- package/dist/chunk-667BQCEM.js.map +1 -0
- package/dist/chunk-FCEZDH42.mjs +20 -0
- package/dist/chunk-FCEZDH42.mjs.map +1 -0
- package/dist/chunk-KXYLEUSW.mjs +242 -0
- package/dist/chunk-KXYLEUSW.mjs.map +1 -0
- package/dist/chunk-O42L6HOX.js +242 -0
- package/dist/chunk-O42L6HOX.js.map +1 -0
- package/dist/chunk-ONXNE2A6.mjs +375 -0
- package/dist/chunk-ONXNE2A6.mjs.map +1 -0
- package/dist/chunk-STTOPUZ2.mjs +88 -0
- package/dist/chunk-STTOPUZ2.mjs.map +1 -0
- package/dist/chunk-VMT3LALB.mjs +51 -0
- package/dist/chunk-VMT3LALB.mjs.map +1 -0
- package/dist/chunk-VNNVLQLJ.mjs +65 -0
- package/dist/chunk-VNNVLQLJ.mjs.map +1 -0
- package/dist/chunk-WF65EDRZ.js +88 -0
- package/dist/chunk-WF65EDRZ.js.map +1 -0
- package/dist/chunk-XE4OXN2W.js +12 -0
- package/dist/chunk-XE4OXN2W.js.map +1 -0
- package/dist/chunk-XVKTIYHY.js +51 -0
- package/dist/chunk-XVKTIYHY.js.map +1 -0
- package/dist/chunk-YSLEF5C5.mjs +1 -0
- package/dist/chunk-YSLEF5C5.mjs.map +1 -0
- package/dist/cli/index.js +134 -81
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +93 -41
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.js +3 -3
- package/dist/client/index.mjs +2 -2
- package/dist/codegen/index.js +4 -4
- package/dist/codegen/index.mjs +3 -3
- package/dist/dev-server/index.js +4 -4
- package/dist/dev-server/index.mjs +3 -3
- package/dist/index.js +93 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +75 -33
- package/dist/index.mjs.map +1 -1
- package/dist/nest/index.js +3 -3
- package/dist/nest/index.mjs +2 -2
- package/dist/next/index.js +22 -9
- package/dist/next/index.js.map +1 -1
- package/dist/next/index.mjs +23 -9
- package/dist/next/index.mjs.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.mjs +2 -2
- package/package.json +126 -112
- package/templates/.dockerignore +43 -0
- package/templates/.env.example +17 -0
- package/templates/Dockerfile +60 -0
- package/templates/Procfile +1 -0
- package/templates/README.md +58 -0
- package/templates/api-sdk.ts +115 -0
- package/templates/docker-compose.yml +34 -0
- package/templates/nestjs-api/src/auth/auth.controller.ts +4 -7
- package/templates/nestjs-api/src/auth/auth.module.ts +4 -1
- package/templates/nestjs-api/src/auth/auth.service.ts +8 -13
- package/templates/nestjs-api/src/todos/todos.controller.ts +0 -7
- package/templates/nestjs-api/src/todos/todos.module.ts +3 -1
- package/templates/nestjs-api/src/users/users.controller.ts +0 -3
- package/templates/nestjs-api/src/users/users.module.ts +3 -1
- package/templates/nextjs-web/app/actions/auth.ts +49 -20
- package/templates/nextjs-web/lib/api.ts +115 -0
- package/templates/nextjs-web/next-env.d.ts +1 -1
- package/templates/nixpacks.toml +11 -0
- package/templates/root-package.json +32 -0
- package/templates/server.ts +66 -0
- package/templates/tsconfig.json +31 -0
package/dist/next/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/next/provider.tsx","../../src/client/fetcher.ts","../../src/next/useAuth.ts"],"sourcesContent":["'use client';\r\n\r\nimport React, { createContext, useContext, ReactNode } from 'react';\r\nimport { FusionFetcher } from '../client/fetcher';\r\n\r\ninterface FusionContextType {\r\n client: FusionFetcher;\r\n}\r\n\r\nconst FusionContext = createContext<FusionContextType | null>(null);\r\n\r\nexport interface FusionProviderProps {\r\n children: ReactNode;\r\n baseUrl?: string;\r\n}\r\n\r\n/**\r\n * FusionProvider - Provides API client to React components\r\n * Usage:\r\n * ```tsx\r\n * <FusionProvider baseUrl=\"/api\">\r\n * <App />\r\n * </FusionProvider>\r\n * ```\r\n */\r\nexport function FusionProvider({ children, baseUrl = '/api' }: FusionProviderProps) {\r\n const client = React.useMemo(() => new FusionFetcher(baseUrl), [baseUrl]);\r\n\r\n return (\r\n <FusionContext.Provider value={{ client }}>\r\n {children}\r\n </FusionContext.Provider>\r\n );\r\n}\r\n\r\n/**\r\n * useFusion hook - Access API client in components\r\n * Usage:\r\n * ```tsx\r\n * const { client } = useFusion();\r\n * const data = await client.get('/users');\r\n * ```\r\n */\r\nexport function useFusion(): FusionContextType {\r\n const context = useContext(FusionContext);\r\n if (!context) {\r\n throw new Error('useFusion must be used within FusionProvider');\r\n }\r\n return context;\r\n}\r\n","// packages/api-client/src/fetcher.ts\r\n\r\nexport class FusionFetcher {\r\n private baseUrl: string;\r\n\r\n constructor(baseUrl: string = '/api') {\r\n this.baseUrl = baseUrl;\r\n }\r\n\r\n private async request<T>(method: string, path: string, body?: any): Promise<T> {\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n\r\n // Automatically attach Fusion Token if present\r\n if (typeof window !== 'undefined') {\r\n const token = localStorage.getItem('fusion_token');\r\n if (token) headers['Authorization'] = `Bearer ${token}`;\r\n }\r\n\r\n const response = await fetch(`${this.baseUrl}${path}`, {\r\n method,\r\n headers,\r\n body: body ? JSON.stringify(body) : undefined,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Fusion API Error: ${response.statusText}`);\r\n }\r\n\r\n return response.json();\r\n }\r\n\r\n get<T>(path: string) { return this.request<T>('GET', path); }\r\n post<T>(path: string, body: any) { return this.request<T>('POST', path, body); }\r\n put<T>(path: string, body: any) { return this.request<T>('PUT', path, body); }\r\n delete<T>(path: string) { return this.request<T>('DELETE', path); }\r\n}\r\n\r\nexport const apiFetcher = new FusionFetcher();\r\n","'use client';\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport { useFusion } from './provider';\r\n\r\nexport interface AuthUser {\r\n id: string;\r\n email: string;\r\n name?: string;\r\n}\r\n\r\nexport interface UseAuthReturn {\r\n user: AuthUser | null;\r\n loading: boolean;\r\n login: (email: string, password: string) => Promise<void>;\r\n logout: () => Promise<void>;\r\n isAuthenticated: boolean;\r\n}\r\n\r\n/**\r\n * useAuth hook - Authentication state management\r\n * Usage:\r\n * ```tsx\r\n * const { user, login, logout, isAuthenticated } = useAuth();\r\n * ```\r\n */\r\nexport function useAuth(): UseAuthReturn {\r\n const { client } = useFusion();\r\n const [user, setUser] = useState<AuthUser | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n\r\n useEffect(() => {\r\n // Check for existing session\r\n const token = localStorage.getItem('fusion_token');\r\n if (token) {\r\n // Validate token and load user\r\n loadUser();\r\n } else {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n const loadUser = async () => {\r\n try {\r\n const userData = await client.get<AuthUser>('/auth/me');\r\n setUser(userData);\r\n } catch (error) {\r\n localStorage.removeItem('fusion_token');\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n const login = async (email: string, password: string) => {\r\n const response = await client.post<{ token: string; user: AuthUser }>('/auth/login', {\r\n email,\r\n password,\r\n });\r\n localStorage.setItem('fusion_token', response.token);\r\n setUser(response.user);\r\n };\r\n\r\n const logout = async () => {\r\n localStorage.removeItem('fusion_token');\r\n setUser(null);\r\n };\r\n\r\n return {\r\n user,\r\n loading,\r\n login,\r\n logout,\r\n isAuthenticated: !!user,\r\n };\r\n}\r\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/next/provider.tsx","../../src/client/fetcher.ts","../../src/next/useAuth.ts"],"sourcesContent":["'use client';\r\n\r\nimport React, { createContext, useContext, ReactNode } from 'react';\r\nimport { FusionFetcher } from '../client/fetcher';\r\n\r\ninterface FusionContextType {\r\n client: FusionFetcher;\r\n}\r\n\r\nconst FusionContext = createContext<FusionContextType | null>(null);\r\n\r\nexport interface FusionProviderProps {\r\n children: ReactNode;\r\n baseUrl?: string;\r\n}\r\n\r\n/**\r\n * FusionProvider - Provides API client to React components\r\n * Usage:\r\n * ```tsx\r\n * <FusionProvider baseUrl=\"/api\">\r\n * <App />\r\n * </FusionProvider>\r\n * ```\r\n */\r\nexport function FusionProvider({ children, baseUrl = '/api' }: FusionProviderProps) {\r\n const client = React.useMemo(() => new FusionFetcher(baseUrl), [baseUrl]);\r\n\r\n return (\r\n <FusionContext.Provider value={{ client }}>\r\n {children}\r\n </FusionContext.Provider>\r\n );\r\n}\r\n\r\n/**\r\n * useFusion hook - Access API client in components\r\n * Usage:\r\n * ```tsx\r\n * const { client } = useFusion();\r\n * const data = await client.get('/users');\r\n * ```\r\n */\r\nexport function useFusion(): FusionContextType {\r\n const context = useContext(FusionContext);\r\n if (!context) {\r\n throw new Error('useFusion must be used within FusionProvider');\r\n }\r\n return context;\r\n}\r\n","// packages/api-client/src/fetcher.ts\r\n\r\nexport class FusionFetcher {\r\n private baseUrl: string;\r\n\r\n constructor(baseUrl: string = '/api') {\r\n this.baseUrl = baseUrl;\r\n }\r\n\r\n private async request<T>(method: string, path: string, body?: any): Promise<T> {\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n\r\n // Automatically attach Fusion Token if present\r\n if (typeof window !== 'undefined') {\r\n const token = localStorage.getItem('fusion_token');\r\n if (token) headers['Authorization'] = `Bearer ${token}`;\r\n }\r\n\r\n const response = await fetch(`${this.baseUrl}${path}`, {\r\n method,\r\n headers,\r\n body: body ? JSON.stringify(body) : undefined,\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Fusion API Error: ${response.statusText}`);\r\n }\r\n\r\n return response.json();\r\n }\r\n\r\n get<T>(path: string) { return this.request<T>('GET', path); }\r\n post<T>(path: string, body: any) { return this.request<T>('POST', path, body); }\r\n put<T>(path: string, body: any) { return this.request<T>('PUT', path, body); }\r\n delete<T>(path: string) { return this.request<T>('DELETE', path); }\r\n}\r\n\r\nexport const apiFetcher = new FusionFetcher();\r\n","'use client';\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport { useFusion } from './provider';\r\n\r\nexport interface AuthUser {\r\n id: string;\r\n email: string;\r\n name?: string;\r\n}\r\n\r\nexport interface UseAuthReturn {\r\n user: AuthUser | null;\r\n loading: boolean;\r\n login: (email: string, password: string) => Promise<void>;\r\n logout: () => Promise<void>;\r\n isAuthenticated: boolean;\r\n}\r\n\r\n/**\r\n * useAuth hook - Authentication state management\r\n * Usage:\r\n * ```tsx\r\n * const { user, login, logout, isAuthenticated } = useAuth();\r\n * ```\r\n */\r\nexport function useAuth(): UseAuthReturn {\r\n const { client } = useFusion();\r\n const [user, setUser] = useState<AuthUser | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n\r\n useEffect(() => {\r\n // Check for existing session\r\n const token = localStorage.getItem('fusion_token');\r\n if (token) {\r\n // Validate token and load user\r\n loadUser();\r\n } else {\r\n setLoading(false);\r\n }\r\n }, []);\r\n\r\n const loadUser = async () => {\r\n try {\r\n const userData = await client.get<AuthUser>('/auth/me');\r\n setUser(userData);\r\n } catch (error) {\r\n localStorage.removeItem('fusion_token');\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n const login = async (email: string, password: string) => {\r\n const response = await client.post<{ token: string; user: AuthUser }>('/auth/login', {\r\n email,\r\n password,\r\n });\r\n localStorage.setItem('fusion_token', response.token);\r\n setUser(response.user);\r\n };\r\n\r\n const logout = async () => {\r\n localStorage.removeItem('fusion_token');\r\n setUser(null);\r\n };\r\n\r\n return {\r\n user,\r\n loading,\r\n login,\r\n logout,\r\n isAuthenticated: !!user,\r\n };\r\n}\r\n"],"mappings":";;;;;AAEA,OAAOA,SAASC,eAAeC,kBAA6B;;;ACArD,IAAMC,gBAAN,MAAMA;EAFb,OAEaA;;;EACDC;EAER,YAAYA,UAAkB,QAAQ;AAClC,SAAKA,UAAUA;EACnB;EAEA,MAAcC,QAAWC,QAAgBC,MAAcC,MAAwB;AAC3E,UAAMC,UAAkC;MACpC,gBAAgB;IACpB;AAGA,QAAI,OAAOC,WAAW,aAAa;AAC/B,YAAMC,QAAQC,aAAaC,QAAQ,cAAA;AACnC,UAAIF,MAAOF,SAAQ,eAAA,IAAmB,UAAUE,KAAAA;IACpD;AAEA,UAAMG,WAAW,MAAMC,MAAM,GAAG,KAAKX,OAAO,GAAGG,IAAAA,IAAQ;MACnDD;MACAG;MACAD,MAAMA,OAAOQ,KAAKC,UAAUT,IAAAA,IAAQU;IACxC,CAAA;AAEA,QAAI,CAACJ,SAASK,IAAI;AACd,YAAM,IAAIC,MAAM,qBAAqBN,SAASO,UAAU,EAAE;IAC9D;AAEA,WAAOP,SAASQ,KAAI;EACxB;EAEAC,IAAOhB,MAAc;AAAE,WAAO,KAAKF,QAAW,OAAOE,IAAAA;EAAO;EAC5DiB,KAAQjB,MAAcC,MAAW;AAAE,WAAO,KAAKH,QAAW,QAAQE,MAAMC,IAAAA;EAAO;EAC/EiB,IAAOlB,MAAcC,MAAW;AAAE,WAAO,KAAKH,QAAW,OAAOE,MAAMC,IAAAA;EAAO;EAC7EkB,OAAUnB,MAAc;AAAE,WAAO,KAAKF,QAAW,UAAUE,IAAAA;EAAO;AACtE;AAEO,IAAMoB,aAAa,IAAIxB,cAAAA;;;AD9B9B,IAAMyB,gBAAgBC,8BAAwC,IAAA;AAgBvD,SAASC,eAAe,EAAEC,UAAUC,UAAU,OAAM,GAAuB;AAC9E,QAAMC,SAASC,MAAMC,QAAQ,MAAM,IAAIC,cAAcJ,OAAAA,GAAU;IAACA;GAAQ;AAExE,SACI,sBAAA,cAACJ,cAAcS,UAAQ;IAACC,OAAO;MAAEL;IAAO;KACnCF,QAAAA;AAGb;AARgBD;AAkBT,SAASS,YAAAA;AACZ,QAAMC,UAAUC,WAAWb,aAAAA;AAC3B,MAAI,CAACY,SAAS;AACV,UAAM,IAAIE,MAAM,8CAAA;EACpB;AACA,SAAOF;AACX;AANgBD;;;AEzChB,SAASI,UAAUC,iBAAiB;AAwB7B,SAASC,UAAAA;AACZ,QAAM,EAAEC,OAAM,IAAKC,UAAAA;AACnB,QAAM,CAACC,MAAMC,OAAAA,IAAWC,SAA0B,IAAA;AAClD,QAAM,CAACC,SAASC,UAAAA,IAAcF,SAAS,IAAA;AAEvCG,YAAU,MAAA;AAEN,UAAMC,QAAQC,aAAaC,QAAQ,cAAA;AACnC,QAAIF,OAAO;AAEPG,eAAAA;IACJ,OAAO;AACHL,iBAAW,KAAA;IACf;EACJ,GAAG,CAAA,CAAE;AAEL,QAAMK,WAAW,mCAAA;AACb,QAAI;AACA,YAAMC,WAAW,MAAMZ,OAAOa,IAAc,UAAA;AAC5CV,cAAQS,QAAAA;IACZ,SAASE,OAAO;AACZL,mBAAaM,WAAW,cAAA;IAC5B,UAAA;AACIT,iBAAW,KAAA;IACf;EACJ,GATiB;AAWjB,QAAMU,QAAQ,8BAAOC,OAAeC,aAAAA;AAChC,UAAMC,WAAW,MAAMnB,OAAOoB,KAAwC,eAAe;MACjFH;MACAC;IACJ,CAAA;AACAT,iBAAaY,QAAQ,gBAAgBF,SAASX,KAAK;AACnDL,YAAQgB,SAASjB,IAAI;EACzB,GAPc;AASd,QAAMoB,SAAS,mCAAA;AACXb,iBAAaM,WAAW,cAAA;AACxBZ,YAAQ,IAAA;EACZ,GAHe;AAKf,SAAO;IACHD;IACAG;IACAW;IACAM;IACAC,iBAAiB,CAAC,CAACrB;EACvB;AACJ;AAhDgBH;","names":["React","createContext","useContext","FusionFetcher","baseUrl","request","method","path","body","headers","window","token","localStorage","getItem","response","fetch","JSON","stringify","undefined","ok","Error","statusText","json","get","post","put","delete","apiFetcher","FusionContext","createContext","FusionProvider","children","baseUrl","client","React","useMemo","FusionFetcher","Provider","value","useFusion","context","useContext","Error","useState","useEffect","useAuth","client","useFusion","user","setUser","useState","loading","setLoading","useEffect","token","localStorage","getItem","loadUser","userData","get","error","removeItem","login","email","password","response","post","setItem","logout","isAuthenticated"]}
|
package/dist/types/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";require('../chunk-
|
|
1
|
+
"use strict";require('../chunk-2KAQYLVN.js');
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/types/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import "../chunk-
|
|
2
|
-
import "../chunk-
|
|
1
|
+
import "../chunk-YSLEF5C5.mjs";
|
|
2
|
+
import "../chunk-FCEZDH42.mjs";
|
|
3
3
|
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED
|
@@ -1,113 +1,127 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "wexts",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"module": "./dist/index.mjs",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"bin": {
|
|
9
|
-
"wexts": "./dist/cli/index.js"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"dist",
|
|
13
|
-
"templates"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"type"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"
|
|
87
|
-
"http-proxy": "^1.
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
},
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "wexts",
|
|
3
|
+
"version": "3.0.1",
|
|
4
|
+
"description": "Build production-ready full-stack apps with Next.js 16 + NestJS 11 in a unified runtime. Zero URLs. Zero configuration.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"wexts": "./dist/cli/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"templates",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.mjs",
|
|
21
|
+
"require": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./client": {
|
|
24
|
+
"types": "./dist/client/index.d.ts",
|
|
25
|
+
"import": "./dist/client/index.mjs",
|
|
26
|
+
"require": "./dist/client/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./types": {
|
|
29
|
+
"types": "./dist/types/index.d.ts",
|
|
30
|
+
"import": "./dist/types/index.mjs",
|
|
31
|
+
"require": "./dist/types/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./nest": {
|
|
34
|
+
"types": "./dist/nest/index.d.ts",
|
|
35
|
+
"import": "./dist/nest/index.mjs",
|
|
36
|
+
"require": "./dist/nest/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./next": {
|
|
39
|
+
"types": "./dist/next/index.d.ts",
|
|
40
|
+
"import": "./dist/next/index.mjs",
|
|
41
|
+
"require": "./dist/next/index.js"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsup",
|
|
46
|
+
"dev": "tsup --watch",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:watch": "vitest",
|
|
49
|
+
"lint": "eslint src",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"release": "npm run build && npm publish --access public"
|
|
52
|
+
},
|
|
53
|
+
"keywords": [
|
|
54
|
+
"wexts",
|
|
55
|
+
"framework",
|
|
56
|
+
"fullstack",
|
|
57
|
+
"nestjs",
|
|
58
|
+
"nextjs",
|
|
59
|
+
"monorepo",
|
|
60
|
+
"typescript",
|
|
61
|
+
"react",
|
|
62
|
+
"unified-runtime",
|
|
63
|
+
"zero-config",
|
|
64
|
+
"type-safe",
|
|
65
|
+
"prisma",
|
|
66
|
+
"jwt-auth",
|
|
67
|
+
"docker",
|
|
68
|
+
"next.js",
|
|
69
|
+
"nest.js"
|
|
70
|
+
],
|
|
71
|
+
"author": "WEXTS Team",
|
|
72
|
+
"license": "MIT",
|
|
73
|
+
"repository": {
|
|
74
|
+
"type": "git",
|
|
75
|
+
"url": "https://github.com/ziadmustafa1/wexts.git"
|
|
76
|
+
},
|
|
77
|
+
"homepage": "https://github.com/ziadmustafa1/wexts#readme",
|
|
78
|
+
"bugs": {
|
|
79
|
+
"url": "https://github.com/ziadmustafa1/wexts/issues"
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=20.9.0",
|
|
83
|
+
"pnpm": ">=10.0.0"
|
|
84
|
+
},
|
|
85
|
+
"devDependencies": {
|
|
86
|
+
"@swc/core": "^1.15.3",
|
|
87
|
+
"@types/http-proxy": "^1.17.15",
|
|
88
|
+
"@types/node": "^22.10.2",
|
|
89
|
+
"@types/react": "^19.0.6",
|
|
90
|
+
"eslint": "^9.17.0",
|
|
91
|
+
"tsup": "^8.3.5",
|
|
92
|
+
"vitest": "^2.1.8"
|
|
93
|
+
},
|
|
94
|
+
"dependencies": {
|
|
95
|
+
"chokidar": "^4.0.3",
|
|
96
|
+
"commander": "^12.1.0",
|
|
97
|
+
"consola": "^3.2.3",
|
|
98
|
+
"http-proxy": "^1.18.1",
|
|
99
|
+
"inquirer": "^12.4.0",
|
|
100
|
+
"picocolors": "^1.1.1",
|
|
101
|
+
"reflect-metadata": "^0.2.2",
|
|
102
|
+
"typescript": "^5.9.3"
|
|
103
|
+
},
|
|
104
|
+
"peerDependencies": {
|
|
105
|
+
"@nestjs/common": "^11.0.0",
|
|
106
|
+
"@nestjs/core": "^11.0.0",
|
|
107
|
+
"next": "^16.0.0",
|
|
108
|
+
"react": "^19.0.0"
|
|
109
|
+
},
|
|
110
|
+
"peerDependenciesMeta": {
|
|
111
|
+
"@nestjs/common": {
|
|
112
|
+
"optional": true
|
|
113
|
+
},
|
|
114
|
+
"@nestjs/core": {
|
|
115
|
+
"optional": true
|
|
116
|
+
},
|
|
117
|
+
"react": {
|
|
118
|
+
"optional": true
|
|
119
|
+
},
|
|
120
|
+
"next": {
|
|
121
|
+
"optional": true
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"publishConfig": {
|
|
125
|
+
"access": "public"
|
|
126
|
+
}
|
|
113
127
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Environment files
|
|
2
|
+
.env
|
|
3
|
+
.env.local
|
|
4
|
+
.env.*.local
|
|
5
|
+
|
|
6
|
+
# Node modules
|
|
7
|
+
node_modules
|
|
8
|
+
dist
|
|
9
|
+
build
|
|
10
|
+
.next
|
|
11
|
+
.turbo
|
|
12
|
+
|
|
13
|
+
# Database
|
|
14
|
+
*.db
|
|
15
|
+
*.db-journal
|
|
16
|
+
|
|
17
|
+
# Logs
|
|
18
|
+
*.log
|
|
19
|
+
npm-debug.log*
|
|
20
|
+
|
|
21
|
+
# OS
|
|
22
|
+
.DS_Store
|
|
23
|
+
Thumbs.db
|
|
24
|
+
|
|
25
|
+
# IDE
|
|
26
|
+
.vscode
|
|
27
|
+
.idea
|
|
28
|
+
*.swp
|
|
29
|
+
|
|
30
|
+
# Git
|
|
31
|
+
.git
|
|
32
|
+
.gitignore
|
|
33
|
+
|
|
34
|
+
# CI/CD
|
|
35
|
+
.github
|
|
36
|
+
|
|
37
|
+
# Documentation
|
|
38
|
+
*.md
|
|
39
|
+
!README.md
|
|
40
|
+
|
|
41
|
+
# Config files
|
|
42
|
+
.prettierrc
|
|
43
|
+
.eslintrc*
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# WEXTS Environment Variables
|
|
2
|
+
# Copy this file to .env and update values
|
|
3
|
+
|
|
4
|
+
# JWT Authentication
|
|
5
|
+
JWT_SECRET=your-super-secret-jwt-key-change-in-production
|
|
6
|
+
JWT_EXPIRES_IN=7d
|
|
7
|
+
|
|
8
|
+
# Database (Local Development - SQLite)
|
|
9
|
+
DATABASE_URL="file:./apps/api/dev.db"
|
|
10
|
+
|
|
11
|
+
# Database (Production - PostgreSQL)
|
|
12
|
+
# DATABASE_URL="postgresql://user:password@host:5432/dbname"
|
|
13
|
+
# DIRECT_URL="postgresql://user:password@host:5432/dbname"
|
|
14
|
+
|
|
15
|
+
# Server
|
|
16
|
+
PORT=3000
|
|
17
|
+
NODE_ENV=development
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Stage 1: Build
|
|
2
|
+
FROM node:20-alpine AS builder
|
|
3
|
+
|
|
4
|
+
# Install pnpm
|
|
5
|
+
RUN npm install -g pnpm
|
|
6
|
+
|
|
7
|
+
# Set working directory
|
|
8
|
+
WORKDIR /app
|
|
9
|
+
|
|
10
|
+
# Copy package files
|
|
11
|
+
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
12
|
+
COPY apps/web/package.json ./apps/web/
|
|
13
|
+
COPY apps/api/package.json ./apps/api/
|
|
14
|
+
|
|
15
|
+
# Install dependencies
|
|
16
|
+
RUN pnpm install --frozen-lockfile
|
|
17
|
+
|
|
18
|
+
# Copy source code
|
|
19
|
+
COPY . .
|
|
20
|
+
|
|
21
|
+
# Generate Prisma Client
|
|
22
|
+
RUN cd apps/api && npx prisma generate
|
|
23
|
+
|
|
24
|
+
# Build all apps
|
|
25
|
+
RUN pnpm run build
|
|
26
|
+
|
|
27
|
+
# Stage 2: Production
|
|
28
|
+
FROM node:20-alpine AS runner
|
|
29
|
+
|
|
30
|
+
# Install pnpm
|
|
31
|
+
RUN npm install -g pnpm
|
|
32
|
+
|
|
33
|
+
WORKDIR /app
|
|
34
|
+
|
|
35
|
+
# Copy package files
|
|
36
|
+
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
37
|
+
COPY apps/web/package.json ./apps/web/
|
|
38
|
+
COPY apps/api/package.json ./apps/api/
|
|
39
|
+
|
|
40
|
+
# Install production dependencies only
|
|
41
|
+
RUN pnpm install --prod --frozen-lockfile
|
|
42
|
+
|
|
43
|
+
# Copy built files from builder
|
|
44
|
+
COPY --from=builder /app/dist ./dist
|
|
45
|
+
COPY --from=builder /app/apps/web/.next ./apps/web/.next
|
|
46
|
+
COPY --from=builder /app/apps/web/public ./apps/web/public
|
|
47
|
+
COPY --from=builder /app/apps/api/dist ./apps/api/dist
|
|
48
|
+
COPY --from=builder /app/apps/api/prisma ./apps/api/prisma
|
|
49
|
+
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
|
|
50
|
+
COPY --from=builder /app/server.ts ./server.ts
|
|
51
|
+
|
|
52
|
+
# Expose port
|
|
53
|
+
EXPOSE 3000
|
|
54
|
+
|
|
55
|
+
# Set environment
|
|
56
|
+
ENV NODE_ENV=production
|
|
57
|
+
ENV PORT=3000
|
|
58
|
+
|
|
59
|
+
# Start application
|
|
60
|
+
CMD ["node", "dist/server.js"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
web: pnpm start
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# WEXTS Templates
|
|
2
|
+
|
|
3
|
+
This directory contains all the templates needed to create a WEXTS unified server application.
|
|
4
|
+
|
|
5
|
+
## 📁 Files Included
|
|
6
|
+
|
|
7
|
+
### Core Templates
|
|
8
|
+
- `server.ts` - Unified server that runs Next.js + NestJS in one process
|
|
9
|
+
- `api-sdk.ts` - Type-safe SDK for API calls (zero URLs needed!)
|
|
10
|
+
- `root-package.json` - Root package.json with all scripts
|
|
11
|
+
- `tsconfig.json` - TypeScript configuration
|
|
12
|
+
- `.env.example` - Environment variables template
|
|
13
|
+
|
|
14
|
+
### Deployment Templates
|
|
15
|
+
- `Dockerfile` - Docker multi-stage build
|
|
16
|
+
- `docker-compose.yml` - Docker Compose with PostgreSQL
|
|
17
|
+
- `.dockerignore` - Docker ignore file
|
|
18
|
+
- `nixpacks.toml` - Railway deployment config
|
|
19
|
+
- `Procfile` - Render/Heroku deployment
|
|
20
|
+
|
|
21
|
+
### App Templates
|
|
22
|
+
- `nestjs-api/` - NestJS backend template
|
|
23
|
+
- `nextjs-web/` - Next.js frontend template
|
|
24
|
+
|
|
25
|
+
## 🚀 Usage
|
|
26
|
+
|
|
27
|
+
These templates are used by the WEXTS CLI when creating a new project:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx wexts create my-app
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## ✨ Features
|
|
34
|
+
|
|
35
|
+
✅ **Unified Server** - Single Node.js process for frontend + backend
|
|
36
|
+
✅ **Zero URLs** - Type-safe SDK without explicit API URLs
|
|
37
|
+
✅ **Smart Routing** - Automatic routing between Next.js and NestJS
|
|
38
|
+
✅ **Docker Ready** - Complete Docker setup included
|
|
39
|
+
✅ **Multi-Platform Deploy** - Railway, Render, Docker, VPS
|
|
40
|
+
|
|
41
|
+
## 📦 Template Structure
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
project/
|
|
45
|
+
├── server.ts ← Unified server
|
|
46
|
+
├── package.json ← Root package
|
|
47
|
+
├── tsconfig.json ← TS config
|
|
48
|
+
├── .env.example ← Env template
|
|
49
|
+
├── Dockerfile ← Docker build
|
|
50
|
+
├── docker-compose.yml ← Docker + DB
|
|
51
|
+
├── apps/
|
|
52
|
+
│ ├── api/ ← From nestjs-api template
|
|
53
|
+
│ └── web/ ← From nextjs-web template
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 🔧 Customization
|
|
57
|
+
|
|
58
|
+
Templates can be customized before project creation. See CLI documentation for details.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WEXTS Internal SDK
|
|
3
|
+
* Type-safe API client - ZERO URLs needed!
|
|
4
|
+
* Works in both Client and Server Components
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// HTTP client - uses relative paths only
|
|
8
|
+
async function request<T>(method: string, path: string, data?: any): Promise<T> {
|
|
9
|
+
const url = `/api${path}`;
|
|
10
|
+
|
|
11
|
+
const options: RequestInit = {
|
|
12
|
+
method,
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
},
|
|
16
|
+
credentials: 'include',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (data) {
|
|
20
|
+
options.body = JSON.stringify(data);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const response = await fetch(url, options);
|
|
24
|
+
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
27
|
+
throw new Error(error.message || 'Request failed');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return response.json();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ==========================================
|
|
34
|
+
// TYPE-SAFE API - NO URLs ANYWHERE!
|
|
35
|
+
// ==========================================
|
|
36
|
+
|
|
37
|
+
export const api = {
|
|
38
|
+
/**
|
|
39
|
+
* Authentication API
|
|
40
|
+
*/
|
|
41
|
+
auth: {
|
|
42
|
+
/**
|
|
43
|
+
* Register new user
|
|
44
|
+
* @example await api.auth.register({ email, password, name })
|
|
45
|
+
*/
|
|
46
|
+
register: (data: { email: string; password: string; name?: string }) =>
|
|
47
|
+
request<{ user: any; access_token: string }>('POST', '/auth/register', data),
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Login user
|
|
51
|
+
* @example await api.auth.login({ email, password })
|
|
52
|
+
*/
|
|
53
|
+
login: (data: { email: string; password: string }) =>
|
|
54
|
+
request<{ user: any; access_token: string }>('POST', '/auth/login', data),
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get current user
|
|
58
|
+
* @example const user = await api.auth.me()
|
|
59
|
+
*/
|
|
60
|
+
me: () => request<any>('GET', '/auth/me'),
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Users API
|
|
65
|
+
*/
|
|
66
|
+
users: {
|
|
67
|
+
/**
|
|
68
|
+
* Get current user profile
|
|
69
|
+
* @example const profile = await api.users.me()
|
|
70
|
+
*/
|
|
71
|
+
me: () => request<any>('GET', '/users/me'),
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Todos API
|
|
76
|
+
*/
|
|
77
|
+
todos: {
|
|
78
|
+
/**
|
|
79
|
+
* Get all todos
|
|
80
|
+
* @example const todos = await api.todos.findAll()
|
|
81
|
+
*/
|
|
82
|
+
findAll: () => request<any[]>('GET', '/todos'),
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get single todo
|
|
86
|
+
* @example const todo = await api.todos.findOne('123')
|
|
87
|
+
*/
|
|
88
|
+
findOne: (id: string) => request<any>('GET', `/todos/${id}`),
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Create new todo
|
|
92
|
+
* @example await api.todos.create({ title: 'Task', description: 'Do it' })
|
|
93
|
+
*/
|
|
94
|
+
create: (data: { title: string; description?: string }) =>
|
|
95
|
+
request<any>('POST', '/todos', data),
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Update todo
|
|
99
|
+
* @example await api.todos.update('123', { completed: true })
|
|
100
|
+
*/
|
|
101
|
+
update: (id: string, data: { title?: string; description?: string; completed?: boolean }) =>
|
|
102
|
+
request<any>('PUT', `/todos/${id}`, data),
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Delete todo
|
|
106
|
+
* @example await api.todos.delete('123')
|
|
107
|
+
*/
|
|
108
|
+
delete: (id: string) => request<void>('DELETE', `/todos/${id}`),
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Export type-safe API
|
|
114
|
+
*/
|
|
115
|
+
export type API = typeof api;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
# WEXTS Application
|
|
5
|
+
app:
|
|
6
|
+
build:
|
|
7
|
+
context: .
|
|
8
|
+
dockerfile: Dockerfile
|
|
9
|
+
ports:
|
|
10
|
+
- "3000:3000"
|
|
11
|
+
environment:
|
|
12
|
+
- NODE_ENV=production
|
|
13
|
+
- DATABASE_URL=postgresql://wexts:wexts_password@postgres:5432/wexts_db
|
|
14
|
+
- JWT_SECRET=your-super-secret-jwt-key-change-this
|
|
15
|
+
- PORT=3000
|
|
16
|
+
depends_on:
|
|
17
|
+
- postgres
|
|
18
|
+
restart: unless-stopped
|
|
19
|
+
|
|
20
|
+
# PostgreSQL Database
|
|
21
|
+
postgres:
|
|
22
|
+
image: postgres:16-alpine
|
|
23
|
+
ports:
|
|
24
|
+
- "5432:5432"
|
|
25
|
+
environment:
|
|
26
|
+
- POSTGRES_USER=wexts
|
|
27
|
+
- POSTGRES_PASSWORD=wexts_password
|
|
28
|
+
- POSTGRES_DB=wexts_db
|
|
29
|
+
volumes:
|
|
30
|
+
- postgres_data:/var/lib/postgresql/data
|
|
31
|
+
restart: unless-stopped
|
|
32
|
+
|
|
33
|
+
volumes:
|
|
34
|
+
postgres_data:
|