@kuratchi/js 0.0.21 → 0.0.23
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 +65 -29
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +90 -39
- package/dist/compiler/compiler-shared.d.ts +1 -0
- package/dist/compiler/config-reading.d.ts +6 -0
- package/dist/compiler/config-reading.js +92 -16
- package/dist/compiler/desktop-manifest.d.ts +48 -0
- package/dist/compiler/desktop-manifest.js +175 -0
- package/dist/compiler/durable-object-pipeline.d.ts +7 -3
- package/dist/compiler/durable-object-pipeline.js +30 -34
- package/dist/compiler/index.js +15 -5
- package/dist/compiler/parser.js +6 -0
- package/dist/compiler/routes-module-feature-blocks.js +5 -1
- package/dist/compiler/template.js +37 -3
- package/dist/compiler/worker-output-pipeline.d.ts +1 -0
- package/dist/compiler/worker-output-pipeline.js +11 -6
- package/dist/compiler/wrangler-sync.js +47 -0
- package/dist/create.js +19 -19
- package/dist/index.d.ts +1 -1
- package/dist/runtime/desktop.d.ts +19 -0
- package/dist/runtime/desktop.js +82 -0
- package/dist/runtime/generated-worker.js +11 -3
- package/dist/runtime/types.d.ts +37 -0
- package/package.json +1 -1
package/dist/create.js
CHANGED
|
@@ -192,11 +192,11 @@ function scaffold(dir, opts) {
|
|
|
192
192
|
if (orm || enableDO) {
|
|
193
193
|
dirs.push('src/schemas');
|
|
194
194
|
}
|
|
195
|
-
if (orm) {
|
|
196
|
-
dirs.push('src/
|
|
195
|
+
if (orm || enableDO || auth) {
|
|
196
|
+
dirs.push('src/server');
|
|
197
197
|
}
|
|
198
198
|
if (enableDO) {
|
|
199
|
-
dirs.push('src/
|
|
199
|
+
dirs.push('src/routes/notes');
|
|
200
200
|
}
|
|
201
201
|
if (auth) {
|
|
202
202
|
dirs.push('src/routes/auth', 'src/routes/auth/login', 'src/routes/auth/signup', 'src/routes/admin');
|
|
@@ -214,19 +214,19 @@ function scaffold(dir, opts) {
|
|
|
214
214
|
write(dir, 'src/routes/index.html', genLandingPage(opts));
|
|
215
215
|
if (orm) {
|
|
216
216
|
write(dir, 'src/schemas/app.ts', genSchema(opts));
|
|
217
|
-
write(dir, 'src/
|
|
217
|
+
write(dir, 'src/server/items.ts', genItemsCrud());
|
|
218
218
|
write(dir, 'src/routes/items/index.html', genItemsPage());
|
|
219
219
|
}
|
|
220
220
|
if (enableDO) {
|
|
221
221
|
write(dir, 'src/schemas/notes.ts', genNotesSchema());
|
|
222
222
|
write(dir, 'src/server/notes.do.ts', genNotesDoHandler());
|
|
223
|
-
write(dir, 'src/
|
|
223
|
+
write(dir, 'src/server/notes.ts', genNotesDb());
|
|
224
224
|
write(dir, 'src/routes/notes/index.html', genNotesPage());
|
|
225
225
|
}
|
|
226
226
|
if (auth) {
|
|
227
227
|
write(dir, '.dev.vars', genDevVars());
|
|
228
|
-
write(dir, 'src/
|
|
229
|
-
write(dir, 'src/
|
|
228
|
+
write(dir, 'src/server/auth.ts', genAuthFunctions());
|
|
229
|
+
write(dir, 'src/server/admin.ts', genAdminLoader());
|
|
230
230
|
write(dir, 'src/routes/auth/login/index.html', genLoginPage());
|
|
231
231
|
write(dir, 'src/routes/auth/signup/index.html', genSignupPage());
|
|
232
232
|
write(dir, 'src/routes/admin/index.html', genAdminPage());
|
|
@@ -449,7 +449,7 @@ export async function deleteNote(id: number): Promise<void> {
|
|
|
449
449
|
}
|
|
450
450
|
function genNotesPage() {
|
|
451
451
|
return `<script>
|
|
452
|
-
import { getNotes, addNote, deleteNote } from '$
|
|
452
|
+
import { getNotes, addNote, deleteNote } from '$server/notes';
|
|
453
453
|
|
|
454
454
|
const notes = await getNotes();
|
|
455
455
|
</script>
|
|
@@ -473,7 +473,7 @@ if (notes.length === 0) {
|
|
|
473
473
|
for (const note of notes) {
|
|
474
474
|
<article>
|
|
475
475
|
<span>{note.title}</span>
|
|
476
|
-
<button data-
|
|
476
|
+
<button data-post={deleteNote(note.id)} data-refresh="" type="button">Remove</button>
|
|
477
477
|
</article>
|
|
478
478
|
}
|
|
479
479
|
</section>
|
|
@@ -631,10 +631,10 @@ ${types}
|
|
|
631
631
|
`;
|
|
632
632
|
}
|
|
633
633
|
function genItemsCrud() {
|
|
634
|
-
return `import { env } from 'cloudflare:workers';
|
|
635
|
-
import { kuratchiORM } from '@kuratchi/orm';
|
|
636
|
-
import { redirect } from '${FRAMEWORK_PACKAGE_NAME}';
|
|
637
|
-
import type { Item } from '
|
|
634
|
+
return `import { env } from 'cloudflare:workers';
|
|
635
|
+
import { kuratchiORM } from '@kuratchi/orm';
|
|
636
|
+
import { redirect } from '${FRAMEWORK_PACKAGE_NAME}';
|
|
637
|
+
import type { Item } from '../schemas/app';
|
|
638
638
|
|
|
639
639
|
const db = kuratchiORM(() => (env as any).DB);
|
|
640
640
|
|
|
@@ -664,7 +664,7 @@ export async function toggleItem(id: number): Promise<void> {
|
|
|
664
664
|
}
|
|
665
665
|
function genItemsPage() {
|
|
666
666
|
return `<script>
|
|
667
|
-
import { getItems, addItem, deleteItem, toggleItem } from '$
|
|
667
|
+
import { getItems, addItem, deleteItem, toggleItem } from '$server/items';
|
|
668
668
|
import EmptyState from '@kuratchi/ui/empty-state.html';
|
|
669
669
|
|
|
670
670
|
const items = await getItems();
|
|
@@ -690,10 +690,10 @@ if (items.length === 0) {
|
|
|
690
690
|
<article>
|
|
691
691
|
<span style={item.done ? 'text-decoration: line-through; opacity: 0.5' : ''}>{item.title}</span>
|
|
692
692
|
<div>
|
|
693
|
-
<button data-
|
|
693
|
+
<button data-post={toggleItem(item.id)} data-refresh="" type="button">
|
|
694
694
|
{item.done ? '↩' : '✓'}
|
|
695
695
|
</button>
|
|
696
|
-
<button data-
|
|
696
|
+
<button data-post={deleteItem(item.id)} data-refresh="" type="button">✕</button>
|
|
697
697
|
</div>
|
|
698
698
|
</article>
|
|
699
699
|
}
|
|
@@ -905,7 +905,7 @@ export async function getAdminData() {
|
|
|
905
905
|
}
|
|
906
906
|
function genLoginPage() {
|
|
907
907
|
return `<script>
|
|
908
|
-
import { signIn } from '$
|
|
908
|
+
import { signIn } from '$server/auth';
|
|
909
909
|
import AuthCard from '@kuratchi/ui/auth-card.html';
|
|
910
910
|
</script>
|
|
911
911
|
|
|
@@ -933,7 +933,7 @@ function genLoginPage() {
|
|
|
933
933
|
}
|
|
934
934
|
function genSignupPage() {
|
|
935
935
|
return `<script>
|
|
936
|
-
import { signUp } from '$
|
|
936
|
+
import { signUp } from '$server/auth';
|
|
937
937
|
import AuthCard from '@kuratchi/ui/auth-card.html';
|
|
938
938
|
</script>
|
|
939
939
|
|
|
@@ -965,7 +965,7 @@ function genSignupPage() {
|
|
|
965
965
|
}
|
|
966
966
|
function genAdminPage() {
|
|
967
967
|
return `<script>
|
|
968
|
-
import { getAdminData, signOut } from '$
|
|
968
|
+
import { getAdminData, signOut } from '$server/admin';
|
|
969
969
|
import Badge from '@kuratchi/ui/badge.html';
|
|
970
970
|
import Card from '@kuratchi/ui/card.html';
|
|
971
971
|
import DataList from '@kuratchi/ui/data-list.html';
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { SchemaValidationError, schema, } from './runtime/schema.js';
|
|
|
12
12
|
export { ActionError } from './runtime/action.js';
|
|
13
13
|
export { PageError } from './runtime/page-error.js';
|
|
14
14
|
export { extractSubdomainSlug, extractSlugFromPrefix, matchContainerViewPath, rewriteProxyLocationHeader, buildContainerRequest, createContainerEnvVars, startContainer, proxyToContainer, handleContainerRouting, forwardJsonPostToContainerDO, matchSiteViewPath, buildSiteContainerRequest, createWpContainerEnvVars, startSiteContainer, proxyToSiteContainer, } from './runtime/containers.js';
|
|
15
|
-
export type { AppConfig, kuratchiConfig, DatabaseConfig, AuthConfig, RouteContext, RouteModule, RuntimeContext, RuntimeDefinition, RuntimeStep, RuntimeNext, RuntimeErrorResult, } from './runtime/types.js';
|
|
15
|
+
export type { AppConfig, kuratchiConfig, DatabaseConfig, DesktopConfig, DesktopRemoteBindingConfig, DesktopWindowConfig, AuthConfig, RouteContext, RouteModule, RuntimeContext, RuntimeDefinition, RuntimeStep, RuntimeNext, RuntimeErrorResult, } from './runtime/types.js';
|
|
16
16
|
export type { RpcOf } from './runtime/do.js';
|
|
17
17
|
export type { SchemaType, InferSchema } from './runtime/schema.js';
|
|
18
18
|
export { url, pathname, searchParams, headers, method, params, slug } from './runtime/request.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface DesktopNotificationPayload {
|
|
2
|
+
title: string;
|
|
3
|
+
body?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface DesktopCommandRequest {
|
|
6
|
+
command: string;
|
|
7
|
+
workingDirectory?: string;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface DesktopCommandResult {
|
|
11
|
+
ok: boolean;
|
|
12
|
+
error?: string | null;
|
|
13
|
+
exitCode: number;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
stdout: string;
|
|
16
|
+
stderr: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function showDesktopNotification(payload: DesktopNotificationPayload): Promise<boolean>;
|
|
19
|
+
export declare function runDesktopCommand(request: DesktopCommandRequest): Promise<DesktopCommandResult | null>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { getRequest } from './context.js';
|
|
2
|
+
function parseCookie(header, name) {
|
|
3
|
+
if (!header)
|
|
4
|
+
return null;
|
|
5
|
+
for (const part of header.split(';')) {
|
|
6
|
+
const [rawKey, ...rest] = part.split('=');
|
|
7
|
+
if ((rawKey ?? '').trim() !== name)
|
|
8
|
+
continue;
|
|
9
|
+
return rest.join('=').trim() || null;
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
function getDesktopApiOrigin() {
|
|
14
|
+
const request = getRequest();
|
|
15
|
+
const headerOrigin = request.headers.get('x-kuratchi-desktop-api-origin')
|
|
16
|
+
|| request.headers.get('x-kuratchi-desktop-origin');
|
|
17
|
+
if (headerOrigin)
|
|
18
|
+
return headerOrigin;
|
|
19
|
+
const cookieOrigin = parseCookie(request.headers.get('cookie'), '__kuratchi_desktop_api');
|
|
20
|
+
if (!cookieOrigin)
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
return decodeURIComponent(cookieOrigin);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return cookieOrigin;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function showDesktopNotification(payload) {
|
|
30
|
+
const title = payload.title?.trim();
|
|
31
|
+
if (!title) {
|
|
32
|
+
throw new Error('showDesktopNotification requires a title.');
|
|
33
|
+
}
|
|
34
|
+
const desktopApiOrigin = getDesktopApiOrigin();
|
|
35
|
+
if (!desktopApiOrigin) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const response = await fetch(new URL('/notifications/show', desktopApiOrigin), {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: {
|
|
41
|
+
'content-type': 'application/json',
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({
|
|
44
|
+
title,
|
|
45
|
+
body: payload.body ?? '',
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
if (response.status === 404) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
console.warn(`[kuratchi] Desktop notification request failed with status ${response.status}.`);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const payloadResult = await response.json().catch(() => null);
|
|
56
|
+
return payloadResult?.ok === true;
|
|
57
|
+
}
|
|
58
|
+
export async function runDesktopCommand(request) {
|
|
59
|
+
const command = request.command?.trim();
|
|
60
|
+
if (!command) {
|
|
61
|
+
throw new Error('runDesktopCommand requires a command.');
|
|
62
|
+
}
|
|
63
|
+
const desktopApiOrigin = getDesktopApiOrigin();
|
|
64
|
+
if (!desktopApiOrigin) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const response = await fetch(new URL('/commands/run', desktopApiOrigin), {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: {
|
|
70
|
+
'content-type': 'application/json',
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify({
|
|
73
|
+
command,
|
|
74
|
+
workingDirectory: request.workingDirectory ?? null,
|
|
75
|
+
timeoutMs: request.timeoutMs ?? 30000,
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw new Error(`[kuratchi] Desktop command request failed with status ${response.status}.`);
|
|
80
|
+
}
|
|
81
|
+
return await response.json();
|
|
82
|
+
}
|
|
@@ -392,14 +392,22 @@ function __attachCookies(response) {
|
|
|
392
392
|
const csrfCookie = getCsrfCookieHeader();
|
|
393
393
|
const hasCookies = (cookies && cookies.length > 0) || csrfCookie;
|
|
394
394
|
if (hasCookies) {
|
|
395
|
-
|
|
395
|
+
// Clone the response properly to avoid body stream issues with WARP/proxy layers.
|
|
396
|
+
// Using response.clone() ensures the body stream is properly duplicated.
|
|
397
|
+
const cloned = response.clone();
|
|
398
|
+
const newHeaders = new Headers(cloned.headers);
|
|
396
399
|
if (cookies) {
|
|
397
400
|
for (const header of cookies)
|
|
398
|
-
|
|
401
|
+
newHeaders.append('Set-Cookie', header);
|
|
399
402
|
}
|
|
400
403
|
if (csrfCookie) {
|
|
401
|
-
|
|
404
|
+
newHeaders.append('Set-Cookie', csrfCookie);
|
|
402
405
|
}
|
|
406
|
+
const newResponse = new Response(cloned.body, {
|
|
407
|
+
status: cloned.status,
|
|
408
|
+
statusText: cloned.statusText,
|
|
409
|
+
headers: newHeaders,
|
|
410
|
+
});
|
|
403
411
|
return __secHeaders(newResponse);
|
|
404
412
|
}
|
|
405
413
|
return __secHeaders(response);
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -78,6 +78,35 @@ export interface DatabaseConfig {
|
|
|
78
78
|
type?: 'd1' | 'do';
|
|
79
79
|
/** Skip migrations for this database (e.g., in production). Default: false */
|
|
80
80
|
skipMigrations?: boolean;
|
|
81
|
+
/** Use the deployed Cloudflare resource during local development. */
|
|
82
|
+
remote?: boolean;
|
|
83
|
+
}
|
|
84
|
+
export interface DesktopWindowConfig {
|
|
85
|
+
title?: string;
|
|
86
|
+
width?: number;
|
|
87
|
+
height?: number;
|
|
88
|
+
}
|
|
89
|
+
export interface DesktopRemoteBindingConfig {
|
|
90
|
+
type: 'd1' | 'r2';
|
|
91
|
+
remote?: boolean;
|
|
92
|
+
}
|
|
93
|
+
export interface DesktopConfig {
|
|
94
|
+
/** Human-readable app name shown by the desktop host. */
|
|
95
|
+
appName?: string;
|
|
96
|
+
/** Stable app identifier used by the host/runtime. */
|
|
97
|
+
appId?: string;
|
|
98
|
+
/** Initial route loaded by the host. Defaults to '/'. */
|
|
99
|
+
initialPath?: string;
|
|
100
|
+
/** Single-window host settings. */
|
|
101
|
+
window?: DesktopWindowConfig;
|
|
102
|
+
/** Desktop-native bindings exposed by the host. */
|
|
103
|
+
bindings?: {
|
|
104
|
+
notifications?: boolean;
|
|
105
|
+
files?: boolean;
|
|
106
|
+
[key: string]: any;
|
|
107
|
+
};
|
|
108
|
+
/** Remote Cloudflare bindings the desktop runtime should proxy. */
|
|
109
|
+
remoteBindings?: Record<string, DesktopRemoteBindingConfig>;
|
|
81
110
|
}
|
|
82
111
|
/**
|
|
83
112
|
* Framework configuration â€" the user-facing config file (kuratchi.config.ts)
|
|
@@ -108,6 +137,12 @@ export interface kuratchiConfig<E extends Env = Env> {
|
|
|
108
137
|
ui?: {
|
|
109
138
|
/** Theme to inject: 'default' uses @kuratchi/ui's built-in theme, or a path to a custom CSS file */
|
|
110
139
|
theme?: 'default' | string;
|
|
140
|
+
/** Corner radius preference used by the built-in UI helpers. */
|
|
141
|
+
radius?: 'default' | 'none' | 'full';
|
|
142
|
+
/** Optional first-party styling library integration. */
|
|
143
|
+
library?: 'tailwindcss';
|
|
144
|
+
/** Optional plugin list for the selected UI library. */
|
|
145
|
+
plugins?: string[];
|
|
111
146
|
};
|
|
112
147
|
/** Auth configuration â€" @kuratchi/auth plugin setup */
|
|
113
148
|
auth?: AuthConfig | Record<string, any>;
|
|
@@ -137,6 +172,8 @@ export interface kuratchiConfig<E extends Env = Env> {
|
|
|
137
172
|
/** DO source files (e.g. ['auth.do.ts', 'sites.do.ts']) */
|
|
138
173
|
files?: string[];
|
|
139
174
|
}>;
|
|
175
|
+
/** Desktop target configuration consumed by `kuratchi run`. */
|
|
176
|
+
desktop?: DesktopConfig;
|
|
140
177
|
}
|
|
141
178
|
/** Auth configuration for kuratchi.config.ts */
|
|
142
179
|
export interface AuthConfig {
|