@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.
Files changed (253) hide show
  1. package/dist/bin.cjs +1636 -601
  2. package/dist/bin.cjs.map +1 -1
  3. package/dist/bin.js +1086 -139
  4. package/dist/bin.js.map +1 -1
  5. package/dist/{chunk-MVP5PDT4.js → chunk-EEZNRI5W.js} +51 -6
  6. package/dist/chunk-EEZNRI5W.js.map +1 -0
  7. package/dist/{chunk-YGVO4POI.js → chunk-Q2FBCOQD.js} +27 -15
  8. package/dist/chunk-Q2FBCOQD.js.map +1 -0
  9. package/dist/{chunk-CMSX76KM.js → chunk-VLTPEATY.js} +172 -99
  10. package/dist/chunk-VLTPEATY.js.map +1 -0
  11. package/dist/create.cjs +130 -54
  12. package/dist/create.cjs.map +1 -1
  13. package/dist/create.js +2 -2
  14. package/dist/index.cjs +143 -62
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +8 -6
  17. package/dist/index.d.ts +8 -6
  18. package/dist/index.js +2 -2
  19. package/package.json +9 -3
  20. package/templates/react-basic/README.md.hbs +10 -1
  21. package/templates/react-basic/src/App.tsx +112 -126
  22. package/templates/react-basic/src/index.css +165 -159
  23. package/templates/react-basic/src/kora-worker.ts +0 -1
  24. package/templates/react-basic/src/main.tsx +12 -12
  25. package/templates/react-basic/src/modules/todos/todo.mutations.ts +25 -0
  26. package/templates/react-basic/src/modules/todos/todo.queries.ts +5 -0
  27. package/templates/react-basic/src/modules/todos/todo.schema.ts +10 -0
  28. package/templates/react-basic/src/modules/todos/useTodos.ts +31 -0
  29. package/templates/react-basic/src/schema.ts +6 -12
  30. package/templates/react-basic/src/vite-env.d.ts +17 -0
  31. package/templates/react-basic/{tsconfig.json → tsconfig.json.hbs} +1 -2
  32. package/templates/react-basic/vite.config.ts +57 -50
  33. package/templates/react-sync/.env.example +19 -0
  34. package/templates/react-sync/README.md.hbs +43 -1
  35. package/templates/react-sync/package.json.hbs +3 -0
  36. package/templates/react-sync/server.ts +90 -26
  37. package/templates/react-sync/src/App.tsx +132 -130
  38. package/templates/react-sync/src/auth.ts +25 -0
  39. package/templates/react-sync/src/index.css +212 -165
  40. package/templates/react-sync/src/kora-worker.ts +0 -1
  41. package/templates/react-sync/src/main.tsx +34 -18
  42. package/templates/react-sync/src/modules/todos/todo.mutations.ts +25 -0
  43. package/templates/react-sync/src/modules/todos/todo.queries.ts +5 -0
  44. package/templates/react-sync/src/modules/todos/todo.schema.ts +10 -0
  45. package/templates/react-sync/src/modules/todos/useTodos.ts +31 -0
  46. package/templates/react-sync/src/schema.ts +6 -12
  47. package/templates/react-sync/src/vite-env.d.ts +17 -0
  48. package/templates/react-sync/{tsconfig.json → tsconfig.json.hbs} +1 -2
  49. package/templates/react-sync/vite.config.ts +67 -60
  50. package/templates/react-tailwind/README.md.hbs +10 -1
  51. package/templates/react-tailwind/src/App.tsx +186 -197
  52. package/templates/react-tailwind/src/index.css +3 -3
  53. package/templates/react-tailwind/src/kora-worker.ts +0 -1
  54. package/templates/react-tailwind/src/main.tsx +19 -12
  55. package/templates/react-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  56. package/templates/react-tailwind/src/modules/todos/todo.queries.ts +5 -0
  57. package/templates/react-tailwind/src/modules/todos/todo.schema.ts +10 -0
  58. package/templates/react-tailwind/src/modules/todos/useTodos.ts +31 -0
  59. package/templates/react-tailwind/src/schema.ts +6 -12
  60. package/templates/react-tailwind/src/vite-env.d.ts +17 -0
  61. package/templates/react-tailwind/{tsconfig.json → tsconfig.json.hbs} +1 -2
  62. package/templates/react-tailwind/vite.config.ts +57 -50
  63. package/templates/react-tailwind-sync/.env.example +19 -0
  64. package/templates/react-tailwind-sync/README.md.hbs +43 -1
  65. package/templates/react-tailwind-sync/package.json.hbs +3 -0
  66. package/templates/react-tailwind-sync/server.ts +90 -26
  67. package/templates/react-tailwind-sync/src/App.tsx +246 -222
  68. package/templates/react-tailwind-sync/src/auth.ts +25 -0
  69. package/templates/react-tailwind-sync/src/index.css +3 -3
  70. package/templates/react-tailwind-sync/src/kora-worker.ts +0 -1
  71. package/templates/react-tailwind-sync/src/main.tsx +48 -18
  72. package/templates/react-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  73. package/templates/react-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  74. package/templates/react-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  75. package/templates/react-tailwind-sync/src/modules/todos/useTodos.ts +31 -0
  76. package/templates/react-tailwind-sync/src/schema.ts +6 -12
  77. package/templates/react-tailwind-sync/src/vite-env.d.ts +17 -0
  78. package/templates/react-tailwind-sync/{tsconfig.json → tsconfig.json.hbs} +1 -2
  79. package/templates/react-tailwind-sync/vite.config.ts +67 -60
  80. package/templates/svelte-basic/.env.example +3 -0
  81. package/templates/svelte-basic/README.md.hbs +62 -0
  82. package/templates/svelte-basic/index.html.hbs +12 -0
  83. package/templates/svelte-basic/kora.config.ts +12 -0
  84. package/templates/svelte-basic/package.json.hbs +27 -0
  85. package/templates/svelte-basic/src/App.svelte +131 -0
  86. package/templates/svelte-basic/src/Root.svelte +14 -0
  87. package/templates/svelte-basic/src/index.css +287 -0
  88. package/templates/svelte-basic/src/kora-worker.ts +9 -0
  89. package/templates/svelte-basic/src/main.ts +16 -0
  90. package/templates/svelte-basic/src/modules/todos/todo.mutations.ts +25 -0
  91. package/templates/svelte-basic/src/modules/todos/todo.queries.ts +5 -0
  92. package/templates/svelte-basic/src/modules/todos/todo.schema.ts +10 -0
  93. package/templates/svelte-basic/src/modules/todos/useTodos.ts +25 -0
  94. package/templates/svelte-basic/src/schema.ts +9 -0
  95. package/templates/svelte-basic/src/vite-env.d.ts +12 -0
  96. package/templates/svelte-basic/svelte.config.js +5 -0
  97. package/templates/svelte-basic/tsconfig.json.hbs +14 -0
  98. package/templates/svelte-basic/vite.config.ts +52 -0
  99. package/templates/svelte-sync/.env.example +25 -0
  100. package/templates/svelte-sync/README.md.hbs +105 -0
  101. package/templates/svelte-sync/index.html.hbs +12 -0
  102. package/templates/svelte-sync/kora.config.ts +17 -0
  103. package/templates/svelte-sync/package.json.hbs +43 -0
  104. package/templates/svelte-sync/server.ts +90 -0
  105. package/templates/svelte-sync/src/App.svelte +154 -0
  106. package/templates/svelte-sync/src/Root.svelte +22 -0
  107. package/templates/svelte-sync/src/auth.ts +25 -0
  108. package/templates/svelte-sync/src/index.css +339 -0
  109. package/templates/svelte-sync/src/kora-worker.ts +9 -0
  110. package/templates/svelte-sync/src/main.ts +32 -0
  111. package/templates/svelte-sync/src/modules/todos/todo.mutations.ts +25 -0
  112. package/templates/svelte-sync/src/modules/todos/todo.queries.ts +5 -0
  113. package/templates/svelte-sync/src/modules/todos/todo.schema.ts +10 -0
  114. package/templates/svelte-sync/src/modules/todos/useTodos.ts +25 -0
  115. package/templates/svelte-sync/src/schema.ts +9 -0
  116. package/templates/svelte-sync/src/vite-env.d.ts +21 -0
  117. package/templates/svelte-sync/svelte.config.js +5 -0
  118. package/templates/svelte-sync/tsconfig.json.hbs +14 -0
  119. package/templates/svelte-sync/vite.config.ts +79 -0
  120. package/templates/svelte-tailwind/README.md.hbs +62 -0
  121. package/templates/svelte-tailwind/index.html.hbs +12 -0
  122. package/templates/svelte-tailwind/kora.config.ts +12 -0
  123. package/templates/svelte-tailwind/package.json.hbs +30 -0
  124. package/templates/svelte-tailwind/src/App.svelte +79 -0
  125. package/templates/svelte-tailwind/src/Root.svelte +14 -0
  126. package/templates/svelte-tailwind/src/index.css +7 -0
  127. package/templates/svelte-tailwind/src/kora-worker.ts +9 -0
  128. package/templates/svelte-tailwind/src/main.ts +16 -0
  129. package/templates/svelte-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  130. package/templates/svelte-tailwind/src/modules/todos/todo.queries.ts +5 -0
  131. package/templates/svelte-tailwind/src/modules/todos/todo.schema.ts +10 -0
  132. package/templates/svelte-tailwind/src/modules/todos/useTodos.ts +25 -0
  133. package/templates/svelte-tailwind/src/schema.ts +9 -0
  134. package/templates/svelte-tailwind/src/vite-env.d.ts +12 -0
  135. package/templates/svelte-tailwind/svelte.config.js +5 -0
  136. package/templates/svelte-tailwind/tsconfig.json.hbs +14 -0
  137. package/templates/svelte-tailwind/vite.config.ts +53 -0
  138. package/templates/svelte-tailwind-sync/.env.example +25 -0
  139. package/templates/svelte-tailwind-sync/README.md.hbs +105 -0
  140. package/templates/svelte-tailwind-sync/index.html.hbs +12 -0
  141. package/templates/svelte-tailwind-sync/kora.config.ts +17 -0
  142. package/templates/svelte-tailwind-sync/package.json.hbs +46 -0
  143. package/templates/svelte-tailwind-sync/server.ts +90 -0
  144. package/templates/svelte-tailwind-sync/src/App.svelte +196 -0
  145. package/templates/svelte-tailwind-sync/src/Root.svelte +26 -0
  146. package/templates/svelte-tailwind-sync/src/auth.ts +25 -0
  147. package/templates/svelte-tailwind-sync/src/index.css +7 -0
  148. package/templates/svelte-tailwind-sync/src/kora-worker.ts +9 -0
  149. package/templates/svelte-tailwind-sync/src/main.ts +32 -0
  150. package/templates/svelte-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  151. package/templates/svelte-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  152. package/templates/svelte-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  153. package/templates/svelte-tailwind-sync/src/modules/todos/useTodos.ts +25 -0
  154. package/templates/svelte-tailwind-sync/src/schema.ts +9 -0
  155. package/templates/svelte-tailwind-sync/src/vite-env.d.ts +21 -0
  156. package/templates/svelte-tailwind-sync/svelte.config.js +5 -0
  157. package/templates/svelte-tailwind-sync/tsconfig.json.hbs +14 -0
  158. package/templates/svelte-tailwind-sync/vite.config.ts +80 -0
  159. package/templates/tauri-react/.env.example +20 -0
  160. package/templates/tauri-react/README.md.hbs +36 -1
  161. package/templates/tauri-react/dev.ts +11 -11
  162. package/templates/tauri-react/package.json.hbs +2 -0
  163. package/templates/tauri-react/server.ts +86 -12
  164. package/templates/tauri-react/src/App.tsx +769 -544
  165. package/templates/tauri-react/src/AppShell.tsx +125 -83
  166. package/templates/tauri-react/src/SetupScreen.tsx +257 -208
  167. package/templates/tauri-react/src/auth.ts +45 -0
  168. package/templates/tauri-react/src/main.tsx +3 -3
  169. package/templates/tauri-react/src/modules/todos/todo.mutations.ts +25 -0
  170. package/templates/tauri-react/src/modules/todos/todo.queries.ts +5 -0
  171. package/templates/tauri-react/src/modules/todos/todo.schema.ts +10 -0
  172. package/templates/tauri-react/src/modules/todos/useTodos.ts +31 -0
  173. package/templates/tauri-react/src/schema.ts +6 -12
  174. package/templates/tauri-react/src/sync-config.ts +47 -47
  175. package/templates/tauri-react/src/updater.ts +15 -15
  176. package/templates/tauri-react/src/vite-env.d.ts +17 -0
  177. package/templates/tauri-react/src-tauri/tauri.conf.json +1 -1
  178. package/templates/tauri-react/tsconfig.json.hbs +14 -0
  179. package/templates/tauri-react/vite.config.ts +9 -9
  180. package/templates/vue-basic/README.md.hbs +62 -0
  181. package/templates/vue-basic/index.html.hbs +12 -0
  182. package/templates/vue-basic/kora.config.ts +12 -0
  183. package/templates/vue-basic/package.json.hbs +26 -0
  184. package/templates/vue-basic/src/App.vue +117 -0
  185. package/templates/vue-basic/src/index.css +287 -0
  186. package/templates/vue-basic/src/kora-worker.ts +9 -0
  187. package/templates/vue-basic/src/main.ts +20 -0
  188. package/templates/vue-basic/src/modules/todos/todo.mutations.ts +25 -0
  189. package/templates/vue-basic/src/modules/todos/todo.queries.ts +5 -0
  190. package/templates/vue-basic/src/modules/todos/todo.schema.ts +10 -0
  191. package/templates/vue-basic/src/modules/todos/useTodos.ts +32 -0
  192. package/templates/vue-basic/src/schema.ts +9 -0
  193. package/templates/vue-basic/src/vite-env.d.ts +17 -0
  194. package/templates/vue-basic/tsconfig.json.hbs +15 -0
  195. package/templates/vue-basic/vite.config.ts +69 -0
  196. package/templates/vue-sync/.env.example +25 -0
  197. package/templates/vue-sync/README.md.hbs +105 -0
  198. package/templates/vue-sync/index.html.hbs +12 -0
  199. package/templates/vue-sync/kora.config.ts +17 -0
  200. package/templates/vue-sync/package.json.hbs +42 -0
  201. package/templates/vue-sync/server.ts +90 -0
  202. package/templates/vue-sync/src/App.vue +135 -0
  203. package/templates/vue-sync/src/auth.ts +25 -0
  204. package/templates/vue-sync/src/index.css +339 -0
  205. package/templates/vue-sync/src/kora-worker.ts +9 -0
  206. package/templates/vue-sync/src/main.ts +43 -0
  207. package/templates/vue-sync/src/modules/todos/todo.mutations.ts +25 -0
  208. package/templates/vue-sync/src/modules/todos/todo.queries.ts +5 -0
  209. package/templates/vue-sync/src/modules/todos/todo.schema.ts +10 -0
  210. package/templates/vue-sync/src/modules/todos/useTodos.ts +32 -0
  211. package/templates/vue-sync/src/schema.ts +9 -0
  212. package/templates/vue-sync/src/vite-env.d.ts +26 -0
  213. package/templates/vue-sync/tsconfig.json.hbs +15 -0
  214. package/templates/vue-sync/vite.config.ts +79 -0
  215. package/templates/vue-tailwind/README.md.hbs +62 -0
  216. package/templates/vue-tailwind/index.html.hbs +12 -0
  217. package/templates/vue-tailwind/kora.config.ts +12 -0
  218. package/templates/vue-tailwind/package.json.hbs +29 -0
  219. package/templates/vue-tailwind/src/App.vue +142 -0
  220. package/templates/vue-tailwind/src/index.css +7 -0
  221. package/templates/vue-tailwind/src/kora-worker.ts +9 -0
  222. package/templates/vue-tailwind/src/main.ts +31 -0
  223. package/templates/vue-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  224. package/templates/vue-tailwind/src/modules/todos/todo.queries.ts +5 -0
  225. package/templates/vue-tailwind/src/modules/todos/todo.schema.ts +10 -0
  226. package/templates/vue-tailwind/src/modules/todos/useTodos.ts +32 -0
  227. package/templates/vue-tailwind/src/schema.ts +9 -0
  228. package/templates/vue-tailwind/src/vite-env.d.ts +17 -0
  229. package/templates/vue-tailwind/tsconfig.json.hbs +15 -0
  230. package/templates/vue-tailwind/vite.config.ts +70 -0
  231. package/templates/vue-tailwind-sync/.env.example +25 -0
  232. package/templates/vue-tailwind-sync/README.md.hbs +105 -0
  233. package/templates/vue-tailwind-sync/index.html.hbs +12 -0
  234. package/templates/vue-tailwind-sync/kora.config.ts +17 -0
  235. package/templates/vue-tailwind-sync/package.json.hbs +45 -0
  236. package/templates/vue-tailwind-sync/server.ts +90 -0
  237. package/templates/vue-tailwind-sync/src/App.vue +245 -0
  238. package/templates/vue-tailwind-sync/src/auth.ts +25 -0
  239. package/templates/vue-tailwind-sync/src/index.css +7 -0
  240. package/templates/vue-tailwind-sync/src/kora-worker.ts +9 -0
  241. package/templates/vue-tailwind-sync/src/main.ts +56 -0
  242. package/templates/vue-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  243. package/templates/vue-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  244. package/templates/vue-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  245. package/templates/vue-tailwind-sync/src/modules/todos/useTodos.ts +32 -0
  246. package/templates/vue-tailwind-sync/src/schema.ts +9 -0
  247. package/templates/vue-tailwind-sync/src/vite-env.d.ts +26 -0
  248. package/templates/vue-tailwind-sync/tsconfig.json.hbs +15 -0
  249. package/templates/vue-tailwind-sync/vite.config.ts +80 -0
  250. package/dist/chunk-CMSX76KM.js.map +0 -1
  251. package/dist/chunk-MVP5PDT4.js.map +0 -1
  252. package/dist/chunk-YGVO4POI.js.map +0 -1
  253. 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 import_citty6 = require("citty");
26
+ var import_citty11 = require("citty");
27
27
 
28
- // src/commands/create/create-command.ts
29
- var import_node_child_process2 = require("child_process");
30
- var import_node_fs2 = require("fs");
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((resolve9) => {
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
- resolve9(trimmed || defaultValue || "");
670
+ resolve11(trimmed || defaultValue || "");
138
671
  });
139
672
  });
140
673
  }
141
674
  function promptSelect(message, choices, options) {
142
- return new Promise((resolve9) => {
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
- resolve9(selected.value);
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((resolve9) => {
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
- resolve9(defaultValue);
713
+ resolve11(defaultValue);
181
714
  return;
182
715
  }
183
716
  if (normalized === "y" || normalized === "yes") {
184
717
  rl.close();
185
- resolve9(true);
718
+ resolve11(true);
186
719
  return;
187
720
  }
188
721
  if (normalized === "n" || normalized === "no") {
189
722
  rl.close();
190
- resolve9(false);
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 import_node_child_process = require("child_process");
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 import_promises2 = require("fs/promises");
447
- var import_node_path2 = require("path");
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, import_node_path2.resolve)(startDir);
890
+ let current = (0, import_node_path6.resolve)(startDir);
474
891
  for (; ; ) {
475
- if (await fileExists((0, import_node_path2.join)(current, "pnpm-workspace.yaml"))) {
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, import_node_path2.join)(current, "turbo.json"))) {
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, import_node_path2.join)(current, "package.json");
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, import_promises2.readFile)(packageJsonPath, "utf-8");
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, import_node_path2.dirname)(current);
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, import_node_path2.join)(targetDir, ".vscode");
505
- const extensionsPath = (0, import_node_path2.join)(vscodeDir, "extensions.json");
506
- await (0, import_promises2.mkdir)(vscodeDir, { recursive: true });
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, import_promises2.writeFile)(extensionsPath, `${JSON.stringify(next, null, 2)}
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, import_node_path2.join)(monorepoRoot, "packages", projectName);
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, import_promises2.readFile)(path, "utf-8");
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, import_promises2.access)(path);
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, import_node_path2.resolve)(startDir);
967
+ let current = (0, import_node_path6.resolve)(startDir);
551
968
  for (; ; ) {
552
- const candidate = (0, import_node_path2.join)(current, entryName);
969
+ const candidate = (0, import_node_path6.join)(current, entryName);
553
970
  try {
554
- await (0, import_promises2.stat)(candidate);
971
+ await (0, import_promises4.stat)(candidate);
555
972
  return current;
556
973
  } catch {
557
974
  }
558
- const parent = (0, import_node_path2.dirname)(current);
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 (coming soon)", value: "vue", disabled: true },
645
- { label: "Svelte (coming soon)", value: "svelte", disabled: true },
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 import_promises3 = require("fs/promises");
782
- var import_node_path3 = require("path");
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 serverPath = (0, import_node_path3.join)(options.targetDir, "server.ts");
793
- const envPath = (0, import_node_path3.join)(options.targetDir, ".env.example");
794
- const readmePath = (0, import_node_path3.join)(options.targetDir, "README.md");
795
- const serverTemplate = [
796
- "import { createPostgresServerStore, createProductionServer } from '@korajs/server'",
797
- "",
798
- `// PostgreSQL provider preset: ${providerName}`,
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 connection string (${providerName})`,
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 uses `createPostgresServerStore` in `server.ts` and reads `DATABASE_URL` from the environment. See `.env.example` for provider-specific examples.",
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, import_promises3.writeFile)(serverPath, serverTemplate, "utf-8");
848
- await (0, import_promises3.writeFile)(envPath, envTemplate, "utf-8");
849
- await (0, import_promises3.writeFile)(readmePath, readmeTemplate, "utf-8");
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 import_promises4 = require("fs/promises");
894
- var import_node_path4 = require("path");
895
- var import_node_url = require("url");
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, import_node_path4.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
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, import_node_path4.resolve)(dir, "templates", templateName);
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, import_node_path4.dirname)(dir);
1351
+ dir = (0, import_node_path8.dirname)(dir);
911
1352
  }
912
- const currentDir = (0, import_node_path4.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
913
- return (0, import_node_path4.resolve)(currentDir, "..", "..", "..", "templates", templateName);
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, import_promises4.mkdir)(dest, { recursive: true });
999
- const entries = await (0, import_promises4.readdir)(src);
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, import_node_path4.join)(src, entry);
1002
- const srcStat = await (0, import_promises4.stat)(srcPath);
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, import_node_path4.join)(dest, entry), vars);
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, import_promises4.readFile)(srcPath, "utf-8");
1491
+ const content = await (0, import_promises6.readFile)(srcPath, "utf-8");
1009
1492
  const outputName = entry.slice(0, -4);
1010
- await (0, import_promises4.writeFile)((0, import_node_path4.join)(dest, outputName), substituteVariables(content, vars), "utf-8");
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, import_promises4.copyFile)(srcPath, (0, import_node_path4.join)(dest, entry));
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, import_citty.defineCommand)({
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-tailwind, react-sync, react-basic, tauri-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 !== "react") {
1128
- logger.error(`Framework "${selection.framework}" is not available yet. Use "react".`);
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, import_node_path5.resolve)(process.cwd(), projectName);
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, import_node_child_process2.execSync)(getInstallCommand(pm), { cwd: targetDir, stdio: "inherit" });
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, import_node_path5.dirname)((0, import_node_url2.fileURLToPath)(import_meta2.url));
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, import_node_path5.resolve)(dir, "package.json");
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, import_node_path5.dirname)(dir);
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 import_node_path16 = require("path");
1355
- var import_citty2 = require("citty");
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 import_node_child_process4 = require("child_process");
1373
- var import_node_path8 = require("path");
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 import_node_child_process3 = require("child_process");
1862
+ var import_node_child_process5 = require("child_process");
1377
1863
  var import_node_fs3 = require("fs");
1378
- var import_promises5 = require("fs/promises");
1379
- var import_node_path6 = require("path");
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, import_node_path6.join)(outDir, "assets");
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, import_promises5.readdir)(assetsDir);
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, import_promises5.copyFile)((0, import_node_path6.join)(assetsDir, hashedWasm), (0, import_node_path6.join)(assetsDir, "sqlite3.wasm"));
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, import_node_path6.resolve)(
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, import_promises5.copyFile)(proxyFile, (0, import_node_path6.join)(assetsDir, "sqlite3-opfs-async-proxy.js"));
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((resolve9, reject) => {
1424
- const child = (0, import_node_child_process3.spawn)(command, args, {
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
- resolve9();
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 import_promises6 = require("fs/promises");
1444
- var import_promises7 = require("fs/promises");
1445
- var import_node_path7 = require("path");
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, import_promises6.mkdir)(options.deployDirectory, { recursive: true });
1462
- const outputFilePath = (0, import_node_path7.join)(options.deployDirectory, "server-bundled.js");
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, import_node_path7.join)(projectRoot, candidate);
1970
+ const fullPath = (0, import_node_path11.join)(projectRoot, candidate);
1485
1971
  try {
1486
- await (0, import_promises7.access)(fullPath);
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, import_node_path8.join)(config.projectRoot, ".kora", "deploy");
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, import_node_path8.join)(deployDirectory, "dist"),
2081
+ outDir: (0, import_node_path12.join)(deployDirectory, "dist"),
1596
2082
  mode: "production"
1597
2083
  });
1598
2084
  return {
1599
- clientDirectory: client.outDir,
1600
- serverBundlePath: (0, import_node_path8.join)(deployDirectory, "server-bundled.js"),
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((resolve9) => {
1833
- const child = (0, import_node_child_process4.spawn)(command, args, {
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
- resolve9({ exitCode: 1, stdout, stderr: `${stderr}
2333
+ resolve11({ exitCode: 1, stdout, stderr: `${stderr}
1848
2334
  ${error.message}` });
1849
2335
  });
1850
2336
  child.on("exit", (code) => {
1851
- resolve9({ exitCode: code ?? 1, stdout: stdout.trim(), stderr: stderr.trim() });
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 import_node_child_process5 = require("child_process");
1866
- var import_node_path9 = require("path");
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, import_node_path9.join)(config.projectRoot, ".kora", "deploy");
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, import_node_path9.join)(deployDirectory, "dist"),
2432
+ outDir: (0, import_node_path13.join)(deployDirectory, "dist"),
1947
2433
  mode: "production"
1948
2434
  });
1949
2435
  return {
1950
- clientDirectory: client.outDir,
1951
- serverBundlePath: (0, import_node_path9.join)(deployDirectory, "server-bundled.js"),
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((resolve9) => {
2180
- const child = (0, import_node_child_process5.spawn)(command, args, {
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
- resolve9({ exitCode: 1, stdout, stderr: `${stderr}
2680
+ resolve11({ exitCode: 1, stdout, stderr: `${stderr}
2195
2681
  ${error.message}` });
2196
2682
  });
2197
2683
  child.on("exit", (code) => {
2198
- resolve9({ exitCode: code ?? 1, stdout: stdout.trim(), stderr: stderr.trim() });
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 import_node_child_process6 = require("child_process");
2251
- var import_node_path11 = require("path");
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 import_promises8 = require("fs/promises");
2255
- var import_node_path10 = require("path");
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, import_promises8.mkdir)(deployDirectory, { recursive: true });
2286
- const filePath = (0, import_node_path10.join)(deployDirectory, "fly.toml");
2287
- await (0, import_promises8.writeFile)(filePath, generateFlyToml(options), "utf-8");
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, import_node_path11.join)(config.projectRoot, ".kora", "deploy");
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, import_node_path11.join)(deployDirectory, "dist"),
2878
+ outDir: (0, import_node_path15.join)(deployDirectory, "dist"),
2393
2879
  mode: "production"
2394
2880
  });
2395
2881
  return {
2396
- clientDirectory: client.outDir,
2397
- serverBundlePath: (0, import_node_path11.join)(deployDirectory, "server-bundled.js"),
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((resolve9) => {
2524
- const child = (0, import_node_child_process6.spawn)(command, args, {
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
- resolve9({
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
- resolve9({
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 import_node_child_process7 = require("child_process");
2619
- var import_node_path13 = require("path");
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 import_promises9 = require("fs/promises");
2623
- var import_node_path12 = require("path");
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, import_promises9.mkdir)(deployDirectory, { recursive: true });
2649
- const filePath = (0, import_node_path12.join)(deployDirectory, "railway.json");
2650
- await (0, import_promises9.writeFile)(filePath, generateRailwayJson(options), "utf-8");
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, import_node_path13.join)(config.projectRoot, ".kora", "deploy");
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, import_node_path13.join)(deployDirectory, "dist"),
3236
+ outDir: (0, import_node_path17.join)(deployDirectory, "dist"),
2751
3237
  mode: "production"
2752
3238
  });
2753
3239
  return {
2754
- clientDirectory: client.outDir,
2755
- serverBundlePath: (0, import_node_path13.join)(deployDirectory, "server-bundled.js"),
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((resolve9) => {
2860
- const child = (0, import_node_child_process7.spawn)(command, args, {
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
- resolve9({
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
- resolve9({
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 import_promises10 = require("fs/promises");
3041
- var import_node_path14 = require("path");
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 ?? "dist";
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
- lines.push(`COPY ${clientDirectory} ./dist`);
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, import_promises10.mkdir)(deployDirectory, { recursive: true });
3106
- const dockerfilePath = (0, import_node_path14.join)(deployDirectory, "Dockerfile");
3107
- await (0, import_promises10.writeFile)(dockerfilePath, generateDockerfile(options), "utf-8");
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, import_node_path14.join)(deployDirectory, "package.json");
3110
- await (0, import_promises10.writeFile)(pkgJsonPath, generateDeployPackageJson(options.nativeDependencies), "utf-8");
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, import_promises10.mkdir)(deployDirectory, { recursive: true });
3116
- const dockerIgnorePath = (0, import_node_path14.join)(deployDirectory, ".dockerignore");
3117
- await (0, import_promises10.writeFile)(dockerIgnorePath, generateDockerIgnore(), "utf-8");
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 import_promises11 = require("fs/promises");
3123
- var import_node_path15 = require("path");
3124
- var KORA_DEPLOY_DIRECTORY = (0, import_node_path15.join)(".kora", "deploy");
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, import_node_path15.join)(projectRoot, KORA_DEPLOY_DIRECTORY);
3615
+ return (0, import_node_path19.join)(projectRoot, KORA_DEPLOY_DIRECTORY);
3128
3616
  }
3129
3617
  function resolveDeployStatePath(projectRoot) {
3130
- return (0, import_node_path15.join)(resolveDeployDirectory(projectRoot), DEPLOY_STATE_FILENAME);
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, import_promises11.readFile)(statePath, "utf-8");
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, import_promises11.rm)(resolveDeployDirectory(projectRoot), { recursive: true, force: true });
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, import_promises11.mkdir)(deployDir, { recursive: true });
3188
- await (0, import_promises11.writeFile)(statePath, `${JSON.stringify(state, null, 2)}
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, import_citty2.defineCommand)({
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, import_citty2.defineCommand)({
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, import_citty2.defineCommand)({
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, import_citty2.defineCommand)({
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, import_node_path16.basename)(projectRoot));
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 import_promises13 = require("fs/promises");
3556
- var import_node_path19 = require("path");
3557
- var import_node_path20 = require("path");
3558
- var import_citty3 = require("citty");
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 import_node_child_process9 = require("child_process");
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, import_node_child_process9.spawn)(config.command, config.args, options);
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((resolve9) => {
3666
- resolveExit = resolve9;
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((resolve9) => {
3734
- setTimeout(resolve9, ms);
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 import_node_child_process10 = require("child_process");
3740
- var import_node_fs4 = require("fs");
3741
- var import_node_path18 = require("path");
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, import_node_fs4.watch)(this.config.schemaPath, () => {
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, import_node_path18.join)(
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((resolve9, reject) => {
3797
- const child = (0, import_node_child_process10.spawn)(command, args, {
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
- resolve9();
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, import_citty3.defineCommand)({
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, import_node_path19.join)(projectRoot, "node_modules", ".bin", "vite"));
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, import_node_path19.join)(projectRoot, "node_modules", "@korajs", "server", "package.json")
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, import_node_path20.resolve)(projectRoot, config.schema);
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((resolve9) => {
3924
- resolveFinished = resolve9;
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, import_node_path19.join)(projectRoot, "server.ts"), (0, import_node_path19.join)(projectRoot, "server.js")];
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, import_promises13.access)(candidate);
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, import_promises13.access)(path);
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, import_node_path19.join)(projectRoot, "kora-sync.db") };
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, import_node_path20.resolve)(projectRoot, store.filename) : (0, import_node_path19.join)(projectRoot, "kora-sync.db");
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
- const server = createKoraServer({ store, port: Number(config.port ?? 3001) });
4100
- const shutdown = async () => {
4101
- try {
4102
- await server.stop();
4103
- } catch {
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 import_promises14 = require("fs/promises");
4120
- var import_node_path21 = require("path");
4121
- var import_citty4 = require("citty");
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 = toPascalCase(name);
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 toPascalCase(name) {
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, import_citty4.defineCommand)({
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, import_citty4.defineCommand)({
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, import_node_path21.resolve)(args.schema);
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 = extractSchema(schemaModule);
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, import_node_path21.resolve)(projectRoot, outputFile);
4275
- await (0, import_promises14.mkdir)((0, import_node_path21.dirname)(outputPath), { recursive: true });
4276
- await (0, import_promises14.writeFile)(outputPath, output, "utf-8");
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 extractSchema(mod) {
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 (isSchemaDefinition(candidate)) return candidate;
5119
+ if (isSchemaDefinition2(candidate)) return candidate;
4287
5120
  return null;
4288
5121
  }
4289
- function isSchemaDefinition(value) {
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 import_promises15 = require("fs/promises");
4297
- var import_node_path23 = require("path");
4298
- var import_citty5 = require("citty");
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/schema-loader.ts
4865
- var import_node_child_process11 = require("child_process");
4866
- var import_node_path22 = require("path");
4867
- var import_node_url4 = require("url");
4868
- async function loadSchemaDefinition(schemaPath, projectRoot) {
4869
- const ext = (0, import_node_path22.extname)(schemaPath);
4870
- const moduleValue = ext === ".ts" || ext === ".mts" || ext === ".cts" ? await loadTypeScriptModule(schemaPath, projectRoot) : await import(`${(0, import_node_url4.pathToFileURL)(schemaPath).href}?t=${Date.now()}-${Math.random()}`);
4871
- return extractSchema2(moduleValue);
4872
- }
4873
- function extractSchema2(value) {
4874
- if (typeof value !== "object" || value === null) {
4875
- throw new Error("Schema module must export an object.");
4876
- }
4877
- const moduleRecord = value;
4878
- const candidate = moduleRecord.default ?? moduleRecord;
4879
- if (!isSchemaDefinition2(candidate)) {
4880
- throw new Error("Schema module must export a valid SchemaDefinition as default export.");
4881
- }
4882
- return candidate;
4883
- }
4884
- function isSchemaDefinition2(value) {
4885
- if (typeof value !== "object" || value === null) return false;
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, import_citty5.defineCommand)({
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, import_node_path23.resolve)(projectRoot, args.schema) : typeof config?.schema === "string" ? (0, import_node_path23.resolve)(projectRoot, config.schema) : await findSchemaFile(projectRoot);
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, import_node_path23.join)(projectRoot, SNAPSHOT_PATH);
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, import_node_path23.resolve)(projectRoot, args["output-dir"]) : (0, import_node_path23.resolve)(projectRoot, MIGRATIONS_DIR);
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, import_promises15.mkdir)(outputDir, { recursive: true });
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, import_promises15.readFile)(path, "utf-8");
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, import_promises15.mkdir)((0, import_node_path23.dirname)(path), { recursive: true });
5110
- await (0, import_promises15.writeFile)(path, `${JSON.stringify(schema, null, 2)}
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, import_promises15.readdir)(outputDir).catch(() => []);
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, import_node_path23.join)(outputDir, filename);
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, import_promises15.writeFile)(path, fileContent, "utf-8");
5130
- await writeMigrationManifest((0, import_node_path23.join)(outputDir, `${migrationId}.json`), {
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, import_promises15.writeFile)(path, `${JSON.stringify(manifest, null, 2)}
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, import_promises15.readdir)(outputDir).catch(() => []);
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, import_node_path23.join)(outputDir, `${id}.json`);
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, import_node_path23.join)(outputDir, file);
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, import_promises15.readFile)(path, "utf-8");
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, import_promises15.readFile)(path, "utf-8");
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(match[1], 10),
5195
- toVersion: Number.parseInt(match[2], 10)
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, import_node_path23.resolve)(projectRoot, dbArg);
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, import_node_path23.join)(projectRoot, "kora-sync.db");
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, import_node_path23.resolve)(projectRoot, sync.store.filename);
6140
+ return (0, import_node_path26.resolve)(projectRoot, sync.store.filename);
5243
6141
  }
5244
- return (0, import_node_path23.join)(projectRoot, "kora-sync.db");
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, import_citty6.defineCommand)({
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
- migrate: migrateCommand
6305
+ logs: logsCommand,
6306
+ migrate: migrateCommand,
6307
+ status: statusCommand
5273
6308
  }
5274
6309
  });
5275
- (0, import_citty6.runMain)(main);
6310
+ (0, import_citty11.runMain)(main);
5276
6311
  //# sourceMappingURL=bin.cjs.map