@llmnative/react 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +387 -364
  2. package/bin/cli.js +117 -98
  3. package/dist/index.css +1 -1
  4. package/dist/index.js +21 -823
  5. package/dist/index.mjs +14246 -9296
  6. package/dist/style.css +1 -0
  7. package/dist/types/src/App.d.ts +72 -14
  8. package/dist/types/src/Config.d.ts +28 -5
  9. package/dist/types/src/Global.d.ts +2 -2
  10. package/dist/types/src/I18n.d.ts +185 -0
  11. package/dist/types/src/Theme.d.ts +153 -81
  12. package/dist/types/src/auth.d.ts +13 -1
  13. package/dist/types/src/components/Component.d.ts +13 -27
  14. package/dist/types/src/components/ErrorBoundary.d.ts +30 -0
  15. package/dist/types/src/components/blocks/Brand.d.ts +4 -4
  16. package/dist/types/src/components/blocks/Breadcrumbs.d.ts +50 -4
  17. package/dist/types/src/components/blocks/Carousel.d.ts +5 -5
  18. package/dist/types/src/components/blocks/Dropdown.d.ts +26 -20
  19. package/dist/types/src/components/blocks/ListCard.d.ts +31 -0
  20. package/dist/types/src/components/blocks/Menu.d.ts +13 -16
  21. package/dist/types/src/components/blocks/Notifications.d.ts +8 -3
  22. package/dist/types/src/components/blocks/ProviderSwitcher.d.ts +63 -0
  23. package/dist/types/src/components/blocks/Search.d.ts +2 -2
  24. package/dist/types/src/components/blocks/SideNav.d.ts +45 -0
  25. package/dist/types/src/components/blocks/ThemeSwitcher.d.ts +24 -0
  26. package/dist/types/src/components/blocks/Toolbar.d.ts +19 -0
  27. package/dist/types/src/components/index.d.ts +27 -6
  28. package/dist/types/src/components/types.d.ts +10 -3
  29. package/dist/types/src/components/ui/Alert.d.ts +16 -4
  30. package/dist/types/src/components/ui/Badge.d.ts +6 -5
  31. package/dist/types/src/components/ui/Buttons.d.ts +13 -12
  32. package/dist/types/src/components/ui/Card.d.ts +8 -7
  33. package/dist/types/src/components/ui/Code.d.ts +1 -1
  34. package/dist/types/src/components/ui/Gallery.d.ts +13 -14
  35. package/dist/types/src/components/ui/GridSystem.d.ts +1 -1
  36. package/dist/types/src/components/ui/Icon.d.ts +3 -1
  37. package/dist/types/src/components/ui/Image.d.ts +1 -1
  38. package/dist/types/src/components/ui/ImageAvatar.d.ts +1 -1
  39. package/dist/types/src/components/ui/Loader.d.ts +1 -1
  40. package/dist/types/src/components/ui/LocaleSwitcher.d.ts +14 -0
  41. package/dist/types/src/components/ui/Modal.d.ts +42 -15
  42. package/dist/types/src/components/ui/Pagination.d.ts +14 -3
  43. package/dist/types/src/components/ui/Percentage.d.ts +5 -5
  44. package/dist/types/src/components/ui/Repeat.d.ts +15 -6
  45. package/dist/types/src/components/ui/Tab.d.ts +29 -2
  46. package/dist/types/src/components/ui/TabDynamic.d.ts +4 -3
  47. package/dist/types/src/components/ui/Table.d.ts +15 -14
  48. package/dist/types/src/components/ui/fields/CodeEditor.d.ts +43 -0
  49. package/dist/types/src/components/ui/fields/ContextMenu.d.ts +38 -0
  50. package/dist/types/src/components/ui/fields/Crop.d.ts +4 -2
  51. package/dist/types/src/components/ui/fields/ImageField.d.ts +31 -0
  52. package/dist/types/src/components/ui/fields/Input.d.ts +43 -14
  53. package/dist/types/src/components/ui/fields/Prompt.d.ts +2 -31
  54. package/dist/types/src/components/ui/fields/RichText.d.ts +86 -0
  55. package/dist/types/src/components/ui/fields/Select.d.ts +13 -10
  56. package/dist/types/src/components/ui/fields/Upload.d.ts +37 -2
  57. package/dist/types/src/components/ui/fields/UploadCSV.d.ts +2 -0
  58. package/dist/types/src/components/ui/useRecordSelection.d.ts +1 -2
  59. package/dist/types/src/components/widgets/Form.d.ts +80 -43
  60. package/dist/types/src/components/widgets/ImageEditor.d.ts +4 -5
  61. package/dist/types/src/components/widgets/MarkdownReader.d.ts +4 -4
  62. package/dist/types/src/components/widgets/Prompt.d.ts +71 -23
  63. package/dist/types/src/components/widgets/TabDynamic.d.ts +4 -3
  64. package/dist/types/src/components/widgets/form-controller.d.ts +42 -0
  65. package/dist/types/src/components/widgets/grid-core/GridCore.d.ts +1 -1
  66. package/dist/types/src/components/widgets/grid-core/GridDB.d.ts +1 -1
  67. package/dist/types/src/components/widgets/grid-core/GridGalleryView.d.ts +1 -1
  68. package/dist/types/src/components/widgets/grid-core/GridTableView.d.ts +1 -1
  69. package/dist/types/src/components/widgets/grid-core/types.d.ts +77 -27
  70. package/dist/types/src/components/widgets/grid-core/useGridActions.d.ts +4 -8
  71. package/dist/types/src/components/widgets/grid-core/useGridColumns.d.ts +2 -2
  72. package/dist/types/src/components/widgets/grid-core/useGridDBRecords.d.ts +1 -2
  73. package/dist/types/src/components/widgets/grid-core/useGridPreparedRecords.d.ts +2 -2
  74. package/dist/types/src/components/widgets/grid-core/useGridSelection.d.ts +4 -5
  75. package/dist/types/src/components/widgets/grid-core/utils.d.ts +11 -3
  76. package/dist/types/src/conf/Prompt.d.ts +1 -1
  77. package/dist/types/src/conf/i18n/ar.d.ts +2 -0
  78. package/dist/types/src/conf/i18n/de.d.ts +2 -0
  79. package/dist/types/src/conf/i18n/en.d.ts +2 -0
  80. package/dist/types/src/conf/i18n/index.d.ts +6 -0
  81. package/dist/types/src/conf/i18n/it.d.ts +2 -0
  82. package/dist/types/src/conf/i18n/ru.d.ts +2 -0
  83. package/dist/types/src/conf/i18n/zh.d.ts +2 -0
  84. package/dist/types/src/constant.d.ts +1 -1
  85. package/dist/types/src/index.d.ts +38 -13
  86. package/dist/types/src/libs/converter.d.ts +4 -4
  87. package/dist/types/src/libs/editorHeight.d.ts +12 -0
  88. package/dist/types/src/libs/fetch.d.ts +4 -4
  89. package/dist/types/src/libs/imageBuilder.d.ts +63 -14
  90. package/dist/types/src/libs/imageVariants.d.ts +35 -0
  91. package/dist/types/src/libs/index.d.ts +1 -4
  92. package/dist/types/src/libs/promptUtils.d.ts +24 -0
  93. package/dist/types/src/libs/sanitizer.d.ts +2 -2
  94. package/dist/types/src/libs/utils.d.ts +9 -10
  95. package/dist/types/src/pages/index.d.ts +0 -2
  96. package/dist/types/src/providers/ProviderConfiguration.d.ts +6 -0
  97. package/dist/types/src/providers/ProviderRegistryContext.d.ts +51 -0
  98. package/dist/types/src/providers/ProviderSession.d.ts +68 -0
  99. package/dist/types/src/providers/ai/AIProvider.d.ts +57 -0
  100. package/dist/types/src/providers/ai/AIProviderContext.d.ts +13 -0
  101. package/dist/types/src/providers/ai/anthropic.d.ts +2 -0
  102. package/dist/types/src/providers/ai/deepseek.d.ts +1 -0
  103. package/dist/types/src/providers/ai/gemini.d.ts +2 -0
  104. package/dist/types/src/providers/ai/glm.d.ts +1 -0
  105. package/dist/types/src/providers/ai/index.d.ts +9 -134
  106. package/dist/types/src/providers/ai/mistral.d.ts +1 -0
  107. package/dist/types/src/providers/ai/openai.d.ts +1 -0
  108. package/dist/types/src/providers/ai/openaiCompatible.d.ts +17 -0
  109. package/dist/types/src/providers/ai/opencode.d.ts +2 -0
  110. package/dist/types/src/providers/ai/openrouter.d.ts +1 -0
  111. package/dist/types/src/providers/ai/shared.d.ts +46 -0
  112. package/dist/types/src/providers/api/ApiProvider.d.ts +43 -0
  113. package/dist/types/src/providers/api/direct.d.ts +14 -0
  114. package/dist/types/src/providers/api/firebase.d.ts +14 -0
  115. package/dist/types/src/providers/api/mock.d.ts +7 -0
  116. package/dist/types/src/providers/api/supabase.d.ts +14 -0
  117. package/dist/types/src/providers/auth/AuthProvider.d.ts +2 -2
  118. package/dist/types/src/providers/auth/firebase/FirebaseAuthProvider.d.ts +26 -0
  119. package/dist/types/src/providers/auth/google/GoogleAuth.d.ts +2 -2
  120. package/dist/types/src/providers/auth/supabase/SupabaseAuthProvider.d.ts +48 -0
  121. package/dist/types/src/providers/credentials/CredentialsProvider.d.ts +3 -0
  122. package/dist/types/src/providers/credentials/CredentialsProviderContext.d.ts +12 -0
  123. package/dist/types/src/providers/credentials/google/GoogleServiceAccountProvider.d.ts +8 -0
  124. package/dist/types/src/providers/data/DataProvider.d.ts +27 -1
  125. package/dist/types/src/providers/data/firebase.d.ts +5 -4
  126. package/dist/types/src/providers/data/firestore.d.ts +38 -0
  127. package/dist/types/src/providers/data/mock.d.ts +8 -1
  128. package/dist/types/src/providers/data/supabase.d.ts +36 -10
  129. package/dist/types/src/providers/firebase-init.d.ts +15 -4
  130. package/dist/types/src/providers/icon/PhosphorIconProvider.d.ts +1 -1
  131. package/dist/types/src/providers/manifest.d.ts +20 -14
  132. package/dist/types/src/providers/proxy/index.d.ts +7 -0
  133. package/dist/types/src/providers/proxy/vite.d.ts +3 -0
  134. package/dist/types/src/providers/scrape/index.d.ts +2 -1
  135. package/dist/types/src/providers/seo/google/keyword.d.ts +6 -1
  136. package/dist/types/src/providers/storage/StorageProvider.d.ts +155 -4
  137. package/dist/types/src/providers/storage/dropbox.d.ts +3 -3
  138. package/dist/types/src/providers/storage/firebase.d.ts +38 -7
  139. package/dist/types/src/providers/storage/supabase.d.ts +47 -7
  140. package/dist/types/src/providers/supabase-init.d.ts +35 -0
  141. package/dist/types/src/types/FormFields.d.ts +4 -2
  142. package/dist/types/src/types/FormSchema.d.ts +29 -0
  143. package/dist/types/themes/tokyo-night-light.d.ts +3 -0
  144. package/dist/types/themes/tokyo-night.d.ts +3 -0
  145. package/dist/types/themes/vscode-dark-plus.d.ts +3 -0
  146. package/dist/types/themes/vscode-light-plus.d.ts +3 -0
  147. package/dist/vite.js +1 -0
  148. package/dist/vite.mjs +54 -0
  149. package/package.json +45 -14
  150. package/scripts/cli/proxy-templates/cloudflare.ts +34 -0
  151. package/scripts/cli/proxy-templates/express.ts +77 -0
  152. package/scripts/cli/proxy-templates/nextjs-app.ts +42 -0
  153. package/scripts/cli/proxy-templates/nextjs-pages.ts +41 -0
  154. package/scripts/cli/proxy-templates/scaffold.json +33 -0
  155. package/scripts/cli/setup-devtools.js +155 -85
  156. package/scripts/cli/setup-project.js +618 -484
  157. package/themes/cyber.ts +469 -405
  158. package/themes/default.ts +469 -405
  159. package/themes/flat.ts +469 -405
  160. package/themes/tokyo-night-light.ts +38 -0
  161. package/themes/tokyo-night.ts +38 -0
  162. package/themes/vscode-dark-plus.ts +29 -0
  163. package/themes/vscode-light-plus.ts +29 -0
  164. package/dist/index.js.map +0 -1
  165. package/dist/index.mjs.map +0 -1
  166. package/dist/types/src/components/FormEnhancer.d.ts +0 -26
  167. package/dist/types/src/components/Template.d.ts +0 -18
  168. package/dist/types/src/components/ui/fields/AssistantAI.d.ts +0 -23
  169. package/dist/types/src/components/ui/fields/Command.d.ts +0 -15
  170. package/dist/types/src/components/ui/fields/ImageUrl.d.ts +0 -7
  171. package/dist/types/src/libs/cache.d.ts +0 -9
  172. package/dist/types/src/libs/database.d.ts +0 -3
  173. package/dist/types/src/libs/log.d.ts +0 -2
  174. package/dist/types/src/libs/seo.d.ts +0 -8
  175. package/dist/types/src/libs/storage.d.ts +0 -2
  176. package/dist/types/src/pages/Blog.d.ts +0 -3
  177. package/dist/types/src/pages/BlogPost.d.ts +0 -16
  178. package/dist/types/src/pages/Helper.d.ts +0 -8
  179. package/dist/types/src/providers/auth/google/apis/auth.d.ts +0 -4
  180. package/dist/types/src/types/Block.d.ts +0 -4
  181. package/dist/types/src/types/Section.d.ts +0 -4
package/dist/vite.mjs ADDED
@@ -0,0 +1,54 @@
1
+ const u = "x-llmnative-proxy", l = "/api/proxy", y = async (t) => {
2
+ const e = [];
3
+ for await (const r of t)
4
+ e.push(Buffer.isBuffer(r) ? r : Buffer.from(r));
5
+ return e.length === 0 ? void 0 : Buffer.concat(e);
6
+ }, p = async (t, e, r) => {
7
+ const a = new URL(t.url || "/", "http://localhost").searchParams.get("url");
8
+ if (t.headers[u] !== "1") {
9
+ e.statusCode = 403, e.setHeader("Content-Type", "application/json"), e.end(JSON.stringify({ error: "Proxy access denied." }));
10
+ return;
11
+ }
12
+ if (!a) {
13
+ e.statusCode = 400, e.setHeader("Content-Type", "application/json"), e.end(JSON.stringify({ error: 'Missing "url" query parameter.' }));
14
+ return;
15
+ }
16
+ try {
17
+ const o = new URL(a);
18
+ if (o.pathname.startsWith(r) && ["localhost", "127.0.0.1"].includes(o.hostname)) {
19
+ e.statusCode = 508, e.setHeader("Content-Type", "application/json"), e.end(JSON.stringify({ error: "Recursive proxy target detected.", target: a }));
20
+ return;
21
+ }
22
+ const d = await y(t), f = new Headers(), h = /* @__PURE__ */ new Set([u, "host", "origin", "referer", "content-length", "transfer-encoding", "connection"]);
23
+ Object.entries(t.headers).forEach(([i, n]) => {
24
+ !n || h.has(i.toLowerCase()) || f.set(i, Array.isArray(n) ? n.join(", ") : n);
25
+ });
26
+ const c = await fetch(a, {
27
+ method: t.method || "GET",
28
+ headers: f,
29
+ body: d && !["GET", "HEAD"].includes((t.method || "GET").toUpperCase()) ? d : void 0,
30
+ redirect: "follow"
31
+ });
32
+ e.statusCode = c.status, c.headers.forEach((i, n) => {
33
+ n.toLowerCase() !== "content-encoding" && e.setHeader(n, i);
34
+ }), e.end(Buffer.from(await c.arrayBuffer()));
35
+ } catch (o) {
36
+ e.statusCode = 502, e.setHeader("Content-Type", "application/json"), e.end(JSON.stringify({ error: o instanceof Error ? o.message : "Proxy request failed." }));
37
+ }
38
+ }, g = (t = l) => ({
39
+ name: "llmnative-dev-proxy",
40
+ configureServer(e) {
41
+ e.middlewares.use(t, (r, s) => {
42
+ p(r, s, t);
43
+ });
44
+ },
45
+ configurePreviewServer(e) {
46
+ e.middlewares.use(t, (r, s) => {
47
+ p(r, s, t);
48
+ });
49
+ }
50
+ });
51
+ export {
52
+ l as PROXY_PATH,
53
+ g as createProxyPlugin
54
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmnative/react",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "LLM-native React framework for deterministic UI generation. Data-driven by default, schema-driven optional. Ultra-low token consumption — built for AI agents, not just humans.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -8,26 +8,30 @@
8
8
  "style": "dist/index.css",
9
9
  "exports": {
10
10
  ".": {
11
- "types": "./dist/types/index.d.ts",
11
+ "types": "./dist/types/src/index.d.ts",
12
12
  "import": "./dist/index.mjs",
13
13
  "require": "./dist/index.js"
14
14
  },
15
+ "./vite": "./dist/vite.mjs",
15
16
  "./dist/index.css": "./dist/index.css"
16
17
  },
17
- "scripts": {
18
- "prebuild": "node scripts/dev/build-local.js --restore-only",
19
- "build": "vite build && tsc --emitDeclarationOnly",
20
- "build:dev": "vite build --mode development && tsc --emitDeclarationOnly",
21
- "watch:dev": "vite build --watch",
18
+ "scripts": {
19
+ "prebuild": "node scripts/dev/build-local.js --restore-only",
20
+ "themes:compile:vscode": "node scripts/themes/compile-vscode-themes.js",
21
+ "build": "npm run themes:compile:vscode && vite build && tsc --emitDeclarationOnly",
22
+ "build:dev": "npm run themes:compile:vscode && vite build --mode development && tsc --emitDeclarationOnly",
23
+ "watch:dev": "vite build --watch",
22
24
  "build:local": "node scripts/dev/build-local.js",
23
25
  "prepublishOnly": "npm run build",
24
26
  "updateReadme": "node scripts/dev/updateReadme.js",
25
27
  "test": "vitest run",
28
+ "test:e2e:proxy-scaffold": "node scripts/e2e/test-proxy-scaffold.js",
29
+ "test:e2e:proxy-runtime": "vitest run tests/e2e/proxy-runtime.test.ts --environment node",
26
30
  "test:watch": "vitest",
27
31
  "test:coverage": "vitest run --coverage"
28
32
  },
29
33
  "bin": {
30
- "llmnative": "./bin/cli.js"
34
+ "llmnative": "bin/cli.js"
31
35
  },
32
36
  "files": [
33
37
  "dist",
@@ -65,11 +69,12 @@
65
69
  "homepage": "https://github.com/sherpadvisorylab/llmnative-react#readme",
66
70
  "peerDependencies": {
67
71
  "@phosphor-icons/react": "^2.0.0",
72
+ "@supabase/supabase-js": "^2.107.0",
68
73
  "firebase": "^10.14.0",
69
74
  "lucide-react": "^0.400.0",
70
- "react": "^18.2.0",
71
- "react-dom": "^18.2.0",
72
- "react-router-dom": "^6.22.0"
75
+ "react": "^19.0.0",
76
+ "react-dom": "^19.0.0",
77
+ "react-router-dom": "^6.30.0"
73
78
  },
74
79
  "peerDependenciesMeta": {
75
80
  "@phosphor-icons/react": {
@@ -87,6 +92,7 @@
87
92
  "@babel/preset-react": "^7.25.7",
88
93
  "@babel/preset-typescript": "^7.25.7",
89
94
  "@phosphor-icons/react": "^2.1.10",
95
+ "@supabase/supabase-js": "^2.107.0",
90
96
  "@tailwindcss/postcss": "^4.2.4",
91
97
  "@testing-library/jest-dom": "^6.9.1",
92
98
  "@testing-library/react": "^16.3.2",
@@ -107,14 +113,36 @@
107
113
  "react-router-dom": "^6.30.3",
108
114
  "tailwindcss": "^4.2.4",
109
115
  "typescript": "^5.6.2",
110
- "vite": "^5.4.21",
111
- "vitest": "2.1"
116
+ "vite": "^5.4.21",
117
+ "vitest": "2.1"
112
118
  },
113
119
  "dependencies": {
120
+ "@codemirror/autocomplete": "^6.20.3",
121
+ "@codemirror/commands": "^6.10.3",
122
+ "@codemirror/lang-css": "^6.3.1",
123
+ "@codemirror/lang-html": "^6.4.11",
124
+ "@codemirror/lang-javascript": "^6.2.5",
125
+ "@codemirror/lang-json": "^6.0.2",
126
+ "@codemirror/lang-liquid": "^6.3.2",
127
+ "@codemirror/language": "^6.12.3",
128
+ "@codemirror/lint": "^6.9.7",
129
+ "@codemirror/search": "^6.7.1",
130
+ "@codemirror/state": "^6.6.0",
131
+ "@codemirror/view": "^6.43.1",
132
+ "@tiptap/extension-character-count": "^3.26.1",
133
+ "@tiptap/extension-image": "^3.26.1",
134
+ "@tiptap/extension-link": "^3.26.1",
135
+ "@tiptap/extension-placeholder": "^3.26.1",
136
+ "@tiptap/extension-table": "^3.26.1",
137
+ "@tiptap/extension-underline": "^3.26.1",
138
+ "@tiptap/react": "^3.26.1",
139
+ "@tiptap/starter-kit": "^3.26.1",
114
140
  "buffer": "^6.0.3",
115
141
  "clsx": "^2.1.1",
142
+ "codemirror": "^6.0.2",
143
+ "liquidjs": "^10.27.0",
116
144
  "papaparse": "^5.5.3",
117
- "prismjs": "^ 1.30.0",
145
+ "prismjs": "^1.30.0",
118
146
  "react-markdown": "^10.1.0",
119
147
  "rehype-autolink-headings": "^7.1.0",
120
148
  "rehype-sanitize": "^6.0.0",
@@ -122,5 +150,8 @@
122
150
  "remark-gfm": "^4.0.1",
123
151
  "tailwind-merge": "^3.5.0",
124
152
  "tui-image-editor": "^3.15.3"
153
+ },
154
+ "publishConfig": {
155
+ "access": "public"
125
156
  }
126
157
  }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * LLMNative proxy - Cloudflare Worker.
3
+ *
4
+ * Deploy as a Worker or use as a Pages Function at:
5
+ * functions/api/proxy.ts
6
+ */
7
+ export default {
8
+ async fetch(request: Request): Promise<Response> {
9
+ const { searchParams } = new URL(request.url);
10
+ const targetUrl = searchParams.get('url');
11
+
12
+ if (!targetUrl) {
13
+ return Response.json({ error: 'Missing url query parameter' }, { status: 400 });
14
+ }
15
+
16
+ const headers = new Headers(request.headers);
17
+ headers.delete('host');
18
+ headers.delete('x-llmnative-proxy');
19
+
20
+ const hasBody = !['GET', 'HEAD'].includes(request.method.toUpperCase());
21
+
22
+ const upstream = await fetch(targetUrl, {
23
+ method: request.method,
24
+ headers,
25
+ body: hasBody ? request.body : undefined,
26
+ ...(hasBody ? { duplex: 'half' } : {}),
27
+ } as RequestInit);
28
+
29
+ return new Response(upstream.body, {
30
+ status: upstream.status,
31
+ headers: upstream.headers,
32
+ });
33
+ },
34
+ };
@@ -0,0 +1,77 @@
1
+ // @ts-nocheck - scaffold template, copied to user project by CLI
2
+ import type { IncomingMessage, ServerResponse } from 'node:http';
3
+
4
+ const PROXY_CALLER_HEADER = 'x-llmnative-proxy';
5
+ export const PROXY_PATH = '/api/proxy';
6
+
7
+ type AppLike = {
8
+ all: (route: string, handler: (req: IncomingMessage, res: ServerResponse) => void) => unknown;
9
+ };
10
+
11
+ const readRequestBody = async (req: IncomingMessage): Promise<Buffer | undefined> => {
12
+ const chunks: Buffer[] = [];
13
+ for await (const chunk of req) {
14
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
15
+ }
16
+ return chunks.length === 0 ? undefined : Buffer.concat(chunks);
17
+ };
18
+
19
+ export const proxyHandler = async (req: IncomingMessage, res: ServerResponse) => {
20
+ const requestUrl = new URL(req.url || '/', 'http://localhost');
21
+ const target = requestUrl.searchParams.get('url');
22
+
23
+ if (req.headers[PROXY_CALLER_HEADER] !== '1') {
24
+ res.statusCode = 403;
25
+ res.setHeader('Content-Type', 'application/json');
26
+ res.end(JSON.stringify({ error: 'Proxy access denied.' }));
27
+ return;
28
+ }
29
+
30
+ if (!target) {
31
+ res.statusCode = 400;
32
+ res.setHeader('Content-Type', 'application/json');
33
+ res.end(JSON.stringify({ error: 'Missing "url" query parameter.' }));
34
+ return;
35
+ }
36
+
37
+ try {
38
+ const targetUrl = new URL(target);
39
+ if (targetUrl.pathname.startsWith(PROXY_PATH) && ['localhost', '127.0.0.1'].includes(targetUrl.hostname)) {
40
+ res.statusCode = 508;
41
+ res.setHeader('Content-Type', 'application/json');
42
+ res.end(JSON.stringify({ error: 'Recursive proxy target detected.', target }));
43
+ return;
44
+ }
45
+
46
+ const body = await readRequestBody(req);
47
+ const headers = new Headers();
48
+ const skipHeaders = new Set([PROXY_CALLER_HEADER, 'host', 'origin', 'referer', 'content-length', 'transfer-encoding', 'connection']);
49
+
50
+ Object.entries(req.headers).forEach(([key, value]) => {
51
+ if (!value || skipHeaders.has(key.toLowerCase())) return;
52
+ headers.set(key, Array.isArray(value) ? value.join(', ') : value);
53
+ });
54
+
55
+ const upstream = await fetch(target, {
56
+ method: req.method || 'GET',
57
+ headers,
58
+ body: body && !['GET', 'HEAD'].includes((req.method || 'GET').toUpperCase()) ? body : undefined,
59
+ redirect: 'follow',
60
+ });
61
+
62
+ res.statusCode = upstream.status;
63
+ upstream.headers.forEach((value, key) => {
64
+ if (key.toLowerCase() === 'content-encoding') return;
65
+ res.setHeader(key, value);
66
+ });
67
+ res.end(Buffer.from(await upstream.arrayBuffer()));
68
+ } catch (error) {
69
+ res.statusCode = 502;
70
+ res.setHeader('Content-Type', 'application/json');
71
+ res.end(JSON.stringify({ error: error instanceof Error ? error.message : 'Proxy request failed.' }));
72
+ }
73
+ };
74
+
75
+ export const registerProxy = (app: AppLike, route = PROXY_PATH) => {
76
+ app.all(route, (req, res) => { void proxyHandler(req, res); });
77
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * LLMNative proxy - Next.js App Router.
3
+ *
4
+ * Place this file at:
5
+ * app/api/proxy/route.ts
6
+ */
7
+
8
+ const PASSTHROUGH_METHODS = new Set(['GET', 'HEAD']);
9
+
10
+ async function handler(request: Request): Promise<Response> {
11
+ const { searchParams } = new URL(request.url);
12
+ const targetUrl = searchParams.get('url');
13
+
14
+ if (!targetUrl) {
15
+ return Response.json({ error: 'Missing url query parameter' }, { status: 400 });
16
+ }
17
+
18
+ const headers = new Headers(request.headers);
19
+ headers.delete('host');
20
+ headers.delete('x-llmnative-proxy');
21
+
22
+ const hasBody = !PASSTHROUGH_METHODS.has(request.method.toUpperCase());
23
+
24
+ const upstream = await fetch(targetUrl, {
25
+ method: request.method,
26
+ headers,
27
+ body: hasBody ? request.body : undefined,
28
+ ...(hasBody ? { duplex: 'half' } : {}),
29
+ } as RequestInit);
30
+
31
+ return new Response(upstream.body, {
32
+ status: upstream.status,
33
+ headers: upstream.headers,
34
+ });
35
+ }
36
+
37
+ export const GET = handler;
38
+ export const POST = handler;
39
+ export const PUT = handler;
40
+ export const PATCH = handler;
41
+ export const DELETE = handler;
42
+ export const OPTIONS = handler;
@@ -0,0 +1,41 @@
1
+ // @ts-nocheck - scaffold template, copied to user project by CLI
2
+ import type { NextApiRequest, NextApiResponse } from 'next';
3
+
4
+ /**
5
+ * LLMNative proxy - Next.js Pages Router.
6
+ *
7
+ * Place this file at:
8
+ * pages/api/proxy.ts
9
+ */
10
+ export default async function handler(req: NextApiRequest, res: NextApiResponse) {
11
+ const targetUrl = req.query.url as string | undefined;
12
+
13
+ if (!targetUrl) {
14
+ res.status(400).json({ error: 'Missing url query parameter' });
15
+ return;
16
+ }
17
+
18
+ const headers: Record<string, string> = {};
19
+ for (const [key, value] of Object.entries(req.headers)) {
20
+ if (key === 'host' || key === 'x-llmnative-proxy') continue;
21
+ if (typeof value === 'string') headers[key] = value;
22
+ else if (Array.isArray(value)) headers[key] = value.join(', ');
23
+ }
24
+
25
+ const hasBody = !['GET', 'HEAD'].includes(req.method?.toUpperCase() ?? 'GET');
26
+
27
+ const upstream = await fetch(targetUrl, {
28
+ method: req.method,
29
+ headers,
30
+ body: hasBody ? JSON.stringify(req.body) : undefined,
31
+ });
32
+
33
+ res.status(upstream.status);
34
+ upstream.headers.forEach((value, key) => res.setHeader(key, value));
35
+ const buffer = await upstream.arrayBuffer();
36
+ res.end(Buffer.from(buffer));
37
+ }
38
+
39
+ export const config = {
40
+ api: { bodyParser: true },
41
+ };
@@ -0,0 +1,33 @@
1
+ {
2
+ "vite": {
3
+ "label": "Vite dev server",
4
+ "packageExport": "@llmnative/react/vite",
5
+ "exportName": "createProxyPlugin",
6
+ "outputPath": "dev/proxy.ts",
7
+ "hint": "Import createProxyPlugin() and add it to plugins[] in vite.config.ts"
8
+ },
9
+ "express": {
10
+ "label": "Express",
11
+ "templateFile": "express.ts",
12
+ "outputPath": "server/proxy.ts",
13
+ "hint": "Import registerProxy() and mount it with app.use() in your server entry"
14
+ },
15
+ "nextjs-app": {
16
+ "label": "Next.js App Router",
17
+ "templateFile": "nextjs-app.ts",
18
+ "outputPath": "app/api/proxy/route.ts",
19
+ "hint": "File placed at app/api/proxy/route.ts - no extra setup needed"
20
+ },
21
+ "nextjs-pages": {
22
+ "label": "Next.js Pages Router",
23
+ "templateFile": "nextjs-pages.ts",
24
+ "outputPath": "pages/api/proxy.ts",
25
+ "hint": "File placed at pages/api/proxy.ts - no extra setup needed"
26
+ },
27
+ "cloudflare": {
28
+ "label": "Cloudflare Worker / Pages Function",
29
+ "templateFile": "cloudflare.ts",
30
+ "outputPath": "functions/api/proxy.ts",
31
+ "hint": "File placed at functions/api/proxy.ts for Cloudflare Pages, or deploy as a standalone Worker"
32
+ }
33
+ }
@@ -1,85 +1,155 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const root = process.cwd();
5
-
6
- function ensureFile(filePath, content) {
7
- if (!fs.existsSync(filePath)) {
8
- fs.writeFileSync(filePath, content.trimStart());
9
- console.log(`Created file: ${filePath}`);
10
- }
11
- }
12
-
13
- function setupDevTools() {
14
- console.log('Setting up Vite + React + TypeScript');
15
-
16
- ensureFile(path.join(root, 'tsconfig.json'), `
17
- {
18
- "compilerOptions": {
19
- "target": "ES2021",
20
- "useDefineForClassFields": true,
21
- "lib": ["DOM", "DOM.Iterable", "ES2021"],
22
- "allowJs": false,
23
- "skipLibCheck": true,
24
- "esModuleInterop": true,
25
- "allowSyntheticDefaultImports": true,
26
- "strict": true,
27
- "forceConsistentCasingInFileNames": true,
28
- "module": "ESNext",
29
- "moduleResolution": "Bundler",
30
- "resolveJsonModule": true,
31
- "isolatedModules": true,
32
- "noEmit": true,
33
- "jsx": "react-jsx"
34
- },
35
- "include": ["src"],
36
- "references": [{ "path": "./tsconfig.node.json" }]
37
- }
38
- `);
39
-
40
- ensureFile(path.join(root, 'tsconfig.node.json'), `
41
- {
42
- "compilerOptions": {
43
- "composite": true,
44
- "skipLibCheck": true,
45
- "module": "ESNext",
46
- "moduleResolution": "Bundler",
47
- "allowSyntheticDefaultImports": true,
48
- "strict": true
49
- },
50
- "include": ["vite.config.ts"]
51
- }
52
- `);
53
-
54
- ensureFile(path.join(root, 'vite.config.ts'), `
55
- import { defineConfig } from 'vite';
56
- import react from '@vitejs/plugin-react';
57
-
58
- export default defineConfig(({ mode }) => {
59
- return {
60
- plugins: [react()],
61
- resolve: {
62
- dedupe: ['react', 'react-dom', 'react-router-dom'],
63
- },
64
- build: {
65
- cssMinify: false,
66
- chunkSizeWarningLimit: 10000,
67
- },
68
- define: {
69
- 'process.env.NODE_ENV': JSON.stringify(mode === 'production' ? 'production' : 'development'),
70
- },
71
- };
72
- });
73
- `);
74
-
75
- ensureFile(path.join(root, 'postcss.config.js'), `
76
- export default {
77
- plugins: {
78
- '@tailwindcss/postcss': {},
79
- autoprefixer: {},
80
- },
81
- };
82
- `);
83
- }
84
-
85
- module.exports = { setupDevTools };
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const root = process.cwd();
5
+ // Templates and the vite/packageExport split live in scripts/cli/proxy-templates/scaffold.json —
6
+ // the same JSON the framework's src/providers/proxy/scaffold.ts imports, so there's one shared
7
+ // source instead of two hand-maintained maps that can drift (as they had before this fix).
8
+ const PROXY_TEMPLATES_DIR = path.resolve(__dirname, 'proxy-templates');
9
+ const PROXY_PROVIDERS = require(path.join(PROXY_TEMPLATES_DIR, 'scaffold.json'));
10
+
11
+ function ensureFile(filePath, content) {
12
+ if (!fs.existsSync(filePath)) {
13
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
14
+ fs.writeFileSync(filePath, content.trimStart());
15
+ console.log(`Created file: ${filePath}`);
16
+ }
17
+ }
18
+
19
+ function ensureDir(dirPath) {
20
+ if (!fs.existsSync(dirPath)) {
21
+ fs.mkdirSync(dirPath, { recursive: true });
22
+ console.log(`Created directory: ${dirPath}`);
23
+ }
24
+ }
25
+
26
+ function copyProxyFile(proxyProvider) {
27
+ const descriptor = PROXY_PROVIDERS[proxyProvider];
28
+ if (!descriptor) return;
29
+
30
+ const dest = path.join(root, descriptor.outputPath);
31
+ ensureDir(path.dirname(dest));
32
+ if (fs.existsSync(dest)) return;
33
+
34
+ if (descriptor.packageExport) {
35
+ // vite only: re-export the framework's own build instead of copying a snapshot that
36
+ // could drift from the real implementation — see dist/vite.mjs / package.json exports.
37
+ fs.writeFileSync(dest, `export { ${descriptor.exportName} } from '${descriptor.packageExport}';\n`);
38
+ console.log(`Created file: ${dest}`);
39
+ return;
40
+ }
41
+
42
+ const src = path.join(PROXY_TEMPLATES_DIR, descriptor.templateFile);
43
+ if (!fs.existsSync(src)) {
44
+ console.warn(`Proxy template not found: ${src}`);
45
+ return;
46
+ }
47
+ fs.copyFileSync(src, dest);
48
+ console.log(`Created file: ${dest}`);
49
+ }
50
+
51
+ function createViteConfig(proxyProvider) {
52
+ const usesVitePlugin = proxyProvider === 'vite';
53
+ const importBlock = usesVitePlugin
54
+ ? "import { createProxyPlugin } from './dev/proxy';\n"
55
+ : '';
56
+ const pluginBlock = usesVitePlugin
57
+ ? " env.VITE_PROXY_ENABLED === 'true' ? createProxyPlugin() : null,\n"
58
+ : '';
59
+
60
+ return `
61
+ import { defineConfig, loadEnv } from 'vite';
62
+ import react from '@vitejs/plugin-react';
63
+ ${importBlock}
64
+ export default defineConfig(({ mode }) => {
65
+ const env = loadEnv(mode, process.cwd(), '');
66
+
67
+ return {
68
+ plugins: [
69
+ react(),
70
+ ${pluginBlock} ].filter(Boolean),
71
+ resolve: {
72
+ dedupe: ['react', 'react-dom', 'react-router-dom'],
73
+ },
74
+ build: {
75
+ cssMinify: false,
76
+ chunkSizeWarningLimit: 10000,
77
+ },
78
+ define: {
79
+ 'process.env.NODE_ENV': JSON.stringify(mode === 'production' ? 'production' : 'development'),
80
+ },
81
+ };
82
+ });
83
+ `;
84
+ }
85
+
86
+ function setupDevTools(options = {}) {
87
+ const proxyProvider = options.proxyProvider ?? 'none';
88
+
89
+ console.log('Setting up Vite + React + TypeScript');
90
+
91
+ ensureFile(path.join(root, 'tsconfig.json'), `
92
+ {
93
+ "compilerOptions": {
94
+ "target": "ES2021",
95
+ "useDefineForClassFields": true,
96
+ "lib": ["DOM", "DOM.Iterable", "ES2021"],
97
+ "allowJs": false,
98
+ "skipLibCheck": true,
99
+ "esModuleInterop": true,
100
+ "allowSyntheticDefaultImports": true,
101
+ "strict": true,
102
+ "forceConsistentCasingInFileNames": true,
103
+ "module": "ESNext",
104
+ "moduleResolution": "Bundler",
105
+ "resolveJsonModule": true,
106
+ "isolatedModules": true,
107
+ "noEmit": true,
108
+ "jsx": "react-jsx"
109
+ },
110
+ "include": ["src"],
111
+ "references": [{ "path": "./tsconfig.node.json" }]
112
+ }
113
+ `);
114
+
115
+ ensureFile(path.join(root, 'tsconfig.node.json'), `
116
+ {
117
+ "compilerOptions": {
118
+ "composite": true,
119
+ "skipLibCheck": true,
120
+ "module": "ESNext",
121
+ "moduleResolution": "Bundler",
122
+ "allowSyntheticDefaultImports": true,
123
+ "strict": true
124
+ },
125
+ "include": ["vite.config.ts", "dev/**/*.ts", "server/**/*.ts"]
126
+ }
127
+ `);
128
+
129
+ if (proxyProvider !== 'none') {
130
+ copyProxyFile(proxyProvider);
131
+ }
132
+
133
+ ensureFile(path.join(root, 'vite.config.ts'), createViteConfig(proxyProvider));
134
+
135
+ ensureFile(path.join(root, 'postcss.config.js'), `
136
+ export default {
137
+ plugins: {
138
+ '@tailwindcss/postcss': {},
139
+ autoprefixer: {},
140
+ },
141
+ };
142
+ `);
143
+
144
+ if (proxyProvider !== 'none' && PROXY_PROVIDERS[proxyProvider]) {
145
+ console.log(`\nProxy file created: ${PROXY_PROVIDERS[proxyProvider].outputPath}`);
146
+ if (proxyProvider === 'express') {
147
+ console.log(' → Add registerProxy(app) to your server entry file.');
148
+ }
149
+ if (proxyProvider === 'vite') {
150
+ console.log(' → createProxyPlugin() is already wired into vite.config.ts.');
151
+ }
152
+ }
153
+ }
154
+
155
+ module.exports = { setupDevTools, PROXY_PROVIDERS };