@affectively/aeon-pages 1.3.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.
- package/CHANGELOG.md +112 -0
- package/README.md +625 -0
- package/examples/basic/aeon.config.ts +39 -0
- package/examples/basic/components/Cursor.tsx +86 -0
- package/examples/basic/components/OfflineIndicator.tsx +103 -0
- package/examples/basic/components/PresenceBar.tsx +77 -0
- package/examples/basic/package.json +20 -0
- package/examples/basic/pages/index.tsx +80 -0
- package/package.json +101 -0
- package/packages/analytics/README.md +309 -0
- package/packages/analytics/build.ts +35 -0
- package/packages/analytics/package.json +50 -0
- package/packages/analytics/src/click-tracker.ts +368 -0
- package/packages/analytics/src/context-bridge.ts +319 -0
- package/packages/analytics/src/data-layer.ts +302 -0
- package/packages/analytics/src/gtm-loader.ts +239 -0
- package/packages/analytics/src/index.ts +230 -0
- package/packages/analytics/src/merkle-tree.ts +489 -0
- package/packages/analytics/src/provider.tsx +300 -0
- package/packages/analytics/src/types.ts +320 -0
- package/packages/analytics/src/use-analytics.ts +296 -0
- package/packages/analytics/tsconfig.json +19 -0
- package/packages/benchmarks/src/benchmark.test.ts +691 -0
- package/packages/cli/dist/index.js +61899 -0
- package/packages/cli/package.json +43 -0
- package/packages/cli/src/commands/build.test.ts +682 -0
- package/packages/cli/src/commands/build.ts +890 -0
- package/packages/cli/src/commands/dev.ts +473 -0
- package/packages/cli/src/commands/init.ts +409 -0
- package/packages/cli/src/commands/start.ts +297 -0
- package/packages/cli/src/index.ts +105 -0
- package/packages/directives/src/use-aeon.ts +272 -0
- package/packages/mcp-server/package.json +51 -0
- package/packages/mcp-server/src/index.ts +178 -0
- package/packages/mcp-server/src/resources.ts +346 -0
- package/packages/mcp-server/src/tools/index.ts +36 -0
- package/packages/mcp-server/src/tools/navigation.ts +545 -0
- package/packages/mcp-server/tsconfig.json +21 -0
- package/packages/react/package.json +40 -0
- package/packages/react/src/Link.tsx +388 -0
- package/packages/react/src/components/InstallPrompt.tsx +286 -0
- package/packages/react/src/components/OfflineDiagnostics.tsx +677 -0
- package/packages/react/src/components/PushNotifications.tsx +453 -0
- package/packages/react/src/hooks/useAeonNavigation.ts +219 -0
- package/packages/react/src/hooks/useConflicts.ts +277 -0
- package/packages/react/src/hooks/useNetworkState.ts +209 -0
- package/packages/react/src/hooks/usePilotNavigation.ts +254 -0
- package/packages/react/src/hooks/useServiceWorker.ts +278 -0
- package/packages/react/src/hooks.ts +195 -0
- package/packages/react/src/index.ts +151 -0
- package/packages/react/src/provider.tsx +467 -0
- package/packages/react/tsconfig.json +19 -0
- package/packages/runtime/README.md +399 -0
- package/packages/runtime/build.ts +48 -0
- package/packages/runtime/package.json +71 -0
- package/packages/runtime/schema.sql +40 -0
- package/packages/runtime/src/api-routes.ts +465 -0
- package/packages/runtime/src/benchmark.ts +171 -0
- package/packages/runtime/src/cache.ts +479 -0
- package/packages/runtime/src/durable-object.ts +1341 -0
- package/packages/runtime/src/index.ts +360 -0
- package/packages/runtime/src/navigation.test.ts +421 -0
- package/packages/runtime/src/navigation.ts +422 -0
- package/packages/runtime/src/nextjs-adapter.ts +272 -0
- package/packages/runtime/src/offline/encrypted-queue.test.ts +607 -0
- package/packages/runtime/src/offline/encrypted-queue.ts +478 -0
- package/packages/runtime/src/offline/encryption.test.ts +412 -0
- package/packages/runtime/src/offline/encryption.ts +397 -0
- package/packages/runtime/src/offline/types.ts +465 -0
- package/packages/runtime/src/predictor.ts +371 -0
- package/packages/runtime/src/registry.ts +351 -0
- package/packages/runtime/src/router/context-extractor.ts +661 -0
- package/packages/runtime/src/router/esi-control-react.tsx +2053 -0
- package/packages/runtime/src/router/esi-control.ts +541 -0
- package/packages/runtime/src/router/esi-cyrano.ts +779 -0
- package/packages/runtime/src/router/esi-format-react.tsx +1744 -0
- package/packages/runtime/src/router/esi-react.tsx +1065 -0
- package/packages/runtime/src/router/esi-translate-observer.ts +476 -0
- package/packages/runtime/src/router/esi-translate-react.tsx +556 -0
- package/packages/runtime/src/router/esi-translate.ts +503 -0
- package/packages/runtime/src/router/esi.ts +666 -0
- package/packages/runtime/src/router/heuristic-adapter.test.ts +295 -0
- package/packages/runtime/src/router/heuristic-adapter.ts +557 -0
- package/packages/runtime/src/router/index.ts +298 -0
- package/packages/runtime/src/router/merkle-capability.ts +473 -0
- package/packages/runtime/src/router/speculation.ts +451 -0
- package/packages/runtime/src/router/types.ts +630 -0
- package/packages/runtime/src/router.test.ts +470 -0
- package/packages/runtime/src/router.ts +302 -0
- package/packages/runtime/src/server.ts +481 -0
- package/packages/runtime/src/service-worker-push.ts +319 -0
- package/packages/runtime/src/service-worker.ts +553 -0
- package/packages/runtime/src/skeleton-hydrate.ts +237 -0
- package/packages/runtime/src/speculation.test.ts +389 -0
- package/packages/runtime/src/speculation.ts +486 -0
- package/packages/runtime/src/storage.test.ts +1297 -0
- package/packages/runtime/src/storage.ts +1048 -0
- package/packages/runtime/src/sync/conflict-resolver.test.ts +528 -0
- package/packages/runtime/src/sync/conflict-resolver.ts +565 -0
- package/packages/runtime/src/sync/coordinator.test.ts +608 -0
- package/packages/runtime/src/sync/coordinator.ts +596 -0
- package/packages/runtime/src/tree-compiler.ts +295 -0
- package/packages/runtime/src/types.ts +728 -0
- package/packages/runtime/src/worker.ts +327 -0
- package/packages/runtime/tsconfig.json +20 -0
- package/packages/runtime/wasm/aeon_pages_runtime.d.ts +504 -0
- package/packages/runtime/wasm/aeon_pages_runtime.js +1657 -0
- package/packages/runtime/wasm/aeon_pages_runtime_bg.wasm +0 -0
- package/packages/runtime/wasm/aeon_pages_runtime_bg.wasm.d.ts +196 -0
- package/packages/runtime/wasm/package.json +21 -0
- package/packages/runtime/wrangler.toml +41 -0
- package/packages/runtime-wasm/Cargo.lock +436 -0
- package/packages/runtime-wasm/Cargo.toml +29 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime.d.ts +480 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime.js +1568 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm +0 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm.d.ts +192 -0
- package/packages/runtime-wasm/pkg/package.json +21 -0
- package/packages/runtime-wasm/src/hydrate.rs +352 -0
- package/packages/runtime-wasm/src/lib.rs +191 -0
- package/packages/runtime-wasm/src/render.rs +629 -0
- package/packages/runtime-wasm/src/router.rs +298 -0
- package/packages/runtime-wasm/src/skeleton.rs +430 -0
- package/rfcs/RFC-001-ZERO-DEPENDENCY-RENDERING.md +1446 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aeon Pages Cloudflare Worker
|
|
3
|
+
*
|
|
4
|
+
* Entry point for Cloudflare Workers deployment.
|
|
5
|
+
* Handles routing to Durable Objects, API routes, and static assets.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Export Durable Object classes
|
|
9
|
+
export { AeonPageSession, AeonRoutesRegistry } from './durable-object';
|
|
10
|
+
|
|
11
|
+
// Import API router
|
|
12
|
+
import { ApiRouter, createApiRouter } from './api-routes';
|
|
13
|
+
import type {
|
|
14
|
+
AeonEnv,
|
|
15
|
+
ApiRouteModule,
|
|
16
|
+
ExecutionContext,
|
|
17
|
+
DurableObjectNamespace,
|
|
18
|
+
} from './types';
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// WORKER FACTORY
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/** Options for creating an Aeon worker */
|
|
25
|
+
export interface AeonWorkerOptions<E extends AeonEnv = AeonEnv> {
|
|
26
|
+
/** API routes to register */
|
|
27
|
+
apiRoutes?: Record<string, ApiRouteModule<E>>;
|
|
28
|
+
|
|
29
|
+
/** CORS configuration */
|
|
30
|
+
cors?: {
|
|
31
|
+
origin?: string | string[];
|
|
32
|
+
methods?: string[];
|
|
33
|
+
headers?: string[];
|
|
34
|
+
credentials?: boolean;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Custom fetch handler to run before Aeon routing */
|
|
38
|
+
onRequest?: (
|
|
39
|
+
request: Request,
|
|
40
|
+
env: E,
|
|
41
|
+
ctx: ExecutionContext,
|
|
42
|
+
) => Promise<Response | null>;
|
|
43
|
+
|
|
44
|
+
/** Custom 404 handler */
|
|
45
|
+
notFound?: (request: Request, env: E) => Response | Promise<Response>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create an Aeon worker with API route support
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* import { createAeonWorker } from '@affectively/aeon-pages-runtime/worker';
|
|
54
|
+
*
|
|
55
|
+
* export default createAeonWorker({
|
|
56
|
+
* apiRoutes: {
|
|
57
|
+
* '/api/chat': {
|
|
58
|
+
* POST: async ({ request, env }) => {
|
|
59
|
+
* const body = await request.json();
|
|
60
|
+
* const result = await env.AI.run('@cf/meta/llama-3-8b-instruct', { ... });
|
|
61
|
+
* return Response.json({ result });
|
|
62
|
+
* },
|
|
63
|
+
* },
|
|
64
|
+
* '/api/users/[id]': {
|
|
65
|
+
* GET: async ({ params, env }) => {
|
|
66
|
+
* const user = await env.DB.prepare('SELECT * FROM users WHERE id = ?')
|
|
67
|
+
* .bind(params.id)
|
|
68
|
+
* .first();
|
|
69
|
+
* return Response.json(user);
|
|
70
|
+
* },
|
|
71
|
+
* },
|
|
72
|
+
* },
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function createAeonWorker<E extends AeonEnv = AeonEnv>(
|
|
77
|
+
options: AeonWorkerOptions<E> = {},
|
|
78
|
+
): ExportedHandler<E> {
|
|
79
|
+
// Create API router and register routes
|
|
80
|
+
const apiRouter = createApiRouter<E>();
|
|
81
|
+
if (options.apiRoutes) {
|
|
82
|
+
apiRouter.registerAll(options.apiRoutes);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Build CORS headers
|
|
86
|
+
const corsConfig = {
|
|
87
|
+
origin: '*',
|
|
88
|
+
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
|
|
89
|
+
headers: ['Content-Type', 'Authorization'],
|
|
90
|
+
credentials: false,
|
|
91
|
+
...options.cors,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const getCorsHeaders = (
|
|
95
|
+
requestOrigin?: string | null,
|
|
96
|
+
): Record<string, string> => {
|
|
97
|
+
let allowedOrigin = '*';
|
|
98
|
+
if (typeof corsConfig.origin === 'string') {
|
|
99
|
+
allowedOrigin = corsConfig.origin;
|
|
100
|
+
} else if (Array.isArray(corsConfig.origin) && requestOrigin) {
|
|
101
|
+
if (corsConfig.origin.includes(requestOrigin)) {
|
|
102
|
+
allowedOrigin = requestOrigin;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const headers: Record<string, string> = {
|
|
107
|
+
'Access-Control-Allow-Origin': allowedOrigin,
|
|
108
|
+
'Access-Control-Allow-Methods': corsConfig.methods.join(', '),
|
|
109
|
+
'Access-Control-Allow-Headers': corsConfig.headers.join(', '),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
if (corsConfig.credentials) {
|
|
113
|
+
headers['Access-Control-Allow-Credentials'] = 'true';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return headers;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
async fetch(
|
|
121
|
+
request: Request,
|
|
122
|
+
env: E,
|
|
123
|
+
ctx: ExecutionContext,
|
|
124
|
+
): Promise<Response> {
|
|
125
|
+
const url = new URL(request.url);
|
|
126
|
+
const corsHeaders = getCorsHeaders(request.headers.get('Origin'));
|
|
127
|
+
|
|
128
|
+
// Handle preflight
|
|
129
|
+
if (request.method === 'OPTIONS') {
|
|
130
|
+
return new Response(null, {
|
|
131
|
+
status: 204,
|
|
132
|
+
headers: {
|
|
133
|
+
...corsHeaders,
|
|
134
|
+
'Access-Control-Max-Age': '86400',
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
// Custom onRequest handler
|
|
141
|
+
if (options.onRequest) {
|
|
142
|
+
const customResponse = await options.onRequest(request, env, ctx);
|
|
143
|
+
if (customResponse) {
|
|
144
|
+
return addCorsHeaders(customResponse, corsHeaders);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// API routes - /api/*
|
|
149
|
+
if (url.pathname.startsWith('/api/')) {
|
|
150
|
+
const response = await apiRouter.handle(request, env, ctx);
|
|
151
|
+
if (response) {
|
|
152
|
+
return addCorsHeaders(response, corsHeaders);
|
|
153
|
+
}
|
|
154
|
+
// No matching API route - fall through to 404
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Session routes - /session/*
|
|
158
|
+
if (url.pathname.startsWith('/session/')) {
|
|
159
|
+
return handleSessionRequest(
|
|
160
|
+
request,
|
|
161
|
+
env as unknown as BaseEnv,
|
|
162
|
+
corsHeaders,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Routes registry - /routes
|
|
167
|
+
if (url.pathname.startsWith('/routes')) {
|
|
168
|
+
return handleRoutesRequest(
|
|
169
|
+
request,
|
|
170
|
+
env as unknown as BaseEnv,
|
|
171
|
+
corsHeaders,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Health check
|
|
176
|
+
if (url.pathname === '/health') {
|
|
177
|
+
return new Response(
|
|
178
|
+
JSON.stringify({ status: 'ok', env: env.ENVIRONMENT }),
|
|
179
|
+
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } },
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Custom 404 handler
|
|
184
|
+
if (options.notFound) {
|
|
185
|
+
const notFoundResponse = await options.notFound(request, env);
|
|
186
|
+
return addCorsHeaders(notFoundResponse, corsHeaders);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return new Response('Not found', { status: 404, headers: corsHeaders });
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error('Worker error:', error);
|
|
192
|
+
return new Response(
|
|
193
|
+
JSON.stringify({
|
|
194
|
+
error: 'Internal server error',
|
|
195
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
196
|
+
}),
|
|
197
|
+
{
|
|
198
|
+
status: 500,
|
|
199
|
+
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Add CORS headers to a response */
|
|
208
|
+
function addCorsHeaders(
|
|
209
|
+
response: Response,
|
|
210
|
+
corsHeaders: Record<string, string>,
|
|
211
|
+
): Response {
|
|
212
|
+
const newHeaders = new Headers(response.headers);
|
|
213
|
+
for (const [key, value] of Object.entries(corsHeaders)) {
|
|
214
|
+
if (!newHeaders.has(key)) {
|
|
215
|
+
newHeaders.set(key, value);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return new Response(response.body, {
|
|
219
|
+
status: response.status,
|
|
220
|
+
statusText: response.statusText,
|
|
221
|
+
headers: newHeaders,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Cloudflare Worker exported handler interface */
|
|
226
|
+
interface ExportedHandler<E = unknown> {
|
|
227
|
+
fetch(request: Request, env: E, ctx: ExecutionContext): Promise<Response>;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// =============================================================================
|
|
231
|
+
// LEGACY DEFAULT EXPORT (for backwards compatibility)
|
|
232
|
+
// =============================================================================
|
|
233
|
+
|
|
234
|
+
interface BaseEnv {
|
|
235
|
+
PAGE_SESSIONS: DurableObjectNamespace;
|
|
236
|
+
ROUTES_REGISTRY: DurableObjectNamespace;
|
|
237
|
+
ENVIRONMENT?: string;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export default createAeonWorker();
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Handle session requests - route to PageSession Durable Object
|
|
244
|
+
*/
|
|
245
|
+
async function handleSessionRequest(
|
|
246
|
+
request: Request,
|
|
247
|
+
env: BaseEnv,
|
|
248
|
+
corsHeaders: Record<string, string>,
|
|
249
|
+
): Promise<Response> {
|
|
250
|
+
const url = new URL(request.url);
|
|
251
|
+
|
|
252
|
+
// Extract session ID from path: /session/:sessionId/...
|
|
253
|
+
const pathParts = url.pathname.split('/').filter(Boolean);
|
|
254
|
+
const sessionId = pathParts[1];
|
|
255
|
+
|
|
256
|
+
if (!sessionId) {
|
|
257
|
+
return new Response('Session ID required', {
|
|
258
|
+
status: 400,
|
|
259
|
+
headers: corsHeaders,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Get or create the Durable Object instance
|
|
264
|
+
const id = env.PAGE_SESSIONS.idFromName(sessionId);
|
|
265
|
+
const stub = env.PAGE_SESSIONS.get(id);
|
|
266
|
+
|
|
267
|
+
// Forward the request to the DO
|
|
268
|
+
const doUrl = new URL(request.url);
|
|
269
|
+
doUrl.pathname = '/' + pathParts.slice(2).join('/') || '/session';
|
|
270
|
+
|
|
271
|
+
const doRequest = new Request(doUrl.toString(), {
|
|
272
|
+
method: request.method,
|
|
273
|
+
headers: request.headers,
|
|
274
|
+
body: request.body,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const response = await stub.fetch(doRequest);
|
|
278
|
+
|
|
279
|
+
// Add CORS headers to response
|
|
280
|
+
const newHeaders = new Headers(response.headers);
|
|
281
|
+
Object.entries(corsHeaders).forEach(([key, value]) => {
|
|
282
|
+
newHeaders.set(key, value);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return new Response(response.body, {
|
|
286
|
+
status: response.status,
|
|
287
|
+
statusText: response.statusText,
|
|
288
|
+
headers: newHeaders,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Handle routes registry requests - route to singleton RoutesRegistry DO
|
|
294
|
+
*/
|
|
295
|
+
async function handleRoutesRequest(
|
|
296
|
+
request: Request,
|
|
297
|
+
env: BaseEnv,
|
|
298
|
+
corsHeaders: Record<string, string>,
|
|
299
|
+
): Promise<Response> {
|
|
300
|
+
// Use a singleton DO for routes registry
|
|
301
|
+
const id = env.ROUTES_REGISTRY.idFromName('__routes__');
|
|
302
|
+
const stub = env.ROUTES_REGISTRY.get(id);
|
|
303
|
+
|
|
304
|
+
const url = new URL(request.url);
|
|
305
|
+
const doUrl = new URL(request.url);
|
|
306
|
+
doUrl.pathname = url.pathname.replace('/routes', '') || '/routes';
|
|
307
|
+
|
|
308
|
+
const doRequest = new Request(doUrl.toString(), {
|
|
309
|
+
method: request.method,
|
|
310
|
+
headers: request.headers,
|
|
311
|
+
body: request.body,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
const response = await stub.fetch(doRequest);
|
|
315
|
+
|
|
316
|
+
// Add CORS headers
|
|
317
|
+
const newHeaders = new Headers(response.headers);
|
|
318
|
+
Object.entries(corsHeaders).forEach(([key, value]) => {
|
|
319
|
+
newHeaders.set(key, value);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
return new Response(response.body, {
|
|
323
|
+
status: response.status,
|
|
324
|
+
statusText: response.statusText,
|
|
325
|
+
headers: newHeaders,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ESNext", "DOM", "WebWorker"],
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationDir": "./dist",
|
|
9
|
+
"emitDeclarationOnly": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"outDir": "./dist",
|
|
15
|
+
"rootDir": "./src",
|
|
16
|
+
"types": ["bun"]
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
20
|
+
}
|