@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.
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/bin.cjs +9465 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/bin.js +3150 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-5NI2FEQL.js +92 -0
- package/dist/chunk-5NI2FEQL.js.map +1 -0
- package/dist/chunk-6C4BHSRA.js +82 -0
- package/dist/chunk-6C4BHSRA.js.map +1 -0
- package/dist/chunk-7Y4J2GXP.js +796 -0
- package/dist/chunk-7Y4J2GXP.js.map +1 -0
- package/dist/chunk-BURFQVMV.js +719 -0
- package/dist/chunk-BURFQVMV.js.map +1 -0
- package/dist/chunk-TVIRMDER.js +2569 -0
- package/dist/chunk-TVIRMDER.js.map +1 -0
- package/dist/create.cjs +1368 -0
- package/dist/create.cjs.map +1 -0
- package/dist/create.js +11 -0
- package/dist/create.js.map +1 -0
- package/dist/index.cjs +3489 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +772 -0
- package/dist/index.d.ts +772 -0
- package/dist/index.js +129 -0
- package/dist/index.js.map +1 -0
- package/dist/lab-manager-KUDII6BT.js +280 -0
- package/dist/lab-manager-KUDII6BT.js.map +1 -0
- package/dist/schema-loader-GGHECMTM.js +9 -0
- package/dist/schema-loader-GGHECMTM.js.map +1 -0
- package/dist/spectator-manager-FTH2NFSE.js +145 -0
- package/dist/spectator-manager-FTH2NFSE.js.map +1 -0
- package/dist/studio-server-XLGZNGG5.js +1747 -0
- package/dist/studio-server-XLGZNGG5.js.map +1 -0
- package/dist/vite.cjs +351 -0
- package/dist/vite.cjs.map +1 -0
- package/dist/vite.d.cts +161 -0
- package/dist/vite.d.ts +161 -0
- package/dist/vite.js +321 -0
- package/dist/vite.js.map +1 -0
- package/package.json +79 -0
- package/templates/react-basic/.env.example +3 -0
- package/templates/react-basic/AGENTS.md +87 -0
- package/templates/react-basic/README.md.hbs +62 -0
- package/templates/react-basic/index.html.hbs +12 -0
- package/templates/react-basic/kora.config.ts +13 -0
- package/templates/react-basic/package.json.hbs +35 -0
- package/templates/react-basic/src/App.tsx +132 -0
- package/templates/react-basic/src/index.css +287 -0
- package/templates/react-basic/src/kora-worker.ts +9 -0
- package/templates/react-basic/src/main.tsx +24 -0
- package/templates/react-basic/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-basic/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-basic/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-basic/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-basic/src/schema.ts +9 -0
- package/templates/react-basic/src/vite-env.d.ts +18 -0
- package/templates/react-basic/tsconfig.json.hbs +14 -0
- package/templates/react-basic/vite.config.ts +88 -0
- package/templates/react-sync/.env.example +25 -0
- package/templates/react-sync/AGENTS.md +87 -0
- package/templates/react-sync/README.md.hbs +106 -0
- package/templates/react-sync/index.html.hbs +12 -0
- package/templates/react-sync/kora.config.ts +17 -0
- package/templates/react-sync/package.json.hbs +44 -0
- package/templates/react-sync/server.ts +141 -0
- package/templates/react-sync/src/App.tsx +160 -0
- package/templates/react-sync/src/auth.ts +25 -0
- package/templates/react-sync/src/index.css +350 -0
- package/templates/react-sync/src/kora-worker.ts +9 -0
- package/templates/react-sync/src/main.tsx +52 -0
- package/templates/react-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-sync/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-sync/src/schema.ts +9 -0
- package/templates/react-sync/src/vite-env.d.ts +19 -0
- package/templates/react-sync/tsconfig.json.hbs +14 -0
- package/templates/react-sync/vite.config.ts +105 -0
- package/templates/react-tailwind/.env.example +3 -0
- package/templates/react-tailwind/AGENTS.md +87 -0
- package/templates/react-tailwind/README.md.hbs +62 -0
- package/templates/react-tailwind/index.html.hbs +12 -0
- package/templates/react-tailwind/kora.config.ts +13 -0
- package/templates/react-tailwind/package.json.hbs +38 -0
- package/templates/react-tailwind/src/App.tsx +201 -0
- package/templates/react-tailwind/src/index.css +7 -0
- package/templates/react-tailwind/src/kora-worker.ts +9 -0
- package/templates/react-tailwind/src/main.tsx +31 -0
- package/templates/react-tailwind/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/react-tailwind/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-tailwind/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-tailwind/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-tailwind/src/schema.ts +9 -0
- package/templates/react-tailwind/src/vite-env.d.ts +18 -0
- package/templates/react-tailwind/tsconfig.json.hbs +14 -0
- package/templates/react-tailwind/vite.config.ts +95 -0
- package/templates/react-tailwind-sync/.env.example +25 -0
- package/templates/react-tailwind-sync/AGENTS.md +89 -0
- package/templates/react-tailwind-sync/README.md.hbs +106 -0
- package/templates/react-tailwind-sync/index.html.hbs +12 -0
- package/templates/react-tailwind-sync/kora.config.ts +17 -0
- package/templates/react-tailwind-sync/package.json.hbs +47 -0
- package/templates/react-tailwind-sync/server.ts +141 -0
- package/templates/react-tailwind-sync/src/App.tsx +293 -0
- package/templates/react-tailwind-sync/src/auth.ts +25 -0
- package/templates/react-tailwind-sync/src/index.css +7 -0
- package/templates/react-tailwind-sync/src/kora-worker.ts +9 -0
- package/templates/react-tailwind-sync/src/kora.ts +32 -0
- package/templates/react-tailwind-sync/src/main.tsx +44 -0
- package/templates/react-tailwind-sync/src/modules/todos/todo.mutations.ts +21 -0
- package/templates/react-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/react-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/react-tailwind-sync/src/modules/todos/useTodos.ts +31 -0
- package/templates/react-tailwind-sync/src/schema.ts +9 -0
- package/templates/react-tailwind-sync/src/vite-env.d.ts +19 -0
- package/templates/react-tailwind-sync/tsconfig.json.hbs +14 -0
- package/templates/react-tailwind-sync/vite.config.ts +112 -0
- package/templates/svelte-basic/.env.example +3 -0
- package/templates/svelte-basic/AGENTS.md +76 -0
- package/templates/svelte-basic/README.md.hbs +62 -0
- package/templates/svelte-basic/index.html.hbs +12 -0
- package/templates/svelte-basic/kora.config.ts +12 -0
- package/templates/svelte-basic/package.json.hbs +33 -0
- package/templates/svelte-basic/src/App.svelte +128 -0
- package/templates/svelte-basic/src/Root.svelte +14 -0
- package/templates/svelte-basic/src/index.css +287 -0
- package/templates/svelte-basic/src/kora-worker.ts +9 -0
- package/templates/svelte-basic/src/main.ts +16 -0
- package/templates/svelte-basic/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-basic/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-basic/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-basic/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-basic/src/schema.ts +9 -0
- package/templates/svelte-basic/src/vite-env.d.ts +12 -0
- package/templates/svelte-basic/svelte.config.js +5 -0
- package/templates/svelte-basic/tsconfig.json.hbs +14 -0
- package/templates/svelte-basic/vite.config.ts +57 -0
- package/templates/svelte-sync/.env.example +25 -0
- package/templates/svelte-sync/AGENTS.md +76 -0
- package/templates/svelte-sync/README.md.hbs +106 -0
- package/templates/svelte-sync/index.html.hbs +12 -0
- package/templates/svelte-sync/kora.config.ts +17 -0
- package/templates/svelte-sync/package.json.hbs +43 -0
- package/templates/svelte-sync/server.ts +141 -0
- package/templates/svelte-sync/src/App.svelte +151 -0
- package/templates/svelte-sync/src/Root.svelte +22 -0
- package/templates/svelte-sync/src/auth.ts +25 -0
- package/templates/svelte-sync/src/index.css +339 -0
- package/templates/svelte-sync/src/kora-worker.ts +9 -0
- package/templates/svelte-sync/src/main.ts +32 -0
- package/templates/svelte-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-sync/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-sync/src/schema.ts +9 -0
- package/templates/svelte-sync/src/vite-env.d.ts +22 -0
- package/templates/svelte-sync/svelte.config.js +5 -0
- package/templates/svelte-sync/tsconfig.json.hbs +14 -0
- package/templates/svelte-sync/vite.config.ts +91 -0
- package/templates/svelte-tailwind/AGENTS.md +76 -0
- package/templates/svelte-tailwind/README.md.hbs +62 -0
- package/templates/svelte-tailwind/index.html.hbs +12 -0
- package/templates/svelte-tailwind/kora.config.ts +12 -0
- package/templates/svelte-tailwind/package.json.hbs +36 -0
- package/templates/svelte-tailwind/src/App.svelte +76 -0
- package/templates/svelte-tailwind/src/Root.svelte +14 -0
- package/templates/svelte-tailwind/src/index.css +7 -0
- package/templates/svelte-tailwind/src/kora-worker.ts +9 -0
- package/templates/svelte-tailwind/src/main.ts +16 -0
- package/templates/svelte-tailwind/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-tailwind/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-tailwind/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-tailwind/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-tailwind/src/schema.ts +9 -0
- package/templates/svelte-tailwind/src/vite-env.d.ts +12 -0
- package/templates/svelte-tailwind/svelte.config.js +5 -0
- package/templates/svelte-tailwind/tsconfig.json.hbs +14 -0
- package/templates/svelte-tailwind/vite.config.ts +64 -0
- package/templates/svelte-tailwind-sync/.env.example +25 -0
- package/templates/svelte-tailwind-sync/AGENTS.md +76 -0
- package/templates/svelte-tailwind-sync/README.md.hbs +106 -0
- package/templates/svelte-tailwind-sync/index.html.hbs +12 -0
- package/templates/svelte-tailwind-sync/kora.config.ts +17 -0
- package/templates/svelte-tailwind-sync/package.json.hbs +46 -0
- package/templates/svelte-tailwind-sync/server.ts +141 -0
- package/templates/svelte-tailwind-sync/src/App.svelte +193 -0
- package/templates/svelte-tailwind-sync/src/Root.svelte +26 -0
- package/templates/svelte-tailwind-sync/src/auth.ts +25 -0
- package/templates/svelte-tailwind-sync/src/index.css +7 -0
- package/templates/svelte-tailwind-sync/src/kora-worker.ts +9 -0
- package/templates/svelte-tailwind-sync/src/main.ts +32 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/svelte-tailwind-sync/src/modules/todos/useTodos.ts +25 -0
- package/templates/svelte-tailwind-sync/src/schema.ts +9 -0
- package/templates/svelte-tailwind-sync/src/vite-env.d.ts +22 -0
- package/templates/svelte-tailwind-sync/svelte.config.js +5 -0
- package/templates/svelte-tailwind-sync/tsconfig.json.hbs +14 -0
- package/templates/svelte-tailwind-sync/vite.config.ts +98 -0
- package/templates/tauri-react/.env.example +27 -0
- package/templates/tauri-react/.github/workflows/release-desktop.yml +107 -0
- package/templates/tauri-react/AGENTS.md +91 -0
- package/templates/tauri-react/README.md.hbs +183 -0
- package/templates/tauri-react/dev.ts +37 -0
- package/templates/tauri-react/index.html.hbs +16 -0
- package/templates/tauri-react/kora.config.ts +17 -0
- package/templates/tauri-react/package.json.hbs +46 -0
- package/templates/tauri-react/server.ts +148 -0
- package/templates/tauri-react/src/App.tsx +803 -0
- package/templates/tauri-react/src/AppShell.tsx +158 -0
- package/templates/tauri-react/src/SetupScreen.tsx +288 -0
- package/templates/tauri-react/src/auth.ts +45 -0
- package/templates/tauri-react/src/main.tsx +9 -0
- package/templates/tauri-react/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/tauri-react/src/modules/todos/todo.queries.ts +5 -0
- package/templates/tauri-react/src/modules/todos/todo.schema.ts +10 -0
- package/templates/tauri-react/src/modules/todos/useTodos.ts +31 -0
- package/templates/tauri-react/src/schema.ts +9 -0
- package/templates/tauri-react/src/sync-config.ts +103 -0
- package/templates/tauri-react/src/updater.ts +38 -0
- package/templates/tauri-react/src/vite-env.d.ts +19 -0
- package/templates/tauri-react/src-tauri/Cargo.toml +19 -0
- package/templates/tauri-react/src-tauri/build.rs +3 -0
- package/templates/tauri-react/src-tauri/capabilities/default.json +12 -0
- package/templates/tauri-react/src-tauri/icons/128x128.png +0 -0
- package/templates/tauri-react/src-tauri/icons/128x128@2x.png +0 -0
- package/templates/tauri-react/src-tauri/icons/32x32.png +0 -0
- package/templates/tauri-react/src-tauri/icons/icon.icns +0 -0
- package/templates/tauri-react/src-tauri/icons/icon.ico +0 -0
- package/templates/tauri-react/src-tauri/src/lib.rs +8 -0
- package/templates/tauri-react/src-tauri/src/main.rs +6 -0
- package/templates/tauri-react/src-tauri/tauri.conf.json +44 -0
- package/templates/tauri-react/tsconfig.json.hbs +14 -0
- package/templates/tauri-react/vite.config.ts +15 -0
- package/templates/vue-basic/AGENTS.md +84 -0
- package/templates/vue-basic/README.md.hbs +62 -0
- package/templates/vue-basic/index.html.hbs +12 -0
- package/templates/vue-basic/kora.config.ts +12 -0
- package/templates/vue-basic/package.json.hbs +32 -0
- package/templates/vue-basic/src/App.vue +117 -0
- package/templates/vue-basic/src/index.css +287 -0
- package/templates/vue-basic/src/kora-worker.ts +9 -0
- package/templates/vue-basic/src/main.ts +20 -0
- package/templates/vue-basic/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-basic/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-basic/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-basic/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-basic/src/schema.ts +9 -0
- package/templates/vue-basic/src/vite-env.d.ts +17 -0
- package/templates/vue-basic/tsconfig.json.hbs +15 -0
- package/templates/vue-basic/vite.config.ts +74 -0
- package/templates/vue-sync/.env.example +25 -0
- package/templates/vue-sync/AGENTS.md +84 -0
- package/templates/vue-sync/README.md.hbs +106 -0
- package/templates/vue-sync/index.html.hbs +12 -0
- package/templates/vue-sync/kora.config.ts +17 -0
- package/templates/vue-sync/package.json.hbs +42 -0
- package/templates/vue-sync/server.ts +141 -0
- package/templates/vue-sync/src/App.vue +135 -0
- package/templates/vue-sync/src/auth.ts +25 -0
- package/templates/vue-sync/src/index.css +339 -0
- package/templates/vue-sync/src/kora-worker.ts +9 -0
- package/templates/vue-sync/src/main.ts +47 -0
- package/templates/vue-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-sync/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-sync/src/schema.ts +9 -0
- package/templates/vue-sync/src/vite-env.d.ts +27 -0
- package/templates/vue-sync/tsconfig.json.hbs +15 -0
- package/templates/vue-sync/vite.config.ts +91 -0
- package/templates/vue-tailwind/AGENTS.md +84 -0
- package/templates/vue-tailwind/README.md.hbs +62 -0
- package/templates/vue-tailwind/index.html.hbs +12 -0
- package/templates/vue-tailwind/kora.config.ts +12 -0
- package/templates/vue-tailwind/package.json.hbs +35 -0
- package/templates/vue-tailwind/src/App.vue +135 -0
- package/templates/vue-tailwind/src/index.css +7 -0
- package/templates/vue-tailwind/src/kora-worker.ts +9 -0
- package/templates/vue-tailwind/src/main.ts +31 -0
- package/templates/vue-tailwind/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-tailwind/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-tailwind/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-tailwind/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-tailwind/src/schema.ts +9 -0
- package/templates/vue-tailwind/src/vite-env.d.ts +17 -0
- package/templates/vue-tailwind/tsconfig.json.hbs +15 -0
- package/templates/vue-tailwind/vite.config.ts +75 -0
- package/templates/vue-tailwind-sync/.env.example +25 -0
- package/templates/vue-tailwind-sync/AGENTS.md +84 -0
- package/templates/vue-tailwind-sync/README.md.hbs +106 -0
- package/templates/vue-tailwind-sync/index.html.hbs +12 -0
- package/templates/vue-tailwind-sync/kora.config.ts +17 -0
- package/templates/vue-tailwind-sync/package.json.hbs +45 -0
- package/templates/vue-tailwind-sync/server.ts +141 -0
- package/templates/vue-tailwind-sync/src/App.vue +245 -0
- package/templates/vue-tailwind-sync/src/auth.ts +25 -0
- package/templates/vue-tailwind-sync/src/index.css +7 -0
- package/templates/vue-tailwind-sync/src/kora-worker.ts +9 -0
- package/templates/vue-tailwind-sync/src/main.ts +56 -0
- package/templates/vue-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
- package/templates/vue-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
- package/templates/vue-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
- package/templates/vue-tailwind-sync/src/modules/todos/useTodos.ts +32 -0
- package/templates/vue-tailwind-sync/src/schema.ts +9 -0
- package/templates/vue-tailwind-sync/src/vite-env.d.ts +27 -0
- package/templates/vue-tailwind-sync/tsconfig.json.hbs +15 -0
- 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,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,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,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)
|