veryfront 0.1.403 → 0.1.405

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 (39) hide show
  1. package/esm/cli/commands/install/registry.d.ts.map +1 -1
  2. package/esm/cli/commands/install/registry.js +9 -3
  3. package/esm/cli/templates/manifest.d.ts +30 -0
  4. package/esm/cli/templates/manifest.js +30 -0
  5. package/esm/deno.js +1 -1
  6. package/esm/src/agent/hosted-child-status.d.ts +5 -0
  7. package/esm/src/agent/hosted-child-status.d.ts.map +1 -1
  8. package/esm/src/agent/hosted-child-status.js +14 -0
  9. package/esm/src/agent/index.d.ts +1 -1
  10. package/esm/src/agent/index.d.ts.map +1 -1
  11. package/esm/src/agent/index.js +1 -1
  12. package/esm/src/config/schemas/config.schema.d.ts +18 -0
  13. package/esm/src/config/schemas/config.schema.d.ts.map +1 -1
  14. package/esm/src/config/schemas/config.schema.js +16 -0
  15. package/esm/src/observability/file-log-subscriber.d.ts +32 -0
  16. package/esm/src/observability/file-log-subscriber.d.ts.map +1 -0
  17. package/esm/src/observability/file-log-subscriber.js +163 -0
  18. package/esm/src/observability/index.d.ts +1 -0
  19. package/esm/src/observability/index.d.ts.map +1 -1
  20. package/esm/src/observability/index.js +1 -0
  21. package/esm/src/server/bootstrap.d.ts.map +1 -1
  22. package/esm/src/server/bootstrap.js +149 -97
  23. package/esm/src/utils/version-constant.d.ts +1 -1
  24. package/esm/src/utils/version-constant.js +1 -1
  25. package/package.json +1 -1
  26. package/src/cli/commands/install/registry.ts +10 -3
  27. package/src/cli/templates/manifest.js +30 -0
  28. package/src/deno.js +1 -1
  29. package/src/deps/esm.sh/@types/react-dom@19.2.3/client.d.ts +1 -1
  30. package/src/deps/esm.sh/@types/{react@19.2.3 → react@19.2.14}/global.d.ts +1 -0
  31. package/src/deps/esm.sh/@types/{react@19.2.3 → react@19.2.14}/index.d.ts +93 -24
  32. package/src/deps/esm.sh/react-dom@19.2.4/client.d.ts +1 -1
  33. package/src/src/agent/hosted-child-status.ts +27 -0
  34. package/src/src/agent/index.ts +2 -0
  35. package/src/src/config/schemas/config.schema.ts +16 -0
  36. package/src/src/observability/file-log-subscriber.ts +187 -0
  37. package/src/src/observability/index.ts +7 -0
  38. package/src/src/server/bootstrap.ts +184 -119
  39. package/src/src/utils/version-constant.ts +1 -1
@@ -16,6 +16,8 @@ import { initializeEsbuild } from "../platform/compat/esbuild.js";
16
16
  import { logger } from "../utils/index.js";
17
17
  import { isDebugEnabled } from "../utils/constants/env.js";
18
18
  import { getEnvSource, hasEnvLoaded, loadEnv, markEnvLoaded, supportsEnvFiles, } from "../utils/env-loader.js";
19
+ import { getLogBuffer } from "../observability/log-buffer.js";
20
+ import { createFileLogSubscriber, } from "../observability/file-log-subscriber.js";
19
21
  import { ReloadNotifier } from "./reload-notifier.js";
20
22
  import { clearDomainCache } from "./utils/domain-lookup.js";
21
23
  const bootstrapLog = logger.component("bootstrap");
@@ -60,14 +62,51 @@ function wireTracingShim() {
60
62
  bootstrapLog.debug("[bootstrap] no TracingExporter extension — using no-op tracer");
61
63
  }
62
64
  }
63
- function combineDispose(extensionLoader, fsDispose) {
65
+ const DEFAULT_FILE_LOG_PATH = ".veryfront/logs/server.log";
66
+ const DEFAULT_FILE_LOG_MAX_SIZE = "10mb";
67
+ const DEFAULT_FILE_LOG_MAX_FILES = 5;
68
+ const DEFAULT_FILE_LOG_LEVEL = "warn";
69
+ const DEFAULT_FILE_LOG_FORMAT = "json";
70
+ function maybeAttachFileLogSubscriber(config) {
71
+ const fileConfig = config.observability?.logging?.file;
72
+ if (!fileConfig?.enabled)
73
+ return null;
74
+ const resolved = {
75
+ enabled: true,
76
+ path: fileConfig.path ?? DEFAULT_FILE_LOG_PATH,
77
+ maxSize: fileConfig.maxSize ?? DEFAULT_FILE_LOG_MAX_SIZE,
78
+ maxFiles: fileConfig.maxFiles ?? DEFAULT_FILE_LOG_MAX_FILES,
79
+ level: fileConfig.level ?? DEFAULT_FILE_LOG_LEVEL,
80
+ format: fileConfig.format ?? DEFAULT_FILE_LOG_FORMAT,
81
+ };
82
+ const subscriber = createFileLogSubscriber(resolved);
83
+ const unsubscribe = getLogBuffer().subscribe(subscriber.getSubscriber());
84
+ bootstrapLog.debug("[bootstrap] File log subscriber attached", {
85
+ path: resolved.path,
86
+ level: resolved.level,
87
+ format: resolved.format,
88
+ });
89
+ return { subscriber, unsubscribe };
90
+ }
91
+ async function teardownFileLog(handle) {
92
+ if (!handle)
93
+ return;
94
+ handle.unsubscribe();
95
+ await handle.subscriber.close();
96
+ }
97
+ function combineDispose(extensionLoader, fsDispose, fileLogHandle) {
64
98
  return async () => {
65
99
  try {
66
100
  await extensionLoader.teardownAll();
67
101
  }
68
102
  finally {
69
- if (fsDispose)
70
- fsDispose();
103
+ try {
104
+ await teardownFileLog(fileLogHandle ?? null);
105
+ }
106
+ finally {
107
+ if (fsDispose)
108
+ fsDispose();
109
+ }
71
110
  }
72
111
  };
73
112
  }
@@ -144,118 +183,131 @@ export async function bootstrap(projectDir, adapter) {
144
183
  await ensureEnvLoaded(projectDir, adapter);
145
184
  bootstrapLog.debug("Loading config with base adapter");
146
185
  let config = await getConfig(projectDir, adapter);
147
- const fsType = config.fs?.type;
148
- const needsFSAdapter = fsType != null && fsType !== "local";
149
- if (!needsFSAdapter) {
150
- bootstrapLog.debug("Using local filesystem (no FSAdapter needed)");
151
- const extensionLoader = await orchestrateExtensions({
152
- projectDir,
153
- config,
154
- logger: bootstrapLog,
155
- primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
156
- builtinExtensions: createBuiltinExtensions(),
157
- });
158
- wireTracingShim();
159
- assertRequiredContracts();
160
- return {
161
- adapter,
162
- config,
163
- usingFSAdapter: false,
164
- extensionLoader,
165
- dispose: combineDispose(extensionLoader),
186
+ let fileLog = maybeAttachFileLogSubscriber(config);
187
+ try {
188
+ const fsType = config.fs?.type;
189
+ const needsFSAdapter = fsType != null && fsType !== "local";
190
+ if (!needsFSAdapter) {
191
+ bootstrapLog.debug("Using local filesystem (no FSAdapter needed)");
192
+ const extensionLoader = await orchestrateExtensions({
193
+ projectDir,
194
+ config,
195
+ logger: bootstrapLog,
196
+ primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
197
+ builtinExtensions: createBuiltinExtensions(),
198
+ });
199
+ wireTracingShim();
200
+ assertRequiredContracts();
201
+ return {
202
+ adapter,
203
+ config,
204
+ usingFSAdapter: false,
205
+ extensionLoader,
206
+ dispose: combineDispose(extensionLoader, undefined, fileLog),
207
+ };
208
+ }
209
+ bootstrapLog.debug("Initializing FSAdapter", { type: fsType });
210
+ // Inject server-layer callbacks into FS config so the platform layer
211
+ // doesn't need to import from the server layer
212
+ const fsWithCallbacks = {
213
+ ...config.fs,
214
+ invalidationCallbacks: {
215
+ triggerReload: (changedPaths, project) => ReloadNotifier.triggerReload(changedPaths, project),
216
+ clearDomainCache,
217
+ },
166
218
  };
167
- }
168
- bootstrapLog.debug("Initializing FSAdapter", { type: fsType });
169
- // Inject server-layer callbacks into FS config so the platform layer
170
- // doesn't need to import from the server layer
171
- const fsWithCallbacks = {
172
- ...config.fs,
173
- invalidationCallbacks: {
174
- triggerReload: (changedPaths, project) => ReloadNotifier.triggerReload(changedPaths, project),
175
- clearDomainCache,
176
- },
177
- };
178
- const enhancedAdapter = await enhanceAdapterWithFS(adapter, { ...config, fs: fsWithCallbacks }, projectDir);
179
- if (enhancedAdapter === adapter) {
219
+ const enhancedAdapter = await enhanceAdapterWithFS(adapter, { ...config, fs: fsWithCallbacks }, projectDir);
220
+ if (enhancedAdapter === adapter) {
221
+ bootstrapLog.debug("Framework initialized successfully", {
222
+ projectDir,
223
+ runtime: adapter.id,
224
+ fsAdapter: "local",
225
+ });
226
+ const extensionLoader = await orchestrateExtensions({
227
+ projectDir,
228
+ config,
229
+ logger: bootstrapLog,
230
+ primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
231
+ builtinExtensions: createBuiltinExtensions(),
232
+ });
233
+ wireTracingShim();
234
+ assertRequiredContracts();
235
+ return {
236
+ adapter,
237
+ config,
238
+ usingFSAdapter: false,
239
+ extensionLoader,
240
+ dispose: combineDispose(extensionLoader, undefined, fileLog),
241
+ };
242
+ }
243
+ const isProxyMode = config.fs?.veryfront?.proxyMode === true;
244
+ const isProductionMode = config.fs?.veryfront?.productionMode === true;
245
+ if (isProxyMode) {
246
+ bootstrapLog.debug("Skipping config reload in proxy mode (using local config)");
247
+ }
248
+ else if (isProductionMode) {
249
+ bootstrapLog.debug("Skipping config reload in production mode (using local config)");
250
+ }
251
+ else {
252
+ bootstrapLog.debug("Reloading config with FSAdapter");
253
+ clearConfigCache();
254
+ const originalConfig = config;
255
+ const reloadedConfig = await getConfig(projectDir, enhancedAdapter);
256
+ const usesDefaultDevConfig = reloadedConfig.dev?.port === 3000 &&
257
+ reloadedConfig.dev?.host === "localhost" &&
258
+ !reloadedConfig.dev?.hmr;
259
+ if (usesDefaultDevConfig && originalConfig.dev) {
260
+ bootstrapLog.debug("Keeping original config (FSAdapter returned defaults)");
261
+ config = originalConfig;
262
+ }
263
+ else {
264
+ config = reloadedConfig;
265
+ }
266
+ // Re-attach file log subscriber if config was reloaded with different settings
267
+ const newFileLog = maybeAttachFileLogSubscriber(config);
268
+ if (newFileLog) {
269
+ await teardownFileLog(fileLog);
270
+ fileLog = newFileLog;
271
+ }
272
+ }
180
273
  bootstrapLog.debug("Framework initialized successfully", {
181
274
  projectDir,
182
275
  runtime: adapter.id,
183
- fsAdapter: "local",
276
+ fsAdapter: fsType,
184
277
  });
185
- const extensionLoader = await orchestrateExtensions({
278
+ let fsDispose;
279
+ if (isExtendedFSAdapter(enhancedAdapter.fs)) {
280
+ const underlying = enhancedAdapter.fs.getUnderlyingAdapter();
281
+ if ("dispose" in underlying &&
282
+ typeof underlying.dispose === "function") {
283
+ fsDispose = () => underlying.dispose();
284
+ }
285
+ }
286
+ // If extension orchestration fails after the FS adapter has been wired up,
287
+ // release the FS resources (WebSocket connections, caches) before
288
+ // propagating the error — otherwise the adapter would leak.
289
+ const extensionLoader = await orchestrateOrDisposeFS(() => orchestrateExtensions({
186
290
  projectDir,
187
291
  config,
188
292
  logger: bootstrapLog,
189
293
  primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
190
294
  builtinExtensions: createBuiltinExtensions(),
191
- });
295
+ }), fsDispose);
192
296
  wireTracingShim();
193
297
  assertRequiredContracts();
194
298
  return {
195
- adapter,
299
+ adapter: enhancedAdapter,
196
300
  config,
197
- usingFSAdapter: false,
301
+ usingFSAdapter: true,
302
+ fsAdapterType: fsType,
198
303
  extensionLoader,
199
- dispose: combineDispose(extensionLoader),
304
+ dispose: combineDispose(extensionLoader, fsDispose, fileLog),
200
305
  };
201
306
  }
202
- const isProxyMode = config.fs?.veryfront?.proxyMode === true;
203
- const isProductionMode = config.fs?.veryfront?.productionMode === true;
204
- if (isProxyMode) {
205
- bootstrapLog.debug("Skipping config reload in proxy mode (using local config)");
206
- }
207
- else if (isProductionMode) {
208
- bootstrapLog.debug("Skipping config reload in production mode (using local config)");
209
- }
210
- else {
211
- bootstrapLog.debug("Reloading config with FSAdapter");
212
- clearConfigCache();
213
- const originalConfig = config;
214
- const reloadedConfig = await getConfig(projectDir, enhancedAdapter);
215
- const usesDefaultDevConfig = reloadedConfig.dev?.port === 3000 &&
216
- reloadedConfig.dev?.host === "localhost" &&
217
- !reloadedConfig.dev?.hmr;
218
- if (usesDefaultDevConfig && originalConfig.dev) {
219
- bootstrapLog.debug("Keeping original config (FSAdapter returned defaults)");
220
- config = originalConfig;
221
- }
222
- else {
223
- config = reloadedConfig;
224
- }
225
- }
226
- bootstrapLog.debug("Framework initialized successfully", {
227
- projectDir,
228
- runtime: adapter.id,
229
- fsAdapter: fsType,
230
- });
231
- let fsDispose;
232
- if (isExtendedFSAdapter(enhancedAdapter.fs)) {
233
- const underlying = enhancedAdapter.fs.getUnderlyingAdapter();
234
- if ("dispose" in underlying &&
235
- typeof underlying.dispose === "function") {
236
- fsDispose = () => underlying.dispose();
237
- }
307
+ catch (err) {
308
+ await teardownFileLog(fileLog);
309
+ throw err;
238
310
  }
239
- // If extension orchestration fails after the FS adapter has been wired up,
240
- // release the FS resources (WebSocket connections, caches) before
241
- // propagating the error — otherwise the adapter would leak.
242
- const extensionLoader = await orchestrateOrDisposeFS(() => orchestrateExtensions({
243
- projectDir,
244
- config,
245
- logger: bootstrapLog,
246
- primeContracts: { [AIProviderRegistryName]: createAIProviderRegistry() },
247
- builtinExtensions: createBuiltinExtensions(),
248
- }), fsDispose);
249
- wireTracingShim();
250
- assertRequiredContracts();
251
- return {
252
- adapter: enhancedAdapter,
253
- config,
254
- usingFSAdapter: true,
255
- fsAdapterType: fsType,
256
- extensionLoader,
257
- dispose: combineDispose(extensionLoader, fsDispose),
258
- };
259
311
  }
260
312
  export async function bootstrapDev(projectDir, adapter) {
261
313
  bootstrapDevLog.debug("Starting development mode initialization");
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.403";
1
+ export declare const VERSION = "0.1.405";
2
2
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.403";
3
+ export const VERSION = "0.1.405";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.403",
3
+ "version": "0.1.405",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -1,4 +1,4 @@
1
- import { readTextFile } from "../../../src/platform/index.js";
1
+ import manifest from "../../templates/manifest.js";
2
2
  import { type AITool, type AIToolId, AIToolIdSchema, AIToolSchema } from "./types.js";
3
3
 
4
4
  const AI_TOOLS_RAW = [
@@ -67,6 +67,13 @@ export function isValidToolId(id: string): id is AIToolId {
67
67
 
68
68
  export function getTemplateContent(toolId: string): Promise<string> {
69
69
  const { template } = getToolById(toolId);
70
- const templatePath = new URL(`../../templates/ai-rules/${template}`, import.meta.url).pathname;
71
- return readTextFile(templatePath);
70
+ const entry = (manifest as Record<string, unknown>).templates as Record<
71
+ string,
72
+ { files: Record<string, string> }
73
+ >;
74
+ const aiRule = entry[`ai-rules:${template}`];
75
+ if (!aiRule) throw new Error(`Template not found in manifest: ${template}`);
76
+ const content = aiRule.files[template];
77
+ if (!content) throw new Error(`Template file not found in manifest: ${template}`);
78
+ return Promise.resolve(content);
72
79
  }
@@ -587,6 +587,36 @@ export default {
587
587
  "app/api/auth/docs-google/callback/route.ts": "import { createOAuthCallbackHandler, docsGoogleConfig } from \"veryfront/oauth\";\nimport { tokenStore } from \"../../../../../lib/token-store.ts\";\nimport { oauthMemoryTokenStore } from \"../../../../../lib/oauth-memory-store.ts\";\n\nconst hybridTokenStore = {\n getTokens(serviceId: string, userId: string) {\n return tokenStore.getToken(userId, serviceId);\n },\n async setTokens(\n serviceId: string,\n userId: string,\n tokens: { accessToken: string; refreshToken?: string; expiresAt?: number },\n ) {\n await tokenStore.setToken(userId, serviceId, tokens);\n },\n async clearTokens(serviceId: string, userId: string) {\n await tokenStore.revokeToken(userId, serviceId);\n },\n setState(\n state: string,\n meta: {\n userId: string;\n serviceId: string;\n codeVerifier?: string;\n redirectUri?: string;\n scopes?: string[];\n createdAt: number;\n },\n ) {\n return oauthMemoryTokenStore.setState(state, meta);\n },\n consumeState(state: string) {\n return oauthMemoryTokenStore.consumeState(state);\n },\n};\n\nexport const GET = createOAuthCallbackHandler(docsGoogleConfig, { tokenStore: hybridTokenStore });\n",
588
588
  "app/api/auth/docs-google/route.ts": "import { createOAuthInitHandler, docsGoogleConfig } from \"veryfront/oauth\";\nimport { oauthMemoryTokenStore } from \"../../../../../lib/oauth-memory-store.ts\";\n\n// TODO: Replace with real user ID from your auth system (e.g., session cookie, JWT).\n// NEVER return a shared constant in production - it breaks per-user token isolation (VULN-AUTH-2).\nfunction getUserId(_request: Request): string {\n return \"current-user\";\n}\n\nexport const GET = createOAuthInitHandler(docsGoogleConfig, {\n tokenStore: oauthMemoryTokenStore,\n getUserId,\n});"
589
589
  }
590
+ },
591
+ "ai-rules:claude-code.md": {
592
+ "files": {
593
+ "claude-code.md": "# Veryfront Project\n\nZero-config React meta-framework for AI-native applications.\n\n## Quick Reference\n\n| Command | Purpose |\n| -------------------------------- | ---------------------------------------- |\n| `veryfront dev` | Start dev server (http://localhost:3000) |\n| `veryfront build` | Production build |\n| `veryfront deploy` | Deploy to Veryfront cloud |\n| `veryfront generate page <name>` | Generate new page |\n| `veryfront generate api <name>` | Generate API route |\n\n## Project Structure\n\n- `src/pages/*.tsx` → Routes (file-based routing)\n- `src/api/*.ts` → API endpoints (`/api/*`)\n- `src/components/` → Shared components\n- `agents/` → AI agents\n- `tools/` → MCP tools\n\n## When Asked to Add Features\n\n1. **Pages**: Create in `src/pages/` (e.g., `about.tsx` → `/about`)\n2. **APIs**: Create in `src/api/` (e.g., `users.ts` → `/api/users`)\n3. **Components**: Create in `src/components/`\n4. **AI Agents**: Create in `agents/`\n\n## Code Patterns\n\n```tsx\n// Page (src/pages/about.tsx)\nexport default function About() {\n return <h1>About</h1>;\n}\n\n// API (src/api/hello.ts)\nexport function GET() {\n return Response.json({ message: \"Hello\" });\n}\n\n// Dynamic API (src/api/users/[id].ts)\nexport function GET(_req: Request, { params }: { params: { id: string } }) {\n return Response.json({ id: params.id });\n}\n```\n\n## Conventions\n\n- Use TypeScript\n- Use React 19 features\n- Use Tailwind for styling\n- Co-locate tests (`*.test.ts`)\n- Use `#veryfront/*` imports\n\n## Testing\n\n- Run `veryfront dev` and check browser\n- API endpoints at `/api/*`\n- Run `deno task test` for tests\n"
594
+ }
595
+ },
596
+ "ai-rules:skill.md": {
597
+ "files": {
598
+ "skill.md": "---\nname: veryfront\ndescription: Build and deploy fullstack AI-native React apps with Veryfront CLI\nlicense: MIT\ncompatibility: Claude Code, Cursor, VS Code, Codex, Gemini CLI\nmetadata:\n author: veryfront\n version: \"1.0\"\n---\n\n# Veryfront\n\nZero-config React meta-framework for AI-native applications.\n\n## Commands\n\n| Command | Purpose |\n| -------------------------------- | --------------------------------- |\n| `veryfront dev` | Start development server with HMR |\n| `veryfront build` | Build for production |\n| `veryfront deploy` | Deploy to Veryfront cloud |\n| `veryfront generate page <name>` | Generate a new page |\n| `veryfront generate api <name>` | Generate an API route |\n\n## Project Structure\n\n```\nsrc/\n├── pages/ # File-based routing (*.tsx → routes)\n├── api/ # API routes (*.ts → /api/*)\n├── components/ # Shared React components\n├── ai/\n│ ├── agents/ # AI agents\n│ └── tools/ # MCP tools\n└── styles/ # Global styles\n```\n\n## Adding Features\n\n- **New page**: Create `src/pages/about.tsx` → `/about`\n- **API endpoint**: Create `src/api/users.ts` → `/api/users`\n- **AI agent**: Create `agents/assistant.ts`\n- **MCP tool**: Create `tools/search.ts`\n\n## Code Examples\n\n### Page Component\n\n```tsx\n// src/pages/about.tsx\nexport default function About() {\n return <h1>About</h1>;\n}\n```\n\n### API Route\n\n```ts\n// src/api/hello.ts\nexport function GET() {\n return Response.json({ message: \"Hello\" });\n}\n```\n\n### Dynamic API Route\n\n```ts\n// src/api/users/[id].ts\nexport function GET(_req: Request, { params }: { params: { id: string } }) {\n return Response.json({ id: params.id });\n}\n```\n\n## Conventions\n\n- TypeScript required\n- React 19 features (use, Server Components)\n- Tailwind CSS for styling\n- Co-locate tests with implementation\n- Use `#veryfront/*` imports for framework modules\n\n## Testing\n\n- Development: `veryfront dev` (http://localhost:3000)\n- API endpoints: `/api/*`\n- Run tests: `deno task test`\n"
599
+ }
600
+ },
601
+ "ai-rules:copilot.md": {
602
+ "files": {
603
+ "copilot.md": "# Veryfront Project Instructions\n\nThis is a Veryfront project - a zero-config React meta-framework for AI-native applications.\n\n## CLI Commands\n\n- `veryfront dev` - Start development server with hot module replacement\n- `veryfront build` - Build for production deployment\n- `veryfront deploy` - Deploy to Veryfront cloud\n- `veryfront generate page <name>` - Scaffold a new page\n- `veryfront generate api <name>` - Scaffold an API route\n\n## File Structure\n\nThe project uses file-based routing:\n\n- `src/pages/*.tsx` - React pages (automatically become routes)\n- `src/api/*.ts` - API endpoints (accessible at `/api/*`)\n- `src/components/` - Shared React components\n- `agents/` - AI agent definitions\n- `tools/` - MCP tool implementations\n\n## Code Patterns\n\n### Creating a Page\n\n```tsx\n// src/pages/about.tsx → accessible at /about\nexport default function About() {\n return <h1>About</h1>;\n}\n```\n\n### Creating an API Endpoint\n\n```ts\n// src/api/hello.ts → accessible at /api/hello\nexport function GET() {\n return Response.json({ message: \"Hello\" });\n}\n\nexport function POST(request: Request) {\n // Handle POST request\n return Response.json({ success: true });\n}\n```\n\n### Dynamic Routes\n\n```ts\n// src/api/users/[id].ts → accessible at /api/users/:id\nexport function GET(_req: Request, { params }: { params: { id: string } }) {\n return Response.json({ userId: params.id });\n}\n```\n\n## Technology Stack\n\n- Runtime: Deno\n- UI: React 19 with Server Components\n- Styling: Tailwind CSS\n- Language: TypeScript (required)\n\n## Best Practices\n\n- Co-locate test files with implementation (`*.test.ts`)\n- Use `#veryfront/*` import aliases for framework modules\n- Follow React 19 patterns (use hook, Server Components)\n- Use Response.json() for API responses\n"
604
+ }
605
+ },
606
+ "ai-rules:windsurf.md": {
607
+ "files": {
608
+ "windsurf.md": "# Veryfront Project\n\nYou are in a Veryfront project - zero-config React meta-framework for AI-native applications.\n\n## Commands\n\n- `veryfront dev` - Start development server with HMR\n- `veryfront build` - Build for production\n- `veryfront deploy` - Deploy to Veryfront cloud\n- `veryfront generate page <name>` - Generate a new page\n- `veryfront generate api <name>` - Generate an API route\n\n## Project Structure\n\n```\nsrc/\n├── pages/ # File-based routing (pages/*.tsx → routes)\n├── api/ # API routes (api/*.ts → /api/*)\n├── components/ # Shared React components\n├── ai/\n│ ├── agents/ # AI agents\n│ └── tools/ # MCP tools\n└── styles/ # Global styles\n```\n\n## Adding Features\n\n- **New page**: Create `src/pages/about.tsx` → accessible at `/about`\n- **API endpoint**: Create `src/api/users.ts` → accessible at `/api/users`\n- **AI agent**: Create `agents/assistant.ts`\n- **MCP tool**: Create `tools/search.ts`\n\n## Conventions\n\n- TypeScript required\n- React 19 features (use, Server Components)\n- Tailwind CSS for styling\n- Co-locate tests with implementation (`*.test.ts`)\n- Use `#veryfront/*` imports for framework modules\n\n## File Patterns\n\n```tsx\n// Page component (src/pages/about.tsx)\nexport default function About() {\n return <h1>About</h1>;\n}\n\n// API route (src/api/hello.ts)\nexport function GET() {\n return Response.json({ message: \"Hello\" });\n}\n\n// API with params (src/api/users/[id].ts)\nexport function GET(_req: Request, { params }: { params: { id: string } }) {\n return Response.json({ id: params.id });\n}\n```\n\n## Testing\n\n- Run `veryfront dev` and check browser at http://localhost:3000\n- API endpoints available at `/api/*`\n- Use `deno task test` to run tests\n"
609
+ }
610
+ },
611
+ "ai-rules:agents.md": {
612
+ "files": {
613
+ "agents.md": "# Veryfront Agent Instructions\n\nThis is a Veryfront project. Veryfront is a zero-config React meta-framework for AI-native applications.\n\n## Commands\n\n- `npx veryfront dev` - Start development server\n- `npx veryfront build` - Build for production\n- `npx veryfront deploy` - Deploy to cloud\n- `npx veryfront generate page <name>` - Generate a page\n- `npx veryfront generate api <name>` - Generate an API route\n\n## File Conventions\n\n- **Pages**: `src/pages/*.tsx` (file-based routing)\n- **APIs**: `src/api/*.ts` (serverless functions at `/api/*`)\n- **Components**: `src/components/*.tsx`\n- **AI Agents**: `agents/*.ts`\n- **MCP Tools**: `tools/*.ts`\n\n## Examples\n\n### Create a new page\n\n```tsx\n// src/pages/about.tsx\nexport default function About() {\n return <h1>About</h1>;\n}\n```\n\nThis page will be accessible at `/about`.\n\n### Create an API endpoint\n\n```ts\n// src/api/hello.ts\nexport function GET() {\n return Response.json({ message: \"Hello\" });\n}\n\nexport function POST(request: Request) {\n return Response.json({ success: true });\n}\n```\n\nThis API will be accessible at `/api/hello`.\n\n### Create a dynamic API route\n\n```ts\n// src/api/users/[id].ts\nexport function GET(_req: Request, { params }: { params: { id: string } }) {\n return Response.json({ userId: params.id });\n}\n```\n\nThis API will be accessible at `/api/users/:id`.\n\n## Technology Stack\n\n- **Runtime**: Deno\n- **UI Framework**: React 19\n- **Styling**: Tailwind CSS\n- **Language**: TypeScript\n\n## Best Practices\n\n- Co-locate tests with implementation files (`*.test.ts`)\n- Use `#veryfront/*` import aliases\n- Use React 19 features (use hook, Server Components)\n- Return `Response.json()` from API routes\n"
614
+ }
615
+ },
616
+ "ai-rules:cursor.md": {
617
+ "files": {
618
+ "cursor.md": "# Veryfront Project\n\nYou are in a Veryfront project - zero-config React meta-framework for AI-native applications.\n\n## Commands\n\n- `veryfront dev` - Start development server with HMR\n- `veryfront build` - Build for production\n- `veryfront deploy` - Deploy to Veryfront cloud\n- `veryfront generate page <name>` - Generate a new page\n- `veryfront generate api <name>` - Generate an API route\n\n## Project Structure\n\n```\nsrc/\n├── pages/ # File-based routing (pages/*.tsx → routes)\n├── api/ # API routes (api/*.ts → /api/*)\n├── components/ # Shared React components\n├── ai/\n│ ├── agents/ # AI agents\n│ └── tools/ # MCP tools\n└── styles/ # Global styles\n```\n\n## Adding Features\n\n- **New page**: Create `src/pages/about.tsx` → accessible at `/about`\n- **API endpoint**: Create `src/api/users.ts` → accessible at `/api/users`\n- **AI agent**: Create `agents/assistant.ts`\n- **MCP tool**: Create `tools/search.ts`\n\n## Conventions\n\n- TypeScript required\n- React 19 features (use, Server Components)\n- Tailwind CSS for styling\n- Co-locate tests with implementation (`*.test.ts`)\n- Use `#veryfront/*` imports for framework modules\n\n## File Patterns\n\n```tsx\n// Page component (src/pages/about.tsx)\nexport default function About() {\n return <h1>About</h1>;\n}\n\n// API route (src/api/hello.ts)\nexport function GET() {\n return Response.json({ message: \"Hello\" });\n}\n\n// API with params (src/api/users/[id].ts)\nexport function GET(_req: Request, { params }: { params: { id: string } }) {\n return Response.json({ id: params.id });\n}\n```\n\n## Testing\n\n- Run `veryfront dev` and check browser at http://localhost:3000\n- API endpoints available at `/api/*`\n- Use `deno task test` to run tests\n"
619
+ }
590
620
  }
591
621
  }
592
622
  };
package/src/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.403",
3
+ "version": "0.1.405",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "workspace": [
@@ -4,7 +4,7 @@
4
4
 
5
5
  // See https://github.com/facebook/react/blob/main/packages/react-dom/client.js to see how the exports are declared,
6
6
 
7
- import React = require("https://esm.sh/@types/react@19.2.3/index.d.ts");
7
+ import React = require("https://esm.sh/@types/react@19.2.14/index.d.ts");
8
8
 
9
9
  export {};
10
10
 
@@ -18,6 +18,7 @@ interface KeyboardEvent extends Event {}
18
18
  interface MouseEvent extends Event {}
19
19
  interface TouchEvent extends Event {}
20
20
  interface PointerEvent extends Event {}
21
+ interface SubmitEvent extends Event {}
21
22
  interface ToggleEvent extends Event {}
22
23
  interface TransitionEvent extends Event {}
23
24
  interface UIEvent extends Event {}
@@ -16,6 +16,7 @@ type NativeKeyboardEvent = KeyboardEvent;
16
16
  type NativeMouseEvent = MouseEvent;
17
17
  type NativeTouchEvent = TouchEvent;
18
18
  type NativePointerEvent = PointerEvent;
19
+ type NativeSubmitEvent = SubmitEvent;
19
20
  type NativeToggleEvent = ToggleEvent;
20
21
  type NativeTransitionEvent = TransitionEvent;
21
22
  type NativeUIEvent = UIEvent;
@@ -225,12 +226,20 @@ declare namespace React {
225
226
 
226
227
  type ComponentState = any;
227
228
 
229
+ interface DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_KEY_TYPES {}
230
+
228
231
  /**
229
232
  * A value which uniquely identifies a node among items in an array.
230
233
  *
231
234
  * @see {@link https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key React Docs}
232
235
  */
233
- type Key = string | number | bigint;
236
+ type Key =
237
+ | string
238
+ | number
239
+ | bigint
240
+ | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_KEY_TYPES[
241
+ keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_KEY_TYPES
242
+ ];
234
243
 
235
244
  /**
236
245
  * @internal The props any component can receive.
@@ -1884,7 +1893,7 @@ declare namespace React {
1884
1893
  *
1885
1894
  * @param callback A synchronous, void callback that will execute as a single, complete React commit.
1886
1895
  *
1887
- * @see https://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks
1896
+ * @see {@link https://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks}
1888
1897
  */
1889
1898
  // NOTES
1890
1899
  // - the order of these signatures matters - typescript will check the signatures in source order.
@@ -1926,7 +1935,31 @@ declare namespace React {
1926
1935
  reducer: (state: State, action: Action) => State,
1927
1936
  ): [State, (action: Action) => void];
1928
1937
 
1929
- export type Usable<T> = PromiseLike<T> | Context<T>;
1938
+ interface UntrackedReactPromise<T> extends PromiseLike<T> {
1939
+ status?: void;
1940
+ }
1941
+
1942
+ export interface PendingReactPromise<T> extends PromiseLike<T> {
1943
+ status: "pending";
1944
+ }
1945
+
1946
+ export interface FulfilledReactPromise<T> extends PromiseLike<T> {
1947
+ status: "fulfilled";
1948
+ value: T;
1949
+ }
1950
+
1951
+ export interface RejectedReactPromise<T> extends PromiseLike<T> {
1952
+ status: "rejected";
1953
+ reason: unknown;
1954
+ }
1955
+
1956
+ export type ReactPromise<T> =
1957
+ | UntrackedReactPromise<T>
1958
+ | PendingReactPromise<T>
1959
+ | FulfilledReactPromise<T>
1960
+ | RejectedReactPromise<T>;
1961
+
1962
+ export type Usable<T> = ReactPromise<T> | Context<T>;
1930
1963
 
1931
1964
  export function use<T>(usable: Usable<T>): T;
1932
1965
 
@@ -2041,15 +2074,28 @@ declare namespace React {
2041
2074
  target: EventTarget & Target;
2042
2075
  }
2043
2076
 
2077
+ /**
2078
+ * @deprecated FormEvent doesn't actually exist.
2079
+ * You probably meant to use {@link ChangeEvent}, {@link InputEvent}, {@link SubmitEvent}, or just {@link SyntheticEvent} instead
2080
+ * depending on the event type.
2081
+ */
2044
2082
  interface FormEvent<T = Element> extends SyntheticEvent<T> {
2045
2083
  }
2046
2084
 
2047
2085
  interface InvalidEvent<T = Element> extends SyntheticEvent<T> {
2048
- target: EventTarget & T;
2049
2086
  }
2050
2087
 
2051
- interface ChangeEvent<T = Element> extends SyntheticEvent<T> {
2052
- target: EventTarget & T;
2088
+ /**
2089
+ * change events bubble in React so their target is generally unknown.
2090
+ * Only for form elements we know their target type because form events can't
2091
+ * be nested.
2092
+ * This type exists purely to narrow `target` for form elements. It doesn't
2093
+ * reflect a DOM event. Change events are just fired as standard {@link SyntheticEvent}.
2094
+ */
2095
+ interface ChangeEvent<CurrentTarget = Element, Target = Element> extends SyntheticEvent<CurrentTarget> {
2096
+ // TODO: This is wrong for change event handlers on arbitrary. Should
2097
+ // be EventTarget & Target, but kept for backward compatibility until React 20.
2098
+ target: EventTarget & CurrentTarget;
2053
2099
  }
2054
2100
 
2055
2101
  interface InputEvent<T = Element> extends SyntheticEvent<T, NativeInputEvent> {
@@ -2119,6 +2165,13 @@ declare namespace React {
2119
2165
  shiftKey: boolean;
2120
2166
  }
2121
2167
 
2168
+ interface SubmitEvent<T = Element> extends SyntheticEvent<T, NativeSubmitEvent> {
2169
+ // `submitter` is available in react@canary
2170
+ // submitter: HTMLElement | null;
2171
+ // SubmitEvents are always targetted at HTMLFormElements.
2172
+ target: EventTarget & HTMLFormElement;
2173
+ }
2174
+
2122
2175
  interface TouchEvent<T = Element> extends UIEvent<T, NativeTouchEvent> {
2123
2176
  altKey: boolean;
2124
2177
  changedTouches: TouchList;
@@ -2174,11 +2227,19 @@ declare namespace React {
2174
2227
  type CompositionEventHandler<T = Element> = EventHandler<CompositionEvent<T>>;
2175
2228
  type DragEventHandler<T = Element> = EventHandler<DragEvent<T>>;
2176
2229
  type FocusEventHandler<T = Element> = EventHandler<FocusEvent<T>>;
2230
+ /**
2231
+ * @deprecated FormEventHandler doesn't actually exist.
2232
+ * You probably meant to use {@link ChangeEventHandler}, {@link InputEventHandler}, {@link SubmitEventHandler}, or just {@link EventHandler} instead
2233
+ * depending on the event type.
2234
+ */
2177
2235
  type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
2178
- type ChangeEventHandler<T = Element> = EventHandler<ChangeEvent<T>>;
2236
+ type ChangeEventHandler<CurrentTarget = Element, Target = Element> = EventHandler<
2237
+ ChangeEvent<CurrentTarget, Target>
2238
+ >;
2179
2239
  type InputEventHandler<T = Element> = EventHandler<InputEvent<T>>;
2180
2240
  type KeyboardEventHandler<T = Element> = EventHandler<KeyboardEvent<T>>;
2181
2241
  type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;
2242
+ type SubmitEventHandler<T = Element> = EventHandler<SubmitEvent<T>>;
2182
2243
  type TouchEventHandler<T = Element> = EventHandler<TouchEvent<T>>;
2183
2244
  type PointerEventHandler<T = Element> = EventHandler<PointerEvent<T>>;
2184
2245
  type UIEventHandler<T = Element> = EventHandler<UIEvent<T>>;
@@ -2232,19 +2293,19 @@ declare namespace React {
2232
2293
  onBlur?: FocusEventHandler<T> | undefined;
2233
2294
  onBlurCapture?: FocusEventHandler<T> | undefined;
2234
2295
 
2235
- // Form Events
2236
- onChange?: FormEventHandler<T> | undefined;
2237
- onChangeCapture?: FormEventHandler<T> | undefined;
2296
+ // form related Events
2297
+ onChange?: ChangeEventHandler<T> | undefined;
2298
+ onChangeCapture?: ChangeEventHandler<T> | undefined;
2238
2299
  onBeforeInput?: InputEventHandler<T> | undefined;
2239
- onBeforeInputCapture?: FormEventHandler<T> | undefined;
2240
- onInput?: FormEventHandler<T> | undefined;
2241
- onInputCapture?: FormEventHandler<T> | undefined;
2242
- onReset?: FormEventHandler<T> | undefined;
2243
- onResetCapture?: FormEventHandler<T> | undefined;
2244
- onSubmit?: FormEventHandler<T> | undefined;
2245
- onSubmitCapture?: FormEventHandler<T> | undefined;
2246
- onInvalid?: FormEventHandler<T> | undefined;
2247
- onInvalidCapture?: FormEventHandler<T> | undefined;
2300
+ onBeforeInputCapture?: InputEventHandler<T> | undefined;
2301
+ onInput?: InputEventHandler<T> | undefined;
2302
+ onInputCapture?: InputEventHandler<T> | undefined;
2303
+ onReset?: ReactEventHandler<T> | undefined;
2304
+ onResetCapture?: ReactEventHandler<T> | undefined;
2305
+ onSubmit?: SubmitEventHandler<T> | undefined;
2306
+ onSubmitCapture?: SubmitEventHandler<T> | undefined;
2307
+ onInvalid?: ReactEventHandler<T> | undefined;
2308
+ onInvalidCapture?: ReactEventHandler<T> | undefined;
2248
2309
 
2249
2310
  // Image Events
2250
2311
  onLoad?: ReactEventHandler<T> | undefined;
@@ -2788,7 +2849,7 @@ declare namespace React {
2788
2849
 
2789
2850
  // Living Standard
2790
2851
  /**
2791
- * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert
2852
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert}
2792
2853
  */
2793
2854
  inert?: boolean | undefined;
2794
2855
  /**
@@ -3251,7 +3312,9 @@ declare namespace React {
3251
3312
  value?: string | readonly string[] | number | undefined;
3252
3313
  width?: number | string | undefined;
3253
3314
 
3254
- onChange?: ChangeEventHandler<T> | undefined;
3315
+ // No other element dispatching change events can be nested in a <input>
3316
+ // so we know the target will be a HTMLInputElement.
3317
+ onChange?: ChangeEventHandler<T, HTMLInputElement> | undefined;
3255
3318
  }
3256
3319
 
3257
3320
  interface KeygenHTMLAttributes<T> extends HTMLAttributes<T> {
@@ -3416,7 +3479,9 @@ declare namespace React {
3416
3479
  required?: boolean | undefined;
3417
3480
  size?: number | undefined;
3418
3481
  value?: string | readonly string[] | number | undefined;
3419
- onChange?: ChangeEventHandler<T> | undefined;
3482
+ // No other element dispatching change events can be nested in a <select>
3483
+ // so we know the target will be a HTMLSelectElement.
3484
+ onChange?: ChangeEventHandler<T, HTMLSelectElement> | undefined;
3420
3485
  }
3421
3486
 
3422
3487
  interface SourceHTMLAttributes<T> extends HTMLAttributes<T> {
@@ -3468,7 +3533,9 @@ declare namespace React {
3468
3533
  value?: string | readonly string[] | number | undefined;
3469
3534
  wrap?: string | undefined;
3470
3535
 
3471
- onChange?: ChangeEventHandler<T> | undefined;
3536
+ // No other element dispatching change events can be nested in a <textarea>
3537
+ // so we know the target will be a HTMLTextAreaElement.
3538
+ onChange?: ChangeEventHandler<T, HTMLTextAreaElement> | undefined;
3472
3539
  }
3473
3540
 
3474
3541
  interface TdHTMLAttributes<T> extends HTMLAttributes<T> {
@@ -3540,6 +3607,9 @@ declare namespace React {
3540
3607
  method?: string | undefined;
3541
3608
  min?: number | string | undefined;
3542
3609
  name?: string | undefined;
3610
+ nonce?: string | undefined;
3611
+ part?: string | undefined;
3612
+ slot?: string | undefined;
3543
3613
  style?: CSSProperties | undefined;
3544
3614
  target?: string | undefined;
3545
3615
  type?: string | undefined;
@@ -4057,7 +4127,6 @@ declare namespace React {
4057
4127
  * Captures which component contained the exception, and its ancestors.
4058
4128
  */
4059
4129
  componentStack?: string | null;
4060
- digest?: string | null;
4061
4130
  }
4062
4131
 
4063
4132
  // Keep in sync with JSX namespace in ./jsx-runtime.d.ts and ./jsx-dev-runtime.d.ts