@korajs/cli 0.4.0 → 0.6.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/dist/bin.cjs +1636 -601
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1086 -139
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-MVP5PDT4.js → chunk-EEZNRI5W.js} +51 -6
- package/dist/chunk-EEZNRI5W.js.map +1 -0
- package/dist/{chunk-YGVO4POI.js → chunk-Q2FBCOQD.js} +27 -15
- package/dist/chunk-Q2FBCOQD.js.map +1 -0
- package/dist/{chunk-CMSX76KM.js → chunk-VLTPEATY.js} +172 -99
- package/dist/chunk-VLTPEATY.js.map +1 -0
- package/dist/create.cjs +130 -54
- package/dist/create.cjs.map +1 -1
- package/dist/create.js +2 -2
- package/dist/index.cjs +143 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -6
- package/dist/index.d.ts +8 -6
- package/dist/index.js +2 -2
- package/package.json +9 -3
- package/templates/react-basic/README.md.hbs +10 -1
- package/templates/react-basic/src/App.tsx +112 -126
- package/templates/react-basic/src/index.css +165 -159
- package/templates/react-basic/src/kora-worker.ts +0 -1
- package/templates/react-basic/src/main.tsx +12 -12
- package/templates/react-basic/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-basic/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-basic/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-basic/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-basic/src/schema.ts +6 -12
- package/templates/react-basic/src/vite-env.d.ts +17 -0
- package/templates/react-basic/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-basic/vite.config.ts +57 -50
- package/templates/react-sync/.env.example +19 -0
- package/templates/react-sync/README.md.hbs +43 -1
- package/templates/react-sync/package.json.hbs +3 -0
- package/templates/react-sync/server.ts +90 -26
- package/templates/react-sync/src/App.tsx +132 -130
- package/templates/react-sync/src/auth.ts +25 -0
- package/templates/react-sync/src/index.css +212 -165
- package/templates/react-sync/src/kora-worker.ts +0 -1
- package/templates/react-sync/src/main.tsx +34 -18
- package/templates/react-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-sync/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-sync/src/schema.ts +6 -12
- package/templates/react-sync/src/vite-env.d.ts +17 -0
- package/templates/react-sync/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-sync/vite.config.ts +67 -60
- package/templates/react-tailwind/README.md.hbs +10 -1
- package/templates/react-tailwind/src/App.tsx +186 -197
- package/templates/react-tailwind/src/index.css +3 -3
- package/templates/react-tailwind/src/kora-worker.ts +0 -1
- package/templates/react-tailwind/src/main.tsx +19 -12
- package/templates/react-tailwind/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-tailwind/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-tailwind/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-tailwind/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-tailwind/src/schema.ts +6 -12
- package/templates/react-tailwind/src/vite-env.d.ts +17 -0
- package/templates/react-tailwind/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-tailwind/vite.config.ts +57 -50
- package/templates/react-tailwind-sync/.env.example +19 -0
- package/templates/react-tailwind-sync/README.md.hbs +43 -1
- package/templates/react-tailwind-sync/package.json.hbs +3 -0
- package/templates/react-tailwind-sync/server.ts +90 -26
- package/templates/react-tailwind-sync/src/App.tsx +246 -222
- package/templates/react-tailwind-sync/src/auth.ts +25 -0
- package/templates/react-tailwind-sync/src/index.css +3 -3
- package/templates/react-tailwind-sync/src/kora-worker.ts +0 -1
- package/templates/react-tailwind-sync/src/main.tsx +48 -18
- package/templates/react-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-tailwind-sync/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-tailwind-sync/src/schema.ts +6 -12
- package/templates/react-tailwind-sync/src/vite-env.d.ts +17 -0
- package/templates/react-tailwind-sync/{tsconfig.json → tsconfig.json.hbs} +1 -2
- package/templates/react-tailwind-sync/vite.config.ts +67 -60
- package/templates/svelte-basic/.env.example +3 -0
- package/templates/svelte-basic/README.md.hbs +62 -0
- package/templates/svelte-basic/index.html.hbs +12 -0
- package/templates/svelte-basic/kora.config.ts +12 -0
- package/templates/svelte-basic/package.json.hbs +27 -0
- package/templates/svelte-basic/src/App.svelte +131 -0
- package/templates/svelte-basic/src/Root.svelte +14 -0
- package/templates/svelte-basic/src/index.css +287 -0
- package/templates/svelte-basic/src/kora-worker.ts +9 -0
- package/templates/svelte-basic/src/main.ts +16 -0
- package/templates/svelte-basic/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-basic/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-basic/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-basic/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-basic/src/schema.ts +9 -0
- package/templates/svelte-basic/src/vite-env.d.ts +12 -0
- package/templates/svelte-basic/svelte.config.js +5 -0
- package/templates/svelte-basic/tsconfig.json.hbs +14 -0
- package/templates/svelte-basic/vite.config.ts +52 -0
- package/templates/svelte-sync/.env.example +25 -0
- package/templates/svelte-sync/README.md.hbs +105 -0
- package/templates/svelte-sync/index.html.hbs +12 -0
- package/templates/svelte-sync/kora.config.ts +17 -0
- package/templates/svelte-sync/package.json.hbs +43 -0
- package/templates/svelte-sync/server.ts +90 -0
- package/templates/svelte-sync/src/App.svelte +154 -0
- package/templates/svelte-sync/src/Root.svelte +22 -0
- package/templates/svelte-sync/src/auth.ts +25 -0
- package/templates/svelte-sync/src/index.css +339 -0
- package/templates/svelte-sync/src/kora-worker.ts +9 -0
- package/templates/svelte-sync/src/main.ts +32 -0
- package/templates/svelte-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-sync/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-sync/src/schema.ts +9 -0
- package/templates/svelte-sync/src/vite-env.d.ts +21 -0
- package/templates/svelte-sync/svelte.config.js +5 -0
- package/templates/svelte-sync/tsconfig.json.hbs +14 -0
- package/templates/svelte-sync/vite.config.ts +79 -0
- package/templates/svelte-tailwind/README.md.hbs +62 -0
- package/templates/svelte-tailwind/index.html.hbs +12 -0
- package/templates/svelte-tailwind/kora.config.ts +12 -0
- package/templates/svelte-tailwind/package.json.hbs +30 -0
- package/templates/svelte-tailwind/src/App.svelte +79 -0
- package/templates/svelte-tailwind/src/Root.svelte +14 -0
- package/templates/svelte-tailwind/src/index.css +7 -0
- package/templates/svelte-tailwind/src/kora-worker.ts +9 -0
- package/templates/svelte-tailwind/src/main.ts +16 -0
- package/templates/svelte-tailwind/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-tailwind/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-tailwind/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-tailwind/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-tailwind/src/schema.ts +9 -0
- package/templates/svelte-tailwind/src/vite-env.d.ts +12 -0
- package/templates/svelte-tailwind/svelte.config.js +5 -0
- package/templates/svelte-tailwind/tsconfig.json.hbs +14 -0
- package/templates/svelte-tailwind/vite.config.ts +53 -0
- package/templates/svelte-tailwind-sync/.env.example +25 -0
- package/templates/svelte-tailwind-sync/README.md.hbs +105 -0
- package/templates/svelte-tailwind-sync/index.html.hbs +12 -0
- package/templates/svelte-tailwind-sync/kora.config.ts +17 -0
- package/templates/svelte-tailwind-sync/package.json.hbs +46 -0
- package/templates/svelte-tailwind-sync/server.ts +90 -0
- package/templates/svelte-tailwind-sync/src/App.svelte +196 -0
- package/templates/svelte-tailwind-sync/src/Root.svelte +26 -0
- package/templates/svelte-tailwind-sync/src/auth.ts +25 -0
- package/templates/svelte-tailwind-sync/src/index.css +7 -0
- package/templates/svelte-tailwind-sync/src/kora-worker.ts +9 -0
- package/templates/svelte-tailwind-sync/src/main.ts +32 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-tailwind-sync/src/schema.ts +9 -0
- package/templates/svelte-tailwind-sync/src/vite-env.d.ts +21 -0
- package/templates/svelte-tailwind-sync/svelte.config.js +5 -0
- package/templates/svelte-tailwind-sync/tsconfig.json.hbs +14 -0
- package/templates/svelte-tailwind-sync/vite.config.ts +80 -0
- package/templates/tauri-react/.env.example +20 -0
- package/templates/tauri-react/README.md.hbs +36 -1
- package/templates/tauri-react/dev.ts +11 -11
- package/templates/tauri-react/package.json.hbs +2 -0
- package/templates/tauri-react/server.ts +86 -12
- package/templates/tauri-react/src/App.tsx +769 -544
- package/templates/tauri-react/src/AppShell.tsx +125 -83
- package/templates/tauri-react/src/SetupScreen.tsx +257 -208
- package/templates/tauri-react/src/auth.ts +45 -0
- package/templates/tauri-react/src/main.tsx +3 -3
- package/templates/tauri-react/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/tauri-react/src/modules/todos/todo.queries.ts +5 -0
- package/templates/tauri-react/src/modules/todos/todo.schema.ts +10 -0
- package/templates/tauri-react/src/modules/todos/useTodos.ts +31 -0
- package/templates/tauri-react/src/schema.ts +6 -12
- package/templates/tauri-react/src/sync-config.ts +47 -47
- package/templates/tauri-react/src/updater.ts +15 -15
- package/templates/tauri-react/src/vite-env.d.ts +17 -0
- package/templates/tauri-react/src-tauri/tauri.conf.json +1 -1
- package/templates/tauri-react/tsconfig.json.hbs +14 -0
- package/templates/tauri-react/vite.config.ts +9 -9
- package/templates/vue-basic/README.md.hbs +62 -0
- package/templates/vue-basic/index.html.hbs +12 -0
- package/templates/vue-basic/kora.config.ts +12 -0
- package/templates/vue-basic/package.json.hbs +26 -0
- package/templates/vue-basic/src/App.vue +117 -0
- package/templates/vue-basic/src/index.css +287 -0
- package/templates/vue-basic/src/kora-worker.ts +9 -0
- package/templates/vue-basic/src/main.ts +20 -0
- package/templates/vue-basic/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-basic/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-basic/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-basic/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-basic/src/schema.ts +9 -0
- package/templates/vue-basic/src/vite-env.d.ts +17 -0
- package/templates/vue-basic/tsconfig.json.hbs +15 -0
- package/templates/vue-basic/vite.config.ts +69 -0
- package/templates/vue-sync/.env.example +25 -0
- package/templates/vue-sync/README.md.hbs +105 -0
- package/templates/vue-sync/index.html.hbs +12 -0
- package/templates/vue-sync/kora.config.ts +17 -0
- package/templates/vue-sync/package.json.hbs +42 -0
- package/templates/vue-sync/server.ts +90 -0
- package/templates/vue-sync/src/App.vue +135 -0
- package/templates/vue-sync/src/auth.ts +25 -0
- package/templates/vue-sync/src/index.css +339 -0
- package/templates/vue-sync/src/kora-worker.ts +9 -0
- package/templates/vue-sync/src/main.ts +43 -0
- package/templates/vue-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-sync/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-sync/src/schema.ts +9 -0
- package/templates/vue-sync/src/vite-env.d.ts +26 -0
- package/templates/vue-sync/tsconfig.json.hbs +15 -0
- package/templates/vue-sync/vite.config.ts +79 -0
- package/templates/vue-tailwind/README.md.hbs +62 -0
- package/templates/vue-tailwind/index.html.hbs +12 -0
- package/templates/vue-tailwind/kora.config.ts +12 -0
- package/templates/vue-tailwind/package.json.hbs +29 -0
- package/templates/vue-tailwind/src/App.vue +142 -0
- package/templates/vue-tailwind/src/index.css +7 -0
- package/templates/vue-tailwind/src/kora-worker.ts +9 -0
- package/templates/vue-tailwind/src/main.ts +31 -0
- package/templates/vue-tailwind/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-tailwind/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-tailwind/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-tailwind/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-tailwind/src/schema.ts +9 -0
- package/templates/vue-tailwind/src/vite-env.d.ts +17 -0
- package/templates/vue-tailwind/tsconfig.json.hbs +15 -0
- package/templates/vue-tailwind/vite.config.ts +70 -0
- package/templates/vue-tailwind-sync/.env.example +25 -0
- package/templates/vue-tailwind-sync/README.md.hbs +105 -0
- package/templates/vue-tailwind-sync/index.html.hbs +12 -0
- package/templates/vue-tailwind-sync/kora.config.ts +17 -0
- package/templates/vue-tailwind-sync/package.json.hbs +45 -0
- package/templates/vue-tailwind-sync/server.ts +90 -0
- package/templates/vue-tailwind-sync/src/App.vue +245 -0
- package/templates/vue-tailwind-sync/src/auth.ts +25 -0
- package/templates/vue-tailwind-sync/src/index.css +7 -0
- package/templates/vue-tailwind-sync/src/kora-worker.ts +9 -0
- package/templates/vue-tailwind-sync/src/main.ts +56 -0
- package/templates/vue-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-tailwind-sync/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-tailwind-sync/src/schema.ts +9 -0
- package/templates/vue-tailwind-sync/src/vite-env.d.ts +26 -0
- package/templates/vue-tailwind-sync/tsconfig.json.hbs +15 -0
- package/templates/vue-tailwind-sync/vite.config.ts +80 -0
- package/dist/chunk-CMSX76KM.js.map +0 -1
- package/dist/chunk-MVP5PDT4.js.map +0 -1
- package/dist/chunk-YGVO4POI.js.map +0 -1
- package/templates/tauri-react/tsconfig.json +0 -15
package/dist/bin.cjs
CHANGED
|
@@ -23,15 +23,224 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
));
|
|
24
24
|
|
|
25
25
|
// src/bin.ts
|
|
26
|
-
var
|
|
26
|
+
var import_citty11 = require("citty");
|
|
27
27
|
|
|
28
|
-
// src/commands/
|
|
29
|
-
var
|
|
30
|
-
var
|
|
31
|
-
var import_node_path5 = require("path");
|
|
32
|
-
var import_node_url2 = require("url");
|
|
28
|
+
// src/commands/backup/backup-command.ts
|
|
29
|
+
var import_promises = require("fs/promises");
|
|
30
|
+
var import_node_path = require("path");
|
|
33
31
|
var import_citty = require("citty");
|
|
34
32
|
|
|
33
|
+
// src/utils/logger.ts
|
|
34
|
+
var RESET = "\x1B[0m";
|
|
35
|
+
var BOLD = "\x1B[1m";
|
|
36
|
+
var DIM = "\x1B[2m";
|
|
37
|
+
var GREEN = "\x1B[32m";
|
|
38
|
+
var YELLOW = "\x1B[33m";
|
|
39
|
+
var RED = "\x1B[31m";
|
|
40
|
+
var CYAN = "\x1B[36m";
|
|
41
|
+
function createLogger(options) {
|
|
42
|
+
const colorDisabled = options?.noColor === true || process.env.NO_COLOR !== void 0 || !process.stdout.isTTY;
|
|
43
|
+
function color(code, text) {
|
|
44
|
+
return colorDisabled ? text : `${code}${text}${RESET}`;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
info(message) {
|
|
48
|
+
console.log(color(CYAN, message));
|
|
49
|
+
},
|
|
50
|
+
success(message) {
|
|
51
|
+
console.log(color(GREEN, ` \u2713 ${message}`));
|
|
52
|
+
},
|
|
53
|
+
warn(message) {
|
|
54
|
+
console.warn(color(YELLOW, ` \u26A0 ${message}`));
|
|
55
|
+
},
|
|
56
|
+
error(message) {
|
|
57
|
+
console.error(color(RED, ` \u2717 ${message}`));
|
|
58
|
+
},
|
|
59
|
+
step(message) {
|
|
60
|
+
console.log(color(DIM, ` ${message}`));
|
|
61
|
+
},
|
|
62
|
+
blank() {
|
|
63
|
+
console.log();
|
|
64
|
+
},
|
|
65
|
+
banner() {
|
|
66
|
+
console.log();
|
|
67
|
+
console.log(
|
|
68
|
+
color(BOLD + CYAN, " Kora.js") + color(DIM, " \u2014 Offline-first application framework")
|
|
69
|
+
);
|
|
70
|
+
console.log();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/commands/backup/backup-command.ts
|
|
76
|
+
var DEFAULT_SYNC_PORT = 3001;
|
|
77
|
+
var backupCommand = (0, import_citty.defineCommand)({
|
|
78
|
+
meta: {
|
|
79
|
+
name: "backup",
|
|
80
|
+
description: "Backup and restore Kora sync server data"
|
|
81
|
+
},
|
|
82
|
+
subCommands: {
|
|
83
|
+
create: (0, import_citty.defineCommand)({
|
|
84
|
+
meta: {
|
|
85
|
+
name: "create",
|
|
86
|
+
description: "Create a backup of the sync server"
|
|
87
|
+
},
|
|
88
|
+
args: {
|
|
89
|
+
url: {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "Sync server URL (default: http://localhost:3001)",
|
|
92
|
+
default: `http://localhost:${DEFAULT_SYNC_PORT}`
|
|
93
|
+
},
|
|
94
|
+
out: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "Output file path (default: kora-backup-<timestamp>.kora)"
|
|
97
|
+
},
|
|
98
|
+
token: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "Backup token (defaults to KORA_BACKUP_TOKEN or KORA_ADMIN_TOKEN)"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
async run({ args }) {
|
|
104
|
+
const logger = createLogger();
|
|
105
|
+
const url = typeof args.url === "string" ? args.url : `http://localhost:${DEFAULT_SYNC_PORT}`;
|
|
106
|
+
const outFile = typeof args.out === "string" ? args.out : `kora-backup-${Date.now()}.kora`;
|
|
107
|
+
const token = typeof args.token === "string" ? args.token : process.env.KORA_BACKUP_TOKEN ?? process.env.KORA_ADMIN_TOKEN;
|
|
108
|
+
logger.banner();
|
|
109
|
+
logger.info(`Exporting backup from ${url}...`);
|
|
110
|
+
try {
|
|
111
|
+
const backupUrl = `${url.replace(/\/$/, "")}/__kora/backup/export`;
|
|
112
|
+
const response = await fetch(backupUrl, {
|
|
113
|
+
method: "POST",
|
|
114
|
+
headers: token ? { Authorization: `Bearer ${token}` } : void 0
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Backup failed: ${error.message ?? response.statusText}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const buffer = await response.arrayBuffer();
|
|
123
|
+
await (0, import_promises.writeFile)(outFile, new Uint8Array(buffer));
|
|
124
|
+
const size = (buffer.byteLength / 1024).toFixed(1);
|
|
125
|
+
logger.success(`Backup saved to ${outFile} (${size} KB)`);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
logger.error("Backup failed");
|
|
128
|
+
if (error instanceof Error) logger.error(error.message);
|
|
129
|
+
logger.blank();
|
|
130
|
+
logger.step("Make sure the Kora sync server is running.");
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}),
|
|
135
|
+
restore: (0, import_citty.defineCommand)({
|
|
136
|
+
meta: {
|
|
137
|
+
name: "restore",
|
|
138
|
+
description: "Restore a backup to the sync server"
|
|
139
|
+
},
|
|
140
|
+
args: {
|
|
141
|
+
file: {
|
|
142
|
+
type: "string",
|
|
143
|
+
description: "Backup file path",
|
|
144
|
+
required: true
|
|
145
|
+
},
|
|
146
|
+
url: {
|
|
147
|
+
type: "string",
|
|
148
|
+
description: "Sync server URL (default: http://localhost:3001)",
|
|
149
|
+
default: `http://localhost:${DEFAULT_SYNC_PORT}`
|
|
150
|
+
},
|
|
151
|
+
merge: {
|
|
152
|
+
type: "boolean",
|
|
153
|
+
description: "Merge with existing data instead of replacing",
|
|
154
|
+
default: false
|
|
155
|
+
},
|
|
156
|
+
token: {
|
|
157
|
+
type: "string",
|
|
158
|
+
description: "Backup token (defaults to KORA_BACKUP_TOKEN or KORA_ADMIN_TOKEN)"
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
async run({ args }) {
|
|
162
|
+
const logger = createLogger();
|
|
163
|
+
const filePath = typeof args.file === "string" ? args.file : "";
|
|
164
|
+
const url = typeof args.url === "string" ? args.url : `http://localhost:${DEFAULT_SYNC_PORT}`;
|
|
165
|
+
const merge = args.merge === true;
|
|
166
|
+
const token = typeof args.token === "string" ? args.token : process.env.KORA_BACKUP_TOKEN ?? process.env.KORA_ADMIN_TOKEN;
|
|
167
|
+
logger.banner();
|
|
168
|
+
logger.info(`Restoring backup from ${filePath} to ${url}...`);
|
|
169
|
+
try {
|
|
170
|
+
const data = await (0, import_promises.readFile)(filePath);
|
|
171
|
+
const restoreUrl = `${url.replace(/\/$/, "")}/__kora/backup/import?merge=${merge}`;
|
|
172
|
+
const response = await fetch(restoreUrl, {
|
|
173
|
+
method: "POST",
|
|
174
|
+
headers: token ? { Authorization: `Bearer ${token}` } : void 0,
|
|
175
|
+
body: data
|
|
176
|
+
});
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
179
|
+
throw new Error(
|
|
180
|
+
`Restore failed: ${error.message ?? response.statusText}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const result = await response.json();
|
|
184
|
+
if (result.success) {
|
|
185
|
+
logger.success(
|
|
186
|
+
`Restored ${result.operationsRestored} operations${result.duration ? ` in ${result.duration}ms` : ""}`
|
|
187
|
+
);
|
|
188
|
+
} else {
|
|
189
|
+
logger.error("Restore completed with errors");
|
|
190
|
+
}
|
|
191
|
+
} catch (error) {
|
|
192
|
+
logger.error("Restore failed");
|
|
193
|
+
if (error instanceof Error) logger.error(error.message);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}),
|
|
198
|
+
info: (0, import_citty.defineCommand)({
|
|
199
|
+
meta: {
|
|
200
|
+
name: "info",
|
|
201
|
+
description: "Show backup file information"
|
|
202
|
+
},
|
|
203
|
+
args: {
|
|
204
|
+
file: {
|
|
205
|
+
type: "string",
|
|
206
|
+
description: "Backup file path",
|
|
207
|
+
required: true
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
async run({ args }) {
|
|
211
|
+
const logger = createLogger();
|
|
212
|
+
const filePath = typeof args.file === "string" ? args.file : "";
|
|
213
|
+
try {
|
|
214
|
+
const data = await (0, import_promises.readFile)(filePath);
|
|
215
|
+
const { readBackupManifest } = await import("@korajs/store");
|
|
216
|
+
const manifest = readBackupManifest(
|
|
217
|
+
new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
|
|
218
|
+
);
|
|
219
|
+
logger.banner();
|
|
220
|
+
logger.info(`Backup: ${filePath}`);
|
|
221
|
+
logger.blank();
|
|
222
|
+
logger.step(`Created: ${new Date(manifest.createdAt).toISOString()}`);
|
|
223
|
+
logger.step(`Node ID: ${manifest.nodeId}`);
|
|
224
|
+
logger.step(`Schema: v${manifest.schemaVersion}`);
|
|
225
|
+
logger.step(`Operations: ${manifest.operationCount.toLocaleString()}`);
|
|
226
|
+
logger.step(`Collections: ${manifest.collections.join(", ") || "(none)"}`);
|
|
227
|
+
logger.step(`Checksum: ${manifest.checksum.slice(0, 16)}...`);
|
|
228
|
+
logger.blank();
|
|
229
|
+
logger.step(`Format version: ${manifest.version}`);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
logger.error("Failed to read backup file");
|
|
232
|
+
if (error instanceof Error) logger.error(error.message);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// src/commands/compact/compact-command.ts
|
|
241
|
+
var import_node_path5 = require("path");
|
|
242
|
+
var import_citty2 = require("citty");
|
|
243
|
+
|
|
35
244
|
// src/errors.ts
|
|
36
245
|
var import_core = require("@korajs/core");
|
|
37
246
|
var ProjectExistsError = class extends import_core.KoraError {
|
|
@@ -81,9 +290,333 @@ var DevServerError = class extends import_core.KoraError {
|
|
|
81
290
|
this.searchPath = searchPath;
|
|
82
291
|
this.name = "DevServerError";
|
|
83
292
|
}
|
|
84
|
-
binary;
|
|
85
|
-
searchPath;
|
|
86
|
-
};
|
|
293
|
+
binary;
|
|
294
|
+
searchPath;
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// src/utils/fs-helpers.ts
|
|
298
|
+
var import_promises2 = require("fs/promises");
|
|
299
|
+
var import_node_path2 = require("path");
|
|
300
|
+
async function directoryExists(path) {
|
|
301
|
+
try {
|
|
302
|
+
await (0, import_promises2.access)(path);
|
|
303
|
+
return true;
|
|
304
|
+
} catch {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async function findProjectRoot(startDir) {
|
|
309
|
+
let current = (0, import_node_path2.resolve)(startDir ?? process.cwd());
|
|
310
|
+
for (; ; ) {
|
|
311
|
+
const pkgPath = (0, import_node_path2.join)(current, "package.json");
|
|
312
|
+
try {
|
|
313
|
+
const content = await (0, import_promises2.readFile)(pkgPath, "utf-8");
|
|
314
|
+
const pkg = JSON.parse(content);
|
|
315
|
+
if (isKoraProject(pkg)) {
|
|
316
|
+
return current;
|
|
317
|
+
}
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
const parent = (0, import_node_path2.dirname)(current);
|
|
321
|
+
if (parent === current) break;
|
|
322
|
+
current = parent;
|
|
323
|
+
}
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
async function findSchemaFile(projectRoot) {
|
|
327
|
+
const candidates = [
|
|
328
|
+
(0, import_node_path2.join)(projectRoot, "src", "schema.ts"),
|
|
329
|
+
(0, import_node_path2.join)(projectRoot, "schema.ts"),
|
|
330
|
+
(0, import_node_path2.join)(projectRoot, "src", "schema.js"),
|
|
331
|
+
(0, import_node_path2.join)(projectRoot, "schema.js")
|
|
332
|
+
];
|
|
333
|
+
for (const candidate of candidates) {
|
|
334
|
+
try {
|
|
335
|
+
await (0, import_promises2.access)(candidate);
|
|
336
|
+
return candidate;
|
|
337
|
+
} catch {
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
async function hasTsxInstalled(projectRoot) {
|
|
343
|
+
try {
|
|
344
|
+
await (0, import_promises2.access)((0, import_node_path2.join)(projectRoot, "node_modules", "tsx", "package.json"));
|
|
345
|
+
return true;
|
|
346
|
+
} catch {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async function resolveProjectBinaryEntryPoint(projectRoot, packageName, binaryName) {
|
|
351
|
+
const pkgJsonPath = (0, import_node_path2.join)(projectRoot, "node_modules", packageName, "package.json");
|
|
352
|
+
try {
|
|
353
|
+
const content = await (0, import_promises2.readFile)(pkgJsonPath, "utf-8");
|
|
354
|
+
const pkg = JSON.parse(content);
|
|
355
|
+
let binPath;
|
|
356
|
+
if (typeof pkg.bin === "string") {
|
|
357
|
+
binPath = pkg.bin;
|
|
358
|
+
} else if (typeof pkg.bin === "object" && pkg.bin !== null) {
|
|
359
|
+
binPath = pkg.bin[binaryName];
|
|
360
|
+
}
|
|
361
|
+
if (!binPath) return null;
|
|
362
|
+
const fullPath = (0, import_node_path2.join)(projectRoot, "node_modules", packageName, binPath);
|
|
363
|
+
await (0, import_promises2.access)(fullPath);
|
|
364
|
+
return fullPath;
|
|
365
|
+
} catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function isKoraProject(pkg) {
|
|
370
|
+
if (typeof pkg !== "object" || pkg === null) return false;
|
|
371
|
+
const record = pkg;
|
|
372
|
+
return hasKoraDep(record.dependencies) || hasKoraDep(record.devDependencies);
|
|
373
|
+
}
|
|
374
|
+
function hasKoraDep(deps) {
|
|
375
|
+
if (typeof deps !== "object" || deps === null) return false;
|
|
376
|
+
return Object.keys(deps).some((key) => key === "kora" || key.startsWith("@korajs/"));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/commands/dev/kora-config.ts
|
|
380
|
+
var import_node_child_process = require("child_process");
|
|
381
|
+
var import_promises3 = require("fs/promises");
|
|
382
|
+
var import_node_path3 = require("path");
|
|
383
|
+
var import_node_url = require("url");
|
|
384
|
+
var CONFIG_CANDIDATES = [
|
|
385
|
+
"kora.config.ts",
|
|
386
|
+
"kora.config.mts",
|
|
387
|
+
"kora.config.cts",
|
|
388
|
+
"kora.config.js",
|
|
389
|
+
"kora.config.mjs",
|
|
390
|
+
"kora.config.cjs"
|
|
391
|
+
];
|
|
392
|
+
async function loadKoraConfig(projectRoot) {
|
|
393
|
+
const configPath = await findKoraConfigFile(projectRoot);
|
|
394
|
+
if (!configPath) return null;
|
|
395
|
+
const ext = (0, import_node_path3.extname)(configPath);
|
|
396
|
+
if (ext === ".ts" || ext === ".mts" || ext === ".cts") {
|
|
397
|
+
const loaded2 = await loadTypeScriptConfig(configPath, projectRoot);
|
|
398
|
+
return toConfigObject(loaded2);
|
|
399
|
+
}
|
|
400
|
+
const loaded = await import((0, import_node_url.pathToFileURL)(configPath).href);
|
|
401
|
+
return toConfigObject(loaded);
|
|
402
|
+
}
|
|
403
|
+
async function findKoraConfigFile(projectRoot) {
|
|
404
|
+
for (const file of CONFIG_CANDIDATES) {
|
|
405
|
+
const candidate = (0, import_node_path3.join)(projectRoot, file);
|
|
406
|
+
try {
|
|
407
|
+
await (0, import_promises3.access)(candidate);
|
|
408
|
+
return candidate;
|
|
409
|
+
} catch {
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
async function loadTypeScriptConfig(configPath, projectRoot) {
|
|
415
|
+
if (!await hasTsxInstalled(projectRoot)) {
|
|
416
|
+
throw new Error(
|
|
417
|
+
`Found TypeScript config at ${configPath}, but "tsx" is not installed in this project. Install tsx or use kora.config.js.`
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
const script = "const configPath = process.argv[process.argv.length - 1];import('node:url').then(u => import(u.pathToFileURL(configPath).href)).then(mod => { const v = mod.default ?? mod; process.stdout.write(JSON.stringify(v)) }).catch(e => { process.stderr.write(String(e)); process.exit(1) })";
|
|
421
|
+
const output = await runCommand(
|
|
422
|
+
process.execPath,
|
|
423
|
+
["--import", "tsx", "--eval", script, configPath],
|
|
424
|
+
projectRoot
|
|
425
|
+
);
|
|
426
|
+
try {
|
|
427
|
+
return JSON.parse(output);
|
|
428
|
+
} catch {
|
|
429
|
+
throw new Error(`Failed to parse ${configPath} output as JSON.`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
async function runCommand(command, args, cwd) {
|
|
433
|
+
return await new Promise((resolve11, reject) => {
|
|
434
|
+
const child = (0, import_node_child_process.spawn)(command, args, {
|
|
435
|
+
cwd,
|
|
436
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
437
|
+
env: process.env
|
|
438
|
+
});
|
|
439
|
+
let stdout = "";
|
|
440
|
+
let stderr = "";
|
|
441
|
+
child.stdout?.on("data", (chunk) => {
|
|
442
|
+
stdout += chunk.toString("utf-8");
|
|
443
|
+
});
|
|
444
|
+
child.stderr?.on("data", (chunk) => {
|
|
445
|
+
stderr += chunk.toString("utf-8");
|
|
446
|
+
});
|
|
447
|
+
child.on("error", (error) => {
|
|
448
|
+
reject(error);
|
|
449
|
+
});
|
|
450
|
+
child.on("exit", (code) => {
|
|
451
|
+
if (code === 0) {
|
|
452
|
+
resolve11(stdout.trim());
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
reject(new Error(`Failed to load kora config (exit ${code ?? "unknown"}): ${stderr.trim()}`));
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
function toConfigObject(mod) {
|
|
460
|
+
if (typeof mod !== "object" || mod === null) {
|
|
461
|
+
throw new Error("kora config must export an object.");
|
|
462
|
+
}
|
|
463
|
+
const value = mod.default ?? mod;
|
|
464
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
465
|
+
throw new Error("kora config must export an object.");
|
|
466
|
+
}
|
|
467
|
+
return value;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/commands/migrate/schema-loader.ts
|
|
471
|
+
var import_node_child_process2 = require("child_process");
|
|
472
|
+
var import_node_path4 = require("path");
|
|
473
|
+
var import_node_url2 = require("url");
|
|
474
|
+
async function loadSchemaDefinition(schemaPath, projectRoot) {
|
|
475
|
+
const ext = (0, import_node_path4.extname)(schemaPath);
|
|
476
|
+
const moduleValue = ext === ".ts" || ext === ".mts" || ext === ".cts" ? await loadTypeScriptModule(schemaPath, projectRoot) : await import(`${(0, import_node_url2.pathToFileURL)(schemaPath).href}?t=${Date.now()}-${Math.random()}`);
|
|
477
|
+
return extractSchema(moduleValue);
|
|
478
|
+
}
|
|
479
|
+
function extractSchema(value) {
|
|
480
|
+
if (typeof value !== "object" || value === null) {
|
|
481
|
+
throw new Error("Schema module must export an object.");
|
|
482
|
+
}
|
|
483
|
+
const moduleRecord = value;
|
|
484
|
+
const candidate = moduleRecord.default ?? moduleRecord;
|
|
485
|
+
if (!isSchemaDefinition(candidate)) {
|
|
486
|
+
throw new Error("Schema module must export a valid SchemaDefinition as default export.");
|
|
487
|
+
}
|
|
488
|
+
return candidate;
|
|
489
|
+
}
|
|
490
|
+
function isSchemaDefinition(value) {
|
|
491
|
+
if (typeof value !== "object" || value === null) return false;
|
|
492
|
+
const object = value;
|
|
493
|
+
return typeof object.version === "number" && typeof object.collections === "object" && object.collections !== null && typeof object.relations === "object" && object.relations !== null;
|
|
494
|
+
}
|
|
495
|
+
async function loadTypeScriptModule(schemaPath, projectRoot) {
|
|
496
|
+
if (!await hasTsxInstalled(projectRoot)) {
|
|
497
|
+
throw new Error(
|
|
498
|
+
`Schema file is TypeScript (${schemaPath}) but local "tsx" was not found. Install tsx in the project.`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
const script = "const modulePath = process.argv[process.argv.length - 1];import('node:url').then(u => import(u.pathToFileURL(modulePath).href)).then(mod => { const v = mod.default ?? mod; process.stdout.write(JSON.stringify(v)) }).catch(e => { process.stderr.write(String(e)); process.exit(1) })";
|
|
502
|
+
const output = await runCommand2(
|
|
503
|
+
process.execPath,
|
|
504
|
+
["--import", "tsx", "--eval", script, schemaPath],
|
|
505
|
+
projectRoot
|
|
506
|
+
);
|
|
507
|
+
try {
|
|
508
|
+
return JSON.parse(output);
|
|
509
|
+
} catch {
|
|
510
|
+
throw new Error(`Failed to parse schema module output for ${schemaPath}`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async function runCommand2(command, args, cwd) {
|
|
514
|
+
return await new Promise((resolve11, reject) => {
|
|
515
|
+
const child = (0, import_node_child_process2.spawn)(command, args, {
|
|
516
|
+
cwd,
|
|
517
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
518
|
+
env: process.env
|
|
519
|
+
});
|
|
520
|
+
let stdout = "";
|
|
521
|
+
let stderr = "";
|
|
522
|
+
child.stdout?.on("data", (chunk) => {
|
|
523
|
+
stdout += chunk.toString("utf-8");
|
|
524
|
+
});
|
|
525
|
+
child.stderr?.on("data", (chunk) => {
|
|
526
|
+
stderr += chunk.toString("utf-8");
|
|
527
|
+
});
|
|
528
|
+
child.on("error", (error) => {
|
|
529
|
+
reject(error);
|
|
530
|
+
});
|
|
531
|
+
child.on("exit", (code) => {
|
|
532
|
+
if (code === 0) {
|
|
533
|
+
resolve11(stdout.trim());
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
reject(
|
|
537
|
+
new Error(`Failed to load TypeScript schema (exit ${code ?? "unknown"}): ${stderr.trim()}`)
|
|
538
|
+
);
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// src/commands/compact/compact-command.ts
|
|
544
|
+
var compactCommand = (0, import_citty2.defineCommand)({
|
|
545
|
+
meta: {
|
|
546
|
+
name: "compact",
|
|
547
|
+
description: "Compact the local Kora operation log after server ack"
|
|
548
|
+
},
|
|
549
|
+
args: {
|
|
550
|
+
db: {
|
|
551
|
+
type: "string",
|
|
552
|
+
description: "Path to the local SQLite database (required)"
|
|
553
|
+
},
|
|
554
|
+
schema: {
|
|
555
|
+
type: "string",
|
|
556
|
+
description: "Path to schema file"
|
|
557
|
+
},
|
|
558
|
+
strategy: {
|
|
559
|
+
type: "string",
|
|
560
|
+
description: "Compaction strategy: after-ack | after-days | never",
|
|
561
|
+
default: "after-ack"
|
|
562
|
+
},
|
|
563
|
+
days: {
|
|
564
|
+
type: "string",
|
|
565
|
+
description: "Age threshold when strategy is after-days",
|
|
566
|
+
default: "30"
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
async run({ args }) {
|
|
570
|
+
const logger = createLogger();
|
|
571
|
+
const projectRoot = await findProjectRoot();
|
|
572
|
+
if (!projectRoot) {
|
|
573
|
+
throw new InvalidProjectError(process.cwd());
|
|
574
|
+
}
|
|
575
|
+
const dbPath = typeof args.db === "string" ? (0, import_node_path5.resolve)(projectRoot, args.db) : void 0;
|
|
576
|
+
if (!dbPath) {
|
|
577
|
+
throw new Error("Missing --db <path> to the local SQLite database file.");
|
|
578
|
+
}
|
|
579
|
+
const config = await loadKoraConfig(projectRoot);
|
|
580
|
+
const schemaPath = typeof args.schema === "string" ? (0, import_node_path5.resolve)(projectRoot, args.schema) : typeof config?.schema === "string" ? (0, import_node_path5.resolve)(projectRoot, config.schema) : void 0;
|
|
581
|
+
if (!schemaPath) {
|
|
582
|
+
throw new Error("Could not resolve schema path. Pass --schema <path>.");
|
|
583
|
+
}
|
|
584
|
+
const schema = await loadSchemaDefinition(schemaPath, projectRoot);
|
|
585
|
+
const { BetterSqlite3Adapter } = await import("@korajs/store/better-sqlite3");
|
|
586
|
+
const { Store } = await import("@korajs/store");
|
|
587
|
+
const strategyName = typeof args.strategy === "string" ? args.strategy : "after-ack";
|
|
588
|
+
const days = Number.parseInt(typeof args.days === "string" ? args.days : "30", 10);
|
|
589
|
+
const store = new Store({
|
|
590
|
+
schema,
|
|
591
|
+
adapter: new BetterSqlite3Adapter(dbPath)
|
|
592
|
+
});
|
|
593
|
+
await store.open();
|
|
594
|
+
let result;
|
|
595
|
+
if (strategyName === "never") {
|
|
596
|
+
result = await store.compact({ mode: "never" });
|
|
597
|
+
} else if (strategyName === "after-days") {
|
|
598
|
+
result = await store.compact({ mode: "after-days", days });
|
|
599
|
+
} else if (strategyName === "after-ack") {
|
|
600
|
+
result = await store.compact({ mode: "after-ack" });
|
|
601
|
+
} else {
|
|
602
|
+
await store.close();
|
|
603
|
+
throw new Error(`Unknown strategy "${strategyName}". Use after-ack, after-days, or never.`);
|
|
604
|
+
}
|
|
605
|
+
await store.close();
|
|
606
|
+
logger.banner();
|
|
607
|
+
logger.success(`Compacted ${result.deletedCount} operation log entries.`);
|
|
608
|
+
for (const [nodeId, seq] of result.watermark) {
|
|
609
|
+
logger.step(` ${nodeId}: seq <= ${seq}`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
// src/commands/create/create-command.ts
|
|
615
|
+
var import_node_child_process4 = require("child_process");
|
|
616
|
+
var import_node_fs2 = require("fs");
|
|
617
|
+
var import_node_path9 = require("path");
|
|
618
|
+
var import_node_url4 = require("url");
|
|
619
|
+
var import_citty3 = require("citty");
|
|
87
620
|
|
|
88
621
|
// src/prompts/preferences.ts
|
|
89
622
|
var import_conf = __toESM(require("conf"), 1);
|
|
@@ -128,18 +661,18 @@ var import_prompts = require("@clack/prompts");
|
|
|
128
661
|
// src/utils/prompt.ts
|
|
129
662
|
var import_node_readline = require("readline");
|
|
130
663
|
function promptText(message, defaultValue, options) {
|
|
131
|
-
return new Promise((
|
|
664
|
+
return new Promise((resolve11) => {
|
|
132
665
|
const rl = createReadline(options);
|
|
133
666
|
const suffix = defaultValue !== void 0 ? ` (${defaultValue})` : "";
|
|
134
667
|
rl.question(` ? ${message}${suffix}: `, (answer) => {
|
|
135
668
|
rl.close();
|
|
136
669
|
const trimmed = answer.trim();
|
|
137
|
-
|
|
670
|
+
resolve11(trimmed || defaultValue || "");
|
|
138
671
|
});
|
|
139
672
|
});
|
|
140
673
|
}
|
|
141
674
|
function promptSelect(message, choices, options) {
|
|
142
|
-
return new Promise((
|
|
675
|
+
return new Promise((resolve11) => {
|
|
143
676
|
const rl = createReadline(options);
|
|
144
677
|
const out = options?.output ?? process.stdout;
|
|
145
678
|
out.write(` ? ${message}
|
|
@@ -157,7 +690,7 @@ function promptSelect(message, choices, options) {
|
|
|
157
690
|
const selected = choices[index];
|
|
158
691
|
if (selected) {
|
|
159
692
|
rl.close();
|
|
160
|
-
|
|
693
|
+
resolve11(selected.value);
|
|
161
694
|
} else {
|
|
162
695
|
out.write(` Please enter a number between 1 and ${choices.length}
|
|
163
696
|
`);
|
|
@@ -169,7 +702,7 @@ function promptSelect(message, choices, options) {
|
|
|
169
702
|
});
|
|
170
703
|
}
|
|
171
704
|
function promptConfirm(message, defaultValue = false, options) {
|
|
172
|
-
return new Promise((
|
|
705
|
+
return new Promise((resolve11) => {
|
|
173
706
|
const rl = createReadline(options);
|
|
174
707
|
const suffix = defaultValue ? "Y/n" : "y/N";
|
|
175
708
|
const ask = () => {
|
|
@@ -177,17 +710,17 @@ function promptConfirm(message, defaultValue = false, options) {
|
|
|
177
710
|
const normalized = answer.trim().toLowerCase();
|
|
178
711
|
if (normalized.length === 0) {
|
|
179
712
|
rl.close();
|
|
180
|
-
|
|
713
|
+
resolve11(defaultValue);
|
|
181
714
|
return;
|
|
182
715
|
}
|
|
183
716
|
if (normalized === "y" || normalized === "yes") {
|
|
184
717
|
rl.close();
|
|
185
|
-
|
|
718
|
+
resolve11(true);
|
|
186
719
|
return;
|
|
187
720
|
}
|
|
188
721
|
if (normalized === "n" || normalized === "no") {
|
|
189
722
|
rl.close();
|
|
190
|
-
|
|
723
|
+
resolve11(false);
|
|
191
724
|
return;
|
|
192
725
|
}
|
|
193
726
|
;
|
|
@@ -297,135 +830,19 @@ var TEMPLATES = [
|
|
|
297
830
|
"react-tailwind",
|
|
298
831
|
"react-sync",
|
|
299
832
|
"react-basic",
|
|
833
|
+
"vue-sync",
|
|
834
|
+
"vue-basic",
|
|
835
|
+
"vue-tailwind-sync",
|
|
836
|
+
"vue-tailwind",
|
|
837
|
+
"svelte-sync",
|
|
838
|
+
"svelte-basic",
|
|
839
|
+
"svelte-tailwind-sync",
|
|
840
|
+
"svelte-tailwind",
|
|
300
841
|
"tauri-react"
|
|
301
842
|
];
|
|
302
843
|
|
|
303
|
-
// src/utils/fs-helpers.ts
|
|
304
|
-
var import_promises = require("fs/promises");
|
|
305
|
-
var import_node_path = require("path");
|
|
306
|
-
async function directoryExists(path) {
|
|
307
|
-
try {
|
|
308
|
-
await (0, import_promises.access)(path);
|
|
309
|
-
return true;
|
|
310
|
-
} catch {
|
|
311
|
-
return false;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
async function findProjectRoot(startDir) {
|
|
315
|
-
let current = (0, import_node_path.resolve)(startDir ?? process.cwd());
|
|
316
|
-
for (; ; ) {
|
|
317
|
-
const pkgPath = (0, import_node_path.join)(current, "package.json");
|
|
318
|
-
try {
|
|
319
|
-
const content = await (0, import_promises.readFile)(pkgPath, "utf-8");
|
|
320
|
-
const pkg = JSON.parse(content);
|
|
321
|
-
if (isKoraProject(pkg)) {
|
|
322
|
-
return current;
|
|
323
|
-
}
|
|
324
|
-
} catch {
|
|
325
|
-
}
|
|
326
|
-
const parent = (0, import_node_path.dirname)(current);
|
|
327
|
-
if (parent === current) break;
|
|
328
|
-
current = parent;
|
|
329
|
-
}
|
|
330
|
-
return null;
|
|
331
|
-
}
|
|
332
|
-
async function findSchemaFile(projectRoot) {
|
|
333
|
-
const candidates = [
|
|
334
|
-
(0, import_node_path.join)(projectRoot, "src", "schema.ts"),
|
|
335
|
-
(0, import_node_path.join)(projectRoot, "schema.ts"),
|
|
336
|
-
(0, import_node_path.join)(projectRoot, "src", "schema.js"),
|
|
337
|
-
(0, import_node_path.join)(projectRoot, "schema.js")
|
|
338
|
-
];
|
|
339
|
-
for (const candidate of candidates) {
|
|
340
|
-
try {
|
|
341
|
-
await (0, import_promises.access)(candidate);
|
|
342
|
-
return candidate;
|
|
343
|
-
} catch {
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return null;
|
|
347
|
-
}
|
|
348
|
-
async function hasTsxInstalled(projectRoot) {
|
|
349
|
-
try {
|
|
350
|
-
await (0, import_promises.access)((0, import_node_path.join)(projectRoot, "node_modules", "tsx", "package.json"));
|
|
351
|
-
return true;
|
|
352
|
-
} catch {
|
|
353
|
-
return false;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
async function resolveProjectBinaryEntryPoint(projectRoot, packageName, binaryName) {
|
|
357
|
-
const pkgJsonPath = (0, import_node_path.join)(projectRoot, "node_modules", packageName, "package.json");
|
|
358
|
-
try {
|
|
359
|
-
const content = await (0, import_promises.readFile)(pkgJsonPath, "utf-8");
|
|
360
|
-
const pkg = JSON.parse(content);
|
|
361
|
-
let binPath;
|
|
362
|
-
if (typeof pkg.bin === "string") {
|
|
363
|
-
binPath = pkg.bin;
|
|
364
|
-
} else if (typeof pkg.bin === "object" && pkg.bin !== null) {
|
|
365
|
-
binPath = pkg.bin[binaryName];
|
|
366
|
-
}
|
|
367
|
-
if (!binPath) return null;
|
|
368
|
-
const fullPath = (0, import_node_path.join)(projectRoot, "node_modules", packageName, binPath);
|
|
369
|
-
await (0, import_promises.access)(fullPath);
|
|
370
|
-
return fullPath;
|
|
371
|
-
} catch {
|
|
372
|
-
return null;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
function isKoraProject(pkg) {
|
|
376
|
-
if (typeof pkg !== "object" || pkg === null) return false;
|
|
377
|
-
const record = pkg;
|
|
378
|
-
return hasKoraDep(record.dependencies) || hasKoraDep(record.devDependencies);
|
|
379
|
-
}
|
|
380
|
-
function hasKoraDep(deps) {
|
|
381
|
-
if (typeof deps !== "object" || deps === null) return false;
|
|
382
|
-
return Object.keys(deps).some((key) => key === "kora" || key.startsWith("@korajs/"));
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// src/utils/logger.ts
|
|
386
|
-
var RESET = "\x1B[0m";
|
|
387
|
-
var BOLD = "\x1B[1m";
|
|
388
|
-
var DIM = "\x1B[2m";
|
|
389
|
-
var GREEN = "\x1B[32m";
|
|
390
|
-
var YELLOW = "\x1B[33m";
|
|
391
|
-
var RED = "\x1B[31m";
|
|
392
|
-
var CYAN = "\x1B[36m";
|
|
393
|
-
function createLogger(options) {
|
|
394
|
-
const colorDisabled = options?.noColor === true || process.env.NO_COLOR !== void 0 || !process.stdout.isTTY;
|
|
395
|
-
function color(code, text) {
|
|
396
|
-
return colorDisabled ? text : `${code}${text}${RESET}`;
|
|
397
|
-
}
|
|
398
|
-
return {
|
|
399
|
-
info(message) {
|
|
400
|
-
console.log(color(CYAN, message));
|
|
401
|
-
},
|
|
402
|
-
success(message) {
|
|
403
|
-
console.log(color(GREEN, ` \u2713 ${message}`));
|
|
404
|
-
},
|
|
405
|
-
warn(message) {
|
|
406
|
-
console.warn(color(YELLOW, ` \u26A0 ${message}`));
|
|
407
|
-
},
|
|
408
|
-
error(message) {
|
|
409
|
-
console.error(color(RED, ` \u2717 ${message}`));
|
|
410
|
-
},
|
|
411
|
-
step(message) {
|
|
412
|
-
console.log(color(DIM, ` ${message}`));
|
|
413
|
-
},
|
|
414
|
-
blank() {
|
|
415
|
-
console.log();
|
|
416
|
-
},
|
|
417
|
-
banner() {
|
|
418
|
-
console.log();
|
|
419
|
-
console.log(
|
|
420
|
-
color(BOLD + CYAN, " Kora.js") + color(DIM, " \u2014 Offline-first application framework")
|
|
421
|
-
);
|
|
422
|
-
console.log();
|
|
423
|
-
}
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
|
|
427
844
|
// src/utils/package-manager.ts
|
|
428
|
-
var
|
|
845
|
+
var import_node_child_process3 = require("child_process");
|
|
429
846
|
function detectPackageManager() {
|
|
430
847
|
const userAgent = process.env.npm_config_user_agent;
|
|
431
848
|
if (!userAgent) return "npm";
|
|
@@ -443,8 +860,8 @@ function getRunDevCommand(pm) {
|
|
|
443
860
|
}
|
|
444
861
|
|
|
445
862
|
// src/commands/create/environment-detection.ts
|
|
446
|
-
var
|
|
447
|
-
var
|
|
863
|
+
var import_promises4 = require("fs/promises");
|
|
864
|
+
var import_node_path6 = require("path");
|
|
448
865
|
function detectEditorFromEnvironment(env = process.env) {
|
|
449
866
|
const termProgram = String(env.TERM_PROGRAM ?? "").toLowerCase();
|
|
450
867
|
const editorValue = `${String(env.EDITOR ?? "")} ${String(env.VISUAL ?? "")}`.toLowerCase();
|
|
@@ -470,18 +887,18 @@ async function detectGitContext(startDir) {
|
|
|
470
887
|
};
|
|
471
888
|
}
|
|
472
889
|
async function detectMonorepoContext(startDir) {
|
|
473
|
-
let current = (0,
|
|
890
|
+
let current = (0, import_node_path6.resolve)(startDir);
|
|
474
891
|
for (; ; ) {
|
|
475
|
-
if (await fileExists((0,
|
|
892
|
+
if (await fileExists((0, import_node_path6.join)(current, "pnpm-workspace.yaml"))) {
|
|
476
893
|
return { isMonorepo: true, root: current, kind: "pnpm-workspace" };
|
|
477
894
|
}
|
|
478
|
-
if (await fileExists((0,
|
|
895
|
+
if (await fileExists((0, import_node_path6.join)(current, "turbo.json"))) {
|
|
479
896
|
return { isMonorepo: true, root: current, kind: "turborepo" };
|
|
480
897
|
}
|
|
481
|
-
const packageJsonPath = (0,
|
|
898
|
+
const packageJsonPath = (0, import_node_path6.join)(current, "package.json");
|
|
482
899
|
if (await fileExists(packageJsonPath)) {
|
|
483
900
|
try {
|
|
484
|
-
const packageJsonRaw = await (0,
|
|
901
|
+
const packageJsonRaw = await (0, import_promises4.readFile)(packageJsonPath, "utf-8");
|
|
485
902
|
const parsed = JSON.parse(packageJsonRaw);
|
|
486
903
|
if (Array.isArray(parsed.workspaces) || isNpmWorkspaceObject(parsed.workspaces)) {
|
|
487
904
|
return { isMonorepo: true, root: current, kind: "npm-workspaces" };
|
|
@@ -489,7 +906,7 @@ async function detectMonorepoContext(startDir) {
|
|
|
489
906
|
} catch {
|
|
490
907
|
}
|
|
491
908
|
}
|
|
492
|
-
const parent = (0,
|
|
909
|
+
const parent = (0, import_node_path6.dirname)(current);
|
|
493
910
|
if (parent === current) {
|
|
494
911
|
return { isMonorepo: false, root: null, kind: "none" };
|
|
495
912
|
}
|
|
@@ -501,9 +918,9 @@ async function applyEditorWorkspacePreset(params) {
|
|
|
501
918
|
if (editor !== "vscode" && editor !== "cursor" && editor !== "windsurf") {
|
|
502
919
|
return { applied: false, filePath: null };
|
|
503
920
|
}
|
|
504
|
-
const vscodeDir = (0,
|
|
505
|
-
const extensionsPath = (0,
|
|
506
|
-
await (0,
|
|
921
|
+
const vscodeDir = (0, import_node_path6.join)(targetDir, ".vscode");
|
|
922
|
+
const extensionsPath = (0, import_node_path6.join)(vscodeDir, "extensions.json");
|
|
923
|
+
await (0, import_promises4.mkdir)(vscodeDir, { recursive: true });
|
|
507
924
|
const recommendations = ["korajs.kora-devtools"];
|
|
508
925
|
const existing = await readJsonObject(extensionsPath);
|
|
509
926
|
const existingRecommendations = Array.isArray(existing?.recommendations) ? existing.recommendations.filter((item) => typeof item === "string") : [];
|
|
@@ -511,19 +928,19 @@ async function applyEditorWorkspacePreset(params) {
|
|
|
511
928
|
const next = {
|
|
512
929
|
recommendations: mergedRecommendations
|
|
513
930
|
};
|
|
514
|
-
await (0,
|
|
931
|
+
await (0, import_promises4.writeFile)(extensionsPath, `${JSON.stringify(next, null, 2)}
|
|
515
932
|
`, "utf-8");
|
|
516
933
|
return { applied: true, filePath: extensionsPath };
|
|
517
934
|
}
|
|
518
935
|
function resolveMonorepoTargetDirectory(monorepoRoot, projectName) {
|
|
519
|
-
return (0,
|
|
936
|
+
return (0, import_node_path6.join)(monorepoRoot, "packages", projectName);
|
|
520
937
|
}
|
|
521
938
|
function dedupeStrings(values) {
|
|
522
939
|
return Array.from(new Set(values));
|
|
523
940
|
}
|
|
524
941
|
async function readJsonObject(path) {
|
|
525
942
|
try {
|
|
526
|
-
const content = await (0,
|
|
943
|
+
const content = await (0, import_promises4.readFile)(path, "utf-8");
|
|
527
944
|
const parsed = JSON.parse(content);
|
|
528
945
|
if (typeof parsed === "object" && parsed !== null) {
|
|
529
946
|
return parsed;
|
|
@@ -540,22 +957,22 @@ function isNpmWorkspaceObject(value) {
|
|
|
540
957
|
}
|
|
541
958
|
async function fileExists(path) {
|
|
542
959
|
try {
|
|
543
|
-
await (0,
|
|
960
|
+
await (0, import_promises4.access)(path);
|
|
544
961
|
return true;
|
|
545
962
|
} catch {
|
|
546
963
|
return false;
|
|
547
964
|
}
|
|
548
965
|
}
|
|
549
966
|
async function findNearestAncestorWithEntry(startDir, entryName) {
|
|
550
|
-
let current = (0,
|
|
967
|
+
let current = (0, import_node_path6.resolve)(startDir);
|
|
551
968
|
for (; ; ) {
|
|
552
|
-
const candidate = (0,
|
|
969
|
+
const candidate = (0, import_node_path6.join)(current, entryName);
|
|
553
970
|
try {
|
|
554
|
-
await (0,
|
|
971
|
+
await (0, import_promises4.stat)(candidate);
|
|
555
972
|
return current;
|
|
556
973
|
} catch {
|
|
557
974
|
}
|
|
558
|
-
const parent = (0,
|
|
975
|
+
const parent = (0, import_node_path6.dirname)(current);
|
|
559
976
|
if (parent === current) {
|
|
560
977
|
return null;
|
|
561
978
|
}
|
|
@@ -567,6 +984,19 @@ async function findNearestAncestorWithEntry(startDir, entryName) {
|
|
|
567
984
|
function determineTemplateFromSelections(input) {
|
|
568
985
|
if (input.platform === "desktop-tauri") return "tauri-react";
|
|
569
986
|
const shouldSync = input.sync && input.db !== "none";
|
|
987
|
+
if (input.framework === "vue") {
|
|
988
|
+
if (input.tailwind && shouldSync) return "vue-tailwind-sync";
|
|
989
|
+
if (input.tailwind && !shouldSync) return "vue-tailwind";
|
|
990
|
+
return shouldSync ? "vue-sync" : "vue-basic";
|
|
991
|
+
}
|
|
992
|
+
if (input.framework === "svelte") {
|
|
993
|
+
if (input.tailwind && shouldSync) return "svelte-tailwind-sync";
|
|
994
|
+
if (input.tailwind && !shouldSync) return "svelte-tailwind";
|
|
995
|
+
return shouldSync ? "svelte-sync" : "svelte-basic";
|
|
996
|
+
}
|
|
997
|
+
if (input.framework === "solid") {
|
|
998
|
+
throw new Error("Solid templates are not available yet. Use react, vue, or svelte.");
|
|
999
|
+
}
|
|
570
1000
|
if (input.tailwind && shouldSync) return "react-tailwind-sync";
|
|
571
1001
|
if (input.tailwind && !shouldSync) return "react-tailwind";
|
|
572
1002
|
if (!input.tailwind && shouldSync) return "react-sync";
|
|
@@ -587,6 +1017,9 @@ function isDatabaseValue(value) {
|
|
|
587
1017
|
function isDatabaseProviderValue(value) {
|
|
588
1018
|
return value === "none" || value === "local" || value === "supabase" || value === "neon" || value === "railway" || value === "vercel-postgres" || value === "custom";
|
|
589
1019
|
}
|
|
1020
|
+
function isSupportedWebFramework(framework) {
|
|
1021
|
+
return framework === "react" || framework === "vue" || framework === "svelte";
|
|
1022
|
+
}
|
|
590
1023
|
|
|
591
1024
|
// src/commands/create/preferences-flow.ts
|
|
592
1025
|
async function resolveCreatePreferencesFlow(params) {
|
|
@@ -626,10 +1059,49 @@ async function resolveCreatePreferencesFlow(params) {
|
|
|
626
1059
|
if (isTauri) {
|
|
627
1060
|
effective.framework = "react";
|
|
628
1061
|
effective.tailwind = false;
|
|
629
|
-
effective.sync = true;
|
|
630
|
-
effective.db = "sqlite";
|
|
631
|
-
effective.dbProvider = "none";
|
|
632
1062
|
effective.auth = "none";
|
|
1063
|
+
if (flags.sync !== void 0) {
|
|
1064
|
+
effective.sync = flags.sync;
|
|
1065
|
+
} else if (!flags.useDefaults && !usedStoredPreferences) {
|
|
1066
|
+
effective.sync = await prompts.confirm("Enable multi-device sync?", true);
|
|
1067
|
+
}
|
|
1068
|
+
if (flags.db !== void 0) {
|
|
1069
|
+
if (!isDatabaseValue(flags.db)) {
|
|
1070
|
+
throw new Error(
|
|
1071
|
+
`Invalid --db value "${flags.db}". Expected one of: none, sqlite, postgres.`
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
effective.db = flags.db;
|
|
1075
|
+
} else if (!effective.sync) {
|
|
1076
|
+
effective.db = "none";
|
|
1077
|
+
} else if (!flags.useDefaults && !usedStoredPreferences) {
|
|
1078
|
+
effective.db = await prompts.select("Sync server database:", [
|
|
1079
|
+
{ label: "SQLite (zero-config; local, LAN, small teams)", value: "sqlite" },
|
|
1080
|
+
{ label: "PostgreSQL (production-scale)", value: "postgres" }
|
|
1081
|
+
]);
|
|
1082
|
+
}
|
|
1083
|
+
if (effective.db === "none") {
|
|
1084
|
+
effective.sync = false;
|
|
1085
|
+
}
|
|
1086
|
+
if (effective.db !== "postgres") {
|
|
1087
|
+
effective.dbProvider = "none";
|
|
1088
|
+
} else if (flags.dbProvider !== void 0) {
|
|
1089
|
+
if (!isDatabaseProviderValue(flags.dbProvider)) {
|
|
1090
|
+
throw new Error(
|
|
1091
|
+
`Invalid --db-provider value "${flags.dbProvider}". Expected one of: none, local, supabase, neon, railway, vercel-postgres, custom.`
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
effective.dbProvider = flags.dbProvider;
|
|
1095
|
+
} else if (!flags.useDefaults && !usedStoredPreferences) {
|
|
1096
|
+
effective.dbProvider = await prompts.select("Database provider:", [
|
|
1097
|
+
{ label: "Local Postgres", value: "local" },
|
|
1098
|
+
{ label: "Supabase", value: "supabase" },
|
|
1099
|
+
{ label: "Neon", value: "neon" },
|
|
1100
|
+
{ label: "Railway", value: "railway" },
|
|
1101
|
+
{ label: "Vercel Postgres", value: "vercel-postgres" },
|
|
1102
|
+
{ label: "Custom connection string", value: "custom" }
|
|
1103
|
+
]);
|
|
1104
|
+
}
|
|
633
1105
|
} else {
|
|
634
1106
|
if (flags.framework !== void 0) {
|
|
635
1107
|
if (!isFrameworkValue(flags.framework)) {
|
|
@@ -641,8 +1113,8 @@ async function resolveCreatePreferencesFlow(params) {
|
|
|
641
1113
|
} else if (!flags.useDefaults && !usedStoredPreferences) {
|
|
642
1114
|
effective.framework = await prompts.select("UI framework:", [
|
|
643
1115
|
{ label: "React", value: "react" },
|
|
644
|
-
{ label: "Vue
|
|
645
|
-
{ label: "Svelte
|
|
1116
|
+
{ label: "Vue 3", value: "vue" },
|
|
1117
|
+
{ label: "Svelte 5", value: "svelte" },
|
|
646
1118
|
{ label: "Solid (coming soon)", value: "solid", disabled: true }
|
|
647
1119
|
]);
|
|
648
1120
|
}
|
|
@@ -707,6 +1179,7 @@ async function resolveCreatePreferencesFlow(params) {
|
|
|
707
1179
|
}
|
|
708
1180
|
const template = determineTemplateFromSelections({
|
|
709
1181
|
platform: effective.platform,
|
|
1182
|
+
framework: effective.framework,
|
|
710
1183
|
tailwind: effective.tailwind,
|
|
711
1184
|
sync: effective.sync,
|
|
712
1185
|
db: effective.db
|
|
@@ -778,8 +1251,8 @@ function validateProjectName(name) {
|
|
|
778
1251
|
}
|
|
779
1252
|
|
|
780
1253
|
// src/commands/create/sync-provider-preset.ts
|
|
781
|
-
var
|
|
782
|
-
var
|
|
1254
|
+
var import_promises5 = require("fs/promises");
|
|
1255
|
+
var import_node_path7 = require("path");
|
|
783
1256
|
async function applySyncProviderPreset(options) {
|
|
784
1257
|
if (!isSyncTemplate(options.template)) {
|
|
785
1258
|
return;
|
|
@@ -789,67 +1262,35 @@ async function applySyncProviderPreset(options) {
|
|
|
789
1262
|
}
|
|
790
1263
|
const providerName = getProviderDisplayName(options.dbProvider);
|
|
791
1264
|
const providerConnectionString = getProviderConnectionStringExample(options.dbProvider);
|
|
792
|
-
const
|
|
793
|
-
const
|
|
794
|
-
const
|
|
795
|
-
const
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
"// Ensure DATABASE_URL is set in your environment.",
|
|
800
|
-
"",
|
|
801
|
-
"async function start(): Promise<void> {",
|
|
802
|
-
" const connectionString = process.env.DATABASE_URL || ''",
|
|
803
|
-
" if (connectionString.length === 0) {",
|
|
804
|
-
" throw new Error('DATABASE_URL is required for PostgreSQL sync server store.')",
|
|
805
|
-
" }",
|
|
806
|
-
"",
|
|
807
|
-
" const store = await createPostgresServerStore({ connectionString })",
|
|
808
|
-
" const server = createProductionServer({",
|
|
809
|
-
" store,",
|
|
810
|
-
" port: Number(process.env.PORT) || 3001,",
|
|
811
|
-
" staticDir: './dist',",
|
|
812
|
-
" syncPath: '/kora-sync',",
|
|
813
|
-
" })",
|
|
814
|
-
"",
|
|
815
|
-
" const url = await server.start()",
|
|
816
|
-
" console.log(`Kora app running at ${url}`)",
|
|
817
|
-
"}",
|
|
818
|
-
"",
|
|
819
|
-
"void start()",
|
|
820
|
-
""
|
|
821
|
-
].join("\n");
|
|
822
|
-
const envTemplate = [
|
|
823
|
-
"# Kora Sync Server",
|
|
824
|
-
"# WebSocket URL for the sync server (used by the client)",
|
|
825
|
-
"VITE_SYNC_URL=ws://localhost:3001",
|
|
826
|
-
"",
|
|
827
|
-
"# Sync server port",
|
|
828
|
-
"PORT=3001",
|
|
1265
|
+
const envPath = (0, import_node_path7.join)(options.targetDir, ".env.example");
|
|
1266
|
+
const readmePath = (0, import_node_path7.join)(options.targetDir, "README.md");
|
|
1267
|
+
const existingReadme = await (0, import_promises5.readFile)(readmePath, "utf-8");
|
|
1268
|
+
const existingEnv = await (0, import_promises5.readFile)(envPath, "utf-8");
|
|
1269
|
+
const trimmedReadme = existingReadme.trimEnd();
|
|
1270
|
+
const trimmedEnv = existingEnv.trimEnd();
|
|
1271
|
+
const envSuffix = [
|
|
829
1272
|
"",
|
|
830
|
-
`# PostgreSQL
|
|
831
|
-
`# Example: ${providerConnectionString}
|
|
832
|
-
"DATABASE_URL=",
|
|
833
|
-
""
|
|
1273
|
+
`# PostgreSQL provider preset: ${providerName}`,
|
|
1274
|
+
`# Example: ${providerConnectionString}`
|
|
834
1275
|
].join("\n");
|
|
835
|
-
const existingReadme = await (0, import_promises3.readFile)(readmePath, "utf-8");
|
|
836
|
-
const trimmedReadme = existingReadme.trimEnd();
|
|
837
1276
|
const readmeSuffix = [
|
|
838
1277
|
"",
|
|
839
1278
|
"## PostgreSQL Provider Preset",
|
|
840
1279
|
"",
|
|
841
1280
|
`Selected DB provider: ${options.dbProvider}`,
|
|
842
1281
|
"",
|
|
843
|
-
"This scaffold
|
|
1282
|
+
"This scaffold keeps one sync server entrypoint. When `DATABASE_URL` is set, `server.ts` uses PostgreSQL. When it is empty, the same server uses SQLite at `KORA_SERVER_DB`.",
|
|
1283
|
+
"",
|
|
1284
|
+
"The generated server uses `createPostgresServerStore` automatically when `DATABASE_URL` is present.",
|
|
844
1285
|
""
|
|
845
1286
|
].join("\n");
|
|
846
1287
|
const readmeTemplate = `${trimmedReadme}${readmeSuffix}`;
|
|
847
|
-
await (0,
|
|
848
|
-
|
|
849
|
-
await (0,
|
|
1288
|
+
await (0, import_promises5.writeFile)(envPath, `${trimmedEnv}${envSuffix}
|
|
1289
|
+
`, "utf-8");
|
|
1290
|
+
await (0, import_promises5.writeFile)(readmePath, readmeTemplate, "utf-8");
|
|
850
1291
|
}
|
|
851
1292
|
function isSyncTemplate(template) {
|
|
852
|
-
return template === "react-sync" || template === "react-tailwind-sync" || template === "tauri-react";
|
|
1293
|
+
return template === "react-sync" || template === "react-tailwind-sync" || template === "vue-sync" || template === "vue-tailwind-sync" || template === "svelte-sync" || template === "svelte-tailwind-sync" || template === "tauri-react";
|
|
853
1294
|
}
|
|
854
1295
|
function getProviderDisplayName(provider) {
|
|
855
1296
|
switch (provider) {
|
|
@@ -890,9 +1331,9 @@ function getProviderConnectionStringExample(provider) {
|
|
|
890
1331
|
|
|
891
1332
|
// src/templates/composer.ts
|
|
892
1333
|
var import_node_fs = require("fs");
|
|
893
|
-
var
|
|
894
|
-
var
|
|
895
|
-
var
|
|
1334
|
+
var import_promises6 = require("fs/promises");
|
|
1335
|
+
var import_node_path8 = require("path");
|
|
1336
|
+
var import_node_url3 = require("url");
|
|
896
1337
|
var import_meta = {};
|
|
897
1338
|
function substituteVariables(template, context) {
|
|
898
1339
|
return template.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
@@ -901,16 +1342,16 @@ function substituteVariables(template, context) {
|
|
|
901
1342
|
});
|
|
902
1343
|
}
|
|
903
1344
|
function getTemplatePath(templateName) {
|
|
904
|
-
let dir = (0,
|
|
1345
|
+
let dir = (0, import_node_path8.dirname)((0, import_node_url3.fileURLToPath)(import_meta.url));
|
|
905
1346
|
for (let i = 0; i < 7; i++) {
|
|
906
|
-
const candidate = (0,
|
|
1347
|
+
const candidate = (0, import_node_path8.resolve)(dir, "templates", templateName);
|
|
907
1348
|
if ((0, import_node_fs.existsSync)(candidate)) {
|
|
908
1349
|
return candidate;
|
|
909
1350
|
}
|
|
910
|
-
dir = (0,
|
|
1351
|
+
dir = (0, import_node_path8.dirname)(dir);
|
|
911
1352
|
}
|
|
912
|
-
const currentDir = (0,
|
|
913
|
-
return (0,
|
|
1353
|
+
const currentDir = (0, import_node_path8.dirname)((0, import_node_url3.fileURLToPath)(import_meta.url));
|
|
1354
|
+
return (0, import_node_path8.resolve)(currentDir, "..", "..", "..", "templates", templateName);
|
|
914
1355
|
}
|
|
915
1356
|
function createCompatibilityLayerPlan(templateName) {
|
|
916
1357
|
const baseLayer = { category: "base", name: "base", sourceTemplate: "react-basic" };
|
|
@@ -977,6 +1418,48 @@ function createCompatibilityLayerPlan(templateName) {
|
|
|
977
1418
|
authLayer
|
|
978
1419
|
]
|
|
979
1420
|
};
|
|
1421
|
+
case "vue-sync":
|
|
1422
|
+
return {
|
|
1423
|
+
compatibilityTarget: templateName,
|
|
1424
|
+
layers: [{ category: "base", name: "vue-sync", sourceTemplate: "vue-sync" }]
|
|
1425
|
+
};
|
|
1426
|
+
case "vue-basic":
|
|
1427
|
+
return {
|
|
1428
|
+
compatibilityTarget: templateName,
|
|
1429
|
+
layers: [{ category: "base", name: "vue-basic", sourceTemplate: "vue-basic" }]
|
|
1430
|
+
};
|
|
1431
|
+
case "svelte-sync":
|
|
1432
|
+
return {
|
|
1433
|
+
compatibilityTarget: templateName,
|
|
1434
|
+
layers: [{ category: "base", name: "svelte-sync", sourceTemplate: "svelte-sync" }]
|
|
1435
|
+
};
|
|
1436
|
+
case "svelte-basic":
|
|
1437
|
+
return {
|
|
1438
|
+
compatibilityTarget: templateName,
|
|
1439
|
+
layers: [{ category: "base", name: "svelte-basic", sourceTemplate: "svelte-basic" }]
|
|
1440
|
+
};
|
|
1441
|
+
case "vue-tailwind-sync":
|
|
1442
|
+
return {
|
|
1443
|
+
compatibilityTarget: templateName,
|
|
1444
|
+
layers: [{ category: "base", name: "vue-tailwind-sync", sourceTemplate: "vue-tailwind-sync" }]
|
|
1445
|
+
};
|
|
1446
|
+
case "vue-tailwind":
|
|
1447
|
+
return {
|
|
1448
|
+
compatibilityTarget: templateName,
|
|
1449
|
+
layers: [{ category: "base", name: "vue-tailwind", sourceTemplate: "vue-tailwind" }]
|
|
1450
|
+
};
|
|
1451
|
+
case "svelte-tailwind-sync":
|
|
1452
|
+
return {
|
|
1453
|
+
compatibilityTarget: templateName,
|
|
1454
|
+
layers: [
|
|
1455
|
+
{ category: "base", name: "svelte-tailwind-sync", sourceTemplate: "svelte-tailwind-sync" }
|
|
1456
|
+
]
|
|
1457
|
+
};
|
|
1458
|
+
case "svelte-tailwind":
|
|
1459
|
+
return {
|
|
1460
|
+
compatibilityTarget: templateName,
|
|
1461
|
+
layers: [{ category: "base", name: "svelte-tailwind", sourceTemplate: "svelte-tailwind" }]
|
|
1462
|
+
};
|
|
980
1463
|
}
|
|
981
1464
|
}
|
|
982
1465
|
async function composeTemplateLayers(plan, targetDir, context) {
|
|
@@ -995,22 +1478,22 @@ async function composeTemplateLayers(plan, targetDir, context) {
|
|
|
995
1478
|
}
|
|
996
1479
|
}
|
|
997
1480
|
async function copyDirectory(src, dest, vars) {
|
|
998
|
-
await (0,
|
|
999
|
-
const entries = await (0,
|
|
1481
|
+
await (0, import_promises6.mkdir)(dest, { recursive: true });
|
|
1482
|
+
const entries = await (0, import_promises6.readdir)(src);
|
|
1000
1483
|
for (const entry of entries) {
|
|
1001
|
-
const srcPath = (0,
|
|
1002
|
-
const srcStat = await (0,
|
|
1484
|
+
const srcPath = (0, import_node_path8.join)(src, entry);
|
|
1485
|
+
const srcStat = await (0, import_promises6.stat)(srcPath);
|
|
1003
1486
|
if (srcStat.isDirectory()) {
|
|
1004
|
-
await copyDirectory(srcPath, (0,
|
|
1487
|
+
await copyDirectory(srcPath, (0, import_node_path8.join)(dest, entry), vars);
|
|
1005
1488
|
continue;
|
|
1006
1489
|
}
|
|
1007
1490
|
if (entry.endsWith(".hbs")) {
|
|
1008
|
-
const content = await (0,
|
|
1491
|
+
const content = await (0, import_promises6.readFile)(srcPath, "utf-8");
|
|
1009
1492
|
const outputName = entry.slice(0, -4);
|
|
1010
|
-
await (0,
|
|
1493
|
+
await (0, import_promises6.writeFile)((0, import_node_path8.join)(dest, outputName), substituteVariables(content, vars), "utf-8");
|
|
1011
1494
|
continue;
|
|
1012
1495
|
}
|
|
1013
|
-
await (0,
|
|
1496
|
+
await (0, import_promises6.copyFile)(srcPath, (0, import_node_path8.join)(dest, entry));
|
|
1014
1497
|
}
|
|
1015
1498
|
}
|
|
1016
1499
|
|
|
@@ -1022,7 +1505,7 @@ async function scaffoldTemplate(templateName, targetDir, context) {
|
|
|
1022
1505
|
|
|
1023
1506
|
// src/commands/create/create-command.ts
|
|
1024
1507
|
var import_meta2 = {};
|
|
1025
|
-
var createCommand = (0,
|
|
1508
|
+
var createCommand = (0, import_citty3.defineCommand)({
|
|
1026
1509
|
meta: {
|
|
1027
1510
|
name: "create",
|
|
1028
1511
|
description: "Create a new Kora application"
|
|
@@ -1039,7 +1522,7 @@ var createCommand = (0, import_citty.defineCommand)({
|
|
|
1039
1522
|
},
|
|
1040
1523
|
template: {
|
|
1041
1524
|
type: "string",
|
|
1042
|
-
description: "Project template (react-tailwind-sync, react-
|
|
1525
|
+
description: "Project template (react-tailwind-sync, react-sync, vue-sync, svelte-sync, tauri-react, ...)"
|
|
1043
1526
|
},
|
|
1044
1527
|
pm: {
|
|
1045
1528
|
type: "string",
|
|
@@ -1124,8 +1607,10 @@ var createCommand = (0, import_citty.defineCommand)({
|
|
|
1124
1607
|
prompts,
|
|
1125
1608
|
store: preferenceStore
|
|
1126
1609
|
});
|
|
1127
|
-
if (selection.framework
|
|
1128
|
-
logger.error(
|
|
1610
|
+
if (!isSupportedWebFramework(selection.framework)) {
|
|
1611
|
+
logger.error(
|
|
1612
|
+
`Framework "${selection.framework}" is not available yet. Use react, vue, or svelte.`
|
|
1613
|
+
);
|
|
1129
1614
|
if (!useDefaults) {
|
|
1130
1615
|
prompts.outro("Project creation aborted.");
|
|
1131
1616
|
}
|
|
@@ -1161,7 +1646,7 @@ var createCommand = (0, import_citty.defineCommand)({
|
|
|
1161
1646
|
const editorDetection = detectEditorFromEnvironment();
|
|
1162
1647
|
const gitContext = await detectGitContext(process.cwd());
|
|
1163
1648
|
const monorepoContext = await detectMonorepoContext(process.cwd());
|
|
1164
|
-
let targetDir = (0,
|
|
1649
|
+
let targetDir = (0, import_node_path9.resolve)(process.cwd(), projectName);
|
|
1165
1650
|
if (!useDefaults && monorepoContext.isMonorepo && monorepoContext.root !== null) {
|
|
1166
1651
|
const useWorkspaceTarget = await prompts.confirm(
|
|
1167
1652
|
`Detected ${formatMonorepoKind(monorepoContext.kind)} at ${monorepoContext.root}. Create app under packages/${projectName}?`,
|
|
@@ -1231,7 +1716,7 @@ var createCommand = (0, import_citty.defineCommand)({
|
|
|
1231
1716
|
if (!args["skip-install"]) {
|
|
1232
1717
|
logger.step("Installing dependencies...");
|
|
1233
1718
|
try {
|
|
1234
|
-
(0,
|
|
1719
|
+
(0, import_node_child_process4.execSync)(getInstallCommand(pm), { cwd: targetDir, stdio: "inherit" });
|
|
1235
1720
|
logger.success("Dependencies installed");
|
|
1236
1721
|
} catch {
|
|
1237
1722
|
logger.warn("Failed to install dependencies. Run install manually.");
|
|
@@ -1270,7 +1755,7 @@ function isValidPackageManager(value) {
|
|
|
1270
1755
|
return PACKAGE_MANAGERS.includes(value);
|
|
1271
1756
|
}
|
|
1272
1757
|
function isSyncTemplate2(template) {
|
|
1273
|
-
return template === "react-sync" || template === "react-tailwind-sync" || template === "tauri-react";
|
|
1758
|
+
return template === "react-sync" || template === "react-tailwind-sync" || template === "vue-sync" || template === "vue-tailwind-sync" || template === "svelte-sync" || template === "svelte-tailwind-sync" || template === "tauri-react";
|
|
1274
1759
|
}
|
|
1275
1760
|
function formatDbProviderForLog(dbProvider) {
|
|
1276
1761
|
switch (dbProvider) {
|
|
@@ -1331,9 +1816,9 @@ function relativeToTarget(targetDir, filePath) {
|
|
|
1331
1816
|
}
|
|
1332
1817
|
function resolveKoraVersion() {
|
|
1333
1818
|
try {
|
|
1334
|
-
let dir = (0,
|
|
1819
|
+
let dir = (0, import_node_path9.dirname)((0, import_node_url4.fileURLToPath)(import_meta2.url));
|
|
1335
1820
|
for (let i = 0; i < 5; i++) {
|
|
1336
|
-
const pkgPath = (0,
|
|
1821
|
+
const pkgPath = (0, import_node_path9.resolve)(dir, "package.json");
|
|
1337
1822
|
if ((0, import_node_fs2.existsSync)(pkgPath)) {
|
|
1338
1823
|
const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf-8"));
|
|
1339
1824
|
if (pkg.name === "@korajs/cli") {
|
|
@@ -1342,7 +1827,7 @@ function resolveKoraVersion() {
|
|
|
1342
1827
|
return `^${parts[0]}.${parts[1]}.0`;
|
|
1343
1828
|
}
|
|
1344
1829
|
}
|
|
1345
|
-
dir = (0,
|
|
1830
|
+
dir = (0, import_node_path9.dirname)(dir);
|
|
1346
1831
|
}
|
|
1347
1832
|
return "latest";
|
|
1348
1833
|
} catch {
|
|
@@ -1351,8 +1836,9 @@ function resolveKoraVersion() {
|
|
|
1351
1836
|
}
|
|
1352
1837
|
|
|
1353
1838
|
// src/commands/deploy/deploy-command.ts
|
|
1354
|
-
var
|
|
1355
|
-
var
|
|
1839
|
+
var import_node_fs4 = require("fs");
|
|
1840
|
+
var import_node_path20 = require("path");
|
|
1841
|
+
var import_citty4 = require("citty");
|
|
1356
1842
|
|
|
1357
1843
|
// src/commands/deploy/adapters/adapter.ts
|
|
1358
1844
|
var DEPLOY_PLATFORMS = [
|
|
@@ -1369,14 +1855,14 @@ function isDeployPlatform(value) {
|
|
|
1369
1855
|
}
|
|
1370
1856
|
|
|
1371
1857
|
// src/commands/deploy/adapters/aws-ecs-adapter.ts
|
|
1372
|
-
var
|
|
1373
|
-
var
|
|
1858
|
+
var import_node_child_process6 = require("child_process");
|
|
1859
|
+
var import_node_path12 = require("path");
|
|
1374
1860
|
|
|
1375
1861
|
// src/commands/deploy/builder/client-builder.ts
|
|
1376
|
-
var
|
|
1862
|
+
var import_node_child_process5 = require("child_process");
|
|
1377
1863
|
var import_node_fs3 = require("fs");
|
|
1378
|
-
var
|
|
1379
|
-
var
|
|
1864
|
+
var import_promises7 = require("fs/promises");
|
|
1865
|
+
var import_node_path10 = require("path");
|
|
1380
1866
|
async function buildClient(options) {
|
|
1381
1867
|
const viteEntryPoint = await resolveProjectBinaryEntryPoint(options.projectRoot, "vite", "vite");
|
|
1382
1868
|
if (!viteEntryPoint) {
|
|
@@ -1397,15 +1883,15 @@ async function buildClient(options) {
|
|
|
1397
1883
|
return { outDir: options.outDir };
|
|
1398
1884
|
}
|
|
1399
1885
|
async function patchSqliteWasmAssets(projectRoot, outDir) {
|
|
1400
|
-
const assetsDir = (0,
|
|
1886
|
+
const assetsDir = (0, import_node_path10.join)(outDir, "assets");
|
|
1401
1887
|
if (!(0, import_node_fs3.existsSync)(assetsDir)) return;
|
|
1402
|
-
const files = await (0,
|
|
1888
|
+
const files = await (0, import_promises7.readdir)(assetsDir);
|
|
1403
1889
|
const hashedWasm = files.find((f) => /^sqlite3-.+\.wasm$/.test(f));
|
|
1404
1890
|
if (hashedWasm && !files.includes("sqlite3.wasm")) {
|
|
1405
|
-
await (0,
|
|
1891
|
+
await (0, import_promises7.copyFile)((0, import_node_path10.join)(assetsDir, hashedWasm), (0, import_node_path10.join)(assetsDir, "sqlite3.wasm"));
|
|
1406
1892
|
}
|
|
1407
1893
|
if (!files.includes("sqlite3-opfs-async-proxy.js")) {
|
|
1408
|
-
const proxyFile = (0,
|
|
1894
|
+
const proxyFile = (0, import_node_path10.resolve)(
|
|
1409
1895
|
projectRoot,
|
|
1410
1896
|
"node_modules",
|
|
1411
1897
|
"@sqlite.org",
|
|
@@ -1415,13 +1901,13 @@ async function patchSqliteWasmAssets(projectRoot, outDir) {
|
|
|
1415
1901
|
"sqlite3-opfs-async-proxy.js"
|
|
1416
1902
|
);
|
|
1417
1903
|
if ((0, import_node_fs3.existsSync)(proxyFile)) {
|
|
1418
|
-
await (0,
|
|
1904
|
+
await (0, import_promises7.copyFile)(proxyFile, (0, import_node_path10.join)(assetsDir, "sqlite3-opfs-async-proxy.js"));
|
|
1419
1905
|
}
|
|
1420
1906
|
}
|
|
1421
1907
|
}
|
|
1422
1908
|
async function runProcess(command, args, cwd) {
|
|
1423
|
-
await new Promise((
|
|
1424
|
-
const child = (0,
|
|
1909
|
+
await new Promise((resolve11, reject) => {
|
|
1910
|
+
const child = (0, import_node_child_process5.spawn)(command, args, {
|
|
1425
1911
|
cwd,
|
|
1426
1912
|
stdio: "inherit",
|
|
1427
1913
|
env: process.env
|
|
@@ -1431,7 +1917,7 @@ async function runProcess(command, args, cwd) {
|
|
|
1431
1917
|
});
|
|
1432
1918
|
child.on("exit", (code) => {
|
|
1433
1919
|
if (code === 0) {
|
|
1434
|
-
|
|
1920
|
+
resolve11();
|
|
1435
1921
|
return;
|
|
1436
1922
|
}
|
|
1437
1923
|
reject(new Error(`Client build failed with exit code ${String(code ?? "unknown")}.`));
|
|
@@ -1440,9 +1926,9 @@ async function runProcess(command, args, cwd) {
|
|
|
1440
1926
|
}
|
|
1441
1927
|
|
|
1442
1928
|
// src/commands/deploy/builder/server-bundler.ts
|
|
1443
|
-
var
|
|
1444
|
-
var
|
|
1445
|
-
var
|
|
1929
|
+
var import_promises8 = require("fs/promises");
|
|
1930
|
+
var import_promises9 = require("fs/promises");
|
|
1931
|
+
var import_node_path11 = require("path");
|
|
1446
1932
|
var import_esbuild = require("esbuild");
|
|
1447
1933
|
var DEFAULT_ENTRY_CANDIDATES = [
|
|
1448
1934
|
"server.ts",
|
|
@@ -1458,8 +1944,8 @@ async function bundleServer(options) {
|
|
|
1458
1944
|
`Could not find a server entry file in ${options.projectRoot}. Looked for: ${candidates.join(", ")}`
|
|
1459
1945
|
);
|
|
1460
1946
|
}
|
|
1461
|
-
await (0,
|
|
1462
|
-
const outputFilePath = (0,
|
|
1947
|
+
await (0, import_promises8.mkdir)(options.deployDirectory, { recursive: true });
|
|
1948
|
+
const outputFilePath = (0, import_node_path11.join)(options.deployDirectory, "server-bundled.js");
|
|
1463
1949
|
await (0, import_esbuild.build)({
|
|
1464
1950
|
entryPoints: [entryFilePath],
|
|
1465
1951
|
outfile: outputFilePath,
|
|
@@ -1481,9 +1967,9 @@ async function bundleServer(options) {
|
|
|
1481
1967
|
}
|
|
1482
1968
|
async function resolveServerEntry(projectRoot, candidates) {
|
|
1483
1969
|
for (const candidate of candidates) {
|
|
1484
|
-
const fullPath = (0,
|
|
1970
|
+
const fullPath = (0, import_node_path11.join)(projectRoot, candidate);
|
|
1485
1971
|
try {
|
|
1486
|
-
await (0,
|
|
1972
|
+
await (0, import_promises9.access)(fullPath);
|
|
1487
1973
|
return fullPath;
|
|
1488
1974
|
} catch {
|
|
1489
1975
|
}
|
|
@@ -1585,19 +2071,19 @@ var AwsEcsAdapter = class {
|
|
|
1585
2071
|
appName: config.appName,
|
|
1586
2072
|
region: config.region
|
|
1587
2073
|
};
|
|
1588
|
-
const deployDirectory = (0,
|
|
2074
|
+
const deployDirectory = (0, import_node_path12.join)(config.projectRoot, ".kora", "deploy");
|
|
1589
2075
|
await bundleServer({
|
|
1590
2076
|
projectRoot: config.projectRoot,
|
|
1591
2077
|
deployDirectory
|
|
1592
2078
|
});
|
|
1593
|
-
const client = await buildClient({
|
|
2079
|
+
const client = config.deployTarget === "sync-server" ? null : await buildClient({
|
|
1594
2080
|
projectRoot: config.projectRoot,
|
|
1595
|
-
outDir: (0,
|
|
2081
|
+
outDir: (0, import_node_path12.join)(deployDirectory, "dist"),
|
|
1596
2082
|
mode: "production"
|
|
1597
2083
|
});
|
|
1598
2084
|
return {
|
|
1599
|
-
clientDirectory: client
|
|
1600
|
-
serverBundlePath: (0,
|
|
2085
|
+
clientDirectory: client?.outDir ?? null,
|
|
2086
|
+
serverBundlePath: (0, import_node_path12.join)(deployDirectory, "server-bundled.js"),
|
|
1601
2087
|
deployDirectory
|
|
1602
2088
|
};
|
|
1603
2089
|
}
|
|
@@ -1829,8 +2315,8 @@ var AwsEcsAdapter = class {
|
|
|
1829
2315
|
};
|
|
1830
2316
|
var NodeAwsCommandRunner = class {
|
|
1831
2317
|
async run(command, args, cwd) {
|
|
1832
|
-
return new Promise((
|
|
1833
|
-
const child = (0,
|
|
2318
|
+
return new Promise((resolve11) => {
|
|
2319
|
+
const child = (0, import_node_child_process6.spawn)(command, args, {
|
|
1834
2320
|
cwd,
|
|
1835
2321
|
env: process.env,
|
|
1836
2322
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -1844,11 +2330,11 @@ var NodeAwsCommandRunner = class {
|
|
|
1844
2330
|
stderr += chunk.toString("utf-8");
|
|
1845
2331
|
});
|
|
1846
2332
|
child.on("error", (error) => {
|
|
1847
|
-
|
|
2333
|
+
resolve11({ exitCode: 1, stdout, stderr: `${stderr}
|
|
1848
2334
|
${error.message}` });
|
|
1849
2335
|
});
|
|
1850
2336
|
child.on("exit", (code) => {
|
|
1851
|
-
|
|
2337
|
+
resolve11({ exitCode: code ?? 1, stdout: stdout.trim(), stderr: stderr.trim() });
|
|
1852
2338
|
});
|
|
1853
2339
|
});
|
|
1854
2340
|
}
|
|
@@ -1862,8 +2348,8 @@ function inferLogLevel(line) {
|
|
|
1862
2348
|
}
|
|
1863
2349
|
|
|
1864
2350
|
// src/commands/deploy/adapters/aws-lightsail-adapter.ts
|
|
1865
|
-
var
|
|
1866
|
-
var
|
|
2351
|
+
var import_node_child_process7 = require("child_process");
|
|
2352
|
+
var import_node_path13 = require("path");
|
|
1867
2353
|
var AwsLightsailAdapter = class {
|
|
1868
2354
|
name = "aws-lightsail";
|
|
1869
2355
|
logger = createLogger();
|
|
@@ -1936,19 +2422,19 @@ var AwsLightsailAdapter = class {
|
|
|
1936
2422
|
appName: config.appName,
|
|
1937
2423
|
region: config.region
|
|
1938
2424
|
};
|
|
1939
|
-
const deployDirectory = (0,
|
|
2425
|
+
const deployDirectory = (0, import_node_path13.join)(config.projectRoot, ".kora", "deploy");
|
|
1940
2426
|
await bundleServer({
|
|
1941
2427
|
projectRoot: config.projectRoot,
|
|
1942
2428
|
deployDirectory
|
|
1943
2429
|
});
|
|
1944
|
-
const client = await buildClient({
|
|
2430
|
+
const client = config.deployTarget === "sync-server" ? null : await buildClient({
|
|
1945
2431
|
projectRoot: config.projectRoot,
|
|
1946
|
-
outDir: (0,
|
|
2432
|
+
outDir: (0, import_node_path13.join)(deployDirectory, "dist"),
|
|
1947
2433
|
mode: "production"
|
|
1948
2434
|
});
|
|
1949
2435
|
return {
|
|
1950
|
-
clientDirectory: client
|
|
1951
|
-
serverBundlePath: (0,
|
|
2436
|
+
clientDirectory: client?.outDir ?? null,
|
|
2437
|
+
serverBundlePath: (0, import_node_path13.join)(deployDirectory, "server-bundled.js"),
|
|
1952
2438
|
deployDirectory
|
|
1953
2439
|
};
|
|
1954
2440
|
}
|
|
@@ -2176,8 +2662,8 @@ var AwsLightsailAdapter = class {
|
|
|
2176
2662
|
};
|
|
2177
2663
|
var NodeAwsLightsailCommandRunner = class {
|
|
2178
2664
|
async run(command, args, cwd) {
|
|
2179
|
-
return new Promise((
|
|
2180
|
-
const child = (0,
|
|
2665
|
+
return new Promise((resolve11) => {
|
|
2666
|
+
const child = (0, import_node_child_process7.spawn)(command, args, {
|
|
2181
2667
|
cwd,
|
|
2182
2668
|
env: process.env,
|
|
2183
2669
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2191,11 +2677,11 @@ var NodeAwsLightsailCommandRunner = class {
|
|
|
2191
2677
|
stderr += chunk.toString("utf-8");
|
|
2192
2678
|
});
|
|
2193
2679
|
child.on("error", (error) => {
|
|
2194
|
-
|
|
2680
|
+
resolve11({ exitCode: 1, stdout, stderr: `${stderr}
|
|
2195
2681
|
${error.message}` });
|
|
2196
2682
|
});
|
|
2197
2683
|
child.on("exit", (code) => {
|
|
2198
|
-
|
|
2684
|
+
resolve11({ exitCode: code ?? 1, stdout: stdout.trim(), stderr: stderr.trim() });
|
|
2199
2685
|
});
|
|
2200
2686
|
});
|
|
2201
2687
|
}
|
|
@@ -2247,12 +2733,12 @@ function inferLogLevel2(line) {
|
|
|
2247
2733
|
}
|
|
2248
2734
|
|
|
2249
2735
|
// src/commands/deploy/adapters/fly-adapter.ts
|
|
2250
|
-
var
|
|
2251
|
-
var
|
|
2736
|
+
var import_node_child_process8 = require("child_process");
|
|
2737
|
+
var import_node_path15 = require("path");
|
|
2252
2738
|
|
|
2253
2739
|
// src/commands/deploy/artifacts/fly-toml-generator.ts
|
|
2254
|
-
var
|
|
2255
|
-
var
|
|
2740
|
+
var import_promises10 = require("fs/promises");
|
|
2741
|
+
var import_node_path14 = require("path");
|
|
2256
2742
|
var GENERATED_HEADER = "# Generated by kora deploy \u2014 do not edit manually";
|
|
2257
2743
|
function generateFlyToml(options) {
|
|
2258
2744
|
const internalPort = options.internalPort ?? 3e3;
|
|
@@ -2277,14 +2763,14 @@ function generateFlyToml(options) {
|
|
|
2277
2763
|
' interval = "30s"',
|
|
2278
2764
|
' method = "GET"',
|
|
2279
2765
|
' timeout = "5s"',
|
|
2280
|
-
' path = "/"',
|
|
2766
|
+
' path = "/health"',
|
|
2281
2767
|
""
|
|
2282
2768
|
].join("\n");
|
|
2283
2769
|
}
|
|
2284
2770
|
async function writeFlyTomlArtifact(deployDirectory, options) {
|
|
2285
|
-
await (0,
|
|
2286
|
-
const filePath = (0,
|
|
2287
|
-
await (0,
|
|
2771
|
+
await (0, import_promises10.mkdir)(deployDirectory, { recursive: true });
|
|
2772
|
+
const filePath = (0, import_node_path14.join)(deployDirectory, "fly.toml");
|
|
2773
|
+
await (0, import_promises10.writeFile)(filePath, generateFlyToml(options), "utf-8");
|
|
2288
2774
|
return filePath;
|
|
2289
2775
|
}
|
|
2290
2776
|
|
|
@@ -2378,7 +2864,7 @@ var FlyAdapter = class {
|
|
|
2378
2864
|
appName: config.appName,
|
|
2379
2865
|
region: config.region
|
|
2380
2866
|
};
|
|
2381
|
-
const deployDirectory = (0,
|
|
2867
|
+
const deployDirectory = (0, import_node_path15.join)(config.projectRoot, ".kora", "deploy");
|
|
2382
2868
|
await writeFlyTomlArtifact(deployDirectory, {
|
|
2383
2869
|
appName: config.appName,
|
|
2384
2870
|
region: config.region ?? "iad"
|
|
@@ -2387,14 +2873,14 @@ var FlyAdapter = class {
|
|
|
2387
2873
|
projectRoot: config.projectRoot,
|
|
2388
2874
|
deployDirectory
|
|
2389
2875
|
});
|
|
2390
|
-
const client = await buildClient({
|
|
2876
|
+
const client = config.deployTarget === "sync-server" ? null : await buildClient({
|
|
2391
2877
|
projectRoot: config.projectRoot,
|
|
2392
|
-
outDir: (0,
|
|
2878
|
+
outDir: (0, import_node_path15.join)(deployDirectory, "dist"),
|
|
2393
2879
|
mode: "production"
|
|
2394
2880
|
});
|
|
2395
2881
|
return {
|
|
2396
|
-
clientDirectory: client
|
|
2397
|
-
serverBundlePath: (0,
|
|
2882
|
+
clientDirectory: client?.outDir ?? null,
|
|
2883
|
+
serverBundlePath: (0, import_node_path15.join)(deployDirectory, "server-bundled.js"),
|
|
2398
2884
|
deployDirectory
|
|
2399
2885
|
};
|
|
2400
2886
|
}
|
|
@@ -2520,8 +3006,8 @@ var FlyAdapter = class {
|
|
|
2520
3006
|
};
|
|
2521
3007
|
var NodeFlyCommandRunner = class {
|
|
2522
3008
|
async run(command, args, cwd) {
|
|
2523
|
-
return await new Promise((
|
|
2524
|
-
const child = (0,
|
|
3009
|
+
return await new Promise((resolve11) => {
|
|
3010
|
+
const child = (0, import_node_child_process8.spawn)(command, args, {
|
|
2525
3011
|
cwd,
|
|
2526
3012
|
env: process.env,
|
|
2527
3013
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2535,7 +3021,7 @@ var NodeFlyCommandRunner = class {
|
|
|
2535
3021
|
stderr += chunk.toString("utf-8");
|
|
2536
3022
|
});
|
|
2537
3023
|
child.on("error", (error) => {
|
|
2538
|
-
|
|
3024
|
+
resolve11({
|
|
2539
3025
|
exitCode: 1,
|
|
2540
3026
|
stdout,
|
|
2541
3027
|
stderr: `${stderr}
|
|
@@ -2543,7 +3029,7 @@ ${error.message}`
|
|
|
2543
3029
|
});
|
|
2544
3030
|
});
|
|
2545
3031
|
child.on("exit", (code) => {
|
|
2546
|
-
|
|
3032
|
+
resolve11({
|
|
2547
3033
|
exitCode: code ?? 1,
|
|
2548
3034
|
stdout: stdout.trim(),
|
|
2549
3035
|
stderr: stderr.trim()
|
|
@@ -2615,12 +3101,12 @@ ${stdout}`.toLowerCase();
|
|
|
2615
3101
|
}
|
|
2616
3102
|
|
|
2617
3103
|
// src/commands/deploy/adapters/railway-adapter.ts
|
|
2618
|
-
var
|
|
2619
|
-
var
|
|
3104
|
+
var import_node_child_process9 = require("child_process");
|
|
3105
|
+
var import_node_path17 = require("path");
|
|
2620
3106
|
|
|
2621
3107
|
// src/commands/deploy/artifacts/railway-json-generator.ts
|
|
2622
|
-
var
|
|
2623
|
-
var
|
|
3108
|
+
var import_promises11 = require("fs/promises");
|
|
3109
|
+
var import_node_path16 = require("path");
|
|
2624
3110
|
var GENERATED_HEADER2 = "Generated by kora deploy - do not edit manually.";
|
|
2625
3111
|
function generateRailwayJson(options) {
|
|
2626
3112
|
const file = {
|
|
@@ -2645,9 +3131,9 @@ function generateRailwayJson(options) {
|
|
|
2645
3131
|
`;
|
|
2646
3132
|
}
|
|
2647
3133
|
async function writeRailwayJsonArtifact(deployDirectory, options) {
|
|
2648
|
-
await (0,
|
|
2649
|
-
const filePath = (0,
|
|
2650
|
-
await (0,
|
|
3134
|
+
await (0, import_promises11.mkdir)(deployDirectory, { recursive: true });
|
|
3135
|
+
const filePath = (0, import_node_path16.join)(deployDirectory, "railway.json");
|
|
3136
|
+
await (0, import_promises11.writeFile)(filePath, generateRailwayJson(options), "utf-8");
|
|
2651
3137
|
return filePath;
|
|
2652
3138
|
}
|
|
2653
3139
|
|
|
@@ -2736,7 +3222,7 @@ var RailwayAdapter = class {
|
|
|
2736
3222
|
appName: config.appName,
|
|
2737
3223
|
region: config.region
|
|
2738
3224
|
};
|
|
2739
|
-
const deployDirectory = (0,
|
|
3225
|
+
const deployDirectory = (0, import_node_path17.join)(config.projectRoot, ".kora", "deploy");
|
|
2740
3226
|
await writeRailwayJsonArtifact(deployDirectory, {
|
|
2741
3227
|
appName: config.appName,
|
|
2742
3228
|
environment: config.environment
|
|
@@ -2745,14 +3231,14 @@ var RailwayAdapter = class {
|
|
|
2745
3231
|
projectRoot: config.projectRoot,
|
|
2746
3232
|
deployDirectory
|
|
2747
3233
|
});
|
|
2748
|
-
const client = await buildClient({
|
|
3234
|
+
const client = config.deployTarget === "sync-server" ? null : await buildClient({
|
|
2749
3235
|
projectRoot: config.projectRoot,
|
|
2750
|
-
outDir: (0,
|
|
3236
|
+
outDir: (0, import_node_path17.join)(deployDirectory, "dist"),
|
|
2751
3237
|
mode: "production"
|
|
2752
3238
|
});
|
|
2753
3239
|
return {
|
|
2754
|
-
clientDirectory: client
|
|
2755
|
-
serverBundlePath: (0,
|
|
3240
|
+
clientDirectory: client?.outDir ?? null,
|
|
3241
|
+
serverBundlePath: (0, import_node_path17.join)(deployDirectory, "server-bundled.js"),
|
|
2756
3242
|
deployDirectory
|
|
2757
3243
|
};
|
|
2758
3244
|
}
|
|
@@ -2856,8 +3342,8 @@ var RailwayAdapter = class {
|
|
|
2856
3342
|
};
|
|
2857
3343
|
var NodeRailwayCommandRunner = class {
|
|
2858
3344
|
async run(command, args, cwd) {
|
|
2859
|
-
return await new Promise((
|
|
2860
|
-
const child = (0,
|
|
3345
|
+
return await new Promise((resolve11) => {
|
|
3346
|
+
const child = (0, import_node_child_process9.spawn)(command, args, {
|
|
2861
3347
|
cwd,
|
|
2862
3348
|
env: process.env,
|
|
2863
3349
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2871,7 +3357,7 @@ var NodeRailwayCommandRunner = class {
|
|
|
2871
3357
|
stderr += chunk.toString("utf-8");
|
|
2872
3358
|
});
|
|
2873
3359
|
child.on("error", (error) => {
|
|
2874
|
-
|
|
3360
|
+
resolve11({
|
|
2875
3361
|
exitCode: 1,
|
|
2876
3362
|
stdout,
|
|
2877
3363
|
stderr: `${stderr}
|
|
@@ -2879,7 +3365,7 @@ ${error.message}`
|
|
|
2879
3365
|
});
|
|
2880
3366
|
});
|
|
2881
3367
|
child.on("exit", (code) => {
|
|
2882
|
-
|
|
3368
|
+
resolve11({
|
|
2883
3369
|
exitCode: code ?? 1,
|
|
2884
3370
|
stdout: stdout.trim(),
|
|
2885
3371
|
stderr: stderr.trim()
|
|
@@ -3037,13 +3523,13 @@ function createDeployAdapter(platform) {
|
|
|
3037
3523
|
}
|
|
3038
3524
|
|
|
3039
3525
|
// src/commands/deploy/artifacts/dockerfile-generator.ts
|
|
3040
|
-
var
|
|
3041
|
-
var
|
|
3526
|
+
var import_promises12 = require("fs/promises");
|
|
3527
|
+
var import_node_path18 = require("path");
|
|
3042
3528
|
var GENERATED_HEADER3 = "# Generated by kora deploy \u2014 do not edit manually";
|
|
3043
3529
|
function generateDockerfile(options = {}) {
|
|
3044
3530
|
const nodeVersion = options.nodeVersion ?? "20-alpine";
|
|
3045
3531
|
const port = options.port ?? 3e3;
|
|
3046
|
-
const clientDirectory = options.clientDirectory
|
|
3532
|
+
const clientDirectory = options.clientDirectory === void 0 ? "dist" : options.clientDirectory;
|
|
3047
3533
|
const serverBundleFile = options.serverBundleFile ?? "server-bundled.js";
|
|
3048
3534
|
const hasNativeDeps = options.nativeDependencies && Object.keys(options.nativeDependencies).length > 0;
|
|
3049
3535
|
const lines = [
|
|
@@ -3063,7 +3549,9 @@ function generateDockerfile(options = {}) {
|
|
|
3063
3549
|
lines.push("RUN npm install --omit=dev");
|
|
3064
3550
|
lines.push("");
|
|
3065
3551
|
}
|
|
3066
|
-
|
|
3552
|
+
if (clientDirectory !== null) {
|
|
3553
|
+
lines.push(`COPY ${clientDirectory} ./dist`);
|
|
3554
|
+
}
|
|
3067
3555
|
lines.push(`COPY ${serverBundleFile} ./server-bundled.js`);
|
|
3068
3556
|
lines.push("");
|
|
3069
3557
|
lines.push(`EXPOSE ${String(port)}`);
|
|
@@ -3102,37 +3590,37 @@ function generateDockerIgnore() {
|
|
|
3102
3590
|
].join("\n");
|
|
3103
3591
|
}
|
|
3104
3592
|
async function writeDockerfileArtifact(deployDirectory, options = {}) {
|
|
3105
|
-
await (0,
|
|
3106
|
-
const dockerfilePath = (0,
|
|
3107
|
-
await (0,
|
|
3593
|
+
await (0, import_promises12.mkdir)(deployDirectory, { recursive: true });
|
|
3594
|
+
const dockerfilePath = (0, import_node_path18.join)(deployDirectory, "Dockerfile");
|
|
3595
|
+
await (0, import_promises12.writeFile)(dockerfilePath, generateDockerfile(options), "utf-8");
|
|
3108
3596
|
if (options.nativeDependencies && Object.keys(options.nativeDependencies).length > 0) {
|
|
3109
|
-
const pkgJsonPath = (0,
|
|
3110
|
-
await (0,
|
|
3597
|
+
const pkgJsonPath = (0, import_node_path18.join)(deployDirectory, "package.json");
|
|
3598
|
+
await (0, import_promises12.writeFile)(pkgJsonPath, generateDeployPackageJson(options.nativeDependencies), "utf-8");
|
|
3111
3599
|
}
|
|
3112
3600
|
return dockerfilePath;
|
|
3113
3601
|
}
|
|
3114
3602
|
async function writeDockerIgnoreArtifact(deployDirectory) {
|
|
3115
|
-
await (0,
|
|
3116
|
-
const dockerIgnorePath = (0,
|
|
3117
|
-
await (0,
|
|
3603
|
+
await (0, import_promises12.mkdir)(deployDirectory, { recursive: true });
|
|
3604
|
+
const dockerIgnorePath = (0, import_node_path18.join)(deployDirectory, ".dockerignore");
|
|
3605
|
+
await (0, import_promises12.writeFile)(dockerIgnorePath, generateDockerIgnore(), "utf-8");
|
|
3118
3606
|
return dockerIgnorePath;
|
|
3119
3607
|
}
|
|
3120
3608
|
|
|
3121
3609
|
// src/commands/deploy/state/deploy-state.ts
|
|
3122
|
-
var
|
|
3123
|
-
var
|
|
3124
|
-
var KORA_DEPLOY_DIRECTORY = (0,
|
|
3610
|
+
var import_promises13 = require("fs/promises");
|
|
3611
|
+
var import_node_path19 = require("path");
|
|
3612
|
+
var KORA_DEPLOY_DIRECTORY = (0, import_node_path19.join)(".kora", "deploy");
|
|
3125
3613
|
var DEPLOY_STATE_FILENAME = "deploy.json";
|
|
3126
3614
|
function resolveDeployDirectory(projectRoot) {
|
|
3127
|
-
return (0,
|
|
3615
|
+
return (0, import_node_path19.join)(projectRoot, KORA_DEPLOY_DIRECTORY);
|
|
3128
3616
|
}
|
|
3129
3617
|
function resolveDeployStatePath(projectRoot) {
|
|
3130
|
-
return (0,
|
|
3618
|
+
return (0, import_node_path19.join)(resolveDeployDirectory(projectRoot), DEPLOY_STATE_FILENAME);
|
|
3131
3619
|
}
|
|
3132
3620
|
async function readDeployState(projectRoot) {
|
|
3133
3621
|
const statePath = resolveDeployStatePath(projectRoot);
|
|
3134
3622
|
try {
|
|
3135
|
-
const source = await (0,
|
|
3623
|
+
const source = await (0, import_promises13.readFile)(statePath, "utf-8");
|
|
3136
3624
|
const parsed = JSON.parse(source);
|
|
3137
3625
|
return parseDeployState(parsed);
|
|
3138
3626
|
} catch (error) {
|
|
@@ -3179,13 +3667,13 @@ async function updateDeployState(projectRoot, patch, now = /* @__PURE__ */ new D
|
|
|
3179
3667
|
return nextState;
|
|
3180
3668
|
}
|
|
3181
3669
|
async function resetDeployState(projectRoot) {
|
|
3182
|
-
await (0,
|
|
3670
|
+
await (0, import_promises13.rm)(resolveDeployDirectory(projectRoot), { recursive: true, force: true });
|
|
3183
3671
|
}
|
|
3184
3672
|
async function persistDeployState(projectRoot, state) {
|
|
3185
3673
|
const deployDir = resolveDeployDirectory(projectRoot);
|
|
3186
3674
|
const statePath = resolveDeployStatePath(projectRoot);
|
|
3187
|
-
await (0,
|
|
3188
|
-
await (0,
|
|
3675
|
+
await (0, import_promises13.mkdir)(deployDir, { recursive: true });
|
|
3676
|
+
await (0, import_promises13.writeFile)(statePath, `${JSON.stringify(state, null, 2)}
|
|
3189
3677
|
`, "utf-8");
|
|
3190
3678
|
}
|
|
3191
3679
|
function parseDeployState(value) {
|
|
@@ -3235,7 +3723,7 @@ function readOptionalString(value, field) {
|
|
|
3235
3723
|
}
|
|
3236
3724
|
|
|
3237
3725
|
// src/commands/deploy/deploy-command.ts
|
|
3238
|
-
var deployCommand = (0,
|
|
3726
|
+
var deployCommand = (0, import_citty4.defineCommand)({
|
|
3239
3727
|
meta: {
|
|
3240
3728
|
name: "deploy",
|
|
3241
3729
|
description: "Deploy a Kora project to your selected platform"
|
|
@@ -3270,7 +3758,7 @@ var deployCommand = (0, import_citty2.defineCommand)({
|
|
|
3270
3758
|
}
|
|
3271
3759
|
},
|
|
3272
3760
|
subCommands: {
|
|
3273
|
-
status: (0,
|
|
3761
|
+
status: (0, import_citty4.defineCommand)({
|
|
3274
3762
|
meta: {
|
|
3275
3763
|
name: "status",
|
|
3276
3764
|
description: "Show current deployment status"
|
|
@@ -3301,7 +3789,7 @@ var deployCommand = (0, import_citty2.defineCommand)({
|
|
|
3301
3789
|
logger.step(`Sync URL: ${state.syncUrl ?? "n/a"}`);
|
|
3302
3790
|
}
|
|
3303
3791
|
}),
|
|
3304
|
-
rollback: (0,
|
|
3792
|
+
rollback: (0, import_citty4.defineCommand)({
|
|
3305
3793
|
meta: {
|
|
3306
3794
|
name: "rollback",
|
|
3307
3795
|
description: "Rollback the current deployment"
|
|
@@ -3332,7 +3820,7 @@ var deployCommand = (0, import_citty2.defineCommand)({
|
|
|
3332
3820
|
logger.success(`Rolled back ${state.platform} deployment to ${deploymentId}.`);
|
|
3333
3821
|
}
|
|
3334
3822
|
}),
|
|
3335
|
-
logs: (0,
|
|
3823
|
+
logs: (0, import_citty4.defineCommand)({
|
|
3336
3824
|
meta: {
|
|
3337
3825
|
name: "logs",
|
|
3338
3826
|
description: "Read deployment logs"
|
|
@@ -3388,12 +3876,14 @@ var deployCommand = (0, import_citty2.defineCommand)({
|
|
|
3388
3876
|
const region = resolveRegion(args.region, existingState?.region, confirmMode);
|
|
3389
3877
|
const deployDirectory = resolveDeployDirectory(projectRoot);
|
|
3390
3878
|
const environment = args.prod === true ? "production" : "preview";
|
|
3879
|
+
const deployTarget = detectDeployTarget(projectRoot);
|
|
3391
3880
|
const config = {
|
|
3392
3881
|
projectRoot,
|
|
3393
3882
|
appName,
|
|
3394
3883
|
region,
|
|
3395
3884
|
environment,
|
|
3396
|
-
confirm: confirmMode
|
|
3885
|
+
confirm: confirmMode,
|
|
3886
|
+
deployTarget
|
|
3397
3887
|
};
|
|
3398
3888
|
const adapter = createDeployAdapter(platform);
|
|
3399
3889
|
configureAdapterContext(adapter, {
|
|
@@ -3405,10 +3895,14 @@ var deployCommand = (0, import_citty2.defineCommand)({
|
|
|
3405
3895
|
logger.info(
|
|
3406
3896
|
`Deploying to ${platform} (${appName}${region ? `, ${region}` : ""}, ${environment})`
|
|
3407
3897
|
);
|
|
3898
|
+
if (deployTarget === "sync-server") {
|
|
3899
|
+
logger.step("Detected Tauri project. Deploying the sync server only.");
|
|
3900
|
+
}
|
|
3408
3901
|
if (confirmMode) {
|
|
3409
3902
|
logger.step("Running in --confirm mode (non-interactive, fail-fast).");
|
|
3410
3903
|
}
|
|
3411
3904
|
await writeDockerfileArtifact(deployDirectory, {
|
|
3905
|
+
clientDirectory: deployTarget === "sync-server" ? null : void 0,
|
|
3412
3906
|
nativeDependencies: {
|
|
3413
3907
|
"better-sqlite3": "^11.0.0",
|
|
3414
3908
|
"drizzle-orm": "^0.45.2",
|
|
@@ -3513,7 +4007,7 @@ function resolveAppName(argValue, storedValue, projectRoot, confirm) {
|
|
|
3513
4007
|
"Missing app name in --confirm mode. Provide --app or run an interactive deploy first."
|
|
3514
4008
|
);
|
|
3515
4009
|
}
|
|
3516
|
-
return sanitizeAppName((0,
|
|
4010
|
+
return sanitizeAppName((0, import_node_path20.basename)(projectRoot));
|
|
3517
4011
|
}
|
|
3518
4012
|
function resolveRegion(argValue, storedValue, confirm) {
|
|
3519
4013
|
if (typeof argValue === "string" && argValue.length > 0) return argValue;
|
|
@@ -3547,109 +4041,21 @@ function configureAdapterContext(adapter, context) {
|
|
|
3547
4041
|
adapter.setContext(context);
|
|
3548
4042
|
}
|
|
3549
4043
|
}
|
|
4044
|
+
function detectDeployTarget(projectRoot) {
|
|
4045
|
+
return (0, import_node_fs4.existsSync)((0, import_node_path20.join)(projectRoot, "src-tauri")) ? "sync-server" : "full-stack";
|
|
4046
|
+
}
|
|
3550
4047
|
function hasContextSetter(adapter) {
|
|
3551
4048
|
return typeof adapter.setContext === "function";
|
|
3552
4049
|
}
|
|
3553
4050
|
|
|
3554
4051
|
// src/commands/dev/dev-command.ts
|
|
3555
|
-
var
|
|
3556
|
-
var
|
|
3557
|
-
var
|
|
3558
|
-
var
|
|
3559
|
-
|
|
3560
|
-
// src/commands/dev/kora-config.ts
|
|
3561
|
-
var import_node_child_process8 = require("child_process");
|
|
3562
|
-
var import_promises12 = require("fs/promises");
|
|
3563
|
-
var import_node_path17 = require("path");
|
|
3564
|
-
var import_node_url3 = require("url");
|
|
3565
|
-
var CONFIG_CANDIDATES = [
|
|
3566
|
-
"kora.config.ts",
|
|
3567
|
-
"kora.config.mts",
|
|
3568
|
-
"kora.config.cts",
|
|
3569
|
-
"kora.config.js",
|
|
3570
|
-
"kora.config.mjs",
|
|
3571
|
-
"kora.config.cjs"
|
|
3572
|
-
];
|
|
3573
|
-
async function loadKoraConfig(projectRoot) {
|
|
3574
|
-
const configPath = await findKoraConfigFile(projectRoot);
|
|
3575
|
-
if (!configPath) return null;
|
|
3576
|
-
const ext = (0, import_node_path17.extname)(configPath);
|
|
3577
|
-
if (ext === ".ts" || ext === ".mts" || ext === ".cts") {
|
|
3578
|
-
const loaded2 = await loadTypeScriptConfig(configPath, projectRoot);
|
|
3579
|
-
return toConfigObject(loaded2);
|
|
3580
|
-
}
|
|
3581
|
-
const loaded = await import((0, import_node_url3.pathToFileURL)(configPath).href);
|
|
3582
|
-
return toConfigObject(loaded);
|
|
3583
|
-
}
|
|
3584
|
-
async function findKoraConfigFile(projectRoot) {
|
|
3585
|
-
for (const file of CONFIG_CANDIDATES) {
|
|
3586
|
-
const candidate = (0, import_node_path17.join)(projectRoot, file);
|
|
3587
|
-
try {
|
|
3588
|
-
await (0, import_promises12.access)(candidate);
|
|
3589
|
-
return candidate;
|
|
3590
|
-
} catch {
|
|
3591
|
-
}
|
|
3592
|
-
}
|
|
3593
|
-
return null;
|
|
3594
|
-
}
|
|
3595
|
-
async function loadTypeScriptConfig(configPath, projectRoot) {
|
|
3596
|
-
if (!await hasTsxInstalled(projectRoot)) {
|
|
3597
|
-
throw new Error(
|
|
3598
|
-
`Found TypeScript config at ${configPath}, but "tsx" is not installed in this project. Install tsx or use kora.config.js.`
|
|
3599
|
-
);
|
|
3600
|
-
}
|
|
3601
|
-
const script = "const configPath = process.argv[process.argv.length - 1];import('node:url').then(u => import(u.pathToFileURL(configPath).href)).then(mod => { const v = mod.default ?? mod; process.stdout.write(JSON.stringify(v)) }).catch(e => { process.stderr.write(String(e)); process.exit(1) })";
|
|
3602
|
-
const output = await runCommand(
|
|
3603
|
-
process.execPath,
|
|
3604
|
-
["--import", "tsx", "--eval", script, configPath],
|
|
3605
|
-
projectRoot
|
|
3606
|
-
);
|
|
3607
|
-
try {
|
|
3608
|
-
return JSON.parse(output);
|
|
3609
|
-
} catch {
|
|
3610
|
-
throw new Error(`Failed to parse ${configPath} output as JSON.`);
|
|
3611
|
-
}
|
|
3612
|
-
}
|
|
3613
|
-
async function runCommand(command, args, cwd) {
|
|
3614
|
-
return await new Promise((resolve9, reject) => {
|
|
3615
|
-
const child = (0, import_node_child_process8.spawn)(command, args, {
|
|
3616
|
-
cwd,
|
|
3617
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
3618
|
-
env: process.env
|
|
3619
|
-
});
|
|
3620
|
-
let stdout = "";
|
|
3621
|
-
let stderr = "";
|
|
3622
|
-
child.stdout?.on("data", (chunk) => {
|
|
3623
|
-
stdout += chunk.toString("utf-8");
|
|
3624
|
-
});
|
|
3625
|
-
child.stderr?.on("data", (chunk) => {
|
|
3626
|
-
stderr += chunk.toString("utf-8");
|
|
3627
|
-
});
|
|
3628
|
-
child.on("error", (error) => {
|
|
3629
|
-
reject(error);
|
|
3630
|
-
});
|
|
3631
|
-
child.on("exit", (code) => {
|
|
3632
|
-
if (code === 0) {
|
|
3633
|
-
resolve9(stdout.trim());
|
|
3634
|
-
return;
|
|
3635
|
-
}
|
|
3636
|
-
reject(new Error(`Failed to load kora config (exit ${code ?? "unknown"}): ${stderr.trim()}`));
|
|
3637
|
-
});
|
|
3638
|
-
});
|
|
3639
|
-
}
|
|
3640
|
-
function toConfigObject(mod) {
|
|
3641
|
-
if (typeof mod !== "object" || mod === null) {
|
|
3642
|
-
throw new Error("kora config must export an object.");
|
|
3643
|
-
}
|
|
3644
|
-
const value = mod.default ?? mod;
|
|
3645
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3646
|
-
throw new Error("kora config must export an object.");
|
|
3647
|
-
}
|
|
3648
|
-
return value;
|
|
3649
|
-
}
|
|
4052
|
+
var import_promises14 = require("fs/promises");
|
|
4053
|
+
var import_node_path22 = require("path");
|
|
4054
|
+
var import_node_path23 = require("path");
|
|
4055
|
+
var import_citty5 = require("citty");
|
|
3650
4056
|
|
|
3651
4057
|
// src/commands/dev/process-manager.ts
|
|
3652
|
-
var
|
|
4058
|
+
var import_node_child_process10 = require("child_process");
|
|
3653
4059
|
var ProcessManager = class {
|
|
3654
4060
|
processes = /* @__PURE__ */ new Map();
|
|
3655
4061
|
spawn(config) {
|
|
@@ -3658,12 +4064,12 @@ var ProcessManager = class {
|
|
|
3658
4064
|
env: { ...process.env, ...config.env },
|
|
3659
4065
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3660
4066
|
};
|
|
3661
|
-
const child = (0,
|
|
4067
|
+
const child = (0, import_node_child_process10.spawn)(config.command, config.args, options);
|
|
3662
4068
|
let resolveExit;
|
|
3663
4069
|
const runningProcess = {
|
|
3664
4070
|
child,
|
|
3665
|
-
exitPromise: new Promise((
|
|
3666
|
-
resolveExit =
|
|
4071
|
+
exitPromise: new Promise((resolve11) => {
|
|
4072
|
+
resolveExit = resolve11;
|
|
3667
4073
|
}),
|
|
3668
4074
|
stdoutBuffer: "",
|
|
3669
4075
|
stderrBuffer: ""
|
|
@@ -3730,15 +4136,15 @@ var ProcessManager = class {
|
|
|
3730
4136
|
}
|
|
3731
4137
|
};
|
|
3732
4138
|
function delay(ms) {
|
|
3733
|
-
return new Promise((
|
|
3734
|
-
setTimeout(
|
|
4139
|
+
return new Promise((resolve11) => {
|
|
4140
|
+
setTimeout(resolve11, ms);
|
|
3735
4141
|
});
|
|
3736
4142
|
}
|
|
3737
4143
|
|
|
3738
4144
|
// src/commands/dev/schema-watcher.ts
|
|
3739
|
-
var
|
|
3740
|
-
var
|
|
3741
|
-
var
|
|
4145
|
+
var import_node_child_process11 = require("child_process");
|
|
4146
|
+
var import_node_fs5 = require("fs");
|
|
4147
|
+
var import_node_path21 = require("path");
|
|
3742
4148
|
var SchemaWatcher = class {
|
|
3743
4149
|
constructor(config) {
|
|
3744
4150
|
this.config = config;
|
|
@@ -3750,7 +4156,7 @@ var SchemaWatcher = class {
|
|
|
3750
4156
|
debounceTimer = null;
|
|
3751
4157
|
start() {
|
|
3752
4158
|
if (this.watcher) return;
|
|
3753
|
-
this.watcher = (0,
|
|
4159
|
+
this.watcher = (0, import_node_fs5.watch)(this.config.schemaPath, () => {
|
|
3754
4160
|
this.scheduleRegeneration();
|
|
3755
4161
|
});
|
|
3756
4162
|
this.watcher.on("error", (error) => {
|
|
@@ -3766,7 +4172,7 @@ var SchemaWatcher = class {
|
|
|
3766
4172
|
this.watcher = null;
|
|
3767
4173
|
}
|
|
3768
4174
|
async regenerate() {
|
|
3769
|
-
const koraBinJs = (0,
|
|
4175
|
+
const koraBinJs = (0, import_node_path21.join)(
|
|
3770
4176
|
this.config.projectRoot,
|
|
3771
4177
|
"node_modules",
|
|
3772
4178
|
"@korajs",
|
|
@@ -3793,8 +4199,8 @@ var SchemaWatcher = class {
|
|
|
3793
4199
|
}
|
|
3794
4200
|
};
|
|
3795
4201
|
async function spawnCommand(command, args, cwd) {
|
|
3796
|
-
await new Promise((
|
|
3797
|
-
const child = (0,
|
|
4202
|
+
await new Promise((resolve11, reject) => {
|
|
4203
|
+
const child = (0, import_node_child_process11.spawn)(command, args, {
|
|
3798
4204
|
cwd,
|
|
3799
4205
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3800
4206
|
env: process.env
|
|
@@ -3810,7 +4216,7 @@ async function spawnCommand(command, args, cwd) {
|
|
|
3810
4216
|
});
|
|
3811
4217
|
child.on("exit", (code) => {
|
|
3812
4218
|
if (code === 0) {
|
|
3813
|
-
|
|
4219
|
+
resolve11();
|
|
3814
4220
|
return;
|
|
3815
4221
|
}
|
|
3816
4222
|
reject(new Error(`Type generation exited with code ${code ?? "unknown"}.`));
|
|
@@ -3832,7 +4238,7 @@ function toError(error) {
|
|
|
3832
4238
|
}
|
|
3833
4239
|
|
|
3834
4240
|
// src/commands/dev/dev-command.ts
|
|
3835
|
-
var devCommand = (0,
|
|
4241
|
+
var devCommand = (0, import_citty5.defineCommand)({
|
|
3836
4242
|
meta: {
|
|
3837
4243
|
name: "dev",
|
|
3838
4244
|
description: "Start the Kora development environment"
|
|
@@ -3872,7 +4278,7 @@ var devCommand = (0, import_citty3.defineCommand)({
|
|
|
3872
4278
|
const watchDebounceMs = typeof config?.dev?.watch === "object" && typeof config.dev.watch.debounceMs === "number" ? config.dev.watch.debounceMs : 300;
|
|
3873
4279
|
const viteEntryPoint = await resolveProjectBinaryEntryPoint(projectRoot, "vite", "vite");
|
|
3874
4280
|
if (!viteEntryPoint) {
|
|
3875
|
-
throw new DevServerError("vite", (0,
|
|
4281
|
+
throw new DevServerError("vite", (0, import_node_path22.join)(projectRoot, "node_modules", ".bin", "vite"));
|
|
3876
4282
|
}
|
|
3877
4283
|
const syncServerFile = await findSyncServerFile(projectRoot);
|
|
3878
4284
|
let managedSyncStore = normalizeManagedSyncStore(config, projectRoot);
|
|
@@ -3888,7 +4294,7 @@ var devCommand = (0, import_citty3.defineCommand)({
|
|
|
3888
4294
|
}
|
|
3889
4295
|
if (shouldStartSync && syncServerFile === null && managedSyncStore) {
|
|
3890
4296
|
const hasServerPackage = await fileExists2(
|
|
3891
|
-
(0,
|
|
4297
|
+
(0, import_node_path22.join)(projectRoot, "node_modules", "@korajs", "server", "package.json")
|
|
3892
4298
|
);
|
|
3893
4299
|
if (!hasServerPackage) {
|
|
3894
4300
|
logger.warn(
|
|
@@ -3905,7 +4311,7 @@ var devCommand = (0, import_citty3.defineCommand)({
|
|
|
3905
4311
|
}
|
|
3906
4312
|
let configuredSchemaPath = null;
|
|
3907
4313
|
if (typeof config?.schema === "string") {
|
|
3908
|
-
const candidate = (0,
|
|
4314
|
+
const candidate = (0, import_node_path23.resolve)(projectRoot, config.schema);
|
|
3909
4315
|
if (await fileExists2(candidate)) {
|
|
3910
4316
|
configuredSchemaPath = candidate;
|
|
3911
4317
|
} else {
|
|
@@ -3920,8 +4326,8 @@ var devCommand = (0, import_citty3.defineCommand)({
|
|
|
3920
4326
|
let schemaWatcher = null;
|
|
3921
4327
|
let shuttingDown = false;
|
|
3922
4328
|
let resolveFinished;
|
|
3923
|
-
const finished = new Promise((
|
|
3924
|
-
resolveFinished =
|
|
4329
|
+
const finished = new Promise((resolve11) => {
|
|
4330
|
+
resolveFinished = resolve11;
|
|
3925
4331
|
});
|
|
3926
4332
|
const onManagedProcessExit = () => {
|
|
3927
4333
|
if (!processManager.hasRunning() && !shuttingDown) {
|
|
@@ -3964,11 +4370,15 @@ var devCommand = (0, import_citty3.defineCommand)({
|
|
|
3964
4370
|
logger.step(" Schema watcher disabled (schema.ts not found)");
|
|
3965
4371
|
}
|
|
3966
4372
|
logger.blank();
|
|
4373
|
+
logger.step(" Kora DevTools overlay: Ctrl+Shift+K (Cmd+Shift+K on macOS) when devtools: true");
|
|
3967
4374
|
processManager.spawn({
|
|
3968
4375
|
label: "vite",
|
|
3969
4376
|
command: process.execPath,
|
|
3970
4377
|
args: [viteEntryPoint, "--port", String(vitePort)],
|
|
3971
4378
|
cwd: projectRoot,
|
|
4379
|
+
env: {
|
|
4380
|
+
KORA_DEV: "1"
|
|
4381
|
+
},
|
|
3972
4382
|
onExit: onManagedProcessExit
|
|
3973
4383
|
});
|
|
3974
4384
|
if (shouldStartSync && hasTsx && syncServerFile) {
|
|
@@ -4019,10 +4429,10 @@ var devCommand = (0, import_citty3.defineCommand)({
|
|
|
4019
4429
|
}
|
|
4020
4430
|
});
|
|
4021
4431
|
async function findSyncServerFile(projectRoot) {
|
|
4022
|
-
const candidates = [(0,
|
|
4432
|
+
const candidates = [(0, import_node_path22.join)(projectRoot, "server.ts"), (0, import_node_path22.join)(projectRoot, "server.js")];
|
|
4023
4433
|
for (const candidate of candidates) {
|
|
4024
4434
|
try {
|
|
4025
|
-
await (0,
|
|
4435
|
+
await (0, import_promises14.access)(candidate);
|
|
4026
4436
|
return candidate;
|
|
4027
4437
|
} catch {
|
|
4028
4438
|
}
|
|
@@ -4031,7 +4441,7 @@ async function findSyncServerFile(projectRoot) {
|
|
|
4031
4441
|
}
|
|
4032
4442
|
async function fileExists2(path) {
|
|
4033
4443
|
try {
|
|
4034
|
-
await (0,
|
|
4444
|
+
await (0, import_promises14.access)(path);
|
|
4035
4445
|
return true;
|
|
4036
4446
|
} catch {
|
|
4037
4447
|
return false;
|
|
@@ -4043,7 +4453,7 @@ function normalizeManagedSyncStore(config, projectRoot) {
|
|
|
4043
4453
|
const store = sync.store;
|
|
4044
4454
|
if (store === void 0) return { type: "memory" };
|
|
4045
4455
|
if (store === "memory") return { type: "memory" };
|
|
4046
|
-
if (store === "sqlite") return { type: "sqlite", filename: (0,
|
|
4456
|
+
if (store === "sqlite") return { type: "sqlite", filename: (0, import_node_path22.join)(projectRoot, "kora-sync.db") };
|
|
4047
4457
|
if (store === "postgres") {
|
|
4048
4458
|
const connectionString = process.env.DATABASE_URL;
|
|
4049
4459
|
if (!connectionString) return null;
|
|
@@ -4052,7 +4462,7 @@ function normalizeManagedSyncStore(config, projectRoot) {
|
|
|
4052
4462
|
if (typeof store === "object" && store !== null) {
|
|
4053
4463
|
if (store.type === "memory") return { type: "memory" };
|
|
4054
4464
|
if (store.type === "sqlite") {
|
|
4055
|
-
const filename = typeof store.filename === "string" && store.filename.length > 0 ? (0,
|
|
4465
|
+
const filename = typeof store.filename === "string" && store.filename.length > 0 ? (0, import_node_path23.resolve)(projectRoot, store.filename) : (0, import_node_path22.join)(projectRoot, "kora-sync.db");
|
|
4056
4466
|
return { type: "sqlite", filename };
|
|
4057
4467
|
}
|
|
4058
4468
|
if (store.type === "postgres" && typeof store.connectionString === "string") {
|
|
@@ -4090,35 +4500,403 @@ if (storeConfig.type === 'memory') {
|
|
|
4090
4500
|
if (typeof storeConfig.connectionString !== 'string' || storeConfig.connectionString.length === 0) {
|
|
4091
4501
|
throw new Error('Managed postgres sync requires a connectionString');
|
|
4092
4502
|
}
|
|
4093
|
-
store = await createPostgresServerStore({
|
|
4094
|
-
connectionString: storeConfig.connectionString,
|
|
4095
|
-
});
|
|
4096
|
-
} else {
|
|
4097
|
-
throw new Error('Unsupported managed sync store type: ' + String(storeConfig.type));
|
|
4503
|
+
store = await createPostgresServerStore({
|
|
4504
|
+
connectionString: storeConfig.connectionString,
|
|
4505
|
+
});
|
|
4506
|
+
} else {
|
|
4507
|
+
throw new Error('Unsupported managed sync store type: ' + String(storeConfig.type));
|
|
4508
|
+
}
|
|
4509
|
+
const server = createKoraServer({ store, port: Number(config.port ?? 3001) });
|
|
4510
|
+
const shutdown = async () => {
|
|
4511
|
+
try {
|
|
4512
|
+
await server.stop();
|
|
4513
|
+
} catch {
|
|
4514
|
+
}
|
|
4515
|
+
process.exit(0);
|
|
4516
|
+
};
|
|
4517
|
+
process.on('SIGINT', () => {
|
|
4518
|
+
void shutdown();
|
|
4519
|
+
});
|
|
4520
|
+
process.on('SIGTERM', () => {
|
|
4521
|
+
void shutdown();
|
|
4522
|
+
});
|
|
4523
|
+
await server.start();
|
|
4524
|
+
process.stdout.write('Managed sync server running on ws://localhost:' + String(config.port ?? 3001) + '\\n');
|
|
4525
|
+
await new Promise(() => {});
|
|
4526
|
+
`;
|
|
4527
|
+
|
|
4528
|
+
// src/commands/doctor/doctor-command.ts
|
|
4529
|
+
var import_citty6 = require("citty");
|
|
4530
|
+
|
|
4531
|
+
// src/commands/doctor/doctor-checks.ts
|
|
4532
|
+
var import_promises15 = require("fs/promises");
|
|
4533
|
+
var import_node_path24 = require("path");
|
|
4534
|
+
var STATUS_ENDPOINT = "/__kora/status";
|
|
4535
|
+
async function runDoctorChecks(options) {
|
|
4536
|
+
const { projectRoot } = options;
|
|
4537
|
+
const results = [];
|
|
4538
|
+
results.push({
|
|
4539
|
+
id: "project-root",
|
|
4540
|
+
title: "Project root",
|
|
4541
|
+
status: "pass",
|
|
4542
|
+
message: projectRoot
|
|
4543
|
+
});
|
|
4544
|
+
const config = await loadKoraConfig(projectRoot);
|
|
4545
|
+
if (!config) {
|
|
4546
|
+
results.push({
|
|
4547
|
+
id: "kora-config",
|
|
4548
|
+
title: "kora.config",
|
|
4549
|
+
status: "warn",
|
|
4550
|
+
message: "No kora.config.* file found.",
|
|
4551
|
+
fix: "Add kora.config.ts with schema path and dev settings (see create-kora-app templates)."
|
|
4552
|
+
});
|
|
4553
|
+
} else {
|
|
4554
|
+
results.push({
|
|
4555
|
+
id: "kora-config",
|
|
4556
|
+
title: "kora.config",
|
|
4557
|
+
status: "pass",
|
|
4558
|
+
message: "Configuration file loaded."
|
|
4559
|
+
});
|
|
4560
|
+
}
|
|
4561
|
+
const schemaPath = await resolveSchemaPath(projectRoot, config?.schema);
|
|
4562
|
+
if (!schemaPath) {
|
|
4563
|
+
results.push({
|
|
4564
|
+
id: "schema",
|
|
4565
|
+
title: "Schema",
|
|
4566
|
+
status: "fail",
|
|
4567
|
+
message: "Could not find schema.ts in src/ or project root.",
|
|
4568
|
+
fix: "Add src/schema.ts or set schema in kora.config.ts."
|
|
4569
|
+
});
|
|
4570
|
+
} else {
|
|
4571
|
+
try {
|
|
4572
|
+
const schema = await loadSchemaDefinition(schemaPath, projectRoot);
|
|
4573
|
+
results.push({
|
|
4574
|
+
id: "schema",
|
|
4575
|
+
title: "Schema",
|
|
4576
|
+
status: "pass",
|
|
4577
|
+
message: `Loaded schema v${schema.version} (${Object.keys(schema.collections).length} collection(s)).`
|
|
4578
|
+
});
|
|
4579
|
+
const workerCheck = await checkWorkerFile(projectRoot);
|
|
4580
|
+
results.push(workerCheck);
|
|
4581
|
+
const depsCheck = await checkDependencies(projectRoot);
|
|
4582
|
+
results.push(depsCheck);
|
|
4583
|
+
results.push(opfsBrowserNote());
|
|
4584
|
+
if (!options.skipNetwork) {
|
|
4585
|
+
const syncUrl = options.syncHttpUrl ?? await resolveSyncHttpUrl(projectRoot, config);
|
|
4586
|
+
const syncChecks = await checkSyncServer(syncUrl, schema);
|
|
4587
|
+
results.push(...syncChecks);
|
|
4588
|
+
} else {
|
|
4589
|
+
results.push({
|
|
4590
|
+
id: "sync-server",
|
|
4591
|
+
title: "Sync server",
|
|
4592
|
+
status: "skip",
|
|
4593
|
+
message: "Network checks skipped."
|
|
4594
|
+
});
|
|
4595
|
+
}
|
|
4596
|
+
} catch (error) {
|
|
4597
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4598
|
+
results.push({
|
|
4599
|
+
id: "schema",
|
|
4600
|
+
title: "Schema",
|
|
4601
|
+
status: "fail",
|
|
4602
|
+
message: `Failed to load schema: ${message}`,
|
|
4603
|
+
fix: "Ensure the schema exports defineSchema() as default and TypeScript can be evaluated (install tsx)."
|
|
4604
|
+
});
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
return results;
|
|
4608
|
+
}
|
|
4609
|
+
async function resolveSchemaPath(projectRoot, configSchema) {
|
|
4610
|
+
if (typeof configSchema === "string") {
|
|
4611
|
+
const resolved = (0, import_node_path24.resolve)(projectRoot, configSchema);
|
|
4612
|
+
try {
|
|
4613
|
+
await (0, import_promises15.access)(resolved);
|
|
4614
|
+
return resolved;
|
|
4615
|
+
} catch {
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
return findSchemaFile(projectRoot);
|
|
4619
|
+
}
|
|
4620
|
+
async function checkWorkerFile(projectRoot) {
|
|
4621
|
+
const candidates = [
|
|
4622
|
+
(0, import_node_path24.join)(projectRoot, "src", "kora-worker.ts"),
|
|
4623
|
+
(0, import_node_path24.join)(projectRoot, "src", "kora-worker.js")
|
|
4624
|
+
];
|
|
4625
|
+
for (const path of candidates) {
|
|
4626
|
+
try {
|
|
4627
|
+
await (0, import_promises15.access)(path);
|
|
4628
|
+
return {
|
|
4629
|
+
id: "worker",
|
|
4630
|
+
title: "SQLite WASM worker",
|
|
4631
|
+
status: "pass",
|
|
4632
|
+
message: `Found ${path.replace(projectRoot, ".")}.`
|
|
4633
|
+
};
|
|
4634
|
+
} catch {
|
|
4635
|
+
}
|
|
4636
|
+
}
|
|
4637
|
+
return {
|
|
4638
|
+
id: "worker",
|
|
4639
|
+
title: "SQLite WASM worker",
|
|
4640
|
+
status: "warn",
|
|
4641
|
+
message: "No src/kora-worker.ts found.",
|
|
4642
|
+
fix: "Add kora-worker.ts and pass store.workerUrl from Vite (?worker&url) in createApp()."
|
|
4643
|
+
};
|
|
4644
|
+
}
|
|
4645
|
+
async function checkDependencies(projectRoot) {
|
|
4646
|
+
try {
|
|
4647
|
+
const raw = await (0, import_promises15.readFile)((0, import_node_path24.join)(projectRoot, "package.json"), "utf-8");
|
|
4648
|
+
const pkg = JSON.parse(raw);
|
|
4649
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
4650
|
+
const hasKora = Boolean(deps.korajs || deps.kora);
|
|
4651
|
+
const hasStore = Boolean(deps["@korajs/store"]);
|
|
4652
|
+
if (hasKora || hasStore) {
|
|
4653
|
+
return {
|
|
4654
|
+
id: "dependencies",
|
|
4655
|
+
title: "Dependencies",
|
|
4656
|
+
status: "pass",
|
|
4657
|
+
message: hasKora ? "korajs is listed in package.json." : "@korajs/store is listed in package.json."
|
|
4658
|
+
};
|
|
4659
|
+
}
|
|
4660
|
+
return {
|
|
4661
|
+
id: "dependencies",
|
|
4662
|
+
title: "Dependencies",
|
|
4663
|
+
status: "fail",
|
|
4664
|
+
message: "No korajs or @korajs/store dependency found.",
|
|
4665
|
+
fix: "Run pnpm add korajs @korajs/react (or your package manager equivalent)."
|
|
4666
|
+
};
|
|
4667
|
+
} catch {
|
|
4668
|
+
return {
|
|
4669
|
+
id: "dependencies",
|
|
4670
|
+
title: "Dependencies",
|
|
4671
|
+
status: "fail",
|
|
4672
|
+
message: "Could not read package.json.",
|
|
4673
|
+
fix: "Run this command from a Kora app directory with a valid package.json."
|
|
4674
|
+
};
|
|
4675
|
+
}
|
|
4676
|
+
}
|
|
4677
|
+
function opfsBrowserNote() {
|
|
4678
|
+
return {
|
|
4679
|
+
id: "opfs",
|
|
4680
|
+
title: "OPFS persistence",
|
|
4681
|
+
status: "skip",
|
|
4682
|
+
message: "OPFS availability is checked in the browser at runtime. Open the app and confirm no IndexedDB fallback warning in the console.",
|
|
4683
|
+
fix: "Use a Chromium-based browser with storage access; avoid private mode if persistence fails."
|
|
4684
|
+
};
|
|
4685
|
+
}
|
|
4686
|
+
async function resolveSyncHttpUrl(projectRoot, config) {
|
|
4687
|
+
const envUrl = await readEnvSyncUrl(projectRoot);
|
|
4688
|
+
if (envUrl) {
|
|
4689
|
+
return toSyncStatusHttpBase(envUrl);
|
|
4690
|
+
}
|
|
4691
|
+
const syncPort = typeof config?.dev?.sync === "object" && typeof config.dev.sync.port === "number" ? config.dev.sync.port : 3001;
|
|
4692
|
+
return `http://localhost:${syncPort}`;
|
|
4693
|
+
}
|
|
4694
|
+
async function readEnvSyncUrl(projectRoot) {
|
|
4695
|
+
for (const file of [".env", ".env.local"]) {
|
|
4696
|
+
const path = (0, import_node_path24.join)(projectRoot, file);
|
|
4697
|
+
try {
|
|
4698
|
+
const content = await (0, import_promises15.readFile)(path, "utf-8");
|
|
4699
|
+
for (const line of content.split("\n")) {
|
|
4700
|
+
const trimmed = line.trim();
|
|
4701
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
4702
|
+
const match = trimmed.match(/^VITE_SYNC_URL=(.+)$/);
|
|
4703
|
+
if (match?.[1]) {
|
|
4704
|
+
return match[1].trim().replace(/^["']|["']$/g, "");
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
} catch {
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
return null;
|
|
4711
|
+
}
|
|
4712
|
+
function toSyncStatusHttpBase(url) {
|
|
4713
|
+
try {
|
|
4714
|
+
const parsed = new URL(url);
|
|
4715
|
+
if (parsed.protocol === "ws:") {
|
|
4716
|
+
parsed.protocol = "http:";
|
|
4717
|
+
} else if (parsed.protocol === "wss:") {
|
|
4718
|
+
parsed.protocol = "https:";
|
|
4719
|
+
}
|
|
4720
|
+
parsed.pathname = "";
|
|
4721
|
+
parsed.search = "";
|
|
4722
|
+
parsed.hash = "";
|
|
4723
|
+
return parsed.toString().replace(/\/$/, "");
|
|
4724
|
+
} catch {
|
|
4725
|
+
return "http://localhost:3001";
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
async function checkSyncServer(httpBase, schema) {
|
|
4729
|
+
const statusUrl = `${httpBase.replace(/\/$/, "")}${STATUS_ENDPOINT}`;
|
|
4730
|
+
try {
|
|
4731
|
+
const response = await fetch(statusUrl, {
|
|
4732
|
+
signal: AbortSignal.timeout(5e3)
|
|
4733
|
+
});
|
|
4734
|
+
if (!response.ok) {
|
|
4735
|
+
return [
|
|
4736
|
+
{
|
|
4737
|
+
id: "sync-server",
|
|
4738
|
+
title: "Sync server",
|
|
4739
|
+
status: "fail",
|
|
4740
|
+
message: `GET ${statusUrl} returned ${response.status}.`,
|
|
4741
|
+
fix: "Start the sync server with `kora dev` or `pnpm run dev` in a sync template."
|
|
4742
|
+
}
|
|
4743
|
+
];
|
|
4744
|
+
}
|
|
4745
|
+
const body = await response.json();
|
|
4746
|
+
const results = [
|
|
4747
|
+
{
|
|
4748
|
+
id: "sync-server",
|
|
4749
|
+
title: "Sync server",
|
|
4750
|
+
status: "pass",
|
|
4751
|
+
message: `Reachable at ${statusUrl} (server v${body.version ?? "unknown"}).`
|
|
4752
|
+
}
|
|
4753
|
+
];
|
|
4754
|
+
if (typeof body.schemaVersion === "number" && body.schemaVersion !== schema.version) {
|
|
4755
|
+
results.push({
|
|
4756
|
+
id: "schema-version",
|
|
4757
|
+
title: "Schema version",
|
|
4758
|
+
status: "warn",
|
|
4759
|
+
message: `Client schema v${schema.version} differs from server schema v${body.schemaVersion}.`,
|
|
4760
|
+
fix: "Run migrations on the server and client, or align schema.version before syncing."
|
|
4761
|
+
});
|
|
4762
|
+
} else {
|
|
4763
|
+
results.push({
|
|
4764
|
+
id: "schema-version",
|
|
4765
|
+
title: "Schema version",
|
|
4766
|
+
status: "pass",
|
|
4767
|
+
message: `Client and server both use schema v${schema.version}.`
|
|
4768
|
+
});
|
|
4769
|
+
}
|
|
4770
|
+
return results;
|
|
4771
|
+
} catch (error) {
|
|
4772
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
4773
|
+
return [
|
|
4774
|
+
{
|
|
4775
|
+
id: "sync-server",
|
|
4776
|
+
title: "Sync server",
|
|
4777
|
+
status: "warn",
|
|
4778
|
+
message: `Could not reach ${statusUrl}: ${detail}`,
|
|
4779
|
+
fix: "Start the sync server, or pass --skip-network for local-only checks."
|
|
4780
|
+
}
|
|
4781
|
+
];
|
|
4782
|
+
}
|
|
4098
4783
|
}
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4784
|
+
function doctorHasFailures(results) {
|
|
4785
|
+
return results.some((r) => r.status === "fail");
|
|
4786
|
+
}
|
|
4787
|
+
|
|
4788
|
+
// src/commands/doctor/doctor-command.ts
|
|
4789
|
+
var doctorCommand = (0, import_citty6.defineCommand)({
|
|
4790
|
+
meta: {
|
|
4791
|
+
name: "doctor",
|
|
4792
|
+
description: "Check Kora project setup (schema, worker, sync server, versions)"
|
|
4793
|
+
},
|
|
4794
|
+
args: {
|
|
4795
|
+
url: {
|
|
4796
|
+
type: "string",
|
|
4797
|
+
description: "Sync server HTTP base URL for status probe (default: from .env or localhost:3001)"
|
|
4798
|
+
},
|
|
4799
|
+
"skip-network": {
|
|
4800
|
+
type: "boolean",
|
|
4801
|
+
description: "Skip sync server and schema version network checks",
|
|
4802
|
+
default: false
|
|
4803
|
+
}
|
|
4804
|
+
},
|
|
4805
|
+
async run({ args }) {
|
|
4806
|
+
const logger = createLogger();
|
|
4807
|
+
const projectRoot = await findProjectRoot();
|
|
4808
|
+
if (!projectRoot) {
|
|
4809
|
+
throw new InvalidProjectError(process.cwd());
|
|
4810
|
+
}
|
|
4811
|
+
const syncHttpUrl = typeof args.url === "string" ? args.url : void 0;
|
|
4812
|
+
const skipNetwork = args["skip-network"] === true;
|
|
4813
|
+
logger.banner();
|
|
4814
|
+
logger.info(`Checking project at ${projectRoot}`);
|
|
4815
|
+
logger.blank();
|
|
4816
|
+
const results = await runDoctorChecks({
|
|
4817
|
+
projectRoot,
|
|
4818
|
+
syncHttpUrl,
|
|
4819
|
+
skipNetwork
|
|
4820
|
+
});
|
|
4821
|
+
for (const check of results) {
|
|
4822
|
+
const prefix = check.status === "pass" ? "success" : check.status === "warn" ? "warn" : check.status === "fail" ? "error" : "step";
|
|
4823
|
+
logger[prefix](`${check.title}: ${check.message}`);
|
|
4824
|
+
if (check.fix && check.status !== "pass" && check.status !== "skip") {
|
|
4825
|
+
logger.step(` Fix: ${check.fix}`);
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
logger.blank();
|
|
4829
|
+
if (doctorHasFailures(results)) {
|
|
4830
|
+
logger.error("Doctor found blocking issues.");
|
|
4831
|
+
process.exitCode = 1;
|
|
4832
|
+
return;
|
|
4833
|
+
}
|
|
4834
|
+
const warnings = results.filter((r) => r.status === "warn").length;
|
|
4835
|
+
if (warnings > 0) {
|
|
4836
|
+
logger.warn(`Doctor finished with ${warnings} warning(s).`);
|
|
4837
|
+
} else {
|
|
4838
|
+
logger.success("All checks passed.");
|
|
4839
|
+
}
|
|
4104
4840
|
}
|
|
4105
|
-
process.exit(0);
|
|
4106
|
-
};
|
|
4107
|
-
process.on('SIGINT', () => {
|
|
4108
|
-
void shutdown();
|
|
4109
|
-
});
|
|
4110
|
-
process.on('SIGTERM', () => {
|
|
4111
|
-
void shutdown();
|
|
4112
4841
|
});
|
|
4113
|
-
await server.start();
|
|
4114
|
-
process.stdout.write('Managed sync server running on ws://localhost:' + String(config.port ?? 3001) + '\\n');
|
|
4115
|
-
await new Promise(() => {});
|
|
4116
|
-
`;
|
|
4117
4842
|
|
|
4118
4843
|
// src/commands/generate/generate-command.ts
|
|
4119
|
-
var
|
|
4120
|
-
var
|
|
4121
|
-
var
|
|
4844
|
+
var import_promises16 = require("fs/promises");
|
|
4845
|
+
var import_node_path25 = require("path");
|
|
4846
|
+
var import_citty7 = require("citty");
|
|
4847
|
+
|
|
4848
|
+
// src/commands/generate/hook-generator.ts
|
|
4849
|
+
function generateCollectionHooks(schema) {
|
|
4850
|
+
const files = /* @__PURE__ */ new Map();
|
|
4851
|
+
for (const collectionName of Object.keys(schema.collections)) {
|
|
4852
|
+
const pascal = toPascalCase(collectionName);
|
|
4853
|
+
const hookName = `use${pascal}`;
|
|
4854
|
+
const content = [
|
|
4855
|
+
"// Auto-generated by `kora generate hooks` \u2014 safe to edit",
|
|
4856
|
+
`// Schema v${String(schema.version)}`,
|
|
4857
|
+
"",
|
|
4858
|
+
"import { useCollection, useMutation, useQuery } from '@korajs/react'",
|
|
4859
|
+
`import type { ${pascal}InsertInput, ${pascal}Record, ${pascal}UpdateInput } from '../types'`,
|
|
4860
|
+
"",
|
|
4861
|
+
"/**",
|
|
4862
|
+
` * Collection hook for \`${collectionName}\`.`,
|
|
4863
|
+
" * Add useQuery(...) calls with your QueryBuilder factories.",
|
|
4864
|
+
" */",
|
|
4865
|
+
`export function ${hookName}() {`,
|
|
4866
|
+
` const ${collectionName} = useCollection('${collectionName}')`,
|
|
4867
|
+
"",
|
|
4868
|
+
` const insert = useMutation((data: ${pascal}InsertInput) => ${collectionName}.insert(data))`,
|
|
4869
|
+
` const update = useMutation((id: string, data: ${pascal}UpdateInput) =>`,
|
|
4870
|
+
` ${collectionName}.update(id, data),`,
|
|
4871
|
+
" )",
|
|
4872
|
+
` const remove = useMutation((id: string) => ${collectionName}.delete(id))`,
|
|
4873
|
+
"",
|
|
4874
|
+
" return {",
|
|
4875
|
+
` ${collectionName},`,
|
|
4876
|
+
" insert,",
|
|
4877
|
+
" update,",
|
|
4878
|
+
" remove,",
|
|
4879
|
+
" }",
|
|
4880
|
+
"}",
|
|
4881
|
+
""
|
|
4882
|
+
].join("\n");
|
|
4883
|
+
files.set(`${collectionName}.ts`, content);
|
|
4884
|
+
}
|
|
4885
|
+
const indexLines = [
|
|
4886
|
+
"// Auto-generated by `kora generate hooks`",
|
|
4887
|
+
"",
|
|
4888
|
+
...Object.keys(schema.collections).map((name) => {
|
|
4889
|
+
const pascal = toPascalCase(name);
|
|
4890
|
+
return `export { use${pascal} } from './${name}'`;
|
|
4891
|
+
}),
|
|
4892
|
+
""
|
|
4893
|
+
];
|
|
4894
|
+
files.set("index.ts", indexLines.join("\n"));
|
|
4895
|
+
return files;
|
|
4896
|
+
}
|
|
4897
|
+
function toPascalCase(value) {
|
|
4898
|
+
return value.split(/[-_]/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
4899
|
+
}
|
|
4122
4900
|
|
|
4123
4901
|
// src/commands/generate/type-generator.ts
|
|
4124
4902
|
function generateTypes(schema) {
|
|
@@ -4132,7 +4910,7 @@ function generateTypes(schema) {
|
|
|
4132
4910
|
return lines.join("\n");
|
|
4133
4911
|
}
|
|
4134
4912
|
for (const [name, collection] of Object.entries(schema.collections)) {
|
|
4135
|
-
const pascal =
|
|
4913
|
+
const pascal = toPascalCase2(name);
|
|
4136
4914
|
const fields = collection.fields;
|
|
4137
4915
|
lines.push(`export interface ${pascal}Record {`);
|
|
4138
4916
|
lines.push(" readonly id: string");
|
|
@@ -4209,7 +4987,7 @@ function itemKindToTypeScript(kind) {
|
|
|
4209
4987
|
return "unknown";
|
|
4210
4988
|
}
|
|
4211
4989
|
}
|
|
4212
|
-
function
|
|
4990
|
+
function toPascalCase2(name) {
|
|
4213
4991
|
return name.split(/[_-]/).map((part) => {
|
|
4214
4992
|
if (part.length === 0) return "";
|
|
4215
4993
|
const first = part[0];
|
|
@@ -4218,13 +4996,13 @@ function toPascalCase(name) {
|
|
|
4218
4996
|
}
|
|
4219
4997
|
|
|
4220
4998
|
// src/commands/generate/generate-command.ts
|
|
4221
|
-
var generateCommand = (0,
|
|
4999
|
+
var generateCommand = (0, import_citty7.defineCommand)({
|
|
4222
5000
|
meta: {
|
|
4223
5001
|
name: "generate",
|
|
4224
5002
|
description: "Generate code from your Kora schema"
|
|
4225
5003
|
},
|
|
4226
5004
|
subCommands: {
|
|
4227
|
-
types: (0,
|
|
5005
|
+
types: (0, import_citty7.defineCommand)({
|
|
4228
5006
|
meta: {
|
|
4229
5007
|
name: "types",
|
|
4230
5008
|
description: "Generate TypeScript types from your schema"
|
|
@@ -4248,7 +5026,7 @@ var generateCommand = (0, import_citty4.defineCommand)({
|
|
|
4248
5026
|
}
|
|
4249
5027
|
let schemaPath;
|
|
4250
5028
|
if (args.schema && typeof args.schema === "string") {
|
|
4251
|
-
schemaPath = (0,
|
|
5029
|
+
schemaPath = (0, import_node_path25.resolve)(args.schema);
|
|
4252
5030
|
} else {
|
|
4253
5031
|
const found = await findSchemaFile(projectRoot);
|
|
4254
5032
|
if (!found) {
|
|
@@ -4263,7 +5041,7 @@ var generateCommand = (0, import_citty4.defineCommand)({
|
|
|
4263
5041
|
}
|
|
4264
5042
|
logger.step(`Reading schema from ${schemaPath}...`);
|
|
4265
5043
|
const schemaModule = await import(schemaPath);
|
|
4266
|
-
const schema =
|
|
5044
|
+
const schema = extractSchema2(schemaModule);
|
|
4267
5045
|
if (!schema) {
|
|
4268
5046
|
logger.error("Schema file must export a SchemaDefinition as the default export.");
|
|
4269
5047
|
process.exitCode = 1;
|
|
@@ -4271,31 +5049,193 @@ var generateCommand = (0, import_citty4.defineCommand)({
|
|
|
4271
5049
|
}
|
|
4272
5050
|
const output = generateTypes(schema);
|
|
4273
5051
|
const outputFile = typeof args.output === "string" ? args.output : "kora/generated/types.ts";
|
|
4274
|
-
const outputPath = (0,
|
|
4275
|
-
await (0,
|
|
4276
|
-
await (0,
|
|
5052
|
+
const outputPath = (0, import_node_path25.resolve)(projectRoot, outputFile);
|
|
5053
|
+
await (0, import_promises16.mkdir)((0, import_node_path25.dirname)(outputPath), { recursive: true });
|
|
5054
|
+
await (0, import_promises16.writeFile)(outputPath, output, "utf-8");
|
|
4277
5055
|
logger.success(`Generated types at ${outputPath}`);
|
|
4278
5056
|
}
|
|
5057
|
+
}),
|
|
5058
|
+
hooks: (0, import_citty7.defineCommand)({
|
|
5059
|
+
meta: {
|
|
5060
|
+
name: "hooks",
|
|
5061
|
+
description: "Generate per-collection React hook stubs"
|
|
5062
|
+
},
|
|
5063
|
+
args: {
|
|
5064
|
+
schema: {
|
|
5065
|
+
type: "string",
|
|
5066
|
+
description: "Path to schema file"
|
|
5067
|
+
},
|
|
5068
|
+
output: {
|
|
5069
|
+
type: "string",
|
|
5070
|
+
description: "Output directory for hooks",
|
|
5071
|
+
default: "kora/generated/hooks"
|
|
5072
|
+
},
|
|
5073
|
+
types: {
|
|
5074
|
+
type: "string",
|
|
5075
|
+
description: "Relative import path to generated types from hook files",
|
|
5076
|
+
default: "../types"
|
|
5077
|
+
}
|
|
5078
|
+
},
|
|
5079
|
+
async run({ args }) {
|
|
5080
|
+
const logger = createLogger();
|
|
5081
|
+
const projectRoot = await findProjectRoot();
|
|
5082
|
+
if (!projectRoot) {
|
|
5083
|
+
throw new InvalidProjectError(process.cwd());
|
|
5084
|
+
}
|
|
5085
|
+
let schemaPath;
|
|
5086
|
+
if (args.schema && typeof args.schema === "string") {
|
|
5087
|
+
schemaPath = (0, import_node_path25.resolve)(projectRoot, args.schema);
|
|
5088
|
+
} else {
|
|
5089
|
+
const found = await findSchemaFile(projectRoot);
|
|
5090
|
+
if (!found) {
|
|
5091
|
+
throw new SchemaNotFoundError([
|
|
5092
|
+
"src/schema.ts",
|
|
5093
|
+
"schema.ts",
|
|
5094
|
+
"src/schema.js",
|
|
5095
|
+
"schema.js"
|
|
5096
|
+
]);
|
|
5097
|
+
}
|
|
5098
|
+
schemaPath = found;
|
|
5099
|
+
}
|
|
5100
|
+
const schema = await loadSchemaDefinition(schemaPath, projectRoot);
|
|
5101
|
+
const outputDir = typeof args.output === "string" ? (0, import_node_path25.resolve)(projectRoot, args.output) : (0, import_node_path25.resolve)(projectRoot, "kora/generated/hooks");
|
|
5102
|
+
const files = generateCollectionHooks(schema);
|
|
5103
|
+
for (const [name, content] of files) {
|
|
5104
|
+
const adjusted = typeof args.types === "string" && args.types !== "../types" ? content.replace("from '../types'", `from '${args.types}'`) : content;
|
|
5105
|
+
const filePath = (0, import_node_path25.resolve)(outputDir, name);
|
|
5106
|
+
await (0, import_promises16.mkdir)((0, import_node_path25.dirname)(filePath), { recursive: true });
|
|
5107
|
+
await (0, import_promises16.writeFile)(filePath, adjusted, "utf-8");
|
|
5108
|
+
}
|
|
5109
|
+
logger.success(`Generated ${String(files.size)} hook file(s) at ${outputDir}`);
|
|
5110
|
+
logger.step("Run `kora generate types` first if ../types is missing.");
|
|
5111
|
+
}
|
|
4279
5112
|
})
|
|
4280
5113
|
}
|
|
4281
5114
|
});
|
|
4282
|
-
function
|
|
5115
|
+
function extractSchema2(mod) {
|
|
4283
5116
|
if (typeof mod !== "object" || mod === null) return null;
|
|
4284
5117
|
const record = mod;
|
|
4285
5118
|
const candidate = record.default ?? record;
|
|
4286
|
-
if (
|
|
5119
|
+
if (isSchemaDefinition2(candidate)) return candidate;
|
|
4287
5120
|
return null;
|
|
4288
5121
|
}
|
|
4289
|
-
function
|
|
5122
|
+
function isSchemaDefinition2(value) {
|
|
4290
5123
|
if (typeof value !== "object" || value === null) return false;
|
|
4291
5124
|
const obj = value;
|
|
4292
5125
|
return typeof obj.version === "number" && typeof obj.collections === "object" && obj.collections !== null;
|
|
4293
5126
|
}
|
|
4294
5127
|
|
|
5128
|
+
// src/commands/logs/logs-command.ts
|
|
5129
|
+
var import_citty8 = require("citty");
|
|
5130
|
+
var DEFAULT_SYNC_PORT2 = 3001;
|
|
5131
|
+
var EVENTS_ENDPOINT = "/__kora/events";
|
|
5132
|
+
var logsCommand = (0, import_citty8.defineCommand)({
|
|
5133
|
+
meta: {
|
|
5134
|
+
name: "logs",
|
|
5135
|
+
description: "Stream real-time events from a Kora sync server"
|
|
5136
|
+
},
|
|
5137
|
+
args: {
|
|
5138
|
+
url: {
|
|
5139
|
+
type: "string",
|
|
5140
|
+
description: "Sync server URL (default: http://localhost:3001)",
|
|
5141
|
+
default: `http://localhost:${DEFAULT_SYNC_PORT2}`
|
|
5142
|
+
},
|
|
5143
|
+
follow: {
|
|
5144
|
+
type: "boolean",
|
|
5145
|
+
description: "Follow log output (like tail -f)",
|
|
5146
|
+
default: true,
|
|
5147
|
+
alias: "f"
|
|
5148
|
+
},
|
|
5149
|
+
level: {
|
|
5150
|
+
type: "string",
|
|
5151
|
+
description: "Filter by log level (info, warn, error)"
|
|
5152
|
+
},
|
|
5153
|
+
token: {
|
|
5154
|
+
type: "string",
|
|
5155
|
+
description: "Admin token (defaults to KORA_ADMIN_TOKEN)"
|
|
5156
|
+
}
|
|
5157
|
+
},
|
|
5158
|
+
async run({ args }) {
|
|
5159
|
+
const logger = createLogger();
|
|
5160
|
+
const url = typeof args.url === "string" ? args.url : `http://localhost:${DEFAULT_SYNC_PORT2}`;
|
|
5161
|
+
const follow = args.follow !== false;
|
|
5162
|
+
const levelFilter = typeof args.level === "string" ? args.level : null;
|
|
5163
|
+
const token = typeof args.token === "string" ? args.token : process.env.KORA_ADMIN_TOKEN ?? void 0;
|
|
5164
|
+
const eventsUrl = `${url.replace(/\/$/, "")}${EVENTS_ENDPOINT}`;
|
|
5165
|
+
logger.banner();
|
|
5166
|
+
logger.info(`Connecting to ${eventsUrl}...`);
|
|
5167
|
+
logger.blank();
|
|
5168
|
+
try {
|
|
5169
|
+
const response = await fetch(eventsUrl, {
|
|
5170
|
+
headers: token ? { Authorization: `Bearer ${token}` } : void 0
|
|
5171
|
+
});
|
|
5172
|
+
if (!response.ok) {
|
|
5173
|
+
throw new Error(
|
|
5174
|
+
`Failed to connect to ${eventsUrl}: ${response.status} ${response.statusText}`
|
|
5175
|
+
);
|
|
5176
|
+
}
|
|
5177
|
+
const reader = response.body?.getReader();
|
|
5178
|
+
if (!reader) {
|
|
5179
|
+
throw new Error("Response body is not readable");
|
|
5180
|
+
}
|
|
5181
|
+
const decoder = new TextDecoder();
|
|
5182
|
+
let buffer = "";
|
|
5183
|
+
const processLine = (line) => {
|
|
5184
|
+
if (line.startsWith("event: ")) {
|
|
5185
|
+
return;
|
|
5186
|
+
}
|
|
5187
|
+
if (line.startsWith("data: ")) {
|
|
5188
|
+
const data = line.slice(6);
|
|
5189
|
+
try {
|
|
5190
|
+
const parsed = JSON.parse(data);
|
|
5191
|
+
const level = String(parsed.level ?? "info");
|
|
5192
|
+
if (levelFilter && level !== levelFilter) return;
|
|
5193
|
+
const timestamp = parsed.timestamp ? new Date(parsed.timestamp).toISOString() : "";
|
|
5194
|
+
const event = String(parsed.event ?? "unknown");
|
|
5195
|
+
const nodeId = parsed.nodeId ? ` [${parsed.nodeId}]` : "";
|
|
5196
|
+
const session = parsed.sessionId ? ` <${parsed.sessionId}>` : "";
|
|
5197
|
+
const count = parsed.count ? ` (${parsed.count})` : "";
|
|
5198
|
+
const error = parsed.error ? ` \u2014 ${parsed.error}` : "";
|
|
5199
|
+
const prefix = level === "error" ? "\u2717" : level === "warn" ? "\u26A0" : "\u25CF";
|
|
5200
|
+
const color = level === "error" ? "\x1B[31m" : level === "warn" ? "\x1B[33m" : "\x1B[36m";
|
|
5201
|
+
console.log(
|
|
5202
|
+
`${color}${prefix}${"\x1B[0m"} ${timestamp.slice(11, 23)} ${event}${nodeId}${session}${count}${error}`
|
|
5203
|
+
);
|
|
5204
|
+
} catch {
|
|
5205
|
+
}
|
|
5206
|
+
}
|
|
5207
|
+
};
|
|
5208
|
+
const read = async () => {
|
|
5209
|
+
while (true) {
|
|
5210
|
+
const { done, value } = await reader.read();
|
|
5211
|
+
if (done) break;
|
|
5212
|
+
buffer += decoder.decode(value, { stream: true });
|
|
5213
|
+
const lines = buffer.split("\n");
|
|
5214
|
+
buffer = lines.pop() ?? "";
|
|
5215
|
+
for (const line of lines) {
|
|
5216
|
+
processLine(line.trim());
|
|
5217
|
+
}
|
|
5218
|
+
if (!follow) break;
|
|
5219
|
+
}
|
|
5220
|
+
};
|
|
5221
|
+
await read();
|
|
5222
|
+
} catch (error) {
|
|
5223
|
+
logger.error(`Failed to connect to ${eventsUrl}`);
|
|
5224
|
+
if (error instanceof Error) {
|
|
5225
|
+
logger.error(error.message);
|
|
5226
|
+
}
|
|
5227
|
+
logger.blank();
|
|
5228
|
+
logger.step("Make sure the Kora sync server is running.");
|
|
5229
|
+
logger.step("Start it with: kora dev");
|
|
5230
|
+
process.exit(1);
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
});
|
|
5234
|
+
|
|
4295
5235
|
// src/commands/migrate/migrate-command.ts
|
|
4296
|
-
var
|
|
4297
|
-
var
|
|
4298
|
-
var
|
|
5236
|
+
var import_promises17 = require("fs/promises");
|
|
5237
|
+
var import_node_path26 = require("path");
|
|
5238
|
+
var import_citty9 = require("citty");
|
|
4299
5239
|
|
|
4300
5240
|
// src/commands/migrate/migration-generator.ts
|
|
4301
5241
|
var import_core2 = require("@korajs/core");
|
|
@@ -4861,83 +5801,34 @@ async function loadPostgresModule() {
|
|
|
4861
5801
|
}
|
|
4862
5802
|
}
|
|
4863
5803
|
|
|
4864
|
-
// src/commands/migrate/
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
const object = value;
|
|
4887
|
-
return typeof object.version === "number" && typeof object.collections === "object" && object.collections !== null && typeof object.relations === "object" && object.relations !== null;
|
|
4888
|
-
}
|
|
4889
|
-
async function loadTypeScriptModule(schemaPath, projectRoot) {
|
|
4890
|
-
if (!await hasTsxInstalled(projectRoot)) {
|
|
4891
|
-
throw new Error(
|
|
4892
|
-
`Schema file is TypeScript (${schemaPath}) but local "tsx" was not found. Install tsx in the project.`
|
|
4893
|
-
);
|
|
4894
|
-
}
|
|
4895
|
-
const script = "const modulePath = process.argv[process.argv.length - 1];import('node:url').then(u => import(u.pathToFileURL(modulePath).href)).then(mod => { const v = mod.default ?? mod; process.stdout.write(JSON.stringify(v)) }).catch(e => { process.stderr.write(String(e)); process.exit(1) })";
|
|
4896
|
-
const output = await runCommand2(
|
|
4897
|
-
process.execPath,
|
|
4898
|
-
["--import", "tsx", "--eval", script, schemaPath],
|
|
4899
|
-
projectRoot
|
|
4900
|
-
);
|
|
4901
|
-
try {
|
|
4902
|
-
return JSON.parse(output);
|
|
4903
|
-
} catch {
|
|
4904
|
-
throw new Error(`Failed to parse schema module output for ${schemaPath}`);
|
|
4905
|
-
}
|
|
4906
|
-
}
|
|
4907
|
-
async function runCommand2(command, args, cwd) {
|
|
4908
|
-
return await new Promise((resolve9, reject) => {
|
|
4909
|
-
const child = (0, import_node_child_process11.spawn)(command, args, {
|
|
4910
|
-
cwd,
|
|
4911
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
4912
|
-
env: process.env
|
|
4913
|
-
});
|
|
4914
|
-
let stdout = "";
|
|
4915
|
-
let stderr = "";
|
|
4916
|
-
child.stdout?.on("data", (chunk) => {
|
|
4917
|
-
stdout += chunk.toString("utf-8");
|
|
4918
|
-
});
|
|
4919
|
-
child.stderr?.on("data", (chunk) => {
|
|
4920
|
-
stderr += chunk.toString("utf-8");
|
|
4921
|
-
});
|
|
4922
|
-
child.on("error", (error) => {
|
|
4923
|
-
reject(error);
|
|
4924
|
-
});
|
|
4925
|
-
child.on("exit", (code) => {
|
|
4926
|
-
if (code === 0) {
|
|
4927
|
-
resolve9(stdout.trim());
|
|
4928
|
-
return;
|
|
4929
|
-
}
|
|
4930
|
-
reject(
|
|
4931
|
-
new Error(`Failed to load TypeScript schema (exit ${code ?? "unknown"}): ${stderr.trim()}`)
|
|
4932
|
-
);
|
|
4933
|
-
});
|
|
4934
|
-
});
|
|
5804
|
+
// src/commands/migrate/operation-transform-generator.ts
|
|
5805
|
+
function generateOperationTransformModule(fromVersion, toVersion) {
|
|
5806
|
+
return [
|
|
5807
|
+
"import type { Operation, OperationTransform } from '@korajs/core'",
|
|
5808
|
+
"",
|
|
5809
|
+
"/**",
|
|
5810
|
+
` * Operation transforms for schema v${fromVersion} \u2192 v${toVersion}.`,
|
|
5811
|
+
" * Implement `transform` to rewrite or drop legacy operations during sync.",
|
|
5812
|
+
" * @see https://korajs.dev/docs/migrations",
|
|
5813
|
+
" */",
|
|
5814
|
+
"export const operationTransforms: OperationTransform[] = [",
|
|
5815
|
+
" {",
|
|
5816
|
+
` fromVersion: ${fromVersion},`,
|
|
5817
|
+
` toVersion: ${toVersion},`,
|
|
5818
|
+
" transform(operation: Operation): Operation | null {",
|
|
5819
|
+
" // Return null to drop unmigratable operations.",
|
|
5820
|
+
" return operation",
|
|
5821
|
+
" },",
|
|
5822
|
+
" },",
|
|
5823
|
+
"]",
|
|
5824
|
+
""
|
|
5825
|
+
].join("\n");
|
|
4935
5826
|
}
|
|
4936
5827
|
|
|
4937
5828
|
// src/commands/migrate/migrate-command.ts
|
|
4938
5829
|
var SNAPSHOT_PATH = "kora/schema.snapshot.json";
|
|
4939
5830
|
var MIGRATIONS_DIR = "kora/migrations";
|
|
4940
|
-
var migrateCommand = (0,
|
|
5831
|
+
var migrateCommand = (0, import_citty9.defineCommand)({
|
|
4941
5832
|
meta: {
|
|
4942
5833
|
name: "migrate",
|
|
4943
5834
|
description: "Detect schema changes and generate/apply migrations"
|
|
@@ -4979,12 +5870,12 @@ var migrateCommand = (0, import_citty5.defineCommand)({
|
|
|
4979
5870
|
throw new InvalidProjectError(process.cwd());
|
|
4980
5871
|
}
|
|
4981
5872
|
const config = await loadKoraConfig(projectRoot);
|
|
4982
|
-
const resolvedSchemaPath = typeof args.schema === "string" ? (0,
|
|
5873
|
+
const resolvedSchemaPath = typeof args.schema === "string" ? (0, import_node_path26.resolve)(projectRoot, args.schema) : typeof config?.schema === "string" ? (0, import_node_path26.resolve)(projectRoot, config.schema) : await findSchemaFile(projectRoot);
|
|
4983
5874
|
if (!resolvedSchemaPath) {
|
|
4984
5875
|
throw new SchemaNotFoundError(["src/schema.ts", "schema.ts", "src/schema.js", "schema.js"]);
|
|
4985
5876
|
}
|
|
4986
5877
|
const currentSchema = await loadSchemaDefinition(resolvedSchemaPath, projectRoot);
|
|
4987
|
-
const snapshotFile = (0,
|
|
5878
|
+
const snapshotFile = (0, import_node_path26.join)(projectRoot, SNAPSHOT_PATH);
|
|
4988
5879
|
const previousSchema = await readSchemaSnapshot(snapshotFile);
|
|
4989
5880
|
if (!previousSchema) {
|
|
4990
5881
|
if (args["dry-run"] === true) {
|
|
@@ -4997,7 +5888,7 @@ var migrateCommand = (0, import_citty5.defineCommand)({
|
|
|
4997
5888
|
return;
|
|
4998
5889
|
}
|
|
4999
5890
|
const diff = diffSchemas(previousSchema, currentSchema);
|
|
5000
|
-
const outputDir = typeof args["output-dir"] === "string" ? (0,
|
|
5891
|
+
const outputDir = typeof args["output-dir"] === "string" ? (0, import_node_path26.resolve)(projectRoot, args["output-dir"]) : (0, import_node_path26.resolve)(projectRoot, MIGRATIONS_DIR);
|
|
5001
5892
|
if (!diff.hasChanges) {
|
|
5002
5893
|
logger.success("No schema changes detected.");
|
|
5003
5894
|
if (args.apply === true) {
|
|
@@ -5049,7 +5940,7 @@ var migrateCommand = (0, import_citty5.defineCommand)({
|
|
|
5049
5940
|
logger.warn("Dry run enabled: no files written, no migrations applied.");
|
|
5050
5941
|
return;
|
|
5051
5942
|
}
|
|
5052
|
-
await (0,
|
|
5943
|
+
await (0, import_promises17.mkdir)(outputDir, { recursive: true });
|
|
5053
5944
|
const migrationPath = await writeMigrationFile(
|
|
5054
5945
|
outputDir,
|
|
5055
5946
|
diff.fromVersion,
|
|
@@ -5085,7 +5976,7 @@ var migrateCommand = (0, import_citty5.defineCommand)({
|
|
|
5085
5976
|
});
|
|
5086
5977
|
async function readSchemaSnapshot(path) {
|
|
5087
5978
|
try {
|
|
5088
|
-
const content = await (0,
|
|
5979
|
+
const content = await (0, import_promises17.readFile)(path, "utf-8");
|
|
5089
5980
|
return JSON.parse(content);
|
|
5090
5981
|
} catch {
|
|
5091
5982
|
return null;
|
|
@@ -5106,15 +5997,15 @@ function isInteractiveTerminal2() {
|
|
|
5106
5997
|
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
5107
5998
|
}
|
|
5108
5999
|
async function writeSchemaSnapshot(path, schema) {
|
|
5109
|
-
await (0,
|
|
5110
|
-
await (0,
|
|
6000
|
+
await (0, import_promises17.mkdir)((0, import_node_path26.dirname)(path), { recursive: true });
|
|
6001
|
+
await (0, import_promises17.writeFile)(path, `${JSON.stringify(schema, null, 2)}
|
|
5111
6002
|
`, "utf-8");
|
|
5112
6003
|
}
|
|
5113
6004
|
async function writeMigrationFile(outputDir, fromVersion, toVersion, generated) {
|
|
5114
|
-
const existing = await (0,
|
|
6005
|
+
const existing = await (0, import_promises17.readdir)(outputDir).catch(() => []);
|
|
5115
6006
|
const sequence = existing.filter((file) => /^\d{3}-/.test(file)).length + 1;
|
|
5116
6007
|
const filename = `${String(sequence).padStart(3, "0")}-v${fromVersion}-to-v${toVersion}.ts`;
|
|
5117
|
-
const path = (0,
|
|
6008
|
+
const path = (0, import_node_path26.join)(outputDir, filename);
|
|
5118
6009
|
const migrationId = filename.replace(/\.ts$/, "");
|
|
5119
6010
|
const fileContent = [
|
|
5120
6011
|
`export const up = ${JSON.stringify(generated.up, null, 2)} as const`,
|
|
@@ -5126,8 +6017,10 @@ async function writeMigrationFile(outputDir, fromVersion, toVersion, generated)
|
|
|
5126
6017
|
`export const containsBreakingChanges = ${generated.containsBreakingChanges}`,
|
|
5127
6018
|
""
|
|
5128
6019
|
].join("\n");
|
|
5129
|
-
await (0,
|
|
5130
|
-
|
|
6020
|
+
await (0, import_promises17.writeFile)(path, fileContent, "utf-8");
|
|
6021
|
+
const transformsPath = (0, import_node_path26.join)(outputDir, `${migrationId}.transforms.ts`);
|
|
6022
|
+
await (0, import_promises17.writeFile)(transformsPath, generateOperationTransformModule(fromVersion, toVersion), "utf-8");
|
|
6023
|
+
await writeMigrationManifest((0, import_node_path26.join)(outputDir, `${migrationId}.json`), {
|
|
5131
6024
|
id: migrationId,
|
|
5132
6025
|
fromVersion,
|
|
5133
6026
|
toVersion,
|
|
@@ -5139,22 +6032,22 @@ async function writeMigrationFile(outputDir, fromVersion, toVersion, generated)
|
|
|
5139
6032
|
return path;
|
|
5140
6033
|
}
|
|
5141
6034
|
async function writeMigrationManifest(path, manifest) {
|
|
5142
|
-
await (0,
|
|
6035
|
+
await (0, import_promises17.writeFile)(path, `${JSON.stringify(manifest, null, 2)}
|
|
5143
6036
|
`, "utf-8");
|
|
5144
6037
|
}
|
|
5145
6038
|
async function listMigrationManifests(outputDir) {
|
|
5146
|
-
const files = await (0,
|
|
6039
|
+
const files = await (0, import_promises17.readdir)(outputDir).catch(() => []);
|
|
5147
6040
|
const migrationFiles = files.filter((file) => /^\d{3}-.*\.ts$/.test(file)).sort((left, right) => left.localeCompare(right));
|
|
5148
6041
|
const manifests = [];
|
|
5149
6042
|
for (const file of migrationFiles) {
|
|
5150
6043
|
const id = file.replace(/\.ts$/, "");
|
|
5151
|
-
const manifestPath = (0,
|
|
6044
|
+
const manifestPath = (0, import_node_path26.join)(outputDir, `${id}.json`);
|
|
5152
6045
|
const jsonManifest = await readMigrationManifest(manifestPath);
|
|
5153
6046
|
if (jsonManifest) {
|
|
5154
6047
|
manifests.push({ ...jsonManifest, id });
|
|
5155
6048
|
continue;
|
|
5156
6049
|
}
|
|
5157
|
-
const migrationPath = (0,
|
|
6050
|
+
const migrationPath = (0, import_node_path26.join)(outputDir, file);
|
|
5158
6051
|
const sourceManifest = await readMigrationManifestFromSource(migrationPath, id);
|
|
5159
6052
|
manifests.push(sourceManifest);
|
|
5160
6053
|
}
|
|
@@ -5162,7 +6055,7 @@ async function listMigrationManifests(outputDir) {
|
|
|
5162
6055
|
}
|
|
5163
6056
|
async function readMigrationManifest(path) {
|
|
5164
6057
|
try {
|
|
5165
|
-
const content = await (0,
|
|
6058
|
+
const content = await (0, import_promises17.readFile)(path, "utf-8");
|
|
5166
6059
|
return JSON.parse(content);
|
|
5167
6060
|
} catch (error) {
|
|
5168
6061
|
const code = error.code;
|
|
@@ -5173,7 +6066,7 @@ async function readMigrationManifest(path) {
|
|
|
5173
6066
|
}
|
|
5174
6067
|
}
|
|
5175
6068
|
async function readMigrationManifestFromSource(path, id) {
|
|
5176
|
-
const content = await (0,
|
|
6069
|
+
const content = await (0, import_promises17.readFile)(path, "utf-8");
|
|
5177
6070
|
const versions = parseVersionsFromMigrationId(id);
|
|
5178
6071
|
return {
|
|
5179
6072
|
id,
|
|
@@ -5190,9 +6083,14 @@ function parseVersionsFromMigrationId(id) {
|
|
|
5190
6083
|
if (!match) {
|
|
5191
6084
|
throw new Error(`Migration id "${id}" does not include a vX-to-vY version suffix.`);
|
|
5192
6085
|
}
|
|
6086
|
+
const fromVersionText = match[1];
|
|
6087
|
+
const toVersionText = match[2];
|
|
6088
|
+
if (fromVersionText === void 0 || toVersionText === void 0) {
|
|
6089
|
+
throw new Error(`Migration id "${id}" does not include a vX-to-vY version suffix.`);
|
|
6090
|
+
}
|
|
5193
6091
|
return {
|
|
5194
|
-
fromVersion: Number.parseInt(
|
|
5195
|
-
toVersion: Number.parseInt(
|
|
6092
|
+
fromVersion: Number.parseInt(fromVersionText, 10),
|
|
6093
|
+
toVersion: Number.parseInt(toVersionText, 10)
|
|
5196
6094
|
};
|
|
5197
6095
|
}
|
|
5198
6096
|
function parseStringArrayExport(source, exportName) {
|
|
@@ -5230,18 +6128,18 @@ function parseLiteralExport(source, exportName) {
|
|
|
5230
6128
|
}
|
|
5231
6129
|
function resolveSqliteApplyPath(dbArg, projectRoot, config) {
|
|
5232
6130
|
if (typeof dbArg === "string") {
|
|
5233
|
-
return (0,
|
|
6131
|
+
return (0, import_node_path26.resolve)(projectRoot, dbArg);
|
|
5234
6132
|
}
|
|
5235
6133
|
const sync = config?.dev?.sync;
|
|
5236
6134
|
if (typeof sync === "object" && sync !== null) {
|
|
5237
6135
|
if (sync.store === "sqlite") {
|
|
5238
|
-
return (0,
|
|
6136
|
+
return (0, import_node_path26.join)(projectRoot, "kora-sync.db");
|
|
5239
6137
|
}
|
|
5240
6138
|
if (typeof sync.store === "object" && sync.store !== null && sync.store.type === "sqlite") {
|
|
5241
6139
|
if (typeof sync.store.filename === "string" && sync.store.filename.length > 0) {
|
|
5242
|
-
return (0,
|
|
6140
|
+
return (0, import_node_path26.resolve)(projectRoot, sync.store.filename);
|
|
5243
6141
|
}
|
|
5244
|
-
return (0,
|
|
6142
|
+
return (0, import_node_path26.join)(projectRoot, "kora-sync.db");
|
|
5245
6143
|
}
|
|
5246
6144
|
}
|
|
5247
6145
|
return void 0;
|
|
@@ -5258,19 +6156,156 @@ function resolvePostgresConnectionString(config) {
|
|
|
5258
6156
|
return void 0;
|
|
5259
6157
|
}
|
|
5260
6158
|
|
|
6159
|
+
// src/commands/status/status-command.ts
|
|
6160
|
+
var import_citty10 = require("citty");
|
|
6161
|
+
var DEFAULT_SYNC_PORT3 = 3001;
|
|
6162
|
+
var STATUS_ENDPOINT2 = "/__kora/status";
|
|
6163
|
+
async function fetchServerStatus(url, token) {
|
|
6164
|
+
const statusUrl = `${url.replace(/\/$/, "")}${STATUS_ENDPOINT2}`;
|
|
6165
|
+
const response = await fetch(statusUrl, {
|
|
6166
|
+
headers: token ? { Authorization: `Bearer ${token}` } : void 0
|
|
6167
|
+
});
|
|
6168
|
+
if (!response.ok) {
|
|
6169
|
+
throw new Error(
|
|
6170
|
+
`Failed to fetch status from ${statusUrl}: ${response.status} ${response.statusText}`
|
|
6171
|
+
);
|
|
6172
|
+
}
|
|
6173
|
+
return await response.json();
|
|
6174
|
+
}
|
|
6175
|
+
function formatUptime(ms) {
|
|
6176
|
+
const seconds = Math.floor(ms / 1e3);
|
|
6177
|
+
const minutes = Math.floor(seconds / 60);
|
|
6178
|
+
const hours = Math.floor(minutes / 60);
|
|
6179
|
+
const days = Math.floor(hours / 24);
|
|
6180
|
+
const parts = [];
|
|
6181
|
+
if (days > 0) parts.push(`${days}d`);
|
|
6182
|
+
if (hours % 24 > 0) parts.push(`${hours % 24}h`);
|
|
6183
|
+
if (minutes % 60 > 0) parts.push(`${minutes % 60}m`);
|
|
6184
|
+
parts.push(`${seconds % 60}s`);
|
|
6185
|
+
return parts.join(" ");
|
|
6186
|
+
}
|
|
6187
|
+
var statusCommand = (0, import_citty10.defineCommand)({
|
|
6188
|
+
meta: {
|
|
6189
|
+
name: "status",
|
|
6190
|
+
description: "Show Kora sync server status"
|
|
6191
|
+
},
|
|
6192
|
+
args: {
|
|
6193
|
+
url: {
|
|
6194
|
+
type: "string",
|
|
6195
|
+
description: "Sync server URL (default: http://localhost:3001)",
|
|
6196
|
+
default: `http://localhost:${DEFAULT_SYNC_PORT3}`
|
|
6197
|
+
},
|
|
6198
|
+
watch: {
|
|
6199
|
+
type: "boolean",
|
|
6200
|
+
description: "Live-updating status (like htop)",
|
|
6201
|
+
default: false,
|
|
6202
|
+
alias: "w"
|
|
6203
|
+
},
|
|
6204
|
+
token: {
|
|
6205
|
+
type: "string",
|
|
6206
|
+
description: "Admin token (defaults to KORA_ADMIN_TOKEN)"
|
|
6207
|
+
}
|
|
6208
|
+
},
|
|
6209
|
+
async run({ args }) {
|
|
6210
|
+
const logger = createLogger();
|
|
6211
|
+
const url = typeof args.url === "string" ? args.url : `http://localhost:${DEFAULT_SYNC_PORT3}`;
|
|
6212
|
+
const watch2 = args.watch === true;
|
|
6213
|
+
const token = typeof args.token === "string" ? args.token : process.env.KORA_ADMIN_TOKEN ?? void 0;
|
|
6214
|
+
try {
|
|
6215
|
+
if (watch2) {
|
|
6216
|
+
console.clear();
|
|
6217
|
+
logger.banner();
|
|
6218
|
+
logger.info(`Connecting to ${url}...`);
|
|
6219
|
+
const interval = setInterval(async () => {
|
|
6220
|
+
try {
|
|
6221
|
+
const status = await fetchServerStatus(url, token);
|
|
6222
|
+
printStatus(status, url, logger);
|
|
6223
|
+
} catch {
|
|
6224
|
+
}
|
|
6225
|
+
}, 2e3);
|
|
6226
|
+
try {
|
|
6227
|
+
const status = await fetchServerStatus(url, token);
|
|
6228
|
+
printStatus(status, url, logger);
|
|
6229
|
+
} catch {
|
|
6230
|
+
}
|
|
6231
|
+
await new Promise(() => {
|
|
6232
|
+
process.on("SIGINT", () => {
|
|
6233
|
+
clearInterval(interval);
|
|
6234
|
+
process.exit(0);
|
|
6235
|
+
});
|
|
6236
|
+
process.on("SIGTERM", () => {
|
|
6237
|
+
clearInterval(interval);
|
|
6238
|
+
process.exit(0);
|
|
6239
|
+
});
|
|
6240
|
+
});
|
|
6241
|
+
} else {
|
|
6242
|
+
const status = await fetchServerStatus(url, token);
|
|
6243
|
+
printStatus(status, url, logger);
|
|
6244
|
+
}
|
|
6245
|
+
} catch (error) {
|
|
6246
|
+
logger.error(`Failed to connect to ${url}`);
|
|
6247
|
+
if (error instanceof Error) {
|
|
6248
|
+
logger.error(error.message);
|
|
6249
|
+
}
|
|
6250
|
+
logger.blank();
|
|
6251
|
+
logger.step("Make sure the Kora sync server is running.");
|
|
6252
|
+
logger.step("Start it with: kora dev");
|
|
6253
|
+
process.exit(1);
|
|
6254
|
+
}
|
|
6255
|
+
}
|
|
6256
|
+
});
|
|
6257
|
+
function printStatus(status, url, logger) {
|
|
6258
|
+
console.clear();
|
|
6259
|
+
logger.banner();
|
|
6260
|
+
logger.info(`Kora Sync Server \u2014 ${url}`);
|
|
6261
|
+
logger.blank();
|
|
6262
|
+
if (!status.running) {
|
|
6263
|
+
logger.error("Server is not running");
|
|
6264
|
+
return;
|
|
6265
|
+
}
|
|
6266
|
+
logger.step(`Status: Running (uptime: ${formatUptime(status.uptime)}) \u2014 v${status.version}`);
|
|
6267
|
+
logger.step(`Schema: v${status.schemaVersion}`);
|
|
6268
|
+
logger.blank();
|
|
6269
|
+
logger.step(`Connections: ${status.connectedClients} connected`);
|
|
6270
|
+
logger.step(` Peak: ${status.peakConnections}`);
|
|
6271
|
+
logger.step(` Total: ${status.connectionsTotal}`);
|
|
6272
|
+
if (status.connectedNodeIds.length > 0) {
|
|
6273
|
+
logger.step(" Active nodes:");
|
|
6274
|
+
for (const nodeId of status.connectedNodeIds) {
|
|
6275
|
+
logger.step(` \u2022 ${nodeId}`);
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
logger.blank();
|
|
6279
|
+
logger.step("Operations:");
|
|
6280
|
+
logger.step(` Received: ${status.operationsReceived.toLocaleString()}`);
|
|
6281
|
+
logger.step(` Sent: ${status.operationsSent.toLocaleString()}`);
|
|
6282
|
+
logger.step(` Total: ${status.totalOperations.toLocaleString()}`);
|
|
6283
|
+
logger.blank();
|
|
6284
|
+
if (status.errorCount > 0) {
|
|
6285
|
+
logger.warn(`Errors: ${status.errorCount}`);
|
|
6286
|
+
} else {
|
|
6287
|
+
logger.step("Errors: 0");
|
|
6288
|
+
}
|
|
6289
|
+
}
|
|
6290
|
+
|
|
5261
6291
|
// src/bin.ts
|
|
5262
|
-
var main = (0,
|
|
6292
|
+
var main = (0, import_citty11.defineCommand)({
|
|
5263
6293
|
meta: {
|
|
5264
6294
|
name: "kora",
|
|
5265
6295
|
description: "Kora.js \u2014 Offline-first application framework"
|
|
5266
6296
|
},
|
|
5267
6297
|
subCommands: {
|
|
6298
|
+
backup: backupCommand,
|
|
6299
|
+
compact: compactCommand,
|
|
5268
6300
|
create: createCommand,
|
|
5269
6301
|
dev: devCommand,
|
|
6302
|
+
doctor: doctorCommand,
|
|
5270
6303
|
deploy: deployCommand,
|
|
5271
6304
|
generate: generateCommand,
|
|
5272
|
-
|
|
6305
|
+
logs: logsCommand,
|
|
6306
|
+
migrate: migrateCommand,
|
|
6307
|
+
status: statusCommand
|
|
5273
6308
|
}
|
|
5274
6309
|
});
|
|
5275
|
-
(0,
|
|
6310
|
+
(0, import_citty11.runMain)(main);
|
|
5276
6311
|
//# sourceMappingURL=bin.cjs.map
|