@kuratchi/js 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -50
- package/dist/cli.js +59 -17
- package/dist/compiler/index.d.ts +2 -2
- package/dist/compiler/index.js +658 -259
- package/dist/compiler/parser.d.ts +20 -4
- package/dist/compiler/parser.js +296 -12
- package/dist/compiler/template.d.ts +3 -1
- package/dist/compiler/template.js +217 -14
- package/dist/compiler/transpile.d.ts +1 -0
- package/dist/compiler/transpile.js +61 -0
- package/dist/create.js +2 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/runtime/action.d.ts +11 -0
- package/dist/runtime/action.js +14 -0
- package/dist/runtime/app.js +35 -8
- package/dist/runtime/context.d.ts +8 -2
- package/dist/runtime/context.js +31 -1
- package/dist/runtime/do.d.ts +6 -0
- package/dist/runtime/do.js +15 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/page-error.d.ts +16 -0
- package/dist/runtime/page-error.js +20 -0
- package/dist/runtime/types.d.ts +53 -33
- package/package.json +50 -46
package/README.md
CHANGED
|
@@ -18,12 +18,13 @@ bun run dev
|
|
|
18
18
|
|
|
19
19
|
## How it works
|
|
20
20
|
|
|
21
|
-
`kuratchi build` (or `kuratchi watch`) scans `src/routes/` and generates
|
|
21
|
+
`kuratchi build` (or `kuratchi watch`) scans `src/routes/` and generates framework output:
|
|
22
22
|
|
|
23
23
|
| File | Purpose |
|
|
24
24
|
|---|---|
|
|
25
25
|
| `.kuratchi/routes.js` | Compiled routes, actions, RPC handlers, and render functions |
|
|
26
|
-
| `.kuratchi/worker.js` | Stable wrangler entry
|
|
26
|
+
| `.kuratchi/worker.js` | Stable wrangler entry - re-exports the fetch handler and all Durable Object classes |
|
|
27
|
+
| `.kuratchi/do/*.js` | Generated Durable Object RPC proxy modules for `$durable-objects/*` imports |
|
|
27
28
|
|
|
28
29
|
Point wrangler at the entry and you're done. **No `src/index.ts` needed.**
|
|
29
30
|
|
|
@@ -49,8 +50,6 @@ src/routes/layout.html → shared layout wrapping all routes
|
|
|
49
50
|
|
|
50
51
|
```html
|
|
51
52
|
<script>
|
|
52
|
-
// Imports and server-side logic run on every request.
|
|
53
|
-
// Exported functions become actions or RPC handlers automatically.
|
|
54
53
|
import { getItems, addItem, deleteItem } from '$database/items';
|
|
55
54
|
|
|
56
55
|
const items = await getItems();
|
|
@@ -95,7 +94,8 @@ The `$database/` alias resolves to `src/database/`. You can use any path alias c
|
|
|
95
94
|
|
|
96
95
|
```html
|
|
97
96
|
<p>{title}</p>
|
|
98
|
-
<p>{
|
|
97
|
+
<p>{@html bodyHtml}</p> <!-- sanitized HTML -->
|
|
98
|
+
<p>{@raw trustedHtml}</p> <!-- unescaped, unsafe -->
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
### Conditionals
|
|
@@ -131,6 +131,40 @@ Import `.html` components from your `src/lib/` directory or from packages:
|
|
|
131
131
|
</Card>
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
### Client Reactivity (`$:`)
|
|
135
|
+
|
|
136
|
+
Inside client/browser `<script>` tags in the template markup, Kuratchi supports Svelte-style reactive labels:
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<script>
|
|
140
|
+
let users = ['Alice'];
|
|
141
|
+
|
|
142
|
+
$: console.log(`Users: ${users.length}`);
|
|
143
|
+
|
|
144
|
+
function addUser() {
|
|
145
|
+
users.push('Bob'); // reactive update, no reassignment required
|
|
146
|
+
}
|
|
147
|
+
</script>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Block form is also supported:
|
|
151
|
+
|
|
152
|
+
```html
|
|
153
|
+
<script>
|
|
154
|
+
let form = { first: '', last: '' };
|
|
155
|
+
|
|
156
|
+
$: {
|
|
157
|
+
const fullName = `${form.first} ${form.last}`.trim();
|
|
158
|
+
console.log(fullName);
|
|
159
|
+
}
|
|
160
|
+
</script>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Notes:
|
|
164
|
+
- This reactivity runs in browser scripts rendered in the template markup, not in the top server route `<script>` load/action block.
|
|
165
|
+
- Object/array `let` bindings are proxy-backed automatically when `$:` is used.
|
|
166
|
+
- `$: name = expr` works; when replacing proxy-backed values, the compiler preserves reactivity under the hood.
|
|
167
|
+
|
|
134
168
|
## Form actions
|
|
135
169
|
|
|
136
170
|
Export server functions from a route's `<script>` block and reference them with `action={fn}`. The compiler automatically registers them as dispatchable actions.
|
|
@@ -147,12 +181,15 @@ Export server functions from a route's `<script>` block and reference them with
|
|
|
147
181
|
</form>
|
|
148
182
|
```
|
|
149
183
|
|
|
150
|
-
The action function receives the raw `FormData
|
|
184
|
+
The action function receives the raw `FormData`. Throw `ActionError` to surface a message back to the form — see [Error handling](#error-handling).
|
|
151
185
|
|
|
152
186
|
```ts
|
|
153
187
|
// src/database/items.ts
|
|
188
|
+
import { ActionError } from '@kuratchi/js';
|
|
189
|
+
|
|
154
190
|
export async function addItem(formData: FormData): Promise<void> {
|
|
155
|
-
const title = formData.get('title') as string;
|
|
191
|
+
const title = (formData.get('title') as string)?.trim();
|
|
192
|
+
if (!title) throw new ActionError('Title is required');
|
|
156
193
|
// write to DB...
|
|
157
194
|
}
|
|
158
195
|
```
|
|
@@ -170,6 +207,85 @@ export async function createItem(formData: FormData): Promise<void> {
|
|
|
170
207
|
}
|
|
171
208
|
```
|
|
172
209
|
|
|
210
|
+
## Error handling
|
|
211
|
+
|
|
212
|
+
### Action errors
|
|
213
|
+
|
|
214
|
+
Throw `ActionError` from a form action to surface a user-facing message in the template. The error message is bound directly to the action by name — if you have multiple forms on the same page, each has its own isolated error state.
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
import { ActionError } from '@kuratchi/js';
|
|
218
|
+
|
|
219
|
+
export async function signIn(formData: FormData) {
|
|
220
|
+
const email = formData.get('email') as string;
|
|
221
|
+
const password = formData.get('password') as string;
|
|
222
|
+
|
|
223
|
+
if (!email || !password) throw new ActionError('Email and password are required');
|
|
224
|
+
|
|
225
|
+
const user = await db.findUser(email);
|
|
226
|
+
if (!user || !await verify(password, user.passwordHash)) {
|
|
227
|
+
throw new ActionError('Invalid credentials');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
In the template, the action's state object is available under its function name:
|
|
233
|
+
|
|
234
|
+
```html
|
|
235
|
+
<script>
|
|
236
|
+
import { signIn } from '$database/auth';
|
|
237
|
+
</script>
|
|
238
|
+
|
|
239
|
+
<form action={signIn}>
|
|
240
|
+
(signIn.error ? `<p class="error">${signIn.error}</p>` : '')
|
|
241
|
+
<input type="email" name="email" />
|
|
242
|
+
<input type="password" name="password" />
|
|
243
|
+
<button type="submit">Sign in</button>
|
|
244
|
+
</form>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The state object shape: `{ error?: string, loading: boolean, success: boolean }`.
|
|
248
|
+
|
|
249
|
+
- `actionName.error` — set on `ActionError` throw, cleared on next successful action
|
|
250
|
+
- `actionName.loading` — set by the client bridge during form submission (CSS target: `form[data-action-loading]`)
|
|
251
|
+
- `actionName.success` — reserved for future use
|
|
252
|
+
|
|
253
|
+
Throwing a plain `Error` instead of `ActionError` keeps the message hidden in production and shows a generic "Action failed" message. Use `ActionError` for expected validation failures; let plain errors propagate for unexpected crashes.
|
|
254
|
+
|
|
255
|
+
### Load errors
|
|
256
|
+
|
|
257
|
+
Throw `PageError` from a route's load scope to return the correct HTTP error page. Without it, any thrown error becomes a 500.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import { PageError } from '@kuratchi/js';
|
|
261
|
+
|
|
262
|
+
// In src/routes/posts/[id]/page.html <script> block:
|
|
263
|
+
const post = await db.posts.findOne({ id: params.id });
|
|
264
|
+
if (!post) throw new PageError(404);
|
|
265
|
+
if (!post.isPublished && !currentUser?.isAdmin) throw new PageError(403);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
`PageError` accepts any HTTP status. The framework renders the matching custom error page (`src/routes/404.html`, `src/routes/500.html`, etc.) if one exists, otherwise falls back to the built-in error page.
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
throw new PageError(404); // → 404 page
|
|
272
|
+
throw new PageError(403, 'Admin only'); // → 403 page, message shown in dev
|
|
273
|
+
throw new PageError(401, 'Login required'); // → 401 page
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
For soft load failures where the page should still render (e.g. a widget that failed to fetch), return the error as data from `load()` and handle it in the template:
|
|
277
|
+
|
|
278
|
+
```html
|
|
279
|
+
<script>
|
|
280
|
+
const { data: recommendations, error: recError } = await safeGetRecommendations();
|
|
281
|
+
</script>
|
|
282
|
+
|
|
283
|
+
(recError ? '<p class="notice">Could not load recommendations.</p>' : '')
|
|
284
|
+
for (const rec of (recommendations ?? [])) {
|
|
285
|
+
<article>{rec.title}</article>
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
173
289
|
## Progressive enhancement
|
|
174
290
|
|
|
175
291
|
These `data-*` attributes wire up client-side interactivity without writing JavaScript.
|
|
@@ -239,53 +355,93 @@ for (const todo of todos) {
|
|
|
239
355
|
|
|
240
356
|
## RPC
|
|
241
357
|
|
|
242
|
-
|
|
358
|
+
For Durable Objects, RPC is file-driven and automatic.
|
|
359
|
+
|
|
360
|
+
- Put handler logic in a `.do.ts` file.
|
|
361
|
+
- Exported functions in that file become RPC methods.
|
|
362
|
+
- Import RPC methods from `$durable-objects/<file-name-without-.do>`.
|
|
243
363
|
|
|
244
364
|
```html
|
|
245
365
|
<script>
|
|
246
|
-
import {
|
|
247
|
-
|
|
248
|
-
export const rpc = {
|
|
249
|
-
getCount,
|
|
250
|
-
};
|
|
366
|
+
import { getOrgUsers, createOrgUser } from '$durable-objects/auth';
|
|
367
|
+
const users = await getOrgUsers();
|
|
251
368
|
</script>
|
|
369
|
+
|
|
370
|
+
<form action={createOrgUser} method="POST">
|
|
371
|
+
<input type="email" name="email" required />
|
|
372
|
+
<button type="submit">Create</button>
|
|
373
|
+
</form>
|
|
252
374
|
```
|
|
375
|
+
## Durable Objects
|
|
253
376
|
|
|
254
|
-
|
|
377
|
+
Durable Object behavior is enabled by filename suffix.
|
|
255
378
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
379
|
+
- Any file ending in `.do.ts` is treated as a Durable Object handler file.
|
|
380
|
+
- Any file not ending in `.do.ts` is treated as a normal server module.
|
|
381
|
+
- No required folder name. `src/server/auth.do.ts`, `src/server/foo/bar/sites.do.ts`, etc. all work.
|
|
382
|
+
|
|
383
|
+
### Function mode (recommended)
|
|
384
|
+
|
|
385
|
+
Write plain exported functions in a `.do.ts` file. Exported functions become DO RPC methods.
|
|
386
|
+
Use `this.db`, `this.env`, and `this.ctx` inside those functions.
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
// src/server/auth/auth.do.ts
|
|
390
|
+
import { getCurrentUser, hashPassword } from '@kuratchi/auth';
|
|
391
|
+
import { redirect } from '@kuratchi/js';
|
|
392
|
+
|
|
393
|
+
async function randomPassword(length = 24): Promise<string> {
|
|
394
|
+
const alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789';
|
|
395
|
+
const bytes = new Uint8Array(length);
|
|
396
|
+
crypto.getRandomValues(bytes);
|
|
397
|
+
let out = '';
|
|
398
|
+
for (let i = 0; i < length; i++) out += alphabet[bytes[i] % alphabet.length];
|
|
399
|
+
return out;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export async function getOrgUsers() {
|
|
403
|
+
const result = await this.db.users.orderBy({ createdAt: 'asc' }).many();
|
|
404
|
+
return result.data ?? [];
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export async function createOrgUser(formData: FormData) {
|
|
408
|
+
const user = await getCurrentUser();
|
|
409
|
+
if (!user?.orgId) throw new Error('Not authenticated');
|
|
410
|
+
|
|
411
|
+
const email = String(formData.get('email') ?? '').trim().toLowerCase();
|
|
412
|
+
if (!email) throw new Error('Email is required');
|
|
413
|
+
|
|
414
|
+
const passwordHash = await hashPassword(await randomPassword(), undefined, this.env.AUTH_SECRET);
|
|
415
|
+
await this.db.users.insert({ email, role: 'member', passwordHash });
|
|
416
|
+
redirect('/settings/users');
|
|
417
|
+
}
|
|
260
418
|
```
|
|
261
419
|
|
|
262
|
-
|
|
420
|
+
Optional lifecycle exports in function mode:
|
|
263
421
|
|
|
264
|
-
|
|
422
|
+
- `export async function onInit()`
|
|
423
|
+
- `export async function onAlarm(...args)`
|
|
424
|
+
- `export function onMessage(...args)`
|
|
425
|
+
|
|
426
|
+
These lifecycle names are not exposed as RPC methods.
|
|
427
|
+
|
|
428
|
+
### Class mode (optional)
|
|
429
|
+
|
|
430
|
+
Class-based handlers are still supported in `.do.ts` files:
|
|
265
431
|
|
|
266
432
|
```ts
|
|
267
|
-
// src/server/notes.do.ts
|
|
268
433
|
import { kuratchiDO } from '@kuratchi/js';
|
|
269
|
-
import type { Note } from '../schemas/notes';
|
|
270
434
|
|
|
271
435
|
export default class NotesDO extends kuratchiDO {
|
|
272
436
|
static binding = 'NOTES_DO';
|
|
273
437
|
|
|
274
|
-
async getNotes()
|
|
438
|
+
async getNotes() {
|
|
275
439
|
return (await this.db.notes.orderBy({ created_at: 'desc' }).many()).data ?? [];
|
|
276
440
|
}
|
|
277
|
-
|
|
278
|
-
async addNote(title: string): Promise<void> {
|
|
279
|
-
await this.db.notes.insert({ title });
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
async deleteNote(id: number): Promise<void> {
|
|
283
|
-
await this.db.notes.delete({ id });
|
|
284
|
-
}
|
|
285
441
|
}
|
|
286
442
|
```
|
|
287
443
|
|
|
288
|
-
Declare it in `kuratchi.config.ts` and in `wrangler.jsonc`. The compiler exports
|
|
444
|
+
Declare it in `kuratchi.config.ts` and in `wrangler.jsonc`. The compiler exports DO classes from `.kuratchi/worker.js` automatically.
|
|
289
445
|
|
|
290
446
|
```jsonc
|
|
291
447
|
// wrangler.jsonc
|
|
@@ -298,22 +454,6 @@ Declare it in `kuratchi.config.ts` and in `wrangler.jsonc`. The compiler exports
|
|
|
298
454
|
]
|
|
299
455
|
}
|
|
300
456
|
```
|
|
301
|
-
|
|
302
|
-
Call DO methods from your route via a stub module in `src/database/`:
|
|
303
|
-
|
|
304
|
-
```ts
|
|
305
|
-
// src/database/notes.ts
|
|
306
|
-
import { env } from 'cloudflare:workers';
|
|
307
|
-
|
|
308
|
-
function getStub() {
|
|
309
|
-
return (env as any).NOTES_DO.get((env as any).NOTES_DO.idFromName('global'));
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
export const getNotes = () => getStub().getNotes();
|
|
313
|
-
export const addNote = (title: string) => getStub().addNote(title);
|
|
314
|
-
export const deleteNote = (id: number) => getStub().deleteNote(id);
|
|
315
|
-
```
|
|
316
|
-
|
|
317
457
|
## Runtime APIs
|
|
318
458
|
|
|
319
459
|
These are available anywhere in server-side route code:
|
|
@@ -330,7 +470,26 @@ import {
|
|
|
330
470
|
} from '@kuratchi/js';
|
|
331
471
|
```
|
|
332
472
|
|
|
333
|
-
Environment bindings
|
|
473
|
+
## Environment bindings
|
|
474
|
+
|
|
475
|
+
Cloudflare env is server-only.
|
|
476
|
+
|
|
477
|
+
- Route top-level `<script>`, route `load()` functions, server actions, API handlers, and other server modules can read env.
|
|
478
|
+
- Templates, components, and client `<script>` blocks cannot read env directly.
|
|
479
|
+
- If a value must reach the browser, compute it in the server route script and reference it in the template, or return it from `load()` explicitly.
|
|
480
|
+
|
|
481
|
+
```html
|
|
482
|
+
<script>
|
|
483
|
+
import { env } from 'cloudflare:workers';
|
|
484
|
+
const turnstileSiteKey = env.TURNSTILE_SITE_KEY || '';
|
|
485
|
+
</script>
|
|
486
|
+
|
|
487
|
+
if (turnstileSiteKey) {
|
|
488
|
+
<div class="cf-turnstile" data-sitekey={turnstileSiteKey}></div>
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
Server modules can still access env directly:
|
|
334
493
|
|
|
335
494
|
```ts
|
|
336
495
|
import { env } from 'cloudflare:workers';
|
|
@@ -357,7 +516,10 @@ export default defineConfig({
|
|
|
357
516
|
},
|
|
358
517
|
}),
|
|
359
518
|
durableObjects: {
|
|
360
|
-
NOTES_DO: {
|
|
519
|
+
NOTES_DO: {
|
|
520
|
+
className: 'NotesDO',
|
|
521
|
+
files: ['notes.do.ts'],
|
|
522
|
+
},
|
|
361
523
|
},
|
|
362
524
|
auth: kuratchiAuthConfig({
|
|
363
525
|
cookieName: 'kuratchi_session',
|
|
@@ -375,6 +537,20 @@ npx kuratchi build # one-shot build
|
|
|
375
537
|
npx kuratchi watch # watch mode (for use with wrangler dev)
|
|
376
538
|
```
|
|
377
539
|
|
|
540
|
+
## Testing the Framework
|
|
541
|
+
|
|
542
|
+
Run framework tests from `packages/kuratchi-js`:
|
|
543
|
+
|
|
544
|
+
```bash
|
|
545
|
+
bun run test
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
Watch mode:
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
bun run test:watch
|
|
552
|
+
```
|
|
553
|
+
|
|
378
554
|
## TypeScript & Worker types
|
|
379
555
|
|
|
380
556
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { compile } from './compiler/index.js';
|
|
6
6
|
import * as path from 'node:path';
|
|
7
7
|
import * as fs from 'node:fs';
|
|
8
|
+
import * as net from 'node:net';
|
|
8
9
|
import { spawn } from 'node:child_process';
|
|
9
10
|
const args = process.argv.slice(2);
|
|
10
11
|
const command = args[0];
|
|
@@ -79,24 +80,65 @@ function runWatch(withWrangler = false) {
|
|
|
79
80
|
// `kuratchi dev` also starts the wrangler dev server.
|
|
80
81
|
// `kuratchi watch` is the compiler-only mode for custom setups.
|
|
81
82
|
if (withWrangler) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
stdio: 'inherit',
|
|
86
|
-
shell: process.platform === 'win32',
|
|
83
|
+
startWranglerDev().catch((err) => {
|
|
84
|
+
console.error(`[kuratchi] Failed to start wrangler dev: ${err?.message ?? err}`);
|
|
85
|
+
process.exit(1);
|
|
87
86
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function hasPortFlag(inputArgs) {
|
|
90
|
+
for (let i = 0; i < inputArgs.length; i++) {
|
|
91
|
+
const arg = inputArgs[i];
|
|
92
|
+
if (arg === '--port' || arg === '-p')
|
|
93
|
+
return true;
|
|
94
|
+
if (arg.startsWith('--port='))
|
|
95
|
+
return true;
|
|
96
|
+
if (arg.startsWith('-p='))
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
function isPortAvailable(port) {
|
|
102
|
+
return new Promise((resolve) => {
|
|
103
|
+
const server = net.createServer();
|
|
104
|
+
server.once('error', () => resolve(false));
|
|
105
|
+
server.once('listening', () => {
|
|
106
|
+
server.close(() => resolve(true));
|
|
100
107
|
});
|
|
108
|
+
server.listen(port, '127.0.0.1');
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async function findOpenPort(start = 8787, end = 8899) {
|
|
112
|
+
for (let port = start; port <= end; port++) {
|
|
113
|
+
if (await isPortAvailable(port))
|
|
114
|
+
return port;
|
|
115
|
+
}
|
|
116
|
+
throw new Error(`No open dev port found in range ${start}-${end}`);
|
|
117
|
+
}
|
|
118
|
+
async function startWranglerDev() {
|
|
119
|
+
const passthroughArgs = args.slice(1);
|
|
120
|
+
const wranglerArgs = ['wrangler', 'dev', ...passthroughArgs];
|
|
121
|
+
if (!hasPortFlag(passthroughArgs)) {
|
|
122
|
+
const port = await findOpenPort();
|
|
123
|
+
wranglerArgs.push('--port', String(port));
|
|
124
|
+
console.log(`[kuratchi] Starting wrangler dev on port ${port}`);
|
|
101
125
|
}
|
|
126
|
+
const wrangler = spawn('npx', wranglerArgs, {
|
|
127
|
+
cwd: projectDir,
|
|
128
|
+
stdio: 'inherit',
|
|
129
|
+
shell: process.platform === 'win32',
|
|
130
|
+
});
|
|
131
|
+
const cleanup = () => {
|
|
132
|
+
if (!wrangler.killed)
|
|
133
|
+
wrangler.kill();
|
|
134
|
+
};
|
|
135
|
+
process.on('exit', cleanup);
|
|
136
|
+
process.on('SIGINT', () => { cleanup(); process.exit(0); });
|
|
137
|
+
process.on('SIGTERM', () => { cleanup(); process.exit(0); });
|
|
138
|
+
wrangler.on('exit', (code) => {
|
|
139
|
+
if (code !== 0 && code !== null) {
|
|
140
|
+
console.error(`[kuratchi] wrangler exited with code ${code}`);
|
|
141
|
+
}
|
|
142
|
+
process.exit(code ?? 0);
|
|
143
|
+
});
|
|
102
144
|
}
|
package/dist/compiler/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Compiler
|
|
2
|
+
* Compiler â€" scans a project's routes/ directory, parses .html files,
|
|
3
3
|
* and generates a single Worker entry point.
|
|
4
4
|
*/
|
|
5
5
|
export { parseFile } from './parser.js';
|
|
@@ -27,7 +27,7 @@ export interface CompiledRoute {
|
|
|
27
27
|
/**
|
|
28
28
|
* Compile a project's src/routes/ into .kuratchi/routes.js
|
|
29
29
|
*
|
|
30
|
-
* The generated module exports { app }
|
|
30
|
+
* The generated module exports { app } â€" an object with a fetch() method
|
|
31
31
|
* that handles routing, load functions, form actions, and rendering.
|
|
32
32
|
* Returns the path to .kuratchi/worker.js — the stable wrangler entry point that
|
|
33
33
|
* re-exports everything from routes.js (default fetch handler + named DO class exports).
|