@korajs/cli 1.0.0-beta.1 → 1.0.0-beta.11

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 (77) hide show
  1. package/dist/bin.cjs +597 -291
  2. package/dist/bin.cjs.map +1 -1
  3. package/dist/bin.js +123 -36
  4. package/dist/bin.js.map +1 -1
  5. package/dist/{chunk-EOWLAAIV.js → chunk-4IKCHRJ6.js} +273 -57
  6. package/dist/chunk-4IKCHRJ6.js.map +1 -0
  7. package/dist/{chunk-B5YS4STN.js → chunk-G2PBYMMA.js} +2 -2
  8. package/dist/{chunk-B5YS4STN.js.map → chunk-G2PBYMMA.js.map} +1 -1
  9. package/dist/{chunk-BWTKRKNJ.js → chunk-Z7GCXMGW.js} +9 -5
  10. package/dist/chunk-Z7GCXMGW.js.map +1 -0
  11. package/dist/create.cjs +8 -4
  12. package/dist/create.cjs.map +1 -1
  13. package/dist/create.js +2 -2
  14. package/dist/index.cjs +389 -166
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +58 -1
  17. package/dist/index.d.ts +58 -1
  18. package/dist/index.js +12 -2
  19. package/dist/{spectator-manager-E2LH2P54.js → spectator-manager-FTH2NFSE.js} +5 -2
  20. package/dist/spectator-manager-FTH2NFSE.js.map +1 -0
  21. package/package.json +10 -8
  22. package/templates/react-basic/README.md.hbs +1 -1
  23. package/templates/react-basic/src/main.tsx +1 -1
  24. package/templates/react-basic/src/vite-env.d.ts +1 -0
  25. package/templates/react-basic/vite.config.ts +7 -4
  26. package/templates/react-sync/README.md.hbs +6 -5
  27. package/templates/react-sync/server.ts +46 -3
  28. package/templates/react-sync/src/main.tsx +1 -1
  29. package/templates/react-sync/src/vite-env.d.ts +1 -0
  30. package/templates/react-sync/vite.config.ts +14 -4
  31. package/templates/react-tailwind/README.md.hbs +1 -1
  32. package/templates/react-tailwind/src/main.tsx +1 -1
  33. package/templates/react-tailwind/src/vite-env.d.ts +1 -0
  34. package/templates/react-tailwind/vite.config.ts +7 -4
  35. package/templates/react-tailwind-sync/README.md.hbs +6 -5
  36. package/templates/react-tailwind-sync/server.ts +46 -3
  37. package/templates/react-tailwind-sync/src/main.tsx +1 -1
  38. package/templates/react-tailwind-sync/src/vite-env.d.ts +1 -0
  39. package/templates/react-tailwind-sync/vite.config.ts +14 -4
  40. package/templates/svelte-basic/README.md.hbs +1 -1
  41. package/templates/svelte-basic/src/main.ts +1 -1
  42. package/templates/svelte-basic/vite.config.ts +4 -2
  43. package/templates/svelte-sync/README.md.hbs +6 -5
  44. package/templates/svelte-sync/server.ts +46 -3
  45. package/templates/svelte-sync/src/main.ts +1 -1
  46. package/templates/svelte-sync/src/vite-env.d.ts +1 -0
  47. package/templates/svelte-sync/vite.config.ts +11 -2
  48. package/templates/svelte-tailwind/README.md.hbs +1 -1
  49. package/templates/svelte-tailwind/src/main.ts +1 -1
  50. package/templates/svelte-tailwind/vite.config.ts +4 -2
  51. package/templates/svelte-tailwind-sync/README.md.hbs +6 -5
  52. package/templates/svelte-tailwind-sync/server.ts +46 -3
  53. package/templates/svelte-tailwind-sync/src/main.ts +1 -1
  54. package/templates/svelte-tailwind-sync/src/vite-env.d.ts +1 -0
  55. package/templates/svelte-tailwind-sync/vite.config.ts +11 -2
  56. package/templates/tauri-react/server.ts +44 -1
  57. package/templates/tauri-react/src/AppShell.tsx +1 -1
  58. package/templates/tauri-react/src/vite-env.d.ts +1 -0
  59. package/templates/vue-basic/README.md.hbs +1 -1
  60. package/templates/vue-basic/src/main.ts +1 -1
  61. package/templates/vue-basic/vite.config.ts +4 -2
  62. package/templates/vue-sync/README.md.hbs +6 -5
  63. package/templates/vue-sync/server.ts +46 -3
  64. package/templates/vue-sync/src/main.ts +1 -1
  65. package/templates/vue-sync/src/vite-env.d.ts +1 -0
  66. package/templates/vue-sync/vite.config.ts +11 -2
  67. package/templates/vue-tailwind/README.md.hbs +1 -1
  68. package/templates/vue-tailwind/src/main.ts +1 -1
  69. package/templates/vue-tailwind/vite.config.ts +4 -2
  70. package/templates/vue-tailwind-sync/README.md.hbs +6 -5
  71. package/templates/vue-tailwind-sync/server.ts +46 -3
  72. package/templates/vue-tailwind-sync/src/main.ts +1 -1
  73. package/templates/vue-tailwind-sync/src/vite-env.d.ts +1 -0
  74. package/templates/vue-tailwind-sync/vite.config.ts +11 -2
  75. package/dist/chunk-BWTKRKNJ.js.map +0 -1
  76. package/dist/chunk-EOWLAAIV.js.map +0 -1
  77. package/dist/spectator-manager-E2LH2P54.js.map +0 -1
package/dist/bin.cjs CHANGED
@@ -32,25 +32,25 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
32
32
  // src/utils/fs-helpers.ts
33
33
  async function directoryExists(path) {
34
34
  try {
35
- await (0, import_promises2.access)(path);
35
+ await (0, import_promises.access)(path);
36
36
  return true;
37
37
  } catch {
38
38
  return false;
39
39
  }
40
40
  }
41
41
  async function findProjectRoot(startDir) {
42
- let current = (0, import_node_path2.resolve)(startDir ?? process.cwd());
42
+ let current = (0, import_node_path.resolve)(startDir ?? process.cwd());
43
43
  for (; ; ) {
44
- const pkgPath = (0, import_node_path2.join)(current, "package.json");
44
+ const pkgPath = (0, import_node_path.join)(current, "package.json");
45
45
  try {
46
- const content = await (0, import_promises2.readFile)(pkgPath, "utf-8");
46
+ const content = await (0, import_promises.readFile)(pkgPath, "utf-8");
47
47
  const pkg = JSON.parse(content);
48
48
  if (isKoraProject(pkg)) {
49
49
  return current;
50
50
  }
51
51
  } catch {
52
52
  }
53
- const parent = (0, import_node_path2.dirname)(current);
53
+ const parent = (0, import_node_path.dirname)(current);
54
54
  if (parent === current) break;
55
55
  current = parent;
56
56
  }
@@ -58,14 +58,14 @@ async function findProjectRoot(startDir) {
58
58
  }
59
59
  async function findSchemaFile(projectRoot) {
60
60
  const candidates = [
61
- (0, import_node_path2.join)(projectRoot, "src", "schema.ts"),
62
- (0, import_node_path2.join)(projectRoot, "schema.ts"),
63
- (0, import_node_path2.join)(projectRoot, "src", "schema.js"),
64
- (0, import_node_path2.join)(projectRoot, "schema.js")
61
+ (0, import_node_path.join)(projectRoot, "src", "schema.ts"),
62
+ (0, import_node_path.join)(projectRoot, "schema.ts"),
63
+ (0, import_node_path.join)(projectRoot, "src", "schema.js"),
64
+ (0, import_node_path.join)(projectRoot, "schema.js")
65
65
  ];
66
66
  for (const candidate of candidates) {
67
67
  try {
68
- await (0, import_promises2.access)(candidate);
68
+ await (0, import_promises.access)(candidate);
69
69
  return candidate;
70
70
  } catch {
71
71
  }
@@ -74,16 +74,16 @@ async function findSchemaFile(projectRoot) {
74
74
  }
75
75
  async function hasTsxInstalled(projectRoot) {
76
76
  try {
77
- await (0, import_promises2.access)((0, import_node_path2.join)(projectRoot, "node_modules", "tsx", "package.json"));
77
+ await (0, import_promises.access)((0, import_node_path.join)(projectRoot, "node_modules", "tsx", "package.json"));
78
78
  return true;
79
79
  } catch {
80
80
  return false;
81
81
  }
82
82
  }
83
83
  async function resolveProjectBinaryEntryPoint(projectRoot, packageName, binaryName) {
84
- const pkgJsonPath = (0, import_node_path2.join)(projectRoot, "node_modules", packageName, "package.json");
84
+ const pkgJsonPath = (0, import_node_path.join)(projectRoot, "node_modules", packageName, "package.json");
85
85
  try {
86
- const content = await (0, import_promises2.readFile)(pkgJsonPath, "utf-8");
86
+ const content = await (0, import_promises.readFile)(pkgJsonPath, "utf-8");
87
87
  const pkg = JSON.parse(content);
88
88
  let binPath;
89
89
  if (typeof pkg.bin === "string") {
@@ -92,8 +92,8 @@ async function resolveProjectBinaryEntryPoint(projectRoot, packageName, binaryNa
92
92
  binPath = pkg.bin[binaryName];
93
93
  }
94
94
  if (!binPath) return null;
95
- const fullPath = (0, import_node_path2.join)(projectRoot, "node_modules", packageName, binPath);
96
- await (0, import_promises2.access)(fullPath);
95
+ const fullPath = (0, import_node_path.join)(projectRoot, "node_modules", packageName, binPath);
96
+ await (0, import_promises.access)(fullPath);
97
97
  return fullPath;
98
98
  } catch {
99
99
  return null;
@@ -108,12 +108,12 @@ function hasKoraDep(deps) {
108
108
  if (typeof deps !== "object" || deps === null) return false;
109
109
  return Object.keys(deps).some((key) => key === "kora" || key.startsWith("@korajs/"));
110
110
  }
111
- var import_promises2, import_node_path2;
111
+ var import_promises, import_node_path;
112
112
  var init_fs_helpers = __esm({
113
113
  "src/utils/fs-helpers.ts"() {
114
114
  "use strict";
115
- import_promises2 = require("fs/promises");
116
- import_node_path2 = require("path");
115
+ import_promises = require("fs/promises");
116
+ import_node_path = require("path");
117
117
  }
118
118
  });
119
119
 
@@ -123,7 +123,7 @@ __export(schema_loader_exports, {
123
123
  loadSchemaDefinition: () => loadSchemaDefinition
124
124
  });
125
125
  async function loadSchemaDefinition(schemaPath, projectRoot) {
126
- const ext = (0, import_node_path4.extname)(schemaPath);
126
+ const ext = (0, import_node_path5.extname)(schemaPath);
127
127
  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()}`);
128
128
  return extractSchema(moduleValue);
129
129
  }
@@ -190,12 +190,12 @@ async function runCommand2(command, args, cwd) {
190
190
  });
191
191
  });
192
192
  }
193
- var import_node_child_process2, import_node_path4, import_node_url2;
193
+ var import_node_child_process2, import_node_path5, import_node_url2;
194
194
  var init_schema_loader = __esm({
195
195
  "src/commands/migrate/schema-loader.ts"() {
196
196
  "use strict";
197
197
  import_node_child_process2 = require("child_process");
198
- import_node_path4 = require("path");
198
+ import_node_path5 = require("path");
199
199
  import_node_url2 = require("url");
200
200
  init_fs_helpers();
201
201
  }
@@ -1981,13 +1981,13 @@ var spectator_manager_exports = {};
1981
1981
  __export(spectator_manager_exports, {
1982
1982
  SpectatorManager: () => SpectatorManager
1983
1983
  });
1984
- var import_node_fs6, import_node_os, import_node_path27, FORWARDED_EVENTS, MAX_EVENT_BUFFER, SpectatorManager;
1984
+ var import_node_fs7, import_node_os, import_node_path28, FORWARDED_EVENTS, MAX_EVENT_BUFFER, SpectatorManager;
1985
1985
  var init_spectator_manager = __esm({
1986
1986
  "src/commands/studio/spectator-manager.ts"() {
1987
1987
  "use strict";
1988
- import_node_fs6 = require("fs");
1988
+ import_node_fs7 = require("fs");
1989
1989
  import_node_os = require("os");
1990
- import_node_path27 = require("path");
1990
+ import_node_path28 = require("path");
1991
1991
  FORWARDED_EVENTS = [
1992
1992
  "merge:started",
1993
1993
  "merge:conflict",
@@ -2001,8 +2001,8 @@ var init_spectator_manager = __esm({
2001
2001
  SpectatorManager = class {
2002
2002
  constructor(options) {
2003
2003
  this.options = options;
2004
- this.tmpDir = (0, import_node_fs6.mkdtempSync)((0, import_node_path27.join)((0, import_node_os.tmpdir)(), "kora-studio-spectator-"));
2005
- this.dbPath = (0, import_node_path27.join)(this.tmpDir, "spectator-replica.db");
2004
+ this.tmpDir = (0, import_node_fs7.mkdtempSync)((0, import_node_path28.join)((0, import_node_os.tmpdir)(), "kora-studio-spectator-"));
2005
+ this.dbPath = (0, import_node_path28.join)(this.tmpDir, "spectator-replica.db");
2006
2006
  }
2007
2007
  options;
2008
2008
  runtime = null;
@@ -2023,6 +2023,7 @@ var init_spectator_manager = __esm({
2023
2023
  MergeEngine: (await import("@korajs/merge")).MergeEngine,
2024
2024
  SyncEngine: (await import("@korajs/sync")).SyncEngine,
2025
2025
  WebSocketTransport: (await import("@korajs/sync")).WebSocketTransport,
2026
+ WebSocket: (await import("ws")).default,
2026
2027
  testing: await import("korajs/testing"),
2027
2028
  SimpleEventEmitter: (await import("@korajs/core/internal")).SimpleEventEmitter
2028
2029
  };
@@ -2041,7 +2042,9 @@ var init_spectator_manager = __esm({
2041
2042
  const unsubscribeAudit = mods.testing.wireAuditPersistence(store, emitter);
2042
2043
  const syncStore = new mods.testing.MergeAwareSyncStore(store, mergeEngine, emitter);
2043
2044
  const engine = new mods.SyncEngine({
2044
- transport: new mods.WebSocketTransport(),
2045
+ transport: new mods.WebSocketTransport({
2046
+ WebSocketImpl: mods.WebSocket
2047
+ }),
2045
2048
  store: syncStore,
2046
2049
  queueStorage: new mods.testing.StoreQueueStorage(adapter),
2047
2050
  syncState: new mods.testing.StoreSyncStatePersistence(store),
@@ -2099,7 +2102,7 @@ var init_spectator_manager = __esm({
2099
2102
  this.runtime = null;
2100
2103
  }
2101
2104
  if (this.tmpDir) {
2102
- (0, import_node_fs6.rmSync)(this.tmpDir, { recursive: true, force: true });
2105
+ (0, import_node_fs7.rmSync)(this.tmpDir, { recursive: true, force: true });
2103
2106
  this.tmpDir = null;
2104
2107
  }
2105
2108
  }
@@ -2182,13 +2185,13 @@ function summarizeEvent(type, payload) {
2182
2185
  return type.replace("lab:", "");
2183
2186
  }
2184
2187
  }
2185
- var import_node_fs7, import_node_os2, import_node_path28, import_core3, DEFAULT_CHAOS, FORWARDED_EVENTS2, MAX_EVENT_BUFFER2, LabManager;
2188
+ var import_node_fs8, import_node_os2, import_node_path29, import_core3, DEFAULT_CHAOS, FORWARDED_EVENTS2, MAX_EVENT_BUFFER2, LabManager;
2186
2189
  var init_lab_manager = __esm({
2187
2190
  "src/commands/studio/lab-manager.ts"() {
2188
2191
  "use strict";
2189
- import_node_fs7 = require("fs");
2192
+ import_node_fs8 = require("fs");
2190
2193
  import_node_os2 = require("os");
2191
- import_node_path28 = require("path");
2194
+ import_node_path29 = require("path");
2192
2195
  import_core3 = require("@korajs/core");
2193
2196
  DEFAULT_CHAOS = {
2194
2197
  dropRate: 0,
@@ -2246,7 +2249,7 @@ var init_lab_manager = __esm({
2246
2249
  }
2247
2250
  async start(initialDevices = 2) {
2248
2251
  const harness = await this.loadHarness();
2249
- this.tmpDir = (0, import_node_fs7.mkdtempSync)((0, import_node_path28.join)((0, import_node_os2.tmpdir)(), "kora-studio-lab-"));
2252
+ this.tmpDir = (0, import_node_fs8.mkdtempSync)((0, import_node_path29.join)((0, import_node_os2.tmpdir)(), "kora-studio-lab-"));
2250
2253
  this.server = new harness.TestServer(this.schema);
2251
2254
  for (let i = 0; i < initialDevices; i++) {
2252
2255
  await this.addDevice();
@@ -2298,7 +2301,7 @@ var init_lab_manager = __esm({
2298
2301
  nodeId: entry.device.getNodeId(),
2299
2302
  connected: entry.device.isConnected(),
2300
2303
  chaos: { ...entry.chaos },
2301
- dbPath: (0, import_node_path28.join)(this.tmpDir ?? "", `test-device-${name}.db`),
2304
+ dbPath: (0, import_node_path29.join)(this.tmpDir ?? "", `test-device-${name}.db`),
2302
2305
  pendingOperations: entry.device.getSyncEngine()?.getStatus().pendingOperations ?? 0
2303
2306
  };
2304
2307
  }
@@ -2377,7 +2380,7 @@ var init_lab_manager = __esm({
2377
2380
  });
2378
2381
  this.server = null;
2379
2382
  if (this.tmpDir) {
2380
- (0, import_node_fs7.rmSync)(this.tmpDir, { recursive: true, force: true });
2383
+ (0, import_node_fs8.rmSync)(this.tmpDir, { recursive: true, force: true });
2381
2384
  this.tmpDir = null;
2382
2385
  }
2383
2386
  }
@@ -2409,12 +2412,13 @@ var init_lab_manager = __esm({
2409
2412
  });
2410
2413
 
2411
2414
  // src/bin.ts
2412
- var import_citty12 = require("citty");
2415
+ var import_citty13 = require("citty");
2413
2416
 
2414
- // src/commands/backup/backup-command.ts
2415
- var import_promises = require("fs/promises");
2416
- var import_node_path = require("path");
2417
+ // src/commands/agents/agents-command.ts
2418
+ var import_promises2 = require("fs/promises");
2419
+ var import_node_path2 = require("path");
2417
2420
  var import_citty = require("citty");
2421
+ init_fs_helpers();
2418
2422
 
2419
2423
  // src/utils/logger.ts
2420
2424
  var RESET = "\x1B[0m";
@@ -2451,22 +2455,233 @@ function createLogger(options) {
2451
2455
  banner() {
2452
2456
  console.log();
2453
2457
  console.log(
2454
- color(BOLD + CYAN, " Kora.js") + color(DIM, " \u2014 Offline-first application framework")
2458
+ color(BOLD + CYAN, " Kora.js") + color(DIM, ", the offline-first application framework")
2455
2459
  );
2456
2460
  console.log();
2457
2461
  }
2458
2462
  };
2459
2463
  }
2460
2464
 
2465
+ // src/commands/agents/agents-md-content.ts
2466
+ var AGENTS_FRAMEWORKS = ["react", "vue", "svelte"];
2467
+ function isAgentsFramework(value) {
2468
+ return AGENTS_FRAMEWORKS.includes(value);
2469
+ }
2470
+ function detectFrameworkFromManifest(manifest) {
2471
+ if (typeof manifest !== "object" || manifest === null) return null;
2472
+ const record = manifest;
2473
+ const deps = collectDependencyNames(record.dependencies, record.devDependencies);
2474
+ if (deps.has("@korajs/react")) return "react";
2475
+ if (deps.has("@korajs/vue")) return "vue";
2476
+ if (deps.has("@korajs/svelte")) return "svelte";
2477
+ return null;
2478
+ }
2479
+ function collectDependencyNames(...groups) {
2480
+ const names = /* @__PURE__ */ new Set();
2481
+ for (const group of groups) {
2482
+ if (typeof group !== "object" || group === null) continue;
2483
+ for (const name of Object.keys(group)) {
2484
+ names.add(name);
2485
+ }
2486
+ }
2487
+ return names;
2488
+ }
2489
+ var FRAMEWORK_BINDINGS = {
2490
+ react: `## React bindings
2491
+
2492
+ \`\`\`tsx
2493
+ import { KoraProvider, useCollection, useMutation, useQuery, useSyncStatus } from '@korajs/react'
2494
+
2495
+ // Wrap the tree once near your app entry: <KoraProvider app={app}><App /></KoraProvider>
2496
+
2497
+ const todos = useCollection('todos')
2498
+ const rows = useQuery(todos.where({ completed: false }).orderBy('createdAt'))
2499
+ const addTodo = useMutation((data) => todos.insert(data))
2500
+ addTodo.mutate({ title: 'x' }) // fire-and-forget; errors land in addTodo.error
2501
+ await addTodo.mutateAsync({ title: 'x' }) // resolves with the result; throws on failure
2502
+ const status = useSyncStatus() // status.status, status.pendingOperations
2503
+ \`\`\`
2504
+
2505
+ Rules: always render \`mutation.error\` somewhere. Keep \`<StrictMode>\` in place; the hooks are StrictMode-safe. \`useQuery\` returns data synchronously from the local store, so do not add loading spinners for local reads.`,
2506
+ vue: `## Vue bindings
2507
+
2508
+ \`\`\`ts
2509
+ import { useCollection, useMutation, useQuery, useSyncStatus } from '@korajs/vue'
2510
+
2511
+ const todos = useCollection('todos')
2512
+ const rows = useQuery(todos.where({ completed: false }).orderBy('createdAt')) // readonly reactive ref
2513
+ const addTodo = useMutation((data) => todos.insert(data))
2514
+ addTodo.mutate({ title: 'x' }) // fire-and-forget; errors land in addTodo.error
2515
+ const status = useSyncStatus() // status.status, status.pendingOperations
2516
+ \`\`\`
2517
+
2518
+ Rules: surface the mutation's error state in the UI. \`useQuery\` reads synchronously from the local store, so avoid loading spinners for local data. The Kora context comes from \`KoraProvider\`, wired in your app entry (for example \`src/main.ts\`).`,
2519
+ svelte: `## Svelte bindings
2520
+
2521
+ Kora's Svelte bindings from \`@korajs/svelte\` expose \`useCollection\`, \`useQuery\`, \`useMutation\`, and \`useSyncStatus\`, wired through the provider set up in your app entry.
2522
+
2523
+ \`\`\`ts
2524
+ import { useCollection, useMutation, useQuery, useSyncStatus } from '@korajs/svelte'
2525
+
2526
+ const todos = useCollection('todos')
2527
+ const rows = useQuery(todos.where({ completed: false }).orderBy('createdAt')) // reactive store
2528
+ const addTodo = useMutation((data) => todos.insert(data))
2529
+ addTodo.mutate({ title: 'x' }) // fire-and-forget; errors land in addTodo.error
2530
+ const status = useSyncStatus() // readable store; read $status.status in markup
2531
+ \`\`\`
2532
+
2533
+ Rules: queries are reactive stores that read synchronously from the local database, so avoid loading spinners for local reads. Mutations expose an \`error\` state that must be surfaced in the UI.`
2534
+ };
2535
+ function generateAgentsMd(framework) {
2536
+ return `# AGENTS.md
2537
+
2538
+ Guidance for AI coding agents working in this project. Humans: this is useful for you too.
2539
+
2540
+ ## What this project is
2541
+
2542
+ This project uses **Kora.js**, an offline-first application framework. All application data lives in a local SQLite database (WASM + OPFS in the browser, native SQLite on desktop and server) and optionally syncs across devices through a Kora sync server. Offline is the normal state, not an error state. Full docs live at https://korajs.dev, with a machine-readable index at https://korajs.dev/llms.txt and the complete docs in one file at https://korajs.dev/llms-full.txt. Any documentation page is available as raw markdown by appending \`.md\` to its URL.
2543
+
2544
+ ## Golden rules
2545
+
2546
+ 1. **The schema is the source of truth.** Collections are defined with \`defineSchema\` and the \`t.*\` field builders (by convention in \`src/schema.ts\`). To add or change data shapes, edit the schema first; types flow from it automatically. Never hand-write types for records.
2547
+ 2. **Never fetch application data over HTTP.** Do not add REST or GraphQL calls for app data, and do not talk to the sync server directly. Read and write through Kora collections only; sync happens automatically in the background.
2548
+ 3. **Await readiness before direct collection access.** Outside the UI bindings, \`await app.ready\` before calling \`app.<collection>\` methods. The framework bindings handle this for you inside components.
2549
+ 4. **Offline must keep working.** Any feature you add must function with the network off. Never gate a read or write on connectivity. If you are checking \`navigator.onLine\` before a data operation, you are doing it wrong.
2550
+ 5. **Surface mutation errors.** Fire-and-forget \`mutate\` calls fold errors into the mutation state. Always render the mutation's \`error\`, or handle the promise from the \`mutateAsync\` variant. Silent failure is the worst failure.
2551
+ 6. **Do not add a state library for server or app data.** Kora's reactive queries are the store. Do not reach for react-query, SWR, Redux, Zustand, or similar for data that lives in a collection. Local UI state (form inputs, toggles) can use your framework's normal state tools.
2552
+ 7. **Do not add loading spinners for local reads.** \`useQuery\` returns data synchronously from the local store, so there is no loading state to wait on for local data.
2553
+
2554
+ ## Data API cheat sheet
2555
+
2556
+ \`\`\`ts
2557
+ await app.ready
2558
+ const rec = await app.todos.insert({ title: 'x' }) // defaults and .auto() fields applied
2559
+ await app.todos.update(rec.id, { completed: true }) // partial update, changed fields only
2560
+ await app.todos.delete(rec.id)
2561
+ const one = await app.todos.findById(rec.id)
2562
+ const unsubscribe = app.todos
2563
+ .where({ completed: false })
2564
+ .orderBy('createdAt', 'desc')
2565
+ .subscribe((rows) => {/* fires immediately, then on every change */})
2566
+ \`\`\`
2567
+
2568
+ Schema example (\`src/schema.ts\`):
2569
+
2570
+ \`\`\`ts
2571
+ import { defineSchema, t } from 'korajs'
2572
+
2573
+ export default defineSchema({
2574
+ version: 1,
2575
+ collections: {
2576
+ todos: {
2577
+ fields: {
2578
+ title: t.string(),
2579
+ completed: t.boolean().default(false),
2580
+ tags: t.array(t.string()).default([]),
2581
+ priority: t.enum(['low', 'medium', 'high']).default('medium'),
2582
+ createdAt: t.timestamp().auto(), // set automatically; never pass it on insert
2583
+ },
2584
+ indexes: ['completed', 'createdAt'],
2585
+ },
2586
+ },
2587
+ })
2588
+ \`\`\`
2589
+
2590
+ If you change collection shapes, increment \`version\` and run \`npx kora migrate\`.
2591
+
2592
+ ## Conflict handling
2593
+
2594
+ Concurrent edits merge automatically: last-write-wins per field, add-wins for arrays, and character-level CRDT for \`t.richtext()\` fields. When a field needs domain-specific merging (counters, quantities), add a \`resolve\` function in the schema rather than writing sync logic by hand. Never write your own conflict or merge code.
2595
+
2596
+ ## Sync and auth
2597
+
2598
+ Enable sync by adding one line to \`createApp\`: \`createApp({ schema, sync: { url } })\`. When the app is offline, writes queue locally and sync when connectivity returns. If the project uses \`@korajs/auth\`, pass its client as \`createApp({ schema, sync: { url, authClient } })\`. Local writes work without sign-in; sync requires the server to accept the connection.
2599
+
2600
+ ${FRAMEWORK_BINDINGS[framework]}
2601
+
2602
+ ## Commands
2603
+
2604
+ - \`npx kora dev\` runs the dev environment (app server, local sync server when configured, schema watcher).
2605
+ - \`npx kora doctor\` diagnoses a broken setup.
2606
+ - \`npx kora migrate\` applies a schema change to the local store.
2607
+ - In a running app, the DevTools overlay (Ctrl+Shift+K, Cmd+Shift+K on macOS) inspects operations, merges, and sync status.
2608
+ `;
2609
+ }
2610
+
2611
+ // src/commands/agents/agents-command.ts
2612
+ async function fileExists(path) {
2613
+ try {
2614
+ await (0, import_promises2.access)(path);
2615
+ return true;
2616
+ } catch {
2617
+ return false;
2618
+ }
2619
+ }
2620
+ async function readManifest(root) {
2621
+ try {
2622
+ return JSON.parse(await (0, import_promises2.readFile)((0, import_node_path2.join)(root, "package.json"), "utf-8"));
2623
+ } catch {
2624
+ return null;
2625
+ }
2626
+ }
2627
+ function resolveFramework(override, manifest) {
2628
+ if (override !== void 0 && isAgentsFramework(override)) return override;
2629
+ return detectFrameworkFromManifest(manifest) ?? "react";
2630
+ }
2631
+ var agentsCommand = (0, import_citty.defineCommand)({
2632
+ meta: {
2633
+ name: "agents-md",
2634
+ description: "Write an AGENTS.md with Kora rules for AI coding agents into this project"
2635
+ },
2636
+ args: {
2637
+ force: {
2638
+ type: "boolean",
2639
+ description: "Overwrite an existing AGENTS.md",
2640
+ default: false
2641
+ },
2642
+ framework: {
2643
+ type: "string",
2644
+ description: "Override framework detection (react, vue, or svelte)"
2645
+ }
2646
+ },
2647
+ async run({ args }) {
2648
+ const logger = createLogger();
2649
+ const projectRoot = await findProjectRoot() ?? process.cwd();
2650
+ const manifest = await readManifest(projectRoot);
2651
+ const override = typeof args.framework === "string" ? args.framework : void 0;
2652
+ if (override !== void 0 && !isAgentsFramework(override)) {
2653
+ logger.error(`Unknown framework "${override}". Use one of: react, vue, svelte.`);
2654
+ process.exitCode = 1;
2655
+ return;
2656
+ }
2657
+ const framework = resolveFramework(override, manifest);
2658
+ const target = (0, import_node_path2.join)(projectRoot, "AGENTS.md");
2659
+ const force = args.force === true;
2660
+ if (await fileExists(target) && !force) {
2661
+ logger.warn("AGENTS.md already exists. Re-run with --force to overwrite it.");
2662
+ process.exitCode = 1;
2663
+ return;
2664
+ }
2665
+ await (0, import_promises2.writeFile)(target, generateAgentsMd(framework), "utf-8");
2666
+ logger.banner();
2667
+ logger.success(`Wrote AGENTS.md (${framework} bindings) to ${target}`);
2668
+ logger.step("Commit it so coding agents pick up Kora conventions automatically.");
2669
+ logger.blank();
2670
+ }
2671
+ });
2672
+
2461
2673
  // src/commands/backup/backup-command.ts
2674
+ var import_promises3 = require("fs/promises");
2675
+ var import_node_path3 = require("path");
2676
+ var import_citty2 = require("citty");
2462
2677
  var DEFAULT_SYNC_PORT = 3001;
2463
- var backupCommand = (0, import_citty.defineCommand)({
2678
+ var backupCommand = (0, import_citty2.defineCommand)({
2464
2679
  meta: {
2465
2680
  name: "backup",
2466
2681
  description: "Backup and restore Kora sync server data"
2467
2682
  },
2468
2683
  subCommands: {
2469
- create: (0, import_citty.defineCommand)({
2684
+ create: (0, import_citty2.defineCommand)({
2470
2685
  meta: {
2471
2686
  name: "create",
2472
2687
  description: "Create a backup of the sync server"
@@ -2506,7 +2721,7 @@ var backupCommand = (0, import_citty.defineCommand)({
2506
2721
  );
2507
2722
  }
2508
2723
  const buffer = await response.arrayBuffer();
2509
- await (0, import_promises.writeFile)(outFile, new Uint8Array(buffer));
2724
+ await (0, import_promises3.writeFile)(outFile, new Uint8Array(buffer));
2510
2725
  const size = (buffer.byteLength / 1024).toFixed(1);
2511
2726
  logger.success(`Backup saved to ${outFile} (${size} KB)`);
2512
2727
  } catch (error) {
@@ -2518,7 +2733,7 @@ var backupCommand = (0, import_citty.defineCommand)({
2518
2733
  }
2519
2734
  }
2520
2735
  }),
2521
- restore: (0, import_citty.defineCommand)({
2736
+ restore: (0, import_citty2.defineCommand)({
2522
2737
  meta: {
2523
2738
  name: "restore",
2524
2739
  description: "Restore a backup to the sync server"
@@ -2553,7 +2768,7 @@ var backupCommand = (0, import_citty.defineCommand)({
2553
2768
  logger.banner();
2554
2769
  logger.info(`Restoring backup from ${filePath} to ${url}...`);
2555
2770
  try {
2556
- const data = await (0, import_promises.readFile)(filePath);
2771
+ const data = await (0, import_promises3.readFile)(filePath);
2557
2772
  const restoreUrl = `${url.replace(/\/$/, "")}/__kora/backup/import?merge=${merge}`;
2558
2773
  const response = await fetch(restoreUrl, {
2559
2774
  method: "POST",
@@ -2581,7 +2796,7 @@ var backupCommand = (0, import_citty.defineCommand)({
2581
2796
  }
2582
2797
  }
2583
2798
  }),
2584
- info: (0, import_citty.defineCommand)({
2799
+ info: (0, import_citty2.defineCommand)({
2585
2800
  meta: {
2586
2801
  name: "info",
2587
2802
  description: "Show backup file information"
@@ -2597,7 +2812,7 @@ var backupCommand = (0, import_citty.defineCommand)({
2597
2812
  const logger = createLogger();
2598
2813
  const filePath = typeof args.file === "string" ? args.file : "";
2599
2814
  try {
2600
- const data = await (0, import_promises.readFile)(filePath);
2815
+ const data = await (0, import_promises3.readFile)(filePath);
2601
2816
  const { readBackupManifest } = await import("@korajs/store");
2602
2817
  const manifest = readBackupManifest(
2603
2818
  new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
@@ -2624,8 +2839,8 @@ var backupCommand = (0, import_citty.defineCommand)({
2624
2839
  });
2625
2840
 
2626
2841
  // src/commands/compact/compact-command.ts
2627
- var import_node_path5 = require("path");
2628
- var import_citty2 = require("citty");
2842
+ var import_node_path6 = require("path");
2843
+ var import_citty3 = require("citty");
2629
2844
 
2630
2845
  // src/errors.ts
2631
2846
  var import_core = require("@korajs/core");
@@ -2685,8 +2900,8 @@ init_fs_helpers();
2685
2900
 
2686
2901
  // src/commands/dev/kora-config.ts
2687
2902
  var import_node_child_process = require("child_process");
2688
- var import_promises3 = require("fs/promises");
2689
- var import_node_path3 = require("path");
2903
+ var import_promises4 = require("fs/promises");
2904
+ var import_node_path4 = require("path");
2690
2905
  var import_node_url = require("url");
2691
2906
  init_fs_helpers();
2692
2907
  var CONFIG_CANDIDATES = [
@@ -2700,7 +2915,7 @@ var CONFIG_CANDIDATES = [
2700
2915
  async function loadKoraConfig(projectRoot) {
2701
2916
  const configPath = await findKoraConfigFile(projectRoot);
2702
2917
  if (!configPath) return null;
2703
- const ext = (0, import_node_path3.extname)(configPath);
2918
+ const ext = (0, import_node_path4.extname)(configPath);
2704
2919
  if (ext === ".ts" || ext === ".mts" || ext === ".cts") {
2705
2920
  const loaded2 = await loadTypeScriptConfig(configPath, projectRoot);
2706
2921
  return toConfigObject(loaded2);
@@ -2710,9 +2925,9 @@ async function loadKoraConfig(projectRoot) {
2710
2925
  }
2711
2926
  async function findKoraConfigFile(projectRoot) {
2712
2927
  for (const file of CONFIG_CANDIDATES) {
2713
- const candidate = (0, import_node_path3.join)(projectRoot, file);
2928
+ const candidate = (0, import_node_path4.join)(projectRoot, file);
2714
2929
  try {
2715
- await (0, import_promises3.access)(candidate);
2930
+ await (0, import_promises4.access)(candidate);
2716
2931
  return candidate;
2717
2932
  } catch {
2718
2933
  }
@@ -2777,7 +2992,7 @@ function toConfigObject(mod) {
2777
2992
 
2778
2993
  // src/commands/compact/compact-command.ts
2779
2994
  init_schema_loader();
2780
- var compactCommand = (0, import_citty2.defineCommand)({
2995
+ var compactCommand = (0, import_citty3.defineCommand)({
2781
2996
  meta: {
2782
2997
  name: "compact",
2783
2998
  description: "Compact the local Kora operation log after server ack"
@@ -2808,12 +3023,12 @@ var compactCommand = (0, import_citty2.defineCommand)({
2808
3023
  if (!projectRoot) {
2809
3024
  throw new InvalidProjectError(process.cwd());
2810
3025
  }
2811
- const dbPath = typeof args.db === "string" ? (0, import_node_path5.resolve)(projectRoot, args.db) : void 0;
3026
+ const dbPath = typeof args.db === "string" ? (0, import_node_path6.resolve)(projectRoot, args.db) : void 0;
2812
3027
  if (!dbPath) {
2813
3028
  throw new Error("Missing --db <path> to the local SQLite database file.");
2814
3029
  }
2815
3030
  const config = await loadKoraConfig(projectRoot);
2816
- 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;
3031
+ const schemaPath = typeof args.schema === "string" ? (0, import_node_path6.resolve)(projectRoot, args.schema) : typeof config?.schema === "string" ? (0, import_node_path6.resolve)(projectRoot, config.schema) : void 0;
2817
3032
  if (!schemaPath) {
2818
3033
  throw new Error("Could not resolve schema path. Pass --schema <path>.");
2819
3034
  }
@@ -2850,9 +3065,9 @@ var compactCommand = (0, import_citty2.defineCommand)({
2850
3065
  // src/commands/create/create-command.ts
2851
3066
  var import_node_child_process4 = require("child_process");
2852
3067
  var import_node_fs2 = require("fs");
2853
- var import_node_path9 = require("path");
3068
+ var import_node_path10 = require("path");
2854
3069
  var import_node_url4 = require("url");
2855
- var import_citty3 = require("citty");
3070
+ var import_citty4 = require("citty");
2856
3071
 
2857
3072
  // src/prompts/preferences.ts
2858
3073
  var import_conf = __toESM(require("conf"), 1);
@@ -3099,8 +3314,8 @@ function getRunDevCommand(pm) {
3099
3314
  }
3100
3315
 
3101
3316
  // src/commands/create/environment-detection.ts
3102
- var import_promises4 = require("fs/promises");
3103
- var import_node_path6 = require("path");
3317
+ var import_promises5 = require("fs/promises");
3318
+ var import_node_path7 = require("path");
3104
3319
  function detectEditorFromEnvironment(env = process.env) {
3105
3320
  const termProgram = String(env.TERM_PROGRAM ?? "").toLowerCase();
3106
3321
  const editorValue = `${String(env.EDITOR ?? "")} ${String(env.VISUAL ?? "")}`.toLowerCase();
@@ -3126,18 +3341,18 @@ async function detectGitContext(startDir) {
3126
3341
  };
3127
3342
  }
3128
3343
  async function detectMonorepoContext(startDir) {
3129
- let current = (0, import_node_path6.resolve)(startDir);
3344
+ let current = (0, import_node_path7.resolve)(startDir);
3130
3345
  for (; ; ) {
3131
- if (await fileExists((0, import_node_path6.join)(current, "pnpm-workspace.yaml"))) {
3346
+ if (await fileExists2((0, import_node_path7.join)(current, "pnpm-workspace.yaml"))) {
3132
3347
  return { isMonorepo: true, root: current, kind: "pnpm-workspace" };
3133
3348
  }
3134
- if (await fileExists((0, import_node_path6.join)(current, "turbo.json"))) {
3349
+ if (await fileExists2((0, import_node_path7.join)(current, "turbo.json"))) {
3135
3350
  return { isMonorepo: true, root: current, kind: "turborepo" };
3136
3351
  }
3137
- const packageJsonPath = (0, import_node_path6.join)(current, "package.json");
3138
- if (await fileExists(packageJsonPath)) {
3352
+ const packageJsonPath = (0, import_node_path7.join)(current, "package.json");
3353
+ if (await fileExists2(packageJsonPath)) {
3139
3354
  try {
3140
- const packageJsonRaw = await (0, import_promises4.readFile)(packageJsonPath, "utf-8");
3355
+ const packageJsonRaw = await (0, import_promises5.readFile)(packageJsonPath, "utf-8");
3141
3356
  const parsed = JSON.parse(packageJsonRaw);
3142
3357
  if (Array.isArray(parsed.workspaces) || isNpmWorkspaceObject(parsed.workspaces)) {
3143
3358
  return { isMonorepo: true, root: current, kind: "npm-workspaces" };
@@ -3145,7 +3360,7 @@ async function detectMonorepoContext(startDir) {
3145
3360
  } catch {
3146
3361
  }
3147
3362
  }
3148
- const parent = (0, import_node_path6.dirname)(current);
3363
+ const parent = (0, import_node_path7.dirname)(current);
3149
3364
  if (parent === current) {
3150
3365
  return { isMonorepo: false, root: null, kind: "none" };
3151
3366
  }
@@ -3157,9 +3372,9 @@ async function applyEditorWorkspacePreset(params) {
3157
3372
  if (editor !== "vscode" && editor !== "cursor" && editor !== "windsurf") {
3158
3373
  return { applied: false, filePath: null };
3159
3374
  }
3160
- const vscodeDir = (0, import_node_path6.join)(targetDir, ".vscode");
3161
- const extensionsPath = (0, import_node_path6.join)(vscodeDir, "extensions.json");
3162
- await (0, import_promises4.mkdir)(vscodeDir, { recursive: true });
3375
+ const vscodeDir = (0, import_node_path7.join)(targetDir, ".vscode");
3376
+ const extensionsPath = (0, import_node_path7.join)(vscodeDir, "extensions.json");
3377
+ await (0, import_promises5.mkdir)(vscodeDir, { recursive: true });
3163
3378
  const recommendations = ["korajs.kora-devtools"];
3164
3379
  const existing = await readJsonObject(extensionsPath);
3165
3380
  const existingRecommendations = Array.isArray(existing?.recommendations) ? existing.recommendations.filter((item) => typeof item === "string") : [];
@@ -3167,19 +3382,19 @@ async function applyEditorWorkspacePreset(params) {
3167
3382
  const next = {
3168
3383
  recommendations: mergedRecommendations
3169
3384
  };
3170
- await (0, import_promises4.writeFile)(extensionsPath, `${JSON.stringify(next, null, 2)}
3385
+ await (0, import_promises5.writeFile)(extensionsPath, `${JSON.stringify(next, null, 2)}
3171
3386
  `, "utf-8");
3172
3387
  return { applied: true, filePath: extensionsPath };
3173
3388
  }
3174
3389
  function resolveMonorepoTargetDirectory(monorepoRoot, projectName) {
3175
- return (0, import_node_path6.join)(monorepoRoot, "packages", projectName);
3390
+ return (0, import_node_path7.join)(monorepoRoot, "packages", projectName);
3176
3391
  }
3177
3392
  function dedupeStrings(values) {
3178
3393
  return Array.from(new Set(values));
3179
3394
  }
3180
3395
  async function readJsonObject(path) {
3181
3396
  try {
3182
- const content = await (0, import_promises4.readFile)(path, "utf-8");
3397
+ const content = await (0, import_promises5.readFile)(path, "utf-8");
3183
3398
  const parsed = JSON.parse(content);
3184
3399
  if (typeof parsed === "object" && parsed !== null) {
3185
3400
  return parsed;
@@ -3194,24 +3409,24 @@ function isNpmWorkspaceObject(value) {
3194
3409
  const record = value;
3195
3410
  return Array.isArray(record.packages);
3196
3411
  }
3197
- async function fileExists(path) {
3412
+ async function fileExists2(path) {
3198
3413
  try {
3199
- await (0, import_promises4.access)(path);
3414
+ await (0, import_promises5.access)(path);
3200
3415
  return true;
3201
3416
  } catch {
3202
3417
  return false;
3203
3418
  }
3204
3419
  }
3205
3420
  async function findNearestAncestorWithEntry(startDir, entryName) {
3206
- let current = (0, import_node_path6.resolve)(startDir);
3421
+ let current = (0, import_node_path7.resolve)(startDir);
3207
3422
  for (; ; ) {
3208
- const candidate = (0, import_node_path6.join)(current, entryName);
3423
+ const candidate = (0, import_node_path7.join)(current, entryName);
3209
3424
  try {
3210
- await (0, import_promises4.stat)(candidate);
3425
+ await (0, import_promises5.stat)(candidate);
3211
3426
  return current;
3212
3427
  } catch {
3213
3428
  }
3214
- const parent = (0, import_node_path6.dirname)(current);
3429
+ const parent = (0, import_node_path7.dirname)(current);
3215
3430
  if (parent === current) {
3216
3431
  return null;
3217
3432
  }
@@ -3490,8 +3705,8 @@ function validateProjectName(name) {
3490
3705
  }
3491
3706
 
3492
3707
  // src/commands/create/sync-provider-preset.ts
3493
- var import_promises5 = require("fs/promises");
3494
- var import_node_path7 = require("path");
3708
+ var import_promises6 = require("fs/promises");
3709
+ var import_node_path8 = require("path");
3495
3710
  async function applySyncProviderPreset(options) {
3496
3711
  if (!isSyncTemplate(options.template)) {
3497
3712
  return;
@@ -3501,10 +3716,10 @@ async function applySyncProviderPreset(options) {
3501
3716
  }
3502
3717
  const providerName = getProviderDisplayName(options.dbProvider);
3503
3718
  const providerConnectionString = getProviderConnectionStringExample(options.dbProvider);
3504
- const envPath = (0, import_node_path7.join)(options.targetDir, ".env.example");
3505
- const readmePath = (0, import_node_path7.join)(options.targetDir, "README.md");
3506
- const existingReadme = await (0, import_promises5.readFile)(readmePath, "utf-8");
3507
- const existingEnv = await (0, import_promises5.readFile)(envPath, "utf-8");
3719
+ const envPath = (0, import_node_path8.join)(options.targetDir, ".env.example");
3720
+ const readmePath = (0, import_node_path8.join)(options.targetDir, "README.md");
3721
+ const existingReadme = await (0, import_promises6.readFile)(readmePath, "utf-8");
3722
+ const existingEnv = await (0, import_promises6.readFile)(envPath, "utf-8");
3508
3723
  const trimmedReadme = existingReadme.trimEnd();
3509
3724
  const trimmedEnv = existingEnv.trimEnd();
3510
3725
  const envSuffix = [
@@ -3524,9 +3739,9 @@ async function applySyncProviderPreset(options) {
3524
3739
  ""
3525
3740
  ].join("\n");
3526
3741
  const readmeTemplate = `${trimmedReadme}${readmeSuffix}`;
3527
- await (0, import_promises5.writeFile)(envPath, `${trimmedEnv}${envSuffix}
3742
+ await (0, import_promises6.writeFile)(envPath, `${trimmedEnv}${envSuffix}
3528
3743
  `, "utf-8");
3529
- await (0, import_promises5.writeFile)(readmePath, readmeTemplate, "utf-8");
3744
+ await (0, import_promises6.writeFile)(readmePath, readmeTemplate, "utf-8");
3530
3745
  }
3531
3746
  function isSyncTemplate(template) {
3532
3747
  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";
@@ -3570,8 +3785,8 @@ function getProviderConnectionStringExample(provider) {
3570
3785
 
3571
3786
  // src/templates/composer.ts
3572
3787
  var import_node_fs = require("fs");
3573
- var import_promises6 = require("fs/promises");
3574
- var import_node_path8 = require("path");
3788
+ var import_promises7 = require("fs/promises");
3789
+ var import_node_path9 = require("path");
3575
3790
  var import_node_url3 = require("url");
3576
3791
  var import_meta = {};
3577
3792
  function substituteVariables(template, context) {
@@ -3581,16 +3796,16 @@ function substituteVariables(template, context) {
3581
3796
  });
3582
3797
  }
3583
3798
  function getTemplatePath(templateName) {
3584
- let dir = (0, import_node_path8.dirname)((0, import_node_url3.fileURLToPath)(import_meta.url));
3799
+ let dir = (0, import_node_path9.dirname)((0, import_node_url3.fileURLToPath)(import_meta.url));
3585
3800
  for (let i = 0; i < 7; i++) {
3586
- const candidate = (0, import_node_path8.resolve)(dir, "templates", templateName);
3801
+ const candidate = (0, import_node_path9.resolve)(dir, "templates", templateName);
3587
3802
  if ((0, import_node_fs.existsSync)(candidate)) {
3588
3803
  return candidate;
3589
3804
  }
3590
- dir = (0, import_node_path8.dirname)(dir);
3805
+ dir = (0, import_node_path9.dirname)(dir);
3591
3806
  }
3592
- const currentDir = (0, import_node_path8.dirname)((0, import_node_url3.fileURLToPath)(import_meta.url));
3593
- return (0, import_node_path8.resolve)(currentDir, "..", "..", "..", "templates", templateName);
3807
+ const currentDir = (0, import_node_path9.dirname)((0, import_node_url3.fileURLToPath)(import_meta.url));
3808
+ return (0, import_node_path9.resolve)(currentDir, "..", "..", "..", "templates", templateName);
3594
3809
  }
3595
3810
  function createCompatibilityLayerPlan(templateName) {
3596
3811
  const baseLayer = { category: "base", name: "base", sourceTemplate: "react-basic" };
@@ -3723,22 +3938,22 @@ async function composeTemplateLayers(plan, targetDir, context) {
3723
3938
  }
3724
3939
  }
3725
3940
  async function copyDirectory(src, dest, vars) {
3726
- await (0, import_promises6.mkdir)(dest, { recursive: true });
3727
- const entries = await (0, import_promises6.readdir)(src);
3941
+ await (0, import_promises7.mkdir)(dest, { recursive: true });
3942
+ const entries = await (0, import_promises7.readdir)(src);
3728
3943
  for (const entry of entries) {
3729
- const srcPath = (0, import_node_path8.join)(src, entry);
3730
- const srcStat = await (0, import_promises6.stat)(srcPath);
3944
+ const srcPath = (0, import_node_path9.join)(src, entry);
3945
+ const srcStat = await (0, import_promises7.stat)(srcPath);
3731
3946
  if (srcStat.isDirectory()) {
3732
- await copyDirectory(srcPath, (0, import_node_path8.join)(dest, entry), vars);
3947
+ await copyDirectory(srcPath, (0, import_node_path9.join)(dest, entry), vars);
3733
3948
  continue;
3734
3949
  }
3735
3950
  if (entry.endsWith(".hbs")) {
3736
- const content = await (0, import_promises6.readFile)(srcPath, "utf-8");
3951
+ const content = await (0, import_promises7.readFile)(srcPath, "utf-8");
3737
3952
  const outputName = entry.slice(0, -4);
3738
- await (0, import_promises6.writeFile)((0, import_node_path8.join)(dest, outputName), substituteVariables(content, vars), "utf-8");
3953
+ await (0, import_promises7.writeFile)((0, import_node_path9.join)(dest, outputName), substituteVariables(content, vars), "utf-8");
3739
3954
  continue;
3740
3955
  }
3741
- await (0, import_promises6.copyFile)(srcPath, (0, import_node_path8.join)(dest, entry));
3956
+ await (0, import_promises7.copyFile)(srcPath, (0, import_node_path9.join)(dest, entry));
3742
3957
  }
3743
3958
  }
3744
3959
 
@@ -3750,7 +3965,7 @@ async function scaffoldTemplate(templateName, targetDir, context) {
3750
3965
 
3751
3966
  // src/commands/create/create-command.ts
3752
3967
  var import_meta2 = {};
3753
- var createCommand = (0, import_citty3.defineCommand)({
3968
+ var createCommand = (0, import_citty4.defineCommand)({
3754
3969
  meta: {
3755
3970
  name: "create",
3756
3971
  description: "Create a new Kora application"
@@ -3891,7 +4106,7 @@ var createCommand = (0, import_citty3.defineCommand)({
3891
4106
  const editorDetection = detectEditorFromEnvironment();
3892
4107
  const gitContext = await detectGitContext(process.cwd());
3893
4108
  const monorepoContext = await detectMonorepoContext(process.cwd());
3894
- let targetDir = (0, import_node_path9.resolve)(process.cwd(), projectName);
4109
+ let targetDir = (0, import_node_path10.resolve)(process.cwd(), projectName);
3895
4110
  if (!useDefaults && monorepoContext.isMonorepo && monorepoContext.root !== null) {
3896
4111
  const useWorkspaceTarget = await prompts.confirm(
3897
4112
  `Detected ${formatMonorepoKind(monorepoContext.kind)} at ${monorepoContext.root}. Create app under packages/${projectName}?`,
@@ -4061,29 +4276,33 @@ function relativeToTarget(targetDir, filePath) {
4061
4276
  }
4062
4277
  function resolveKoraVersion() {
4063
4278
  try {
4064
- let dir = (0, import_node_path9.dirname)((0, import_node_url4.fileURLToPath)(import_meta2.url));
4279
+ let dir = (0, import_node_path10.dirname)((0, import_node_url4.fileURLToPath)(import_meta2.url));
4065
4280
  for (let i = 0; i < 5; i++) {
4066
- const pkgPath = (0, import_node_path9.resolve)(dir, "package.json");
4281
+ const pkgPath = (0, import_node_path10.resolve)(dir, "package.json");
4067
4282
  if ((0, import_node_fs2.existsSync)(pkgPath)) {
4068
4283
  const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf-8"));
4069
4284
  if (pkg.name === "@korajs/cli") {
4070
- if (pkg.version === "0.0.0") return "latest";
4071
- const parts = pkg.version.split(".");
4072
- return `^${parts[0]}.${parts[1]}.0`;
4285
+ return deriveKoraTemplateVersion(pkg.version);
4073
4286
  }
4074
4287
  }
4075
- dir = (0, import_node_path9.dirname)(dir);
4288
+ dir = (0, import_node_path10.dirname)(dir);
4076
4289
  }
4077
4290
  return "latest";
4078
4291
  } catch {
4079
4292
  return "latest";
4080
4293
  }
4081
4294
  }
4295
+ function deriveKoraTemplateVersion(version) {
4296
+ if (version === "0.0.0") return "latest";
4297
+ if (version.includes("-")) return version;
4298
+ const parts = version.split(".");
4299
+ return `^${parts[0]}.${parts[1]}.0`;
4300
+ }
4082
4301
 
4083
4302
  // src/commands/deploy/deploy-command.ts
4084
4303
  var import_node_fs4 = require("fs");
4085
- var import_node_path20 = require("path");
4086
- var import_citty4 = require("citty");
4304
+ var import_node_path21 = require("path");
4305
+ var import_citty5 = require("citty");
4087
4306
  init_fs_helpers();
4088
4307
 
4089
4308
  // src/commands/deploy/adapters/adapter.ts
@@ -4102,13 +4321,13 @@ function isDeployPlatform(value) {
4102
4321
 
4103
4322
  // src/commands/deploy/adapters/aws-ecs-adapter.ts
4104
4323
  var import_node_child_process6 = require("child_process");
4105
- var import_node_path12 = require("path");
4324
+ var import_node_path13 = require("path");
4106
4325
 
4107
4326
  // src/commands/deploy/builder/client-builder.ts
4108
4327
  var import_node_child_process5 = require("child_process");
4109
4328
  var import_node_fs3 = require("fs");
4110
- var import_promises7 = require("fs/promises");
4111
- var import_node_path10 = require("path");
4329
+ var import_promises8 = require("fs/promises");
4330
+ var import_node_path11 = require("path");
4112
4331
  init_fs_helpers();
4113
4332
  async function buildClient(options) {
4114
4333
  const viteEntryPoint = await resolveProjectBinaryEntryPoint(options.projectRoot, "vite", "vite");
@@ -4130,15 +4349,15 @@ async function buildClient(options) {
4130
4349
  return { outDir: options.outDir };
4131
4350
  }
4132
4351
  async function patchSqliteWasmAssets(projectRoot, outDir) {
4133
- const assetsDir = (0, import_node_path10.join)(outDir, "assets");
4352
+ const assetsDir = (0, import_node_path11.join)(outDir, "assets");
4134
4353
  if (!(0, import_node_fs3.existsSync)(assetsDir)) return;
4135
- const files = await (0, import_promises7.readdir)(assetsDir);
4354
+ const files = await (0, import_promises8.readdir)(assetsDir);
4136
4355
  const hashedWasm = files.find((f) => /^sqlite3-.+\.wasm$/.test(f));
4137
4356
  if (hashedWasm && !files.includes("sqlite3.wasm")) {
4138
- await (0, import_promises7.copyFile)((0, import_node_path10.join)(assetsDir, hashedWasm), (0, import_node_path10.join)(assetsDir, "sqlite3.wasm"));
4357
+ await (0, import_promises8.copyFile)((0, import_node_path11.join)(assetsDir, hashedWasm), (0, import_node_path11.join)(assetsDir, "sqlite3.wasm"));
4139
4358
  }
4140
4359
  if (!files.includes("sqlite3-opfs-async-proxy.js")) {
4141
- const proxyFile = (0, import_node_path10.resolve)(
4360
+ const proxyFile = (0, import_node_path11.resolve)(
4142
4361
  projectRoot,
4143
4362
  "node_modules",
4144
4363
  "@sqlite.org",
@@ -4148,7 +4367,7 @@ async function patchSqliteWasmAssets(projectRoot, outDir) {
4148
4367
  "sqlite3-opfs-async-proxy.js"
4149
4368
  );
4150
4369
  if ((0, import_node_fs3.existsSync)(proxyFile)) {
4151
- await (0, import_promises7.copyFile)(proxyFile, (0, import_node_path10.join)(assetsDir, "sqlite3-opfs-async-proxy.js"));
4370
+ await (0, import_promises8.copyFile)(proxyFile, (0, import_node_path11.join)(assetsDir, "sqlite3-opfs-async-proxy.js"));
4152
4371
  }
4153
4372
  }
4154
4373
  }
@@ -4173,9 +4392,9 @@ async function runProcess(command, args, cwd) {
4173
4392
  }
4174
4393
 
4175
4394
  // src/commands/deploy/builder/server-bundler.ts
4176
- var import_promises8 = require("fs/promises");
4177
4395
  var import_promises9 = require("fs/promises");
4178
- var import_node_path11 = require("path");
4396
+ var import_promises10 = require("fs/promises");
4397
+ var import_node_path12 = require("path");
4179
4398
  var import_esbuild = require("esbuild");
4180
4399
  var DEFAULT_ENTRY_CANDIDATES = [
4181
4400
  "server.ts",
@@ -4191,8 +4410,8 @@ async function bundleServer(options) {
4191
4410
  `Could not find a server entry file in ${options.projectRoot}. Looked for: ${candidates.join(", ")}`
4192
4411
  );
4193
4412
  }
4194
- await (0, import_promises8.mkdir)(options.deployDirectory, { recursive: true });
4195
- const outputFilePath = (0, import_node_path11.join)(options.deployDirectory, "server-bundled.js");
4413
+ await (0, import_promises9.mkdir)(options.deployDirectory, { recursive: true });
4414
+ const outputFilePath = (0, import_node_path12.join)(options.deployDirectory, "server-bundled.js");
4196
4415
  await (0, import_esbuild.build)({
4197
4416
  entryPoints: [entryFilePath],
4198
4417
  outfile: outputFilePath,
@@ -4214,9 +4433,9 @@ async function bundleServer(options) {
4214
4433
  }
4215
4434
  async function resolveServerEntry(projectRoot, candidates) {
4216
4435
  for (const candidate of candidates) {
4217
- const fullPath = (0, import_node_path11.join)(projectRoot, candidate);
4436
+ const fullPath = (0, import_node_path12.join)(projectRoot, candidate);
4218
4437
  try {
4219
- await (0, import_promises9.access)(fullPath);
4438
+ await (0, import_promises10.access)(fullPath);
4220
4439
  return fullPath;
4221
4440
  } catch {
4222
4441
  }
@@ -4318,19 +4537,19 @@ var AwsEcsAdapter = class {
4318
4537
  appName: config.appName,
4319
4538
  region: config.region
4320
4539
  };
4321
- const deployDirectory = (0, import_node_path12.join)(config.projectRoot, ".kora", "deploy");
4540
+ const deployDirectory = (0, import_node_path13.join)(config.projectRoot, ".kora", "deploy");
4322
4541
  await bundleServer({
4323
4542
  projectRoot: config.projectRoot,
4324
4543
  deployDirectory
4325
4544
  });
4326
4545
  const client = config.deployTarget === "sync-server" ? null : await buildClient({
4327
4546
  projectRoot: config.projectRoot,
4328
- outDir: (0, import_node_path12.join)(deployDirectory, "dist"),
4547
+ outDir: (0, import_node_path13.join)(deployDirectory, "dist"),
4329
4548
  mode: "production"
4330
4549
  });
4331
4550
  return {
4332
4551
  clientDirectory: client?.outDir ?? null,
4333
- serverBundlePath: (0, import_node_path12.join)(deployDirectory, "server-bundled.js"),
4552
+ serverBundlePath: (0, import_node_path13.join)(deployDirectory, "server-bundled.js"),
4334
4553
  deployDirectory
4335
4554
  };
4336
4555
  }
@@ -4596,7 +4815,7 @@ function inferLogLevel(line) {
4596
4815
 
4597
4816
  // src/commands/deploy/adapters/aws-lightsail-adapter.ts
4598
4817
  var import_node_child_process7 = require("child_process");
4599
- var import_node_path13 = require("path");
4818
+ var import_node_path14 = require("path");
4600
4819
  var AwsLightsailAdapter = class {
4601
4820
  name = "aws-lightsail";
4602
4821
  logger = createLogger();
@@ -4669,19 +4888,19 @@ var AwsLightsailAdapter = class {
4669
4888
  appName: config.appName,
4670
4889
  region: config.region
4671
4890
  };
4672
- const deployDirectory = (0, import_node_path13.join)(config.projectRoot, ".kora", "deploy");
4891
+ const deployDirectory = (0, import_node_path14.join)(config.projectRoot, ".kora", "deploy");
4673
4892
  await bundleServer({
4674
4893
  projectRoot: config.projectRoot,
4675
4894
  deployDirectory
4676
4895
  });
4677
4896
  const client = config.deployTarget === "sync-server" ? null : await buildClient({
4678
4897
  projectRoot: config.projectRoot,
4679
- outDir: (0, import_node_path13.join)(deployDirectory, "dist"),
4898
+ outDir: (0, import_node_path14.join)(deployDirectory, "dist"),
4680
4899
  mode: "production"
4681
4900
  });
4682
4901
  return {
4683
4902
  clientDirectory: client?.outDir ?? null,
4684
- serverBundlePath: (0, import_node_path13.join)(deployDirectory, "server-bundled.js"),
4903
+ serverBundlePath: (0, import_node_path14.join)(deployDirectory, "server-bundled.js"),
4685
4904
  deployDirectory
4686
4905
  };
4687
4906
  }
@@ -4981,11 +5200,11 @@ function inferLogLevel2(line) {
4981
5200
 
4982
5201
  // src/commands/deploy/adapters/fly-adapter.ts
4983
5202
  var import_node_child_process8 = require("child_process");
4984
- var import_node_path15 = require("path");
5203
+ var import_node_path16 = require("path");
4985
5204
 
4986
5205
  // src/commands/deploy/artifacts/fly-toml-generator.ts
4987
- var import_promises10 = require("fs/promises");
4988
- var import_node_path14 = require("path");
5206
+ var import_promises11 = require("fs/promises");
5207
+ var import_node_path15 = require("path");
4989
5208
  var GENERATED_HEADER = "# Generated by kora deploy \u2014 do not edit manually";
4990
5209
  function generateFlyToml(options) {
4991
5210
  const internalPort = options.internalPort ?? 3e3;
@@ -5015,9 +5234,9 @@ function generateFlyToml(options) {
5015
5234
  ].join("\n");
5016
5235
  }
5017
5236
  async function writeFlyTomlArtifact(deployDirectory, options) {
5018
- await (0, import_promises10.mkdir)(deployDirectory, { recursive: true });
5019
- const filePath = (0, import_node_path14.join)(deployDirectory, "fly.toml");
5020
- await (0, import_promises10.writeFile)(filePath, generateFlyToml(options), "utf-8");
5237
+ await (0, import_promises11.mkdir)(deployDirectory, { recursive: true });
5238
+ const filePath = (0, import_node_path15.join)(deployDirectory, "fly.toml");
5239
+ await (0, import_promises11.writeFile)(filePath, generateFlyToml(options), "utf-8");
5021
5240
  return filePath;
5022
5241
  }
5023
5242
 
@@ -5111,7 +5330,7 @@ var FlyAdapter = class {
5111
5330
  appName: config.appName,
5112
5331
  region: config.region
5113
5332
  };
5114
- const deployDirectory = (0, import_node_path15.join)(config.projectRoot, ".kora", "deploy");
5333
+ const deployDirectory = (0, import_node_path16.join)(config.projectRoot, ".kora", "deploy");
5115
5334
  await writeFlyTomlArtifact(deployDirectory, {
5116
5335
  appName: config.appName,
5117
5336
  region: config.region ?? "iad"
@@ -5122,12 +5341,12 @@ var FlyAdapter = class {
5122
5341
  });
5123
5342
  const client = config.deployTarget === "sync-server" ? null : await buildClient({
5124
5343
  projectRoot: config.projectRoot,
5125
- outDir: (0, import_node_path15.join)(deployDirectory, "dist"),
5344
+ outDir: (0, import_node_path16.join)(deployDirectory, "dist"),
5126
5345
  mode: "production"
5127
5346
  });
5128
5347
  return {
5129
5348
  clientDirectory: client?.outDir ?? null,
5130
- serverBundlePath: (0, import_node_path15.join)(deployDirectory, "server-bundled.js"),
5349
+ serverBundlePath: (0, import_node_path16.join)(deployDirectory, "server-bundled.js"),
5131
5350
  deployDirectory
5132
5351
  };
5133
5352
  }
@@ -5349,11 +5568,11 @@ ${stdout}`.toLowerCase();
5349
5568
 
5350
5569
  // src/commands/deploy/adapters/railway-adapter.ts
5351
5570
  var import_node_child_process9 = require("child_process");
5352
- var import_node_path17 = require("path");
5571
+ var import_node_path18 = require("path");
5353
5572
 
5354
5573
  // src/commands/deploy/artifacts/railway-json-generator.ts
5355
- var import_promises11 = require("fs/promises");
5356
- var import_node_path16 = require("path");
5574
+ var import_promises12 = require("fs/promises");
5575
+ var import_node_path17 = require("path");
5357
5576
  var GENERATED_HEADER2 = "Generated by kora deploy - do not edit manually.";
5358
5577
  function generateRailwayJson(options) {
5359
5578
  const file = {
@@ -5378,9 +5597,9 @@ function generateRailwayJson(options) {
5378
5597
  `;
5379
5598
  }
5380
5599
  async function writeRailwayJsonArtifact(deployDirectory, options) {
5381
- await (0, import_promises11.mkdir)(deployDirectory, { recursive: true });
5382
- const filePath = (0, import_node_path16.join)(deployDirectory, "railway.json");
5383
- await (0, import_promises11.writeFile)(filePath, generateRailwayJson(options), "utf-8");
5600
+ await (0, import_promises12.mkdir)(deployDirectory, { recursive: true });
5601
+ const filePath = (0, import_node_path17.join)(deployDirectory, "railway.json");
5602
+ await (0, import_promises12.writeFile)(filePath, generateRailwayJson(options), "utf-8");
5384
5603
  return filePath;
5385
5604
  }
5386
5605
 
@@ -5469,7 +5688,7 @@ var RailwayAdapter = class {
5469
5688
  appName: config.appName,
5470
5689
  region: config.region
5471
5690
  };
5472
- const deployDirectory = (0, import_node_path17.join)(config.projectRoot, ".kora", "deploy");
5691
+ const deployDirectory = (0, import_node_path18.join)(config.projectRoot, ".kora", "deploy");
5473
5692
  await writeRailwayJsonArtifact(deployDirectory, {
5474
5693
  appName: config.appName,
5475
5694
  environment: config.environment
@@ -5480,12 +5699,12 @@ var RailwayAdapter = class {
5480
5699
  });
5481
5700
  const client = config.deployTarget === "sync-server" ? null : await buildClient({
5482
5701
  projectRoot: config.projectRoot,
5483
- outDir: (0, import_node_path17.join)(deployDirectory, "dist"),
5702
+ outDir: (0, import_node_path18.join)(deployDirectory, "dist"),
5484
5703
  mode: "production"
5485
5704
  });
5486
5705
  return {
5487
5706
  clientDirectory: client?.outDir ?? null,
5488
- serverBundlePath: (0, import_node_path17.join)(deployDirectory, "server-bundled.js"),
5707
+ serverBundlePath: (0, import_node_path18.join)(deployDirectory, "server-bundled.js"),
5489
5708
  deployDirectory
5490
5709
  };
5491
5710
  }
@@ -5770,8 +5989,8 @@ function createDeployAdapter(platform) {
5770
5989
  }
5771
5990
 
5772
5991
  // src/commands/deploy/artifacts/dockerfile-generator.ts
5773
- var import_promises12 = require("fs/promises");
5774
- var import_node_path18 = require("path");
5992
+ var import_promises13 = require("fs/promises");
5993
+ var import_node_path19 = require("path");
5775
5994
  var GENERATED_HEADER3 = "# Generated by kora deploy \u2014 do not edit manually";
5776
5995
  function generateDockerfile(options = {}) {
5777
5996
  const nodeVersion = options.nodeVersion ?? "20-alpine";
@@ -5837,37 +6056,37 @@ function generateDockerIgnore() {
5837
6056
  ].join("\n");
5838
6057
  }
5839
6058
  async function writeDockerfileArtifact(deployDirectory, options = {}) {
5840
- await (0, import_promises12.mkdir)(deployDirectory, { recursive: true });
5841
- const dockerfilePath = (0, import_node_path18.join)(deployDirectory, "Dockerfile");
5842
- await (0, import_promises12.writeFile)(dockerfilePath, generateDockerfile(options), "utf-8");
6059
+ await (0, import_promises13.mkdir)(deployDirectory, { recursive: true });
6060
+ const dockerfilePath = (0, import_node_path19.join)(deployDirectory, "Dockerfile");
6061
+ await (0, import_promises13.writeFile)(dockerfilePath, generateDockerfile(options), "utf-8");
5843
6062
  if (options.nativeDependencies && Object.keys(options.nativeDependencies).length > 0) {
5844
- const pkgJsonPath = (0, import_node_path18.join)(deployDirectory, "package.json");
5845
- await (0, import_promises12.writeFile)(pkgJsonPath, generateDeployPackageJson(options.nativeDependencies), "utf-8");
6063
+ const pkgJsonPath = (0, import_node_path19.join)(deployDirectory, "package.json");
6064
+ await (0, import_promises13.writeFile)(pkgJsonPath, generateDeployPackageJson(options.nativeDependencies), "utf-8");
5846
6065
  }
5847
6066
  return dockerfilePath;
5848
6067
  }
5849
6068
  async function writeDockerIgnoreArtifact(deployDirectory) {
5850
- await (0, import_promises12.mkdir)(deployDirectory, { recursive: true });
5851
- const dockerIgnorePath = (0, import_node_path18.join)(deployDirectory, ".dockerignore");
5852
- await (0, import_promises12.writeFile)(dockerIgnorePath, generateDockerIgnore(), "utf-8");
6069
+ await (0, import_promises13.mkdir)(deployDirectory, { recursive: true });
6070
+ const dockerIgnorePath = (0, import_node_path19.join)(deployDirectory, ".dockerignore");
6071
+ await (0, import_promises13.writeFile)(dockerIgnorePath, generateDockerIgnore(), "utf-8");
5853
6072
  return dockerIgnorePath;
5854
6073
  }
5855
6074
 
5856
6075
  // src/commands/deploy/state/deploy-state.ts
5857
- var import_promises13 = require("fs/promises");
5858
- var import_node_path19 = require("path");
5859
- var KORA_DEPLOY_DIRECTORY = (0, import_node_path19.join)(".kora", "deploy");
6076
+ var import_promises14 = require("fs/promises");
6077
+ var import_node_path20 = require("path");
6078
+ var KORA_DEPLOY_DIRECTORY = (0, import_node_path20.join)(".kora", "deploy");
5860
6079
  var DEPLOY_STATE_FILENAME = "deploy.json";
5861
6080
  function resolveDeployDirectory(projectRoot) {
5862
- return (0, import_node_path19.join)(projectRoot, KORA_DEPLOY_DIRECTORY);
6081
+ return (0, import_node_path20.join)(projectRoot, KORA_DEPLOY_DIRECTORY);
5863
6082
  }
5864
6083
  function resolveDeployStatePath(projectRoot) {
5865
- return (0, import_node_path19.join)(resolveDeployDirectory(projectRoot), DEPLOY_STATE_FILENAME);
6084
+ return (0, import_node_path20.join)(resolveDeployDirectory(projectRoot), DEPLOY_STATE_FILENAME);
5866
6085
  }
5867
6086
  async function readDeployState(projectRoot) {
5868
6087
  const statePath = resolveDeployStatePath(projectRoot);
5869
6088
  try {
5870
- const source = await (0, import_promises13.readFile)(statePath, "utf-8");
6089
+ const source = await (0, import_promises14.readFile)(statePath, "utf-8");
5871
6090
  const parsed = JSON.parse(source);
5872
6091
  return parseDeployState(parsed);
5873
6092
  } catch (error) {
@@ -5914,13 +6133,13 @@ async function updateDeployState(projectRoot, patch, now = /* @__PURE__ */ new D
5914
6133
  return nextState;
5915
6134
  }
5916
6135
  async function resetDeployState(projectRoot) {
5917
- await (0, import_promises13.rm)(resolveDeployDirectory(projectRoot), { recursive: true, force: true });
6136
+ await (0, import_promises14.rm)(resolveDeployDirectory(projectRoot), { recursive: true, force: true });
5918
6137
  }
5919
6138
  async function persistDeployState(projectRoot, state) {
5920
6139
  const deployDir = resolveDeployDirectory(projectRoot);
5921
6140
  const statePath = resolveDeployStatePath(projectRoot);
5922
- await (0, import_promises13.mkdir)(deployDir, { recursive: true });
5923
- await (0, import_promises13.writeFile)(statePath, `${JSON.stringify(state, null, 2)}
6141
+ await (0, import_promises14.mkdir)(deployDir, { recursive: true });
6142
+ await (0, import_promises14.writeFile)(statePath, `${JSON.stringify(state, null, 2)}
5924
6143
  `, "utf-8");
5925
6144
  }
5926
6145
  function parseDeployState(value) {
@@ -5970,7 +6189,7 @@ function readOptionalString(value, field) {
5970
6189
  }
5971
6190
 
5972
6191
  // src/commands/deploy/deploy-command.ts
5973
- var deployCommand = (0, import_citty4.defineCommand)({
6192
+ var deployCommand = (0, import_citty5.defineCommand)({
5974
6193
  meta: {
5975
6194
  name: "deploy",
5976
6195
  description: "Deploy a Kora project to your selected platform"
@@ -6005,7 +6224,7 @@ var deployCommand = (0, import_citty4.defineCommand)({
6005
6224
  }
6006
6225
  },
6007
6226
  subCommands: {
6008
- status: (0, import_citty4.defineCommand)({
6227
+ status: (0, import_citty5.defineCommand)({
6009
6228
  meta: {
6010
6229
  name: "status",
6011
6230
  description: "Show current deployment status"
@@ -6036,7 +6255,7 @@ var deployCommand = (0, import_citty4.defineCommand)({
6036
6255
  logger.step(`Sync URL: ${state.syncUrl ?? "n/a"}`);
6037
6256
  }
6038
6257
  }),
6039
- rollback: (0, import_citty4.defineCommand)({
6258
+ rollback: (0, import_citty5.defineCommand)({
6040
6259
  meta: {
6041
6260
  name: "rollback",
6042
6261
  description: "Rollback the current deployment"
@@ -6067,7 +6286,7 @@ var deployCommand = (0, import_citty4.defineCommand)({
6067
6286
  logger.success(`Rolled back ${state.platform} deployment to ${deploymentId}.`);
6068
6287
  }
6069
6288
  }),
6070
- logs: (0, import_citty4.defineCommand)({
6289
+ logs: (0, import_citty5.defineCommand)({
6071
6290
  meta: {
6072
6291
  name: "logs",
6073
6292
  description: "Read deployment logs"
@@ -6254,7 +6473,7 @@ function resolveAppName(argValue, storedValue, projectRoot, confirm) {
6254
6473
  "Missing app name in --confirm mode. Provide --app or run an interactive deploy first."
6255
6474
  );
6256
6475
  }
6257
- return sanitizeAppName((0, import_node_path20.basename)(projectRoot));
6476
+ return sanitizeAppName((0, import_node_path21.basename)(projectRoot));
6258
6477
  }
6259
6478
  function resolveRegion(argValue, storedValue, confirm) {
6260
6479
  if (typeof argValue === "string" && argValue.length > 0) return argValue;
@@ -6289,17 +6508,18 @@ function configureAdapterContext(adapter, context) {
6289
6508
  }
6290
6509
  }
6291
6510
  function detectDeployTarget(projectRoot) {
6292
- return (0, import_node_fs4.existsSync)((0, import_node_path20.join)(projectRoot, "src-tauri")) ? "sync-server" : "full-stack";
6511
+ return (0, import_node_fs4.existsSync)((0, import_node_path21.join)(projectRoot, "src-tauri")) ? "sync-server" : "full-stack";
6293
6512
  }
6294
6513
  function hasContextSetter(adapter) {
6295
6514
  return typeof adapter.setContext === "function";
6296
6515
  }
6297
6516
 
6298
6517
  // src/commands/dev/dev-command.ts
6299
- var import_promises14 = require("fs/promises");
6300
- var import_node_path22 = require("path");
6518
+ var import_node_fs6 = require("fs");
6519
+ var import_promises15 = require("fs/promises");
6301
6520
  var import_node_path23 = require("path");
6302
- var import_citty5 = require("citty");
6521
+ var import_node_path24 = require("path");
6522
+ var import_citty6 = require("citty");
6303
6523
  init_fs_helpers();
6304
6524
 
6305
6525
  // src/commands/dev/process-manager.ts
@@ -6307,6 +6527,7 @@ var import_node_child_process10 = require("child_process");
6307
6527
  var ProcessManager = class {
6308
6528
  processes = /* @__PURE__ */ new Map();
6309
6529
  spawn(config) {
6530
+ this.processes.get(config.label)?.child.kill("SIGTERM");
6310
6531
  const options = {
6311
6532
  cwd: config.cwd,
6312
6533
  env: { ...process.env, ...config.env },
@@ -6350,6 +6571,20 @@ var ProcessManager = class {
6350
6571
  hasRunning() {
6351
6572
  return this.processes.size > 0;
6352
6573
  }
6574
+ async restart(config) {
6575
+ await this.stop(config.label);
6576
+ this.spawn(config);
6577
+ }
6578
+ async stop(label) {
6579
+ const running = this.processes.get(label);
6580
+ if (!running) return;
6581
+ running.child.kill("SIGTERM");
6582
+ await Promise.race([running.exitPromise, delay(5e3)]);
6583
+ const remaining = this.processes.get(label);
6584
+ if (!remaining) return;
6585
+ remaining.child.kill("SIGKILL");
6586
+ await remaining.exitPromise;
6587
+ }
6353
6588
  async shutdownAll() {
6354
6589
  const running = Array.from(this.processes.values());
6355
6590
  if (running.length === 0) return;
@@ -6392,8 +6627,9 @@ function delay(ms) {
6392
6627
  // src/commands/dev/schema-watcher.ts
6393
6628
  var import_node_child_process11 = require("child_process");
6394
6629
  var import_node_fs5 = require("fs");
6395
- var import_node_path21 = require("path");
6630
+ var import_node_path22 = require("path");
6396
6631
  init_fs_helpers();
6632
+ init_schema_loader();
6397
6633
  var SchemaWatcher = class {
6398
6634
  constructor(config) {
6399
6635
  this.config = config;
@@ -6403,6 +6639,7 @@ var SchemaWatcher = class {
6403
6639
  debounceMs;
6404
6640
  watcher = null;
6405
6641
  debounceTimer = null;
6642
+ schemaVersion = null;
6406
6643
  start() {
6407
6644
  if (this.watcher) return;
6408
6645
  this.watcher = (0, import_node_fs5.watch)(this.config.schemaPath, () => {
@@ -6411,6 +6648,9 @@ var SchemaWatcher = class {
6411
6648
  this.watcher.on("error", (error) => {
6412
6649
  this.config.onError?.(toError(error));
6413
6650
  });
6651
+ void this.refreshSchemaVersion().catch((error) => {
6652
+ this.config.onError?.(toError(error));
6653
+ });
6414
6654
  }
6415
6655
  stop() {
6416
6656
  if (this.debounceTimer) {
@@ -6421,7 +6661,7 @@ var SchemaWatcher = class {
6421
6661
  this.watcher = null;
6422
6662
  }
6423
6663
  async regenerate() {
6424
- const koraBinJs = (0, import_node_path21.join)(
6664
+ const koraBinJs = (0, import_node_path22.join)(
6425
6665
  this.config.projectRoot,
6426
6666
  "node_modules",
6427
6667
  "@korajs",
@@ -6434,6 +6674,15 @@ var SchemaWatcher = class {
6434
6674
  const args = hasTsx ? ["--import", "tsx", koraBinJs, "generate", "types", "--schema", this.config.schemaPath] : [koraBinJs, "generate", "types", "--schema", this.config.schemaPath];
6435
6675
  await spawnCommand(command, args, this.config.projectRoot);
6436
6676
  this.config.onRegenerate?.();
6677
+ await this.refreshSchemaVersion();
6678
+ }
6679
+ async refreshSchemaVersion() {
6680
+ const schema = await loadSchemaDefinition(this.config.schemaPath, this.config.projectRoot);
6681
+ const previous = this.schemaVersion;
6682
+ this.schemaVersion = schema.version;
6683
+ if (previous !== null && previous !== schema.version) {
6684
+ this.config.onSchemaVersionChange?.({ previous, next: schema.version });
6685
+ }
6437
6686
  }
6438
6687
  scheduleRegeneration() {
6439
6688
  if (this.debounceTimer) {
@@ -6487,7 +6736,7 @@ function toError(error) {
6487
6736
  }
6488
6737
 
6489
6738
  // src/commands/dev/dev-command.ts
6490
- var devCommand = (0, import_citty5.defineCommand)({
6739
+ var devCommand = (0, import_citty6.defineCommand)({
6491
6740
  meta: {
6492
6741
  name: "dev",
6493
6742
  description: "Start the Kora development environment"
@@ -6518,6 +6767,7 @@ var devCommand = (0, import_citty5.defineCommand)({
6518
6767
  if (!projectRoot) {
6519
6768
  throw new InvalidProjectError(process.cwd());
6520
6769
  }
6770
+ loadProjectEnv(projectRoot);
6521
6771
  const config = await loadKoraConfig(projectRoot);
6522
6772
  const vitePort = typeof args.port === "string" ? args.port : String(config?.dev?.port ?? 5173);
6523
6773
  const syncPortFromConfig = typeof config?.dev?.sync === "object" && typeof config.dev.sync.port === "number" ? config.dev.sync.port : 3001;
@@ -6527,7 +6777,7 @@ var devCommand = (0, import_citty5.defineCommand)({
6527
6777
  const watchDebounceMs = typeof config?.dev?.watch === "object" && typeof config.dev.watch.debounceMs === "number" ? config.dev.watch.debounceMs : 300;
6528
6778
  const viteEntryPoint = await resolveProjectBinaryEntryPoint(projectRoot, "vite", "vite");
6529
6779
  if (!viteEntryPoint) {
6530
- throw new DevServerError("vite", (0, import_node_path22.join)(projectRoot, "node_modules", ".bin", "vite"));
6780
+ throw new DevServerError("vite", (0, import_node_path23.join)(projectRoot, "node_modules", ".bin", "vite"));
6531
6781
  }
6532
6782
  const syncServerFile = await findSyncServerFile(projectRoot);
6533
6783
  let managedSyncStore = normalizeManagedSyncStore(config, projectRoot);
@@ -6542,8 +6792,8 @@ var devCommand = (0, import_citty5.defineCommand)({
6542
6792
  }
6543
6793
  }
6544
6794
  if (shouldStartSync && syncServerFile === null && managedSyncStore) {
6545
- const hasServerPackage = await fileExists2(
6546
- (0, import_node_path22.join)(projectRoot, "node_modules", "@korajs", "server", "package.json")
6795
+ const hasServerPackage = await fileExists3(
6796
+ (0, import_node_path23.join)(projectRoot, "node_modules", "@korajs", "server", "package.json")
6547
6797
  );
6548
6798
  if (!hasServerPackage) {
6549
6799
  logger.warn(
@@ -6560,8 +6810,8 @@ var devCommand = (0, import_citty5.defineCommand)({
6560
6810
  }
6561
6811
  let configuredSchemaPath = null;
6562
6812
  if (typeof config?.schema === "string") {
6563
- const candidate = (0, import_node_path23.resolve)(projectRoot, config.schema);
6564
- if (await fileExists2(candidate)) {
6813
+ const candidate = (0, import_node_path24.resolve)(projectRoot, config.schema);
6814
+ if (await fileExists3(candidate)) {
6565
6815
  configuredSchemaPath = candidate;
6566
6816
  } else {
6567
6817
  logger.warn(
@@ -6619,7 +6869,9 @@ var devCommand = (0, import_citty5.defineCommand)({
6619
6869
  logger.step(" Schema watcher disabled (schema.ts not found)");
6620
6870
  }
6621
6871
  logger.blank();
6622
- logger.step(" Kora DevTools overlay: Ctrl+Shift+K (Cmd+Shift+K on macOS) when devtools: true");
6872
+ logger.step(
6873
+ " Kora DevTools overlay: Ctrl+Shift+K (Cmd+Shift+K on macOS) when devtools is enabled"
6874
+ );
6623
6875
  processManager.spawn({
6624
6876
  label: "vite",
6625
6877
  command: process.execPath,
@@ -6630,33 +6882,31 @@ var devCommand = (0, import_citty5.defineCommand)({
6630
6882
  },
6631
6883
  onExit: onManagedProcessExit
6632
6884
  });
6633
- if (shouldStartSync && hasTsx && syncServerFile) {
6634
- processManager.spawn({
6635
- label: "sync",
6636
- command: process.execPath,
6637
- args: ["--import", "tsx", syncServerFile],
6638
- cwd: projectRoot,
6639
- env: {
6640
- PORT: String(syncPort),
6641
- KORA_SYNC_PORT: String(syncPort)
6642
- },
6643
- onExit: onManagedProcessExit
6644
- });
6645
- }
6646
- if (shouldStartSync && syncServerFile === null && managedSyncStore !== null) {
6647
- processManager.spawn({
6648
- label: "sync",
6649
- command: process.execPath,
6650
- args: ["--input-type=module", "--eval", MANAGED_SYNC_BOOTSTRAP_SCRIPT],
6651
- cwd: projectRoot,
6652
- env: {
6653
- KORA_DEV_SYNC_CONFIG: JSON.stringify({
6654
- port: Number(syncPort),
6655
- store: managedSyncStore
6656
- })
6657
- },
6658
- onExit: onManagedProcessExit
6659
- });
6885
+ const syncProcessConfig = shouldStartSync && hasTsx && syncServerFile ? {
6886
+ label: "sync",
6887
+ command: process.execPath,
6888
+ args: ["--import", "tsx", syncServerFile],
6889
+ cwd: projectRoot,
6890
+ env: {
6891
+ PORT: String(syncPort),
6892
+ KORA_SYNC_PORT: String(syncPort)
6893
+ },
6894
+ onExit: onManagedProcessExit
6895
+ } : shouldStartSync && syncServerFile === null && managedSyncStore !== null ? {
6896
+ label: "sync",
6897
+ command: process.execPath,
6898
+ args: ["--input-type=module", "--eval", MANAGED_SYNC_BOOTSTRAP_SCRIPT],
6899
+ cwd: projectRoot,
6900
+ env: {
6901
+ KORA_DEV_SYNC_CONFIG: JSON.stringify({
6902
+ port: Number(syncPort),
6903
+ store: managedSyncStore
6904
+ })
6905
+ },
6906
+ onExit: onManagedProcessExit
6907
+ } : null;
6908
+ if (syncProcessConfig) {
6909
+ processManager.spawn(syncProcessConfig);
6660
6910
  }
6661
6911
  if (watchEnabled && schemaPath) {
6662
6912
  schemaWatcher = new SchemaWatcher({
@@ -6666,6 +6916,16 @@ var devCommand = (0, import_citty5.defineCommand)({
6666
6916
  onRegenerate: () => {
6667
6917
  logger.success("Regenerated types from schema changes");
6668
6918
  },
6919
+ onSchemaVersionChange: ({ previous, next }) => {
6920
+ if (!syncProcessConfig) {
6921
+ logger.warn(
6922
+ `Schema version changed from ${previous} to ${next}; restart your sync server before clients can sync.`
6923
+ );
6924
+ return;
6925
+ }
6926
+ logger.warn(`Schema version changed from ${previous} to ${next}; restarting sync server.`);
6927
+ void processManager.restart(syncProcessConfig);
6928
+ },
6669
6929
  onError: (error) => {
6670
6930
  logger.error(`Schema watcher error: ${error.message}`);
6671
6931
  }
@@ -6678,31 +6938,68 @@ var devCommand = (0, import_citty5.defineCommand)({
6678
6938
  }
6679
6939
  });
6680
6940
  async function findSyncServerFile(projectRoot) {
6681
- const candidates = [(0, import_node_path22.join)(projectRoot, "server.ts"), (0, import_node_path22.join)(projectRoot, "server.js")];
6941
+ const candidates = [(0, import_node_path23.join)(projectRoot, "server.ts"), (0, import_node_path23.join)(projectRoot, "server.js")];
6682
6942
  for (const candidate of candidates) {
6683
6943
  try {
6684
- await (0, import_promises14.access)(candidate);
6944
+ await (0, import_promises15.access)(candidate);
6685
6945
  return candidate;
6686
6946
  } catch {
6687
6947
  }
6688
6948
  }
6689
6949
  return null;
6690
6950
  }
6691
- async function fileExists2(path) {
6951
+ async function fileExists3(path) {
6692
6952
  try {
6693
- await (0, import_promises14.access)(path);
6953
+ await (0, import_promises15.access)(path);
6694
6954
  return true;
6695
6955
  } catch {
6696
6956
  return false;
6697
6957
  }
6698
6958
  }
6959
+ function loadProjectEnv(projectRoot, mode = "development") {
6960
+ const files = [".env", ".env.local", `.env.${mode}`, `.env.${mode}.local`];
6961
+ const existingKeys = new Set(Object.keys(process.env));
6962
+ for (const file of files) {
6963
+ const path = (0, import_node_path23.join)(projectRoot, file);
6964
+ if (!(0, import_node_fs6.existsSync)(path)) continue;
6965
+ const parsed = parseEnvFile((0, import_node_fs6.readFileSync)(path, "utf8"));
6966
+ for (const [key, value] of Object.entries(parsed)) {
6967
+ if (!existingKeys.has(key)) {
6968
+ process.env[key] = value;
6969
+ }
6970
+ }
6971
+ }
6972
+ }
6973
+ function parseEnvFile(contents) {
6974
+ const env = {};
6975
+ for (const rawLine of contents.split(/\r?\n/)) {
6976
+ const line = rawLine.trim();
6977
+ if (!line || line.startsWith("#")) continue;
6978
+ const normalized = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
6979
+ const equalsIndex = normalized.indexOf("=");
6980
+ if (equalsIndex <= 0) continue;
6981
+ const key = normalized.slice(0, equalsIndex).trim();
6982
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
6983
+ env[key] = parseEnvValue(normalized.slice(equalsIndex + 1).trim());
6984
+ }
6985
+ return env;
6986
+ }
6987
+ function parseEnvValue(value) {
6988
+ const quote = value[0];
6989
+ if ((quote === '"' || quote === "'") && value[value.length - 1] === quote) {
6990
+ const inner = value.slice(1, -1);
6991
+ return quote === '"' ? inner.replace(/\\n/g, "\n").replace(/\\r/g, "\r") : inner;
6992
+ }
6993
+ const commentIndex = value.search(/\s#/);
6994
+ return (commentIndex >= 0 ? value.slice(0, commentIndex) : value).trim();
6995
+ }
6699
6996
  function normalizeManagedSyncStore(config, projectRoot) {
6700
6997
  const sync = config?.dev?.sync;
6701
6998
  if (typeof sync !== "object" || sync === null) return null;
6702
6999
  const store = sync.store;
6703
7000
  if (store === void 0) return { type: "memory" };
6704
7001
  if (store === "memory") return { type: "memory" };
6705
- if (store === "sqlite") return { type: "sqlite", filename: (0, import_node_path22.join)(projectRoot, "kora-sync.db") };
7002
+ if (store === "sqlite") return { type: "sqlite", filename: (0, import_node_path23.join)(projectRoot, "kora-sync.db") };
6706
7003
  if (store === "postgres") {
6707
7004
  const connectionString = process.env.DATABASE_URL;
6708
7005
  if (!connectionString) return null;
@@ -6711,7 +7008,7 @@ function normalizeManagedSyncStore(config, projectRoot) {
6711
7008
  if (typeof store === "object" && store !== null) {
6712
7009
  if (store.type === "memory") return { type: "memory" };
6713
7010
  if (store.type === "sqlite") {
6714
- 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");
7011
+ const filename = typeof store.filename === "string" && store.filename.length > 0 ? (0, import_node_path24.resolve)(projectRoot, store.filename) : (0, import_node_path23.join)(projectRoot, "kora-sync.db");
6715
7012
  return { type: "sqlite", filename };
6716
7013
  }
6717
7014
  if (store.type === "postgres" && typeof store.connectionString === "string") {
@@ -6775,12 +7072,12 @@ await new Promise(() => {});
6775
7072
  `;
6776
7073
 
6777
7074
  // src/commands/doctor/doctor-command.ts
6778
- var import_citty6 = require("citty");
7075
+ var import_citty7 = require("citty");
6779
7076
  init_fs_helpers();
6780
7077
 
6781
7078
  // src/commands/doctor/doctor-checks.ts
6782
- var import_promises15 = require("fs/promises");
6783
- var import_node_path24 = require("path");
7079
+ var import_promises16 = require("fs/promises");
7080
+ var import_node_path25 = require("path");
6784
7081
  init_fs_helpers();
6785
7082
  init_schema_loader();
6786
7083
  var STATUS_ENDPOINT = "/__kora/status";
@@ -6860,9 +7157,9 @@ async function runDoctorChecks(options) {
6860
7157
  }
6861
7158
  async function resolveSchemaPath(projectRoot, configSchema) {
6862
7159
  if (typeof configSchema === "string") {
6863
- const resolved = (0, import_node_path24.resolve)(projectRoot, configSchema);
7160
+ const resolved = (0, import_node_path25.resolve)(projectRoot, configSchema);
6864
7161
  try {
6865
- await (0, import_promises15.access)(resolved);
7162
+ await (0, import_promises16.access)(resolved);
6866
7163
  return resolved;
6867
7164
  } catch {
6868
7165
  }
@@ -6871,12 +7168,12 @@ async function resolveSchemaPath(projectRoot, configSchema) {
6871
7168
  }
6872
7169
  async function checkWorkerFile(projectRoot) {
6873
7170
  const candidates = [
6874
- (0, import_node_path24.join)(projectRoot, "src", "kora-worker.ts"),
6875
- (0, import_node_path24.join)(projectRoot, "src", "kora-worker.js")
7171
+ (0, import_node_path25.join)(projectRoot, "src", "kora-worker.ts"),
7172
+ (0, import_node_path25.join)(projectRoot, "src", "kora-worker.js")
6876
7173
  ];
6877
7174
  for (const path of candidates) {
6878
7175
  try {
6879
- await (0, import_promises15.access)(path);
7176
+ await (0, import_promises16.access)(path);
6880
7177
  return {
6881
7178
  id: "worker",
6882
7179
  title: "SQLite WASM worker",
@@ -6896,7 +7193,7 @@ async function checkWorkerFile(projectRoot) {
6896
7193
  }
6897
7194
  async function checkDependencies(projectRoot) {
6898
7195
  try {
6899
- const raw = await (0, import_promises15.readFile)((0, import_node_path24.join)(projectRoot, "package.json"), "utf-8");
7196
+ const raw = await (0, import_promises16.readFile)((0, import_node_path25.join)(projectRoot, "package.json"), "utf-8");
6900
7197
  const pkg = JSON.parse(raw);
6901
7198
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
6902
7199
  const hasKora = Boolean(deps.korajs || deps.kora);
@@ -6945,9 +7242,9 @@ async function resolveSyncHttpUrl(projectRoot, config) {
6945
7242
  }
6946
7243
  async function readEnvSyncUrl(projectRoot) {
6947
7244
  for (const file of [".env", ".env.local"]) {
6948
- const path = (0, import_node_path24.join)(projectRoot, file);
7245
+ const path = (0, import_node_path25.join)(projectRoot, file);
6949
7246
  try {
6950
- const content = await (0, import_promises15.readFile)(path, "utf-8");
7247
+ const content = await (0, import_promises16.readFile)(path, "utf-8");
6951
7248
  for (const line of content.split("\n")) {
6952
7249
  const trimmed = line.trim();
6953
7250
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -7038,7 +7335,7 @@ function doctorHasFailures(results) {
7038
7335
  }
7039
7336
 
7040
7337
  // src/commands/doctor/doctor-command.ts
7041
- var doctorCommand = (0, import_citty6.defineCommand)({
7338
+ var doctorCommand = (0, import_citty7.defineCommand)({
7042
7339
  meta: {
7043
7340
  name: "doctor",
7044
7341
  description: "Check Kora project setup (schema, worker, sync server, versions)"
@@ -7093,9 +7390,9 @@ var doctorCommand = (0, import_citty6.defineCommand)({
7093
7390
  });
7094
7391
 
7095
7392
  // src/commands/generate/generate-command.ts
7096
- var import_promises16 = require("fs/promises");
7097
- var import_node_path25 = require("path");
7098
- var import_citty7 = require("citty");
7393
+ var import_promises17 = require("fs/promises");
7394
+ var import_node_path26 = require("path");
7395
+ var import_citty8 = require("citty");
7099
7396
  init_fs_helpers();
7100
7397
  init_schema_loader();
7101
7398
 
@@ -7250,13 +7547,13 @@ function toPascalCase2(name) {
7250
7547
  }
7251
7548
 
7252
7549
  // src/commands/generate/generate-command.ts
7253
- var generateCommand = (0, import_citty7.defineCommand)({
7550
+ var generateCommand = (0, import_citty8.defineCommand)({
7254
7551
  meta: {
7255
7552
  name: "generate",
7256
7553
  description: "Generate code from your Kora schema"
7257
7554
  },
7258
7555
  subCommands: {
7259
- types: (0, import_citty7.defineCommand)({
7556
+ types: (0, import_citty8.defineCommand)({
7260
7557
  meta: {
7261
7558
  name: "types",
7262
7559
  description: "Generate TypeScript types from your schema"
@@ -7280,7 +7577,7 @@ var generateCommand = (0, import_citty7.defineCommand)({
7280
7577
  }
7281
7578
  let schemaPath;
7282
7579
  if (args.schema && typeof args.schema === "string") {
7283
- schemaPath = (0, import_node_path25.resolve)(args.schema);
7580
+ schemaPath = (0, import_node_path26.resolve)(args.schema);
7284
7581
  } else {
7285
7582
  const found = await findSchemaFile(projectRoot);
7286
7583
  if (!found) {
@@ -7303,13 +7600,13 @@ var generateCommand = (0, import_citty7.defineCommand)({
7303
7600
  }
7304
7601
  const output = generateTypes(schema);
7305
7602
  const outputFile = typeof args.output === "string" ? args.output : "kora/generated/types.ts";
7306
- const outputPath = (0, import_node_path25.resolve)(projectRoot, outputFile);
7307
- await (0, import_promises16.mkdir)((0, import_node_path25.dirname)(outputPath), { recursive: true });
7308
- await (0, import_promises16.writeFile)(outputPath, output, "utf-8");
7603
+ const outputPath = (0, import_node_path26.resolve)(projectRoot, outputFile);
7604
+ await (0, import_promises17.mkdir)((0, import_node_path26.dirname)(outputPath), { recursive: true });
7605
+ await (0, import_promises17.writeFile)(outputPath, output, "utf-8");
7309
7606
  logger.success(`Generated types at ${outputPath}`);
7310
7607
  }
7311
7608
  }),
7312
- hooks: (0, import_citty7.defineCommand)({
7609
+ hooks: (0, import_citty8.defineCommand)({
7313
7610
  meta: {
7314
7611
  name: "hooks",
7315
7612
  description: "Generate per-collection React hook stubs"
@@ -7338,7 +7635,7 @@ var generateCommand = (0, import_citty7.defineCommand)({
7338
7635
  }
7339
7636
  let schemaPath;
7340
7637
  if (args.schema && typeof args.schema === "string") {
7341
- schemaPath = (0, import_node_path25.resolve)(projectRoot, args.schema);
7638
+ schemaPath = (0, import_node_path26.resolve)(projectRoot, args.schema);
7342
7639
  } else {
7343
7640
  const found = await findSchemaFile(projectRoot);
7344
7641
  if (!found) {
@@ -7352,13 +7649,13 @@ var generateCommand = (0, import_citty7.defineCommand)({
7352
7649
  schemaPath = found;
7353
7650
  }
7354
7651
  const schema = await loadSchemaDefinition(schemaPath, projectRoot);
7355
- const outputDir = typeof args.output === "string" ? (0, import_node_path25.resolve)(projectRoot, args.output) : (0, import_node_path25.resolve)(projectRoot, "kora/generated/hooks");
7652
+ const outputDir = typeof args.output === "string" ? (0, import_node_path26.resolve)(projectRoot, args.output) : (0, import_node_path26.resolve)(projectRoot, "kora/generated/hooks");
7356
7653
  const files = generateCollectionHooks(schema);
7357
7654
  for (const [name, content] of files) {
7358
7655
  const adjusted = typeof args.types === "string" && args.types !== "../types" ? content.replace("from '../types'", `from '${args.types}'`) : content;
7359
- const filePath = (0, import_node_path25.resolve)(outputDir, name);
7360
- await (0, import_promises16.mkdir)((0, import_node_path25.dirname)(filePath), { recursive: true });
7361
- await (0, import_promises16.writeFile)(filePath, adjusted, "utf-8");
7656
+ const filePath = (0, import_node_path26.resolve)(outputDir, name);
7657
+ await (0, import_promises17.mkdir)((0, import_node_path26.dirname)(filePath), { recursive: true });
7658
+ await (0, import_promises17.writeFile)(filePath, adjusted, "utf-8");
7362
7659
  }
7363
7660
  logger.success(`Generated ${String(files.size)} hook file(s) at ${outputDir}`);
7364
7661
  logger.step("Run `kora generate types` first if ../types is missing.");
@@ -7380,10 +7677,10 @@ function isSchemaDefinition2(value) {
7380
7677
  }
7381
7678
 
7382
7679
  // src/commands/logs/logs-command.ts
7383
- var import_citty8 = require("citty");
7680
+ var import_citty9 = require("citty");
7384
7681
  var DEFAULT_SYNC_PORT2 = 3001;
7385
7682
  var EVENTS_ENDPOINT = "/__kora/events";
7386
- var logsCommand = (0, import_citty8.defineCommand)({
7683
+ var logsCommand = (0, import_citty9.defineCommand)({
7387
7684
  meta: {
7388
7685
  name: "logs",
7389
7686
  description: "Stream real-time events from a Kora sync server"
@@ -7487,9 +7784,9 @@ var logsCommand = (0, import_citty8.defineCommand)({
7487
7784
  });
7488
7785
 
7489
7786
  // src/commands/migrate/migrate-command.ts
7490
- var import_promises17 = require("fs/promises");
7491
- var import_node_path26 = require("path");
7492
- var import_citty9 = require("citty");
7787
+ var import_promises18 = require("fs/promises");
7788
+ var import_node_path27 = require("path");
7789
+ var import_citty10 = require("citty");
7493
7790
  init_fs_helpers();
7494
7791
 
7495
7792
  // src/commands/migrate/migration-generator.ts
@@ -7833,6 +8130,14 @@ function mapFieldType(descriptor) {
7833
8130
  return "INTEGER";
7834
8131
  case "array":
7835
8132
  return "TEXT";
8133
+ case "object":
8134
+ return "TEXT";
8135
+ case "json":
8136
+ return "TEXT";
8137
+ case "blob":
8138
+ return "TEXT";
8139
+ case "secret":
8140
+ return "TEXT";
7836
8141
  case "richtext":
7837
8142
  return "BLOB";
7838
8143
  }
@@ -7848,7 +8153,7 @@ function quoteIdentifier(identifier) {
7848
8153
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier)) {
7849
8154
  throw new Error(`Invalid SQL identifier: ${identifier}`);
7850
8155
  }
7851
- return identifier;
8156
+ return `"${identifier}"`;
7852
8157
  }
7853
8158
  function formatChange(change) {
7854
8159
  switch (change.type) {
@@ -8084,7 +8389,7 @@ function generateOperationTransformModule(fromVersion, toVersion) {
8084
8389
  init_schema_loader();
8085
8390
  var SNAPSHOT_PATH = "kora/schema.snapshot.json";
8086
8391
  var MIGRATIONS_DIR = "kora/migrations";
8087
- var migrateCommand = (0, import_citty9.defineCommand)({
8392
+ var migrateCommand = (0, import_citty10.defineCommand)({
8088
8393
  meta: {
8089
8394
  name: "migrate",
8090
8395
  description: "Detect schema changes and generate/apply migrations"
@@ -8126,12 +8431,12 @@ var migrateCommand = (0, import_citty9.defineCommand)({
8126
8431
  throw new InvalidProjectError(process.cwd());
8127
8432
  }
8128
8433
  const config = await loadKoraConfig(projectRoot);
8129
- 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);
8434
+ const resolvedSchemaPath = typeof args.schema === "string" ? (0, import_node_path27.resolve)(projectRoot, args.schema) : typeof config?.schema === "string" ? (0, import_node_path27.resolve)(projectRoot, config.schema) : await findSchemaFile(projectRoot);
8130
8435
  if (!resolvedSchemaPath) {
8131
8436
  throw new SchemaNotFoundError(["src/schema.ts", "schema.ts", "src/schema.js", "schema.js"]);
8132
8437
  }
8133
8438
  const currentSchema = await loadSchemaDefinition(resolvedSchemaPath, projectRoot);
8134
- const snapshotFile = (0, import_node_path26.join)(projectRoot, SNAPSHOT_PATH);
8439
+ const snapshotFile = (0, import_node_path27.join)(projectRoot, SNAPSHOT_PATH);
8135
8440
  const previousSchema = await readSchemaSnapshot(snapshotFile);
8136
8441
  if (!previousSchema) {
8137
8442
  if (args["dry-run"] === true) {
@@ -8144,7 +8449,7 @@ var migrateCommand = (0, import_citty9.defineCommand)({
8144
8449
  return;
8145
8450
  }
8146
8451
  const diff = diffSchemas(previousSchema, currentSchema);
8147
- const outputDir = typeof args["output-dir"] === "string" ? (0, import_node_path26.resolve)(projectRoot, args["output-dir"]) : (0, import_node_path26.resolve)(projectRoot, MIGRATIONS_DIR);
8452
+ const outputDir = typeof args["output-dir"] === "string" ? (0, import_node_path27.resolve)(projectRoot, args["output-dir"]) : (0, import_node_path27.resolve)(projectRoot, MIGRATIONS_DIR);
8148
8453
  if (!diff.hasChanges) {
8149
8454
  logger.success("No schema changes detected.");
8150
8455
  if (args.apply === true) {
@@ -8196,7 +8501,7 @@ var migrateCommand = (0, import_citty9.defineCommand)({
8196
8501
  logger.warn("Dry run enabled: no files written, no migrations applied.");
8197
8502
  return;
8198
8503
  }
8199
- await (0, import_promises17.mkdir)(outputDir, { recursive: true });
8504
+ await (0, import_promises18.mkdir)(outputDir, { recursive: true });
8200
8505
  const migrationPath = await writeMigrationFile(
8201
8506
  outputDir,
8202
8507
  diff.fromVersion,
@@ -8232,7 +8537,7 @@ var migrateCommand = (0, import_citty9.defineCommand)({
8232
8537
  });
8233
8538
  async function readSchemaSnapshot(path) {
8234
8539
  try {
8235
- const content = await (0, import_promises17.readFile)(path, "utf-8");
8540
+ const content = await (0, import_promises18.readFile)(path, "utf-8");
8236
8541
  return JSON.parse(content);
8237
8542
  } catch {
8238
8543
  return null;
@@ -8253,15 +8558,15 @@ function isInteractiveTerminal2() {
8253
8558
  return process.stdin.isTTY === true && process.stdout.isTTY === true;
8254
8559
  }
8255
8560
  async function writeSchemaSnapshot(path, schema) {
8256
- await (0, import_promises17.mkdir)((0, import_node_path26.dirname)(path), { recursive: true });
8257
- await (0, import_promises17.writeFile)(path, `${JSON.stringify(schema, null, 2)}
8561
+ await (0, import_promises18.mkdir)((0, import_node_path27.dirname)(path), { recursive: true });
8562
+ await (0, import_promises18.writeFile)(path, `${JSON.stringify(schema, null, 2)}
8258
8563
  `, "utf-8");
8259
8564
  }
8260
8565
  async function writeMigrationFile(outputDir, fromVersion, toVersion, generated) {
8261
- const existing = await (0, import_promises17.readdir)(outputDir).catch(() => []);
8566
+ const existing = await (0, import_promises18.readdir)(outputDir).catch(() => []);
8262
8567
  const sequence = existing.filter((file) => /^\d{3}-/.test(file)).length + 1;
8263
8568
  const filename = `${String(sequence).padStart(3, "0")}-v${fromVersion}-to-v${toVersion}.ts`;
8264
- const path = (0, import_node_path26.join)(outputDir, filename);
8569
+ const path = (0, import_node_path27.join)(outputDir, filename);
8265
8570
  const migrationId = filename.replace(/\.ts$/, "");
8266
8571
  const fileContent = [
8267
8572
  `export const up = ${JSON.stringify(generated.up, null, 2)} as const`,
@@ -8273,10 +8578,10 @@ async function writeMigrationFile(outputDir, fromVersion, toVersion, generated)
8273
8578
  `export const containsBreakingChanges = ${generated.containsBreakingChanges}`,
8274
8579
  ""
8275
8580
  ].join("\n");
8276
- await (0, import_promises17.writeFile)(path, fileContent, "utf-8");
8277
- const transformsPath = (0, import_node_path26.join)(outputDir, `${migrationId}.transforms.ts`);
8278
- await (0, import_promises17.writeFile)(transformsPath, generateOperationTransformModule(fromVersion, toVersion), "utf-8");
8279
- await writeMigrationManifest((0, import_node_path26.join)(outputDir, `${migrationId}.json`), {
8581
+ await (0, import_promises18.writeFile)(path, fileContent, "utf-8");
8582
+ const transformsPath = (0, import_node_path27.join)(outputDir, `${migrationId}.transforms.ts`);
8583
+ await (0, import_promises18.writeFile)(transformsPath, generateOperationTransformModule(fromVersion, toVersion), "utf-8");
8584
+ await writeMigrationManifest((0, import_node_path27.join)(outputDir, `${migrationId}.json`), {
8280
8585
  id: migrationId,
8281
8586
  fromVersion,
8282
8587
  toVersion,
@@ -8288,22 +8593,22 @@ async function writeMigrationFile(outputDir, fromVersion, toVersion, generated)
8288
8593
  return path;
8289
8594
  }
8290
8595
  async function writeMigrationManifest(path, manifest) {
8291
- await (0, import_promises17.writeFile)(path, `${JSON.stringify(manifest, null, 2)}
8596
+ await (0, import_promises18.writeFile)(path, `${JSON.stringify(manifest, null, 2)}
8292
8597
  `, "utf-8");
8293
8598
  }
8294
8599
  async function listMigrationManifests(outputDir) {
8295
- const files = await (0, import_promises17.readdir)(outputDir).catch(() => []);
8600
+ const files = await (0, import_promises18.readdir)(outputDir).catch(() => []);
8296
8601
  const migrationFiles = files.filter((file) => /^\d{3}-.*\.ts$/.test(file)).sort((left, right) => left.localeCompare(right));
8297
8602
  const manifests = [];
8298
8603
  for (const file of migrationFiles) {
8299
8604
  const id = file.replace(/\.ts$/, "");
8300
- const manifestPath = (0, import_node_path26.join)(outputDir, `${id}.json`);
8605
+ const manifestPath = (0, import_node_path27.join)(outputDir, `${id}.json`);
8301
8606
  const jsonManifest = await readMigrationManifest(manifestPath);
8302
8607
  if (jsonManifest) {
8303
8608
  manifests.push({ ...jsonManifest, id });
8304
8609
  continue;
8305
8610
  }
8306
- const migrationPath = (0, import_node_path26.join)(outputDir, file);
8611
+ const migrationPath = (0, import_node_path27.join)(outputDir, file);
8307
8612
  const sourceManifest = await readMigrationManifestFromSource(migrationPath, id);
8308
8613
  manifests.push(sourceManifest);
8309
8614
  }
@@ -8311,7 +8616,7 @@ async function listMigrationManifests(outputDir) {
8311
8616
  }
8312
8617
  async function readMigrationManifest(path) {
8313
8618
  try {
8314
- const content = await (0, import_promises17.readFile)(path, "utf-8");
8619
+ const content = await (0, import_promises18.readFile)(path, "utf-8");
8315
8620
  return JSON.parse(content);
8316
8621
  } catch (error) {
8317
8622
  const code = error.code;
@@ -8322,7 +8627,7 @@ async function readMigrationManifest(path) {
8322
8627
  }
8323
8628
  }
8324
8629
  async function readMigrationManifestFromSource(path, id) {
8325
- const content = await (0, import_promises17.readFile)(path, "utf-8");
8630
+ const content = await (0, import_promises18.readFile)(path, "utf-8");
8326
8631
  const versions = parseVersionsFromMigrationId(id);
8327
8632
  return {
8328
8633
  id,
@@ -8384,18 +8689,18 @@ function parseLiteralExport(source, exportName) {
8384
8689
  }
8385
8690
  function resolveSqliteApplyPath(dbArg, projectRoot, config) {
8386
8691
  if (typeof dbArg === "string") {
8387
- return (0, import_node_path26.resolve)(projectRoot, dbArg);
8692
+ return (0, import_node_path27.resolve)(projectRoot, dbArg);
8388
8693
  }
8389
8694
  const sync = config?.dev?.sync;
8390
8695
  if (typeof sync === "object" && sync !== null) {
8391
8696
  if (sync.store === "sqlite") {
8392
- return (0, import_node_path26.join)(projectRoot, "kora-sync.db");
8697
+ return (0, import_node_path27.join)(projectRoot, "kora-sync.db");
8393
8698
  }
8394
8699
  if (typeof sync.store === "object" && sync.store !== null && sync.store.type === "sqlite") {
8395
8700
  if (typeof sync.store.filename === "string" && sync.store.filename.length > 0) {
8396
- return (0, import_node_path26.resolve)(projectRoot, sync.store.filename);
8701
+ return (0, import_node_path27.resolve)(projectRoot, sync.store.filename);
8397
8702
  }
8398
- return (0, import_node_path26.join)(projectRoot, "kora-sync.db");
8703
+ return (0, import_node_path27.join)(projectRoot, "kora-sync.db");
8399
8704
  }
8400
8705
  }
8401
8706
  return void 0;
@@ -8413,7 +8718,7 @@ function resolvePostgresConnectionString(config) {
8413
8718
  }
8414
8719
 
8415
8720
  // src/commands/status/status-command.ts
8416
- var import_citty10 = require("citty");
8721
+ var import_citty11 = require("citty");
8417
8722
  var DEFAULT_SYNC_PORT3 = 3001;
8418
8723
  var STATUS_ENDPOINT2 = "/__kora/status";
8419
8724
  async function fetchServerStatus(url, token) {
@@ -8440,7 +8745,7 @@ function formatUptime(ms) {
8440
8745
  parts.push(`${seconds % 60}s`);
8441
8746
  return parts.join(" ");
8442
8747
  }
8443
- var statusCommand = (0, import_citty10.defineCommand)({
8748
+ var statusCommand = (0, import_citty11.defineCommand)({
8444
8749
  meta: {
8445
8750
  name: "status",
8446
8751
  description: "Show Kora sync server status"
@@ -8545,10 +8850,10 @@ function printStatus(status, url, logger) {
8545
8850
  }
8546
8851
 
8547
8852
  // src/commands/studio/studio-command.ts
8548
- var import_node_fs8 = require("fs");
8549
- var import_node_path29 = require("path");
8550
- var import_citty11 = require("citty");
8551
- var studioCommand = (0, import_citty11.defineCommand)({
8853
+ var import_node_fs9 = require("fs");
8854
+ var import_node_path30 = require("path");
8855
+ var import_citty12 = require("citty");
8856
+ var studioCommand = (0, import_citty12.defineCommand)({
8552
8857
  meta: {
8553
8858
  name: "studio",
8554
8859
  description: "Kora Studio: visualize records, operations, and sync \u2014 or run the sync lab"
@@ -8597,7 +8902,7 @@ var studioCommand = (0, import_citty11.defineCommand)({
8597
8902
  );
8598
8903
  }
8599
8904
  const { loadSchemaDefinition: loadSchemaDefinition2 } = await Promise.resolve().then(() => (init_schema_loader(), schema_loader_exports));
8600
- const schema = await loadSchemaDefinition2((0, import_node_path29.resolve)(process.cwd(), args.schema), process.cwd());
8905
+ const schema = await loadSchemaDefinition2((0, import_node_path30.resolve)(process.cwd(), args.schema), process.cwd());
8601
8906
  const { SpectatorManager: SpectatorManager2 } = await Promise.resolve().then(() => (init_spectator_manager(), spectator_manager_exports));
8602
8907
  const spectator = new SpectatorManager2({
8603
8908
  url: args.connect,
@@ -8624,7 +8929,7 @@ var studioCommand = (0, import_citty11.defineCommand)({
8624
8929
  let schema = defaultLabSchema2();
8625
8930
  if (typeof args.schema === "string") {
8626
8931
  const { loadSchemaDefinition: loadSchemaDefinition2 } = await Promise.resolve().then(() => (init_schema_loader(), schema_loader_exports));
8627
- schema = await loadSchemaDefinition2((0, import_node_path29.resolve)(process.cwd(), args.schema), process.cwd());
8932
+ schema = await loadSchemaDefinition2((0, import_node_path30.resolve)(process.cwd(), args.schema), process.cwd());
8628
8933
  }
8629
8934
  const initialDevices = args.devices !== void 0 ? Math.max(1, Number.parseInt(args.devices, 10) || 2) : 2;
8630
8935
  const lab = new LabManager2(schema);
@@ -8649,8 +8954,8 @@ var studioCommand = (0, import_citty11.defineCommand)({
8649
8954
  "Pass --db <path> to inspect a database, --lab for the sync laboratory, or --connect <wss url> for live spectator mode."
8650
8955
  );
8651
8956
  }
8652
- const dbPath = (0, import_node_path29.resolve)(process.cwd(), args.db);
8653
- if (!(0, import_node_fs8.existsSync)(dbPath)) {
8957
+ const dbPath = (0, import_node_path30.resolve)(process.cwd(), args.db);
8958
+ if (!(0, import_node_fs9.existsSync)(dbPath)) {
8654
8959
  throw new Error(`Database file not found: ${dbPath}`);
8655
8960
  }
8656
8961
  const server = await startStudioServer2({ port, dbPath });
@@ -8668,12 +8973,13 @@ var studioCommand = (0, import_citty11.defineCommand)({
8668
8973
  });
8669
8974
 
8670
8975
  // src/bin.ts
8671
- var main = (0, import_citty12.defineCommand)({
8976
+ var main = (0, import_citty13.defineCommand)({
8672
8977
  meta: {
8673
8978
  name: "kora",
8674
- description: "Kora.js \u2014 Offline-first application framework"
8979
+ description: "Kora.js, the offline-first application framework"
8675
8980
  },
8676
8981
  subCommands: {
8982
+ "agents-md": agentsCommand,
8677
8983
  backup: backupCommand,
8678
8984
  compact: compactCommand,
8679
8985
  create: createCommand,
@@ -8687,5 +8993,5 @@ var main = (0, import_citty12.defineCommand)({
8687
8993
  studio: studioCommand
8688
8994
  }
8689
8995
  });
8690
- (0, import_citty12.runMain)(main);
8996
+ (0, import_citty13.runMain)(main);
8691
8997
  //# sourceMappingURL=bin.cjs.map