@korajs/cli 0.0.0-canary-20261008174129

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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/dist/bin.cjs +9465 -0
  4. package/dist/bin.cjs.map +1 -0
  5. package/dist/bin.js +3150 -0
  6. package/dist/bin.js.map +1 -0
  7. package/dist/chunk-5NI2FEQL.js +92 -0
  8. package/dist/chunk-5NI2FEQL.js.map +1 -0
  9. package/dist/chunk-6C4BHSRA.js +82 -0
  10. package/dist/chunk-6C4BHSRA.js.map +1 -0
  11. package/dist/chunk-7Y4J2GXP.js +796 -0
  12. package/dist/chunk-7Y4J2GXP.js.map +1 -0
  13. package/dist/chunk-BURFQVMV.js +719 -0
  14. package/dist/chunk-BURFQVMV.js.map +1 -0
  15. package/dist/chunk-TVIRMDER.js +2569 -0
  16. package/dist/chunk-TVIRMDER.js.map +1 -0
  17. package/dist/create.cjs +1368 -0
  18. package/dist/create.cjs.map +1 -0
  19. package/dist/create.js +11 -0
  20. package/dist/create.js.map +1 -0
  21. package/dist/index.cjs +3489 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.cts +772 -0
  24. package/dist/index.d.ts +772 -0
  25. package/dist/index.js +129 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lab-manager-KUDII6BT.js +280 -0
  28. package/dist/lab-manager-KUDII6BT.js.map +1 -0
  29. package/dist/schema-loader-GGHECMTM.js +9 -0
  30. package/dist/schema-loader-GGHECMTM.js.map +1 -0
  31. package/dist/spectator-manager-FTH2NFSE.js +145 -0
  32. package/dist/spectator-manager-FTH2NFSE.js.map +1 -0
  33. package/dist/studio-server-XLGZNGG5.js +1747 -0
  34. package/dist/studio-server-XLGZNGG5.js.map +1 -0
  35. package/dist/vite.cjs +351 -0
  36. package/dist/vite.cjs.map +1 -0
  37. package/dist/vite.d.cts +161 -0
  38. package/dist/vite.d.ts +161 -0
  39. package/dist/vite.js +321 -0
  40. package/dist/vite.js.map +1 -0
  41. package/package.json +79 -0
  42. package/templates/react-basic/.env.example +3 -0
  43. package/templates/react-basic/AGENTS.md +87 -0
  44. package/templates/react-basic/README.md.hbs +62 -0
  45. package/templates/react-basic/index.html.hbs +12 -0
  46. package/templates/react-basic/kora.config.ts +13 -0
  47. package/templates/react-basic/package.json.hbs +35 -0
  48. package/templates/react-basic/src/App.tsx +132 -0
  49. package/templates/react-basic/src/index.css +287 -0
  50. package/templates/react-basic/src/kora-worker.ts +9 -0
  51. package/templates/react-basic/src/main.tsx +24 -0
  52. package/templates/react-basic/src/modules/todos/todo.mutations.ts +25 -0
  53. package/templates/react-basic/src/modules/todos/todo.queries.ts +5 -0
  54. package/templates/react-basic/src/modules/todos/todo.schema.ts +10 -0
  55. package/templates/react-basic/src/modules/todos/useTodos.ts +31 -0
  56. package/templates/react-basic/src/schema.ts +9 -0
  57. package/templates/react-basic/src/vite-env.d.ts +18 -0
  58. package/templates/react-basic/tsconfig.json.hbs +14 -0
  59. package/templates/react-basic/vite.config.ts +88 -0
  60. package/templates/react-sync/.env.example +25 -0
  61. package/templates/react-sync/AGENTS.md +87 -0
  62. package/templates/react-sync/README.md.hbs +106 -0
  63. package/templates/react-sync/index.html.hbs +12 -0
  64. package/templates/react-sync/kora.config.ts +17 -0
  65. package/templates/react-sync/package.json.hbs +44 -0
  66. package/templates/react-sync/server.ts +141 -0
  67. package/templates/react-sync/src/App.tsx +160 -0
  68. package/templates/react-sync/src/auth.ts +25 -0
  69. package/templates/react-sync/src/index.css +350 -0
  70. package/templates/react-sync/src/kora-worker.ts +9 -0
  71. package/templates/react-sync/src/main.tsx +52 -0
  72. package/templates/react-sync/src/modules/todos/todo.mutations.ts +25 -0
  73. package/templates/react-sync/src/modules/todos/todo.queries.ts +5 -0
  74. package/templates/react-sync/src/modules/todos/todo.schema.ts +10 -0
  75. package/templates/react-sync/src/modules/todos/useTodos.ts +31 -0
  76. package/templates/react-sync/src/schema.ts +9 -0
  77. package/templates/react-sync/src/vite-env.d.ts +19 -0
  78. package/templates/react-sync/tsconfig.json.hbs +14 -0
  79. package/templates/react-sync/vite.config.ts +105 -0
  80. package/templates/react-tailwind/.env.example +3 -0
  81. package/templates/react-tailwind/AGENTS.md +87 -0
  82. package/templates/react-tailwind/README.md.hbs +62 -0
  83. package/templates/react-tailwind/index.html.hbs +12 -0
  84. package/templates/react-tailwind/kora.config.ts +13 -0
  85. package/templates/react-tailwind/package.json.hbs +38 -0
  86. package/templates/react-tailwind/src/App.tsx +201 -0
  87. package/templates/react-tailwind/src/index.css +7 -0
  88. package/templates/react-tailwind/src/kora-worker.ts +9 -0
  89. package/templates/react-tailwind/src/main.tsx +31 -0
  90. package/templates/react-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  91. package/templates/react-tailwind/src/modules/todos/todo.queries.ts +5 -0
  92. package/templates/react-tailwind/src/modules/todos/todo.schema.ts +10 -0
  93. package/templates/react-tailwind/src/modules/todos/useTodos.ts +31 -0
  94. package/templates/react-tailwind/src/schema.ts +9 -0
  95. package/templates/react-tailwind/src/vite-env.d.ts +18 -0
  96. package/templates/react-tailwind/tsconfig.json.hbs +14 -0
  97. package/templates/react-tailwind/vite.config.ts +95 -0
  98. package/templates/react-tailwind-sync/.env.example +25 -0
  99. package/templates/react-tailwind-sync/AGENTS.md +89 -0
  100. package/templates/react-tailwind-sync/README.md.hbs +106 -0
  101. package/templates/react-tailwind-sync/index.html.hbs +12 -0
  102. package/templates/react-tailwind-sync/kora.config.ts +17 -0
  103. package/templates/react-tailwind-sync/package.json.hbs +47 -0
  104. package/templates/react-tailwind-sync/server.ts +141 -0
  105. package/templates/react-tailwind-sync/src/App.tsx +293 -0
  106. package/templates/react-tailwind-sync/src/auth.ts +25 -0
  107. package/templates/react-tailwind-sync/src/index.css +7 -0
  108. package/templates/react-tailwind-sync/src/kora-worker.ts +9 -0
  109. package/templates/react-tailwind-sync/src/kora.ts +32 -0
  110. package/templates/react-tailwind-sync/src/main.tsx +44 -0
  111. package/templates/react-tailwind-sync/src/modules/todos/todo.mutations.ts +21 -0
  112. package/templates/react-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  113. package/templates/react-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  114. package/templates/react-tailwind-sync/src/modules/todos/useTodos.ts +31 -0
  115. package/templates/react-tailwind-sync/src/schema.ts +9 -0
  116. package/templates/react-tailwind-sync/src/vite-env.d.ts +19 -0
  117. package/templates/react-tailwind-sync/tsconfig.json.hbs +14 -0
  118. package/templates/react-tailwind-sync/vite.config.ts +112 -0
  119. package/templates/svelte-basic/.env.example +3 -0
  120. package/templates/svelte-basic/AGENTS.md +76 -0
  121. package/templates/svelte-basic/README.md.hbs +62 -0
  122. package/templates/svelte-basic/index.html.hbs +12 -0
  123. package/templates/svelte-basic/kora.config.ts +12 -0
  124. package/templates/svelte-basic/package.json.hbs +33 -0
  125. package/templates/svelte-basic/src/App.svelte +128 -0
  126. package/templates/svelte-basic/src/Root.svelte +14 -0
  127. package/templates/svelte-basic/src/index.css +287 -0
  128. package/templates/svelte-basic/src/kora-worker.ts +9 -0
  129. package/templates/svelte-basic/src/main.ts +16 -0
  130. package/templates/svelte-basic/src/modules/todos/todo.mutations.ts +25 -0
  131. package/templates/svelte-basic/src/modules/todos/todo.queries.ts +5 -0
  132. package/templates/svelte-basic/src/modules/todos/todo.schema.ts +10 -0
  133. package/templates/svelte-basic/src/modules/todos/useTodos.ts +25 -0
  134. package/templates/svelte-basic/src/schema.ts +9 -0
  135. package/templates/svelte-basic/src/vite-env.d.ts +12 -0
  136. package/templates/svelte-basic/svelte.config.js +5 -0
  137. package/templates/svelte-basic/tsconfig.json.hbs +14 -0
  138. package/templates/svelte-basic/vite.config.ts +57 -0
  139. package/templates/svelte-sync/.env.example +25 -0
  140. package/templates/svelte-sync/AGENTS.md +76 -0
  141. package/templates/svelte-sync/README.md.hbs +106 -0
  142. package/templates/svelte-sync/index.html.hbs +12 -0
  143. package/templates/svelte-sync/kora.config.ts +17 -0
  144. package/templates/svelte-sync/package.json.hbs +43 -0
  145. package/templates/svelte-sync/server.ts +141 -0
  146. package/templates/svelte-sync/src/App.svelte +151 -0
  147. package/templates/svelte-sync/src/Root.svelte +22 -0
  148. package/templates/svelte-sync/src/auth.ts +25 -0
  149. package/templates/svelte-sync/src/index.css +339 -0
  150. package/templates/svelte-sync/src/kora-worker.ts +9 -0
  151. package/templates/svelte-sync/src/main.ts +32 -0
  152. package/templates/svelte-sync/src/modules/todos/todo.mutations.ts +25 -0
  153. package/templates/svelte-sync/src/modules/todos/todo.queries.ts +5 -0
  154. package/templates/svelte-sync/src/modules/todos/todo.schema.ts +10 -0
  155. package/templates/svelte-sync/src/modules/todos/useTodos.ts +25 -0
  156. package/templates/svelte-sync/src/schema.ts +9 -0
  157. package/templates/svelte-sync/src/vite-env.d.ts +22 -0
  158. package/templates/svelte-sync/svelte.config.js +5 -0
  159. package/templates/svelte-sync/tsconfig.json.hbs +14 -0
  160. package/templates/svelte-sync/vite.config.ts +91 -0
  161. package/templates/svelte-tailwind/AGENTS.md +76 -0
  162. package/templates/svelte-tailwind/README.md.hbs +62 -0
  163. package/templates/svelte-tailwind/index.html.hbs +12 -0
  164. package/templates/svelte-tailwind/kora.config.ts +12 -0
  165. package/templates/svelte-tailwind/package.json.hbs +36 -0
  166. package/templates/svelte-tailwind/src/App.svelte +76 -0
  167. package/templates/svelte-tailwind/src/Root.svelte +14 -0
  168. package/templates/svelte-tailwind/src/index.css +7 -0
  169. package/templates/svelte-tailwind/src/kora-worker.ts +9 -0
  170. package/templates/svelte-tailwind/src/main.ts +16 -0
  171. package/templates/svelte-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  172. package/templates/svelte-tailwind/src/modules/todos/todo.queries.ts +5 -0
  173. package/templates/svelte-tailwind/src/modules/todos/todo.schema.ts +10 -0
  174. package/templates/svelte-tailwind/src/modules/todos/useTodos.ts +25 -0
  175. package/templates/svelte-tailwind/src/schema.ts +9 -0
  176. package/templates/svelte-tailwind/src/vite-env.d.ts +12 -0
  177. package/templates/svelte-tailwind/svelte.config.js +5 -0
  178. package/templates/svelte-tailwind/tsconfig.json.hbs +14 -0
  179. package/templates/svelte-tailwind/vite.config.ts +64 -0
  180. package/templates/svelte-tailwind-sync/.env.example +25 -0
  181. package/templates/svelte-tailwind-sync/AGENTS.md +76 -0
  182. package/templates/svelte-tailwind-sync/README.md.hbs +106 -0
  183. package/templates/svelte-tailwind-sync/index.html.hbs +12 -0
  184. package/templates/svelte-tailwind-sync/kora.config.ts +17 -0
  185. package/templates/svelte-tailwind-sync/package.json.hbs +46 -0
  186. package/templates/svelte-tailwind-sync/server.ts +141 -0
  187. package/templates/svelte-tailwind-sync/src/App.svelte +193 -0
  188. package/templates/svelte-tailwind-sync/src/Root.svelte +26 -0
  189. package/templates/svelte-tailwind-sync/src/auth.ts +25 -0
  190. package/templates/svelte-tailwind-sync/src/index.css +7 -0
  191. package/templates/svelte-tailwind-sync/src/kora-worker.ts +9 -0
  192. package/templates/svelte-tailwind-sync/src/main.ts +32 -0
  193. package/templates/svelte-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  194. package/templates/svelte-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  195. package/templates/svelte-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  196. package/templates/svelte-tailwind-sync/src/modules/todos/useTodos.ts +25 -0
  197. package/templates/svelte-tailwind-sync/src/schema.ts +9 -0
  198. package/templates/svelte-tailwind-sync/src/vite-env.d.ts +22 -0
  199. package/templates/svelte-tailwind-sync/svelte.config.js +5 -0
  200. package/templates/svelte-tailwind-sync/tsconfig.json.hbs +14 -0
  201. package/templates/svelte-tailwind-sync/vite.config.ts +98 -0
  202. package/templates/tauri-react/.env.example +27 -0
  203. package/templates/tauri-react/.github/workflows/release-desktop.yml +107 -0
  204. package/templates/tauri-react/AGENTS.md +91 -0
  205. package/templates/tauri-react/README.md.hbs +183 -0
  206. package/templates/tauri-react/dev.ts +37 -0
  207. package/templates/tauri-react/index.html.hbs +16 -0
  208. package/templates/tauri-react/kora.config.ts +17 -0
  209. package/templates/tauri-react/package.json.hbs +46 -0
  210. package/templates/tauri-react/server.ts +148 -0
  211. package/templates/tauri-react/src/App.tsx +803 -0
  212. package/templates/tauri-react/src/AppShell.tsx +158 -0
  213. package/templates/tauri-react/src/SetupScreen.tsx +288 -0
  214. package/templates/tauri-react/src/auth.ts +45 -0
  215. package/templates/tauri-react/src/main.tsx +9 -0
  216. package/templates/tauri-react/src/modules/todos/todo.mutations.ts +25 -0
  217. package/templates/tauri-react/src/modules/todos/todo.queries.ts +5 -0
  218. package/templates/tauri-react/src/modules/todos/todo.schema.ts +10 -0
  219. package/templates/tauri-react/src/modules/todos/useTodos.ts +31 -0
  220. package/templates/tauri-react/src/schema.ts +9 -0
  221. package/templates/tauri-react/src/sync-config.ts +103 -0
  222. package/templates/tauri-react/src/updater.ts +38 -0
  223. package/templates/tauri-react/src/vite-env.d.ts +19 -0
  224. package/templates/tauri-react/src-tauri/Cargo.toml +19 -0
  225. package/templates/tauri-react/src-tauri/build.rs +3 -0
  226. package/templates/tauri-react/src-tauri/capabilities/default.json +12 -0
  227. package/templates/tauri-react/src-tauri/icons/128x128.png +0 -0
  228. package/templates/tauri-react/src-tauri/icons/128x128@2x.png +0 -0
  229. package/templates/tauri-react/src-tauri/icons/32x32.png +0 -0
  230. package/templates/tauri-react/src-tauri/icons/icon.icns +0 -0
  231. package/templates/tauri-react/src-tauri/icons/icon.ico +0 -0
  232. package/templates/tauri-react/src-tauri/src/lib.rs +8 -0
  233. package/templates/tauri-react/src-tauri/src/main.rs +6 -0
  234. package/templates/tauri-react/src-tauri/tauri.conf.json +44 -0
  235. package/templates/tauri-react/tsconfig.json.hbs +14 -0
  236. package/templates/tauri-react/vite.config.ts +15 -0
  237. package/templates/vue-basic/AGENTS.md +84 -0
  238. package/templates/vue-basic/README.md.hbs +62 -0
  239. package/templates/vue-basic/index.html.hbs +12 -0
  240. package/templates/vue-basic/kora.config.ts +12 -0
  241. package/templates/vue-basic/package.json.hbs +32 -0
  242. package/templates/vue-basic/src/App.vue +117 -0
  243. package/templates/vue-basic/src/index.css +287 -0
  244. package/templates/vue-basic/src/kora-worker.ts +9 -0
  245. package/templates/vue-basic/src/main.ts +20 -0
  246. package/templates/vue-basic/src/modules/todos/todo.mutations.ts +25 -0
  247. package/templates/vue-basic/src/modules/todos/todo.queries.ts +5 -0
  248. package/templates/vue-basic/src/modules/todos/todo.schema.ts +10 -0
  249. package/templates/vue-basic/src/modules/todos/useTodos.ts +32 -0
  250. package/templates/vue-basic/src/schema.ts +9 -0
  251. package/templates/vue-basic/src/vite-env.d.ts +17 -0
  252. package/templates/vue-basic/tsconfig.json.hbs +15 -0
  253. package/templates/vue-basic/vite.config.ts +74 -0
  254. package/templates/vue-sync/.env.example +25 -0
  255. package/templates/vue-sync/AGENTS.md +84 -0
  256. package/templates/vue-sync/README.md.hbs +106 -0
  257. package/templates/vue-sync/index.html.hbs +12 -0
  258. package/templates/vue-sync/kora.config.ts +17 -0
  259. package/templates/vue-sync/package.json.hbs +42 -0
  260. package/templates/vue-sync/server.ts +141 -0
  261. package/templates/vue-sync/src/App.vue +135 -0
  262. package/templates/vue-sync/src/auth.ts +25 -0
  263. package/templates/vue-sync/src/index.css +339 -0
  264. package/templates/vue-sync/src/kora-worker.ts +9 -0
  265. package/templates/vue-sync/src/main.ts +47 -0
  266. package/templates/vue-sync/src/modules/todos/todo.mutations.ts +25 -0
  267. package/templates/vue-sync/src/modules/todos/todo.queries.ts +5 -0
  268. package/templates/vue-sync/src/modules/todos/todo.schema.ts +10 -0
  269. package/templates/vue-sync/src/modules/todos/useTodos.ts +32 -0
  270. package/templates/vue-sync/src/schema.ts +9 -0
  271. package/templates/vue-sync/src/vite-env.d.ts +27 -0
  272. package/templates/vue-sync/tsconfig.json.hbs +15 -0
  273. package/templates/vue-sync/vite.config.ts +91 -0
  274. package/templates/vue-tailwind/AGENTS.md +84 -0
  275. package/templates/vue-tailwind/README.md.hbs +62 -0
  276. package/templates/vue-tailwind/index.html.hbs +12 -0
  277. package/templates/vue-tailwind/kora.config.ts +12 -0
  278. package/templates/vue-tailwind/package.json.hbs +35 -0
  279. package/templates/vue-tailwind/src/App.vue +135 -0
  280. package/templates/vue-tailwind/src/index.css +7 -0
  281. package/templates/vue-tailwind/src/kora-worker.ts +9 -0
  282. package/templates/vue-tailwind/src/main.ts +31 -0
  283. package/templates/vue-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  284. package/templates/vue-tailwind/src/modules/todos/todo.queries.ts +5 -0
  285. package/templates/vue-tailwind/src/modules/todos/todo.schema.ts +10 -0
  286. package/templates/vue-tailwind/src/modules/todos/useTodos.ts +32 -0
  287. package/templates/vue-tailwind/src/schema.ts +9 -0
  288. package/templates/vue-tailwind/src/vite-env.d.ts +17 -0
  289. package/templates/vue-tailwind/tsconfig.json.hbs +15 -0
  290. package/templates/vue-tailwind/vite.config.ts +75 -0
  291. package/templates/vue-tailwind-sync/.env.example +25 -0
  292. package/templates/vue-tailwind-sync/AGENTS.md +84 -0
  293. package/templates/vue-tailwind-sync/README.md.hbs +106 -0
  294. package/templates/vue-tailwind-sync/index.html.hbs +12 -0
  295. package/templates/vue-tailwind-sync/kora.config.ts +17 -0
  296. package/templates/vue-tailwind-sync/package.json.hbs +45 -0
  297. package/templates/vue-tailwind-sync/server.ts +141 -0
  298. package/templates/vue-tailwind-sync/src/App.vue +245 -0
  299. package/templates/vue-tailwind-sync/src/auth.ts +25 -0
  300. package/templates/vue-tailwind-sync/src/index.css +7 -0
  301. package/templates/vue-tailwind-sync/src/kora-worker.ts +9 -0
  302. package/templates/vue-tailwind-sync/src/main.ts +56 -0
  303. package/templates/vue-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  304. package/templates/vue-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  305. package/templates/vue-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  306. package/templates/vue-tailwind-sync/src/modules/todos/useTodos.ts +32 -0
  307. package/templates/vue-tailwind-sync/src/schema.ts +9 -0
  308. package/templates/vue-tailwind-sync/src/vite-env.d.ts +27 -0
  309. package/templates/vue-tailwind-sync/tsconfig.json.hbs +15 -0
  310. package/templates/vue-tailwind-sync/vite.config.ts +92 -0
@@ -0,0 +1,32 @@
1
+ import { createKoraAuthSync } from '@korajs/auth'
2
+ import { createApp as createKoraApp } from 'korajs'
3
+ import { mount } from 'svelte'
4
+ import Root from './Root.svelte'
5
+ import { authClient, completeOAuthCallbackFromLocation } from './auth'
6
+ import schema from './schema'
7
+ import './index.css'
8
+ import koraWorkerUrl from './kora-worker.ts?worker&url'
9
+
10
+ const syncUrl =
11
+ import.meta.env.VITE_SYNC_URL ||
12
+ `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/kora-sync`
13
+
14
+ const kora = createKoraApp({
15
+ schema,
16
+ sync: {
17
+ url: syncUrl,
18
+ authClient: createKoraAuthSync({ authClient, schema }),
19
+ },
20
+ store: {
21
+ workerUrl: koraWorkerUrl,
22
+ },
23
+ devtools: import.meta.env.DEV,
24
+ })
25
+
26
+ kora.ready.then(() => kora.sync?.connect())
27
+
28
+ void completeOAuthCallbackFromLocation().catch((error) => {
29
+ console.error('[Kora Auth] OAuth callback failed:', error)
30
+ })
31
+
32
+ mount(Root, { target: document.getElementById('app')!, props: { kora, authClient } })
@@ -0,0 +1,25 @@
1
+ import type { CollectionAccessor } from 'korajs'
2
+
3
+ export interface CreateTodoInput {
4
+ title: string
5
+ }
6
+
7
+ export interface UpdateTodoStatusInput {
8
+ completed: boolean
9
+ }
10
+
11
+ export function createTodo(todos: CollectionAccessor, data: CreateTodoInput) {
12
+ return todos.insert({ title: data.title })
13
+ }
14
+
15
+ export function updateTodoStatus(
16
+ todos: CollectionAccessor,
17
+ id: string,
18
+ data: UpdateTodoStatusInput,
19
+ ) {
20
+ return todos.update(id, { completed: data.completed })
21
+ }
22
+
23
+ export function deleteTodo(todos: CollectionAccessor, id: string) {
24
+ return todos.delete(id)
25
+ }
@@ -0,0 +1,5 @@
1
+ import type { CollectionAccessor } from 'korajs'
2
+
3
+ export function orderedTodos(todos: CollectionAccessor) {
4
+ return todos.where({}).orderBy('createdAt', 'desc')
5
+ }
@@ -0,0 +1,10 @@
1
+ import { t } from 'korajs'
2
+
3
+ export const todos = {
4
+ fields: {
5
+ title: t.string(),
6
+ completed: t.boolean().default(false),
7
+ createdAt: t.timestamp().auto(),
8
+ },
9
+ indexes: ['completed', 'createdAt'],
10
+ }
@@ -0,0 +1,25 @@
1
+ import { createMutation, createQueryStore, getCollection } from '@korajs/svelte'
2
+ import { derived } from 'svelte/store'
3
+ import {
4
+ type CreateTodoInput,
5
+ type UpdateTodoStatusInput,
6
+ createTodo,
7
+ deleteTodo,
8
+ updateTodoStatus,
9
+ } from './todo.mutations'
10
+ import { orderedTodos } from './todo.queries'
11
+
12
+ export function createTodosStores() {
13
+ const todos = getCollection('todos')
14
+ const allTodos = createQueryStore(orderedTodos(todos))
15
+ const addTodo = createMutation((data: CreateTodoInput) => createTodo(todos, data))
16
+ const toggleTodo = createMutation((id: string, data: UpdateTodoStatusInput) =>
17
+ updateTodoStatus(todos, id, data),
18
+ )
19
+ const removeTodo = createMutation((id: string) => deleteTodo(todos, id))
20
+
21
+ const activeTodos = derived(allTodos, ($all) => $all.filter((todo) => !todo.completed))
22
+ const completedTodos = derived(allTodos, ($all) => $all.filter((todo) => !!todo.completed))
23
+
24
+ return { allTodos, activeTodos, completedTodos, addTodo, toggleTodo, deleteTodo: removeTodo }
25
+ }
@@ -0,0 +1,9 @@
1
+ import { defineSchema } from 'korajs'
2
+ import { todos } from './modules/todos/todo.schema'
3
+
4
+ export default defineSchema({
5
+ version: 1,
6
+ collections: {
7
+ todos,
8
+ },
9
+ })
@@ -0,0 +1,22 @@
1
+ /// <reference types="svelte" />
2
+ /// <reference types="vite/client" />
3
+
4
+ interface ImportMetaEnv {
5
+ readonly DEV: boolean
6
+ readonly VITE_SYNC_URL?: string
7
+ readonly VITE_AUTH_URL?: string
8
+ }
9
+
10
+ interface ImportMeta {
11
+ readonly env: ImportMetaEnv
12
+ }
13
+
14
+ declare module '*?worker&url' {
15
+ const url: string
16
+ export default url
17
+ }
18
+
19
+ declare module '*?url' {
20
+ const url: string
21
+ export default url
22
+ }
@@ -0,0 +1,5 @@
1
+ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
2
+
3
+ export default {
4
+ preprocess: vitePreprocess(),
5
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "noUncheckedIndexedAccess": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "types": ["vite/client"]
11
+ },
12
+ "include": ["src/**/*.ts", "src/**/*.svelte"],
13
+ "exclude": ["node_modules"]
14
+ }
@@ -0,0 +1,98 @@
1
+ import { copyFileSync, existsSync, readdirSync } from 'node:fs'
2
+ import { join, resolve } from 'node:path'
3
+ import { koraServiceWorker } from '@korajs/cli/vite'
4
+ import { svelte } from '@sveltejs/vite-plugin-svelte'
5
+ import tailwindcss from '@tailwindcss/vite'
6
+ import type { Plugin } from 'vite'
7
+ import { defineConfig } from 'vite'
8
+
9
+ function crossOriginIsolation(): Plugin {
10
+ const embedderPolicy = process.env.KORA_COEP_POLICY || 'credentialless'
11
+
12
+ return {
13
+ name: 'cross-origin-isolation',
14
+ configureServer(server) {
15
+ server.middlewares.use((_req, res, next) => {
16
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
17
+ res.setHeader('Cross-Origin-Embedder-Policy', embedderPolicy)
18
+ next()
19
+ })
20
+ },
21
+ configurePreviewServer(server) {
22
+ server.middlewares.use((_req, res, next) => {
23
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
24
+ res.setHeader('Cross-Origin-Embedder-Policy', embedderPolicy)
25
+ next()
26
+ })
27
+ },
28
+ }
29
+ }
30
+
31
+ function sqliteWasmHotfix(): Plugin {
32
+ return {
33
+ name: 'sqlite-wasm-hotfix',
34
+ apply: 'build',
35
+ closeBundle() {
36
+ const assetsDir = resolve('dist', 'assets')
37
+ if (!existsSync(assetsDir)) return
38
+
39
+ for (const file of readdirSync(assetsDir)) {
40
+ if (/^sqlite3-.+\.wasm$/.test(file)) {
41
+ copyFileSync(join(assetsDir, file), join(assetsDir, 'sqlite3.wasm'))
42
+ break
43
+ }
44
+ }
45
+
46
+ const proxyFile = resolve(
47
+ 'node_modules',
48
+ '@sqlite.org',
49
+ 'sqlite-wasm',
50
+ 'sqlite-wasm',
51
+ 'jswasm',
52
+ 'sqlite3-opfs-async-proxy.js',
53
+ )
54
+ if (existsSync(proxyFile)) {
55
+ copyFileSync(proxyFile, join(assetsDir, 'sqlite3-opfs-async-proxy.js'))
56
+ }
57
+ },
58
+ }
59
+ }
60
+
61
+ // koraServiceWorker() (build only, keep it last): precaches the built app shell so the
62
+ // app opens offline, with a "new version available" reload prompt. See @korajs/cli/vite.
63
+ export default defineConfig({
64
+ plugins: [
65
+ svelte(),
66
+ tailwindcss(),
67
+ crossOriginIsolation(),
68
+ sqliteWasmHotfix(),
69
+ koraServiceWorker(),
70
+ ],
71
+ worker: {
72
+ format: 'es',
73
+ },
74
+ optimizeDeps: {
75
+ exclude: ['@sqlite.org/sqlite-wasm', '@korajs/store'],
76
+ include: ['yjs'],
77
+ },
78
+ resolve: {
79
+ dedupe: ['yjs'],
80
+ },
81
+ server: {
82
+ allowedHosts: true,
83
+ watch: {
84
+ ignored: ['**/.kora/**', '**/kora/generated/**', '**/*.db', '**/*.db-shm', '**/*.db-wal'],
85
+ },
86
+ proxy: {
87
+ '/kora-sync': {
88
+ target: 'ws://localhost:3001',
89
+ ws: true,
90
+ rewriteWsOrigin: true,
91
+ },
92
+ '/auth': {
93
+ target: 'http://localhost:3001',
94
+ changeOrigin: true,
95
+ },
96
+ },
97
+ },
98
+ })
@@ -0,0 +1,27 @@
1
+ # Sync server URL (used by the Tauri app to connect to the sync server)
2
+ # In development, defaults to ws://localhost:3001/kora-sync
3
+ # For production builds, set this to your deployed server URL:
4
+ # VITE_SYNC_URL=wss://your-server.example.com/kora-sync
5
+
6
+ # Sync server port
7
+ PORT=3001
8
+
9
+ # Server database
10
+ # Leave DATABASE_URL empty to use SQLite at KORA_SERVER_DB.
11
+ # Set DATABASE_URL for cloud Postgres deployments.
12
+ KORA_SERVER_DB=./.kora/kora-server.db
13
+ DATABASE_URL=
14
+
15
+ # Operational endpoints (/__kora, /__kora/status, /__kora/events, /__kora/metrics, backups)
16
+ # Set these in production. Use Authorization: Bearer <token> when calling protected endpoints.
17
+ KORA_ADMIN_TOKEN=
18
+ KORA_METRICS_TOKEN=
19
+ KORA_BACKUP_TOKEN=
20
+
21
+ # Optional sync path
22
+ KORA_SYNC_PATH=/kora-sync
23
+
24
+ # Auth
25
+ # Set this to enable built-in /auth/* routes and authenticated sync.
26
+ # Generate with: node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"
27
+ KORA_AUTH_SECRET=
@@ -0,0 +1,107 @@
1
+ # Build and release desktop binaries for macOS, Windows, and Linux.
2
+ #
3
+ # Trigger: Push a version tag (e.g., v1.0.0) or run manually.
4
+ #
5
+ # Prerequisites:
6
+ # 1. Generate Tauri signing keys:
7
+ # pnpm tauri signer generate -w ~/.tauri/myapp.key
8
+ # 2. Add secrets to your GitHub repo:
9
+ # - TAURI_SIGNING_PRIVATE_KEY: Contents of ~/.tauri/myapp.key
10
+ # - TAURI_SIGNING_PRIVATE_KEY_PASSWORD: Password used during generation
11
+ # 3. (Optional) For macOS notarization:
12
+ # - APPLE_CERTIFICATE: Base64-encoded .p12 certificate
13
+ # - APPLE_CERTIFICATE_PASSWORD: Certificate password
14
+ # - APPLE_SIGNING_IDENTITY: e.g., "Developer ID Application: Your Name (TEAM_ID)"
15
+ # - APPLE_ID: Apple ID email
16
+ # - APPLE_PASSWORD: App-specific password
17
+ # - APPLE_TEAM_ID: Your Apple Developer Team ID
18
+ #
19
+ # After the workflow runs, a GitHub Release is created with installers
20
+ # for all platforms plus an update manifest (latest.json) for auto-updates.
21
+
22
+ name: Release Desktop
23
+
24
+ on:
25
+ push:
26
+ tags:
27
+ - 'v*'
28
+ workflow_dispatch:
29
+ inputs:
30
+ tag:
31
+ description: 'Version tag (e.g., v1.0.0)'
32
+ required: true
33
+
34
+ permissions:
35
+ contents: write
36
+
37
+ jobs:
38
+ build:
39
+ strategy:
40
+ fail-fast: false
41
+ matrix:
42
+ include:
43
+ - platform: macos-latest
44
+ args: '--target aarch64-apple-darwin'
45
+ rust_target: aarch64-apple-darwin
46
+ - platform: macos-latest
47
+ args: '--target x86_64-apple-darwin'
48
+ rust_target: x86_64-apple-darwin
49
+ - platform: ubuntu-22.04
50
+ args: ''
51
+ rust_target: ''
52
+ - platform: windows-latest
53
+ args: ''
54
+ rust_target: ''
55
+
56
+ runs-on: ${{ matrix.platform }}
57
+
58
+ steps:
59
+ - uses: actions/checkout@v4
60
+
61
+ - name: Install system dependencies (Linux)
62
+ if: matrix.platform == 'ubuntu-22.04'
63
+ run: |
64
+ sudo apt-get update
65
+ sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
66
+
67
+ - uses: actions/setup-node@v4
68
+ with:
69
+ node-version: 20
70
+
71
+ - uses: pnpm/action-setup@v4
72
+ with:
73
+ version: 9
74
+
75
+ - name: Install Rust stable
76
+ uses: dtolnay/rust-toolchain@stable
77
+ with:
78
+ targets: ${{ matrix.rust_target }}
79
+
80
+ - name: Rust cache
81
+ uses: swatinem/rust-cache@v2
82
+ with:
83
+ workspaces: './src-tauri -> target'
84
+
85
+ - name: Install dependencies
86
+ run: pnpm install
87
+
88
+ - name: Build and release
89
+ uses: tauri-apps/tauri-action@v0
90
+ env:
91
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
92
+ TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
93
+ TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
94
+ # macOS code signing (optional)
95
+ APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
96
+ APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
97
+ APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
98
+ APPLE_ID: ${{ secrets.APPLE_ID }}
99
+ APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
100
+ APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
101
+ with:
102
+ tagName: ${{ github.ref_name || github.event.inputs.tag }}
103
+ releaseName: '${{ github.ref_name || github.event.inputs.tag }}'
104
+ releaseBody: 'See the assets below to download and install.'
105
+ releaseDraft: true
106
+ prerelease: false
107
+ args: ${{ matrix.args }}
@@ -0,0 +1,91 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for AI coding agents working in this project. Humans: this is useful for you too.
4
+
5
+ ## What this project is
6
+
7
+ This is a **Kora.js** application: an offline-first app where all data lives in a local SQLite database (WASM + OPFS in the browser) and optionally syncs across devices through a Kora sync server. Offline is the normal state, not an error state. Full docs: https://korajs.dev and machine-readable index at https://korajs.dev/llms.txt (complete docs in one file: https://korajs.dev/llms-full.txt).
8
+
9
+ ## Golden rules
10
+
11
+ 1. **The schema is the source of truth.** All collections are defined in `src/schema.ts` with `defineSchema` and the `t.*` field builders. To add or change data shapes, edit the schema first; types flow from it automatically. Never hand-write types for records.
12
+ 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.
13
+ 3. **Await readiness before direct collection access.** Outside the UI bindings, `await app.ready` before calling `app.<collection>` methods. The bindings handle this for you inside components.
14
+ 4. **Offline must keep working.** Any feature you add must function with the network off. Never gate a write or a read on connectivity. If you find yourself checking `navigator.onLine` before a data operation, you are doing it wrong.
15
+ 5. **Surface mutation errors.** Fire-and-forget mutate calls put errors into the mutation state. Always render the mutation's `error` in the UI or handle the promise from the async variant. Silent failure is the worst failure.
16
+ 6. **Do not touch `src/kora-worker.ts`.** It wires the SQLite WASM binary URL for both dev and production builds. Changing it breaks production builds in ways that only show up after deploy.
17
+ 7. **Do not add a state management library for server or app data.** Kora's reactive queries are the store. Local UI state (form inputs, toggles) can use the framework's normal state tools.
18
+
19
+ ## Data API cheat sheet
20
+
21
+ ```ts
22
+ await app.ready
23
+ const rec = await app.todos.insert({ title: 'x' }) // defaults and .auto() fields applied
24
+ await app.todos.update(rec.id, { completed: true }) // partial update, changed fields only
25
+ await app.todos.delete(rec.id)
26
+ const one = await app.todos.findById(rec.id)
27
+ const unsubscribe = app.todos
28
+ .where({ completed: false })
29
+ .orderBy('createdAt', 'desc')
30
+ .subscribe((rows) => {/* fires immediately, then on every change */})
31
+ ```
32
+
33
+ Schema example (`src/schema.ts`):
34
+
35
+ ```ts
36
+ import { defineSchema, t } from 'korajs'
37
+
38
+ export default defineSchema({
39
+ version: 1,
40
+ collections: {
41
+ todos: {
42
+ fields: {
43
+ title: t.string(),
44
+ completed: t.boolean().default(false),
45
+ tags: t.array(t.string()).default([]),
46
+ priority: t.enum(['low', 'medium', 'high']).default('medium'),
47
+ createdAt: t.timestamp().auto(), // set automatically; never pass it on insert
48
+ },
49
+ indexes: ['completed', 'createdAt'],
50
+ },
51
+ },
52
+ })
53
+ ```
54
+
55
+ If you change a collection's shape, increment `version` and add a `migrations` entry for it (`migrate().addField(...)`): each device migrates its local database on its next open. `npx kora migrate` generates the matching migration for server databases and a transform stub for clients still on the older version.
56
+
57
+ ## Project conventions
58
+
59
+ - Feature code lives in `src/modules/<feature>/` with the pattern: `<feature>.schema.ts` (collection definition, imported into `src/schema.ts`), `<feature>.queries.ts` (query builders), `<feature>.mutations.ts` (mutation functions taking a collection accessor).
60
+ - Conflict handling is declarative. Concurrent edits merge automatically (last-write-wins per field, arrays as multisets that keep every device's additions and removals, objects per top-level key). If a field needs domain-specific merging (counters, quantities), add a `resolve` function in the schema rather than writing sync logic.
61
+ - `kora.config.ts` controls the dev environment (ports, sync server, schema watcher).
62
+
63
+ ## Commands
64
+
65
+ - `npm run dev` starts everything: Vite dev server (port 5173), local sync server (port 3001), and the schema watcher.
66
+ - `npm run build` type-checks and builds for production.
67
+ - `npx kora doctor` diagnoses a broken setup.
68
+ - DevTools overlay: press Ctrl+Shift+K (Cmd+Shift+K on macOS) in the running app to inspect operations, merges, and sync status.
69
+
70
+ ## Sync and auth
71
+
72
+ Sync is configured in `src/main.*` via `createApp({ sync: { url, authClient } })`. In dev, the sync URL is derived from the page host and proxied by Vite; in production set `VITE_SYNC_URL`. Auth (if present in this template) uses `@korajs/auth`; the client is created in `src/auth.ts`. Local writes work without sign-in; sync requires the server to accept the connection.
73
+
74
+ ## React bindings
75
+
76
+ ```tsx
77
+ import { useCollection, useMutation, useQuery, useSyncStatus } from '@korajs/react'
78
+
79
+ const todos = useCollection('todos')
80
+ const rows = useQuery(orderedTodos(todos)) // reactive; [] on the very first render
81
+ const addTodo = useMutation((data) => todos.insert(data))
82
+ addTodo.mutate({ title: 'x' }) // fire-and-forget; errors land in addTodo.error
83
+ await addTodo.mutateAsync({ title: 'x' }) // throws on failure; use when you need the error
84
+ const status = useSyncStatus() // status.status, status.pendingOperations
85
+ ```
86
+
87
+ Rules: always render `mutation.error` somewhere. The app must work under React StrictMode (the framework's hooks are StrictMode-safe; keep `<StrictMode>` in `src/main.tsx`). `useQuery` reads the local store: no network, no spinner. Its very first render returns `[]` before the local query has run; use `useQueryState` when you must tell "loading" from "no rows".
88
+
89
+ ## Tauri specifics
90
+
91
+ This app runs inside Tauri with native SQLite storage through the bundled plugin. Web-only APIs (OPFS) are not the storage path here; do not add browser-storage assumptions. Use `npm run tauri dev` for the desktop shell and consult https://korajs.dev/guide/tauri-desktop.md before changing storage or plugin configuration.
@@ -0,0 +1,183 @@
1
+ # {{projectName}}
2
+
3
+ A native desktop application built with [Kora.js](https://ehoneahobed.github.io/kora/) and [Tauri](https://tauri.app). Uses native SQLite for fast local storage and syncs data across devices via a lightweight sync server.
4
+
5
+ ## Getting Started
6
+
7
+ ```bash
8
+ pnpm dev
9
+ ```
10
+
11
+ This starts both the sync server and the Tauri desktop app. The first launch compiles the Rust backend (2-5 minutes). Subsequent launches are fast.
12
+
13
+ On first launch, you'll see a setup screen to enter your sync server URL. For local development, use `ws://localhost:3001/kora-sync` (the local sync server started by `pnpm dev`). You can also skip setup to use the app in local-only mode.
14
+
15
+ ## Project Structure
16
+
17
+ ```
18
+ src/
19
+ schema.ts # Schema entry point
20
+ auth.ts # Desktop auth client and OAuth callback helpers
21
+ main.tsx # Entry point
22
+ AppShell.tsx # Setup flow + Kora initialization
23
+ App.tsx # Main application UI
24
+ modules/
25
+ todos/
26
+ todo.schema.ts # Todo collection definition
27
+ todo.queries.ts # Reusable todo query builders
28
+ todo.mutations.ts # Todo mutation helpers
29
+ useTodos.ts # React hook connecting todos to the UI
30
+ SetupScreen.tsx # First-launch server configuration
31
+ sync-config.ts # Runtime sync URL storage
32
+ updater.ts # Auto-update checker
33
+ src-tauri/
34
+ Cargo.toml # Rust dependencies (Kora SQLite + updater plugins)
35
+ tauri.conf.json # Window, bundle, and updater config
36
+ capabilities/ # Tauri security permissions
37
+ src/
38
+ main.rs # Rust entry point
39
+ lib.rs # Plugin registration
40
+ server.ts # Kora sync server
41
+ dev.ts # Dev orchestrator (starts sync + Tauri together)
42
+ kora.config.ts # Kora configuration
43
+ .github/workflows/
44
+ release-desktop.yml # CI/CD for cross-platform builds
45
+ ```
46
+
47
+ In a feature module, `schema` defines data shape, `queries` contain reads only, `mutations` contain
48
+ writes, and `useTodos.ts` is the React binding that exposes todo data and actions to components.
49
+
50
+ ## Scripts
51
+
52
+ | Script | Description |
53
+ |--------|-------------|
54
+ | `pnpm dev` | Start sync server + Tauri app together |
55
+ | `pnpm dev:app` | Start Tauri app only (no sync server) |
56
+ | `pnpm dev:server` | Start sync server only |
57
+ | `pnpm build` | Build desktop app for distribution |
58
+ | `pnpm deploy:server` | Deploy sync server to the cloud |
59
+
60
+ ## Distributing to Organizations
61
+
62
+ This app is designed to be distributed to multiple organizations. Each organization gets the **same binary** and connects to their own sync server.
63
+
64
+ ### 1. Deploy a sync server for each organization
65
+
66
+ ```bash
67
+ pnpm deploy:server
68
+ ```
69
+
70
+ Or deploy manually to any Node.js host. Each org gets their own server (or share one with multi-tenant scoping).
71
+
72
+ ### 2. Build the desktop binary
73
+
74
+ ```bash
75
+ pnpm build
76
+ ```
77
+
78
+ No need to bake in a sync URL — users configure it on first launch.
79
+
80
+ If you want to pre-configure the URL (e.g., for a specific org):
81
+
82
+ ```bash
83
+ VITE_SYNC_URL=wss://acme-corp.example.com/kora-sync pnpm build
84
+ ```
85
+
86
+ ### 3. Set up auto-updates
87
+
88
+ 1. Generate signing keys:
89
+ ```bash
90
+ pnpm tauri signer generate -w ~/.tauri/myapp.key
91
+ ```
92
+
93
+ 2. Add the public key to `src-tauri/tauri.conf.json` under `plugins.updater.pubkey`.
94
+
95
+ 3. Set the update endpoint:
96
+ ```json
97
+ "endpoints": ["https://github.com/YOUR_ORG/YOUR_REPO/releases/latest/download/latest.json"]
98
+ ```
99
+
100
+ 4. Add `TAURI_SIGNING_PRIVATE_KEY` and `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` to your GitHub repo secrets.
101
+
102
+ 5. Push a version tag to trigger a release:
103
+ ```bash
104
+ git tag v1.0.0 && git push origin v1.0.0
105
+ ```
106
+
107
+ The GitHub Actions workflow builds for macOS, Windows, and Linux, creating a draft release with all installers.
108
+
109
+ ### 4. Distribute installers
110
+
111
+ Built installers are in `src-tauri/target/release/bundle/`:
112
+
113
+ | Platform | Output |
114
+ |----------|--------|
115
+ | macOS | `.dmg` and `.app` bundle |
116
+ | Windows | `.msi` and `.exe` installer |
117
+ | Linux | `.deb`, `.rpm`, `.AppImage` |
118
+
119
+ Share these with your users. Installed apps auto-update when you push new releases.
120
+
121
+ ## First-Launch Experience
122
+
123
+ When users install and open the app:
124
+
125
+ 1. **Setup screen** asks for the sync server URL
126
+ 2. User enters the URL provided by their organization admin
127
+ 3. App connects, verifies the server, and saves the URL locally
128
+ 4. On subsequent launches, the app connects automatically
129
+
130
+ Users can also skip setup and use the app offline — they can connect later from the settings gear icon.
131
+
132
+ ## Environment Variables
133
+
134
+ | Variable | Default | Description |
135
+ |----------|---------|-------------|
136
+ | `VITE_SYNC_URL` | (none) | Pre-configure sync URL at build time |
137
+ | `VITE_AUTH_URL` | derived from sync URL | HTTP URL for `/auth/*` routes |
138
+ | `PORT` | `3001` | Sync server port |
139
+ | `KORA_SERVER_DB` | `./.kora/kora-server.db` | SQLite file used by the sync server when `DATABASE_URL` is empty |
140
+ | `KORA_AUTH_DB` | `./.kora/kora-auth.db` | SQLite file used by OAuth state and linked identities when `DATABASE_URL` is empty |
141
+ | `DATABASE_URL` | (none) | PostgreSQL connection string for production sync server deployments |
142
+ | `KORA_SYNC_PATH` | `/kora-sync` | WebSocket path exposed by the sync server |
143
+ | `KORA_AUTH_SECRET` | (none) | Enables built-in `/auth/*` routes and authenticated sync when set |
144
+ | `KORA_GOOGLE_CLIENT_ID` | (none) | Enables Google OAuth when set with `KORA_GOOGLE_REDIRECT_URI` |
145
+ | `KORA_GOOGLE_CLIENT_SECRET` | (none) | Google OAuth client secret for confidential web clients |
146
+ | `KORA_GOOGLE_REDIRECT_URI` | (none) | OAuth callback URL for loopback, custom scheme, or hosted handoff |
147
+ | `KORA_OAUTH_PKCE` | `true` when no client secret | Enables PKCE for public desktop/mobile OAuth clients |
148
+ | `KORA_ADMIN_TOKEN` | (none) | Protects `/__kora`, `/__kora/status`, and `/__kora/events` |
149
+ | `KORA_METRICS_TOKEN` | (none) | Protects `/__kora/metrics` |
150
+ | `KORA_BACKUP_TOKEN` | (none) | Protects server backup and restore endpoints |
151
+
152
+ ## Authentication
153
+
154
+ Set `KORA_AUTH_SECRET` to enable built-in auth on the sync server. Generate a secret with:
155
+
156
+ ```bash
157
+ node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"
158
+ ```
159
+
160
+ The template creates a desktop auth client, wraps the app in `AuthProvider`, and wires sync with `createKoraAuthSync({ authClient, schema })`. Google OAuth is enabled on the server when `KORA_GOOGLE_CLIENT_ID` and `KORA_GOOGLE_REDIRECT_URI` are set.
161
+
162
+ For production desktop OAuth, use a loopback callback, custom URL scheme, or hosted handoff that returns `code` and `state` to the app, then complete the flow with `authClient.completeOAuthSignIn('google', { code, state })`. The generated `src/auth.ts` includes the callback completion helper. See the [Authentication Guide](https://ehoneahobed.github.io/kora/guide/authentication#desktop-and-tauri-apps).
163
+
164
+ ## Code Signing
165
+
166
+ ### macOS
167
+
168
+ For distribution outside the Mac App Store, you need a Developer ID certificate from Apple:
169
+
170
+ 1. Enroll in the [Apple Developer Program](https://developer.apple.com/programs/)
171
+ 2. Create a "Developer ID Application" certificate
172
+ 3. Add the certificate and credentials to your GitHub secrets (see `.github/workflows/release-desktop.yml`)
173
+
174
+ ### Windows
175
+
176
+ For SmartScreen trust, get an EV code signing certificate from a provider like DigiCert or Sectigo. Set the certificate in your CI environment.
177
+
178
+ ## Learn More
179
+
180
+ - [Tauri Desktop Guide](https://ehoneahobed.github.io/kora/guide/tauri-desktop)
181
+ - [Deployment Guide](https://ehoneahobed.github.io/kora/guide/deployment)
182
+ - [Sync Configuration](https://ehoneahobed.github.io/kora/guide/sync-configuration)
183
+ - [Schema Design](https://ehoneahobed.github.io/kora/guide/schema-design)