@korajs/cli 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/dist/bin.cjs +76 -7
  2. package/dist/bin.cjs.map +1 -1
  3. package/dist/bin.js +3 -3
  4. package/dist/{chunk-PSRM56B7.js → chunk-EEZNRI5W.js} +51 -6
  5. package/dist/chunk-EEZNRI5W.js.map +1 -0
  6. package/dist/{chunk-SOTZIWIF.js → chunk-Q2FBCOQD.js} +2 -2
  7. package/dist/{chunk-3WNFM3QB.js → chunk-VLTPEATY.js} +70 -4
  8. package/dist/chunk-VLTPEATY.js.map +1 -0
  9. package/dist/create.cjs +76 -7
  10. package/dist/create.cjs.map +1 -1
  11. package/dist/create.js +2 -2
  12. package/dist/index.cjs +65 -3
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +3 -3
  15. package/dist/index.d.ts +3 -3
  16. package/dist/index.js +2 -2
  17. package/package.json +6 -6
  18. package/templates/svelte-basic/.env.example +3 -0
  19. package/templates/svelte-basic/README.md.hbs +62 -0
  20. package/templates/svelte-basic/index.html.hbs +12 -0
  21. package/templates/svelte-basic/kora.config.ts +12 -0
  22. package/templates/svelte-basic/package.json.hbs +27 -0
  23. package/templates/svelte-basic/src/App.svelte +131 -0
  24. package/templates/svelte-basic/src/Root.svelte +14 -0
  25. package/templates/svelte-basic/src/index.css +287 -0
  26. package/templates/svelte-basic/src/kora-worker.ts +9 -0
  27. package/templates/svelte-basic/src/main.ts +16 -0
  28. package/templates/svelte-basic/src/modules/todos/todo.mutations.ts +25 -0
  29. package/templates/svelte-basic/src/modules/todos/todo.queries.ts +5 -0
  30. package/templates/svelte-basic/src/modules/todos/todo.schema.ts +10 -0
  31. package/templates/svelte-basic/src/modules/todos/useTodos.ts +25 -0
  32. package/templates/svelte-basic/src/schema.ts +9 -0
  33. package/templates/svelte-basic/src/vite-env.d.ts +12 -0
  34. package/templates/svelte-basic/svelte.config.js +5 -0
  35. package/templates/svelte-basic/tsconfig.json.hbs +14 -0
  36. package/templates/svelte-basic/vite.config.ts +52 -0
  37. package/templates/svelte-sync/.env.example +25 -0
  38. package/templates/svelte-sync/README.md.hbs +105 -0
  39. package/templates/svelte-sync/index.html.hbs +12 -0
  40. package/templates/svelte-sync/kora.config.ts +17 -0
  41. package/templates/svelte-sync/package.json.hbs +43 -0
  42. package/templates/svelte-sync/server.ts +90 -0
  43. package/templates/svelte-sync/src/App.svelte +154 -0
  44. package/templates/svelte-sync/src/Root.svelte +22 -0
  45. package/templates/svelte-sync/src/auth.ts +25 -0
  46. package/templates/svelte-sync/src/index.css +339 -0
  47. package/templates/svelte-sync/src/kora-worker.ts +9 -0
  48. package/templates/svelte-sync/src/main.ts +32 -0
  49. package/templates/svelte-sync/src/modules/todos/todo.mutations.ts +25 -0
  50. package/templates/svelte-sync/src/modules/todos/todo.queries.ts +5 -0
  51. package/templates/svelte-sync/src/modules/todos/todo.schema.ts +10 -0
  52. package/templates/svelte-sync/src/modules/todos/useTodos.ts +25 -0
  53. package/templates/svelte-sync/src/schema.ts +9 -0
  54. package/templates/svelte-sync/src/vite-env.d.ts +21 -0
  55. package/templates/svelte-sync/svelte.config.js +5 -0
  56. package/templates/svelte-sync/tsconfig.json.hbs +14 -0
  57. package/templates/svelte-sync/vite.config.ts +79 -0
  58. package/templates/svelte-tailwind/README.md.hbs +62 -0
  59. package/templates/svelte-tailwind/index.html.hbs +12 -0
  60. package/templates/svelte-tailwind/kora.config.ts +12 -0
  61. package/templates/svelte-tailwind/package.json.hbs +30 -0
  62. package/templates/svelte-tailwind/src/App.svelte +79 -0
  63. package/templates/svelte-tailwind/src/Root.svelte +14 -0
  64. package/templates/svelte-tailwind/src/index.css +7 -0
  65. package/templates/svelte-tailwind/src/kora-worker.ts +9 -0
  66. package/templates/svelte-tailwind/src/main.ts +16 -0
  67. package/templates/svelte-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  68. package/templates/svelte-tailwind/src/modules/todos/todo.queries.ts +5 -0
  69. package/templates/svelte-tailwind/src/modules/todos/todo.schema.ts +10 -0
  70. package/templates/svelte-tailwind/src/modules/todos/useTodos.ts +25 -0
  71. package/templates/svelte-tailwind/src/schema.ts +9 -0
  72. package/templates/svelte-tailwind/src/vite-env.d.ts +12 -0
  73. package/templates/svelte-tailwind/svelte.config.js +5 -0
  74. package/templates/svelte-tailwind/tsconfig.json.hbs +14 -0
  75. package/templates/svelte-tailwind/vite.config.ts +53 -0
  76. package/templates/svelte-tailwind-sync/.env.example +25 -0
  77. package/templates/svelte-tailwind-sync/README.md.hbs +105 -0
  78. package/templates/svelte-tailwind-sync/index.html.hbs +12 -0
  79. package/templates/svelte-tailwind-sync/kora.config.ts +17 -0
  80. package/templates/svelte-tailwind-sync/package.json.hbs +46 -0
  81. package/templates/svelte-tailwind-sync/server.ts +90 -0
  82. package/templates/svelte-tailwind-sync/src/App.svelte +196 -0
  83. package/templates/svelte-tailwind-sync/src/Root.svelte +26 -0
  84. package/templates/svelte-tailwind-sync/src/auth.ts +25 -0
  85. package/templates/svelte-tailwind-sync/src/index.css +7 -0
  86. package/templates/svelte-tailwind-sync/src/kora-worker.ts +9 -0
  87. package/templates/svelte-tailwind-sync/src/main.ts +32 -0
  88. package/templates/svelte-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  89. package/templates/svelte-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  90. package/templates/svelte-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  91. package/templates/svelte-tailwind-sync/src/modules/todos/useTodos.ts +25 -0
  92. package/templates/svelte-tailwind-sync/src/schema.ts +9 -0
  93. package/templates/svelte-tailwind-sync/src/vite-env.d.ts +21 -0
  94. package/templates/svelte-tailwind-sync/svelte.config.js +5 -0
  95. package/templates/svelte-tailwind-sync/tsconfig.json.hbs +14 -0
  96. package/templates/svelte-tailwind-sync/vite.config.ts +80 -0
  97. package/templates/vue-basic/README.md.hbs +62 -0
  98. package/templates/vue-basic/index.html.hbs +12 -0
  99. package/templates/vue-basic/kora.config.ts +12 -0
  100. package/templates/vue-basic/package.json.hbs +26 -0
  101. package/templates/vue-basic/src/App.vue +117 -0
  102. package/templates/vue-basic/src/index.css +287 -0
  103. package/templates/vue-basic/src/kora-worker.ts +9 -0
  104. package/templates/vue-basic/src/main.ts +20 -0
  105. package/templates/vue-basic/src/modules/todos/todo.mutations.ts +25 -0
  106. package/templates/vue-basic/src/modules/todos/todo.queries.ts +5 -0
  107. package/templates/vue-basic/src/modules/todos/todo.schema.ts +10 -0
  108. package/templates/vue-basic/src/modules/todos/useTodos.ts +32 -0
  109. package/templates/vue-basic/src/schema.ts +9 -0
  110. package/templates/vue-basic/src/vite-env.d.ts +17 -0
  111. package/templates/vue-basic/tsconfig.json.hbs +15 -0
  112. package/templates/vue-basic/vite.config.ts +69 -0
  113. package/templates/vue-sync/.env.example +25 -0
  114. package/templates/vue-sync/README.md.hbs +105 -0
  115. package/templates/vue-sync/index.html.hbs +12 -0
  116. package/templates/vue-sync/kora.config.ts +17 -0
  117. package/templates/vue-sync/package.json.hbs +42 -0
  118. package/templates/vue-sync/server.ts +90 -0
  119. package/templates/vue-sync/src/App.vue +135 -0
  120. package/templates/vue-sync/src/auth.ts +25 -0
  121. package/templates/vue-sync/src/index.css +339 -0
  122. package/templates/vue-sync/src/kora-worker.ts +9 -0
  123. package/templates/vue-sync/src/main.ts +43 -0
  124. package/templates/vue-sync/src/modules/todos/todo.mutations.ts +25 -0
  125. package/templates/vue-sync/src/modules/todos/todo.queries.ts +5 -0
  126. package/templates/vue-sync/src/modules/todos/todo.schema.ts +10 -0
  127. package/templates/vue-sync/src/modules/todos/useTodos.ts +32 -0
  128. package/templates/vue-sync/src/schema.ts +9 -0
  129. package/templates/vue-sync/src/vite-env.d.ts +26 -0
  130. package/templates/vue-sync/tsconfig.json.hbs +15 -0
  131. package/templates/vue-sync/vite.config.ts +79 -0
  132. package/templates/vue-tailwind/README.md.hbs +62 -0
  133. package/templates/vue-tailwind/index.html.hbs +12 -0
  134. package/templates/vue-tailwind/kora.config.ts +12 -0
  135. package/templates/vue-tailwind/package.json.hbs +29 -0
  136. package/templates/vue-tailwind/src/App.vue +142 -0
  137. package/templates/vue-tailwind/src/index.css +7 -0
  138. package/templates/vue-tailwind/src/kora-worker.ts +9 -0
  139. package/templates/vue-tailwind/src/main.ts +31 -0
  140. package/templates/vue-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  141. package/templates/vue-tailwind/src/modules/todos/todo.queries.ts +5 -0
  142. package/templates/vue-tailwind/src/modules/todos/todo.schema.ts +10 -0
  143. package/templates/vue-tailwind/src/modules/todos/useTodos.ts +32 -0
  144. package/templates/vue-tailwind/src/schema.ts +9 -0
  145. package/templates/vue-tailwind/src/vite-env.d.ts +17 -0
  146. package/templates/vue-tailwind/tsconfig.json.hbs +15 -0
  147. package/templates/vue-tailwind/vite.config.ts +70 -0
  148. package/templates/vue-tailwind-sync/.env.example +25 -0
  149. package/templates/vue-tailwind-sync/README.md.hbs +105 -0
  150. package/templates/vue-tailwind-sync/index.html.hbs +12 -0
  151. package/templates/vue-tailwind-sync/kora.config.ts +17 -0
  152. package/templates/vue-tailwind-sync/package.json.hbs +45 -0
  153. package/templates/vue-tailwind-sync/server.ts +90 -0
  154. package/templates/vue-tailwind-sync/src/App.vue +245 -0
  155. package/templates/vue-tailwind-sync/src/auth.ts +25 -0
  156. package/templates/vue-tailwind-sync/src/index.css +7 -0
  157. package/templates/vue-tailwind-sync/src/kora-worker.ts +9 -0
  158. package/templates/vue-tailwind-sync/src/main.ts +56 -0
  159. package/templates/vue-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  160. package/templates/vue-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  161. package/templates/vue-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  162. package/templates/vue-tailwind-sync/src/modules/todos/useTodos.ts +32 -0
  163. package/templates/vue-tailwind-sync/src/schema.ts +9 -0
  164. package/templates/vue-tailwind-sync/src/vite-env.d.ts +26 -0
  165. package/templates/vue-tailwind-sync/tsconfig.json.hbs +15 -0
  166. package/templates/vue-tailwind-sync/vite.config.ts +80 -0
  167. package/dist/chunk-3WNFM3QB.js.map +0 -1
  168. package/dist/chunk-PSRM56B7.js.map +0 -1
  169. /package/dist/{chunk-SOTZIWIF.js.map → chunk-Q2FBCOQD.js.map} +0 -0
@@ -0,0 +1,339 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ margin: 0;
6
+ padding: 0;
7
+ }
8
+
9
+ :root {
10
+ --color-bg: #0a0a0f;
11
+ --color-surface: #111118;
12
+ --color-border: #1e1e2a;
13
+ --color-border-hover: #2a2a3a;
14
+ --color-text: #e4e4e7;
15
+ --color-text-muted: #71717a;
16
+ --color-text-dim: #3f3f46;
17
+ --color-primary: #6366f1;
18
+ --color-primary-hover: #818cf8;
19
+ --color-success: #34d399;
20
+ --color-warning: #fbbf24;
21
+ --color-danger: #f87171;
22
+ --font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
23
+ --radius: 8px;
24
+ }
25
+
26
+ body {
27
+ font-family: var(--font-sans);
28
+ background: var(--color-bg);
29
+ color: var(--color-text);
30
+ line-height: 1.6;
31
+ -webkit-font-smoothing: antialiased;
32
+ }
33
+
34
+ .app {
35
+ max-width: 640px;
36
+ margin: 0 auto;
37
+ padding: 48px 16px;
38
+ }
39
+
40
+ .header {
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: space-between;
44
+ margin-bottom: 32px;
45
+ }
46
+
47
+ .header h1 {
48
+ font-size: 1.5rem;
49
+ font-weight: 700;
50
+ }
51
+
52
+ .header-actions {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 8px;
56
+ }
57
+
58
+ .sync-badge {
59
+ display: inline-flex;
60
+ align-items: center;
61
+ gap: 6px;
62
+ padding: 4px 12px;
63
+ border-radius: 999px;
64
+ background: var(--color-surface);
65
+ font-size: 0.8rem;
66
+ }
67
+
68
+ .sync-dot {
69
+ width: 8px;
70
+ height: 8px;
71
+ border-radius: 50%;
72
+ }
73
+
74
+ .sync-dot.connected,
75
+ .sync-dot.synced {
76
+ background: var(--color-success);
77
+ }
78
+ .sync-dot.syncing {
79
+ background: var(--color-warning);
80
+ }
81
+ .sync-dot.offline {
82
+ background: var(--color-text-muted);
83
+ }
84
+ .sync-dot.error {
85
+ background: var(--color-danger);
86
+ }
87
+
88
+ .auth-button {
89
+ border: 1px solid var(--color-border);
90
+ border-radius: 999px;
91
+ background: var(--color-surface);
92
+ color: var(--color-text);
93
+ cursor: pointer;
94
+ font-size: 0.8rem;
95
+ max-width: 180px;
96
+ overflow: hidden;
97
+ padding: 4px 12px;
98
+ text-overflow: ellipsis;
99
+ white-space: nowrap;
100
+ }
101
+
102
+ .auth-button:hover {
103
+ border-color: var(--color-border-hover);
104
+ }
105
+
106
+ .auth-error {
107
+ border: 1px solid rgba(248, 113, 113, 0.35);
108
+ border-radius: var(--radius);
109
+ color: var(--color-danger);
110
+ font-size: 0.85rem;
111
+ margin-bottom: 16px;
112
+ padding: 10px 12px;
113
+ }
114
+
115
+ .stats {
116
+ display: grid;
117
+ grid-template-columns: repeat(3, 1fr);
118
+ gap: 12px;
119
+ margin-bottom: 32px;
120
+ }
121
+
122
+ .stat-card {
123
+ background: var(--color-surface);
124
+ border: 1px solid var(--color-border);
125
+ border-radius: var(--radius);
126
+ padding: 16px;
127
+ }
128
+
129
+ .stat-card .label {
130
+ font-size: 0.8rem;
131
+ color: var(--color-text-muted);
132
+ }
133
+
134
+ .stat-card .value {
135
+ font-size: 1.5rem;
136
+ font-weight: 700;
137
+ }
138
+
139
+ .stat-card .value.muted {
140
+ color: var(--color-text-muted);
141
+ }
142
+ .stat-card .value.warning {
143
+ color: var(--color-warning);
144
+ }
145
+ .stat-card .value.success {
146
+ color: var(--color-success);
147
+ }
148
+
149
+ .add-form {
150
+ display: flex;
151
+ gap: 12px;
152
+ margin-bottom: 32px;
153
+ }
154
+
155
+ .add-form input {
156
+ flex: 1;
157
+ padding: 10px 16px;
158
+ border: 1px solid var(--color-border);
159
+ border-radius: var(--radius);
160
+ background: var(--color-surface);
161
+ color: var(--color-text);
162
+ font-size: 0.95rem;
163
+ outline: none;
164
+ }
165
+
166
+ .add-form input:focus {
167
+ border-color: var(--color-primary);
168
+ }
169
+
170
+ .add-form input::placeholder {
171
+ color: var(--color-text-muted);
172
+ }
173
+
174
+ .add-form button {
175
+ padding: 10px 20px;
176
+ border: none;
177
+ border-radius: var(--radius);
178
+ background: var(--color-primary);
179
+ color: white;
180
+ font-size: 0.95rem;
181
+ font-weight: 500;
182
+ cursor: pointer;
183
+ }
184
+
185
+ .add-form button:hover {
186
+ background: var(--color-primary-hover);
187
+ }
188
+
189
+ .add-form button:disabled {
190
+ opacity: 0.5;
191
+ cursor: not-allowed;
192
+ }
193
+
194
+ .filters {
195
+ display: flex;
196
+ gap: 8px;
197
+ margin-bottom: 24px;
198
+ }
199
+
200
+ .filter-btn {
201
+ padding: 6px 16px;
202
+ border: none;
203
+ border-radius: var(--radius);
204
+ background: var(--color-surface);
205
+ color: var(--color-text-muted);
206
+ font-size: 0.85rem;
207
+ font-weight: 500;
208
+ cursor: pointer;
209
+ }
210
+
211
+ .filter-btn:hover {
212
+ background: var(--color-border-hover);
213
+ color: var(--color-text);
214
+ }
215
+
216
+ .filter-btn.active {
217
+ background: var(--color-primary);
218
+ color: white;
219
+ }
220
+
221
+ .filter-btn .badge {
222
+ display: inline-block;
223
+ margin-left: 6px;
224
+ padding: 1px 6px;
225
+ border-radius: 999px;
226
+ font-size: 0.75rem;
227
+ background: rgba(255, 255, 255, 0.15);
228
+ }
229
+
230
+ .filter-btn:not(.active) .badge {
231
+ background: var(--color-border);
232
+ }
233
+
234
+ .todo-list {
235
+ display: flex;
236
+ flex-direction: column;
237
+ gap: 8px;
238
+ }
239
+
240
+ .todo-item {
241
+ display: flex;
242
+ align-items: center;
243
+ gap: 12px;
244
+ padding: 12px 16px;
245
+ border: 1px solid var(--color-border);
246
+ border-radius: var(--radius);
247
+ background: var(--color-surface);
248
+ }
249
+
250
+ .todo-item:hover {
251
+ border-color: var(--color-border-hover);
252
+ }
253
+
254
+ .todo-item .toggle {
255
+ background: none;
256
+ border: 2px solid var(--color-text-muted);
257
+ width: 20px;
258
+ height: 20px;
259
+ border-radius: 50%;
260
+ cursor: pointer;
261
+ display: flex;
262
+ align-items: center;
263
+ justify-content: center;
264
+ color: transparent;
265
+ font-size: 0.7rem;
266
+ flex-shrink: 0;
267
+ }
268
+
269
+ .todo-item .toggle.checked {
270
+ border-color: var(--color-success);
271
+ background: var(--color-success);
272
+ color: white;
273
+ }
274
+
275
+ .todo-item .title {
276
+ flex: 1;
277
+ }
278
+
279
+ .todo-item .title.done {
280
+ text-decoration: line-through;
281
+ color: var(--color-text-muted);
282
+ }
283
+
284
+ .todo-item .time {
285
+ font-size: 0.75rem;
286
+ color: var(--color-text-dim);
287
+ }
288
+
289
+ .todo-item .delete-btn {
290
+ background: none;
291
+ border: none;
292
+ color: var(--color-text-dim);
293
+ cursor: pointer;
294
+ padding: 4px;
295
+ opacity: 0;
296
+ transition: opacity 0.15s;
297
+ }
298
+
299
+ .todo-item:hover .delete-btn {
300
+ opacity: 1;
301
+ }
302
+
303
+ .todo-item .delete-btn:hover {
304
+ color: var(--color-danger);
305
+ }
306
+
307
+ .empty-state {
308
+ padding: 48px 0;
309
+ text-align: center;
310
+ color: var(--color-text-dim);
311
+ border: 1px dashed var(--color-border);
312
+ border-radius: var(--radius);
313
+ }
314
+
315
+ .footer {
316
+ display: flex;
317
+ justify-content: space-between;
318
+ margin-top: 24px;
319
+ font-size: 0.85rem;
320
+ color: var(--color-text-muted);
321
+ }
322
+
323
+ .footer button {
324
+ background: none;
325
+ border: none;
326
+ color: var(--color-text-muted);
327
+ cursor: pointer;
328
+ }
329
+
330
+ .footer button:hover {
331
+ color: var(--color-text);
332
+ }
333
+
334
+ .branding {
335
+ margin-top: 48px;
336
+ text-align: center;
337
+ font-size: 0.75rem;
338
+ color: var(--color-text-dim);
339
+ }
@@ -0,0 +1,9 @@
1
+ // Web Worker entry point for SQLite WASM.
2
+ // Loaded automatically by the Kora store to run SQLite in a background thread.
3
+
4
+ // Import the WASM binary URL so Vite resolves it with the correct content hash.
5
+ // Without this, production builds fail because sqlite3 looks for the unhashed filename.
6
+ import sqliteWasmUrl from '@sqlite.org/sqlite-wasm/sqlite3.wasm?url'
7
+ ;(globalThis as Record<string, unknown>).__KORA_SQLITE_WASM_URL = sqliteWasmUrl
8
+
9
+ import '@korajs/store/sqlite-wasm/worker'
@@ -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: true,
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 { getCollection, createMutation, createQueryStore } 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,21 @@
1
+ /// <reference types="svelte" />
2
+ /// <reference types="vite/client" />
3
+
4
+ interface ImportMetaEnv {
5
+ readonly VITE_SYNC_URL?: string
6
+ readonly VITE_AUTH_URL?: string
7
+ }
8
+
9
+ interface ImportMeta {
10
+ readonly env: ImportMetaEnv
11
+ }
12
+
13
+ declare module '*?worker&url' {
14
+ const url: string
15
+ export default url
16
+ }
17
+
18
+ declare module '*?url' {
19
+ const url: string
20
+ export default url
21
+ }
@@ -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,79 @@
1
+ import { copyFileSync, existsSync, readdirSync } from 'node:fs'
2
+ import { join, resolve } from 'node:path'
3
+ import { svelte } from '@sveltejs/vite-plugin-svelte'
4
+ import type { Plugin } from 'vite'
5
+ import { defineConfig } from 'vite'
6
+
7
+ function crossOriginIsolation(): Plugin {
8
+ return {
9
+ name: 'cross-origin-isolation',
10
+ configureServer(server) {
11
+ server.middlewares.use((_req, res, next) => {
12
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
13
+ res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
14
+ next()
15
+ })
16
+ },
17
+ configurePreviewServer(server) {
18
+ server.middlewares.use((_req, res, next) => {
19
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
20
+ res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
21
+ next()
22
+ })
23
+ },
24
+ }
25
+ }
26
+
27
+ function sqliteWasmHotfix(): Plugin {
28
+ return {
29
+ name: 'sqlite-wasm-hotfix',
30
+ apply: 'build',
31
+ closeBundle() {
32
+ const assetsDir = resolve('dist', 'assets')
33
+ if (!existsSync(assetsDir)) return
34
+
35
+ for (const file of readdirSync(assetsDir)) {
36
+ if (/^sqlite3-.+\.wasm$/.test(file)) {
37
+ copyFileSync(join(assetsDir, file), join(assetsDir, 'sqlite3.wasm'))
38
+ break
39
+ }
40
+ }
41
+
42
+ const proxyFile = resolve(
43
+ 'node_modules',
44
+ '@sqlite.org',
45
+ 'sqlite-wasm',
46
+ 'sqlite-wasm',
47
+ 'jswasm',
48
+ 'sqlite3-opfs-async-proxy.js',
49
+ )
50
+ if (existsSync(proxyFile)) {
51
+ copyFileSync(proxyFile, join(assetsDir, 'sqlite3-opfs-async-proxy.js'))
52
+ }
53
+ },
54
+ }
55
+ }
56
+
57
+ export default defineConfig({
58
+ plugins: [svelte(), crossOriginIsolation(), sqliteWasmHotfix()],
59
+ worker: {
60
+ format: 'es',
61
+ },
62
+ optimizeDeps: {
63
+ exclude: ['@sqlite.org/sqlite-wasm', '@korajs/store'],
64
+ include: ['yjs'],
65
+ },
66
+ resolve: {
67
+ dedupe: ['yjs'],
68
+ },
69
+ server: {
70
+ allowedHosts: true,
71
+ proxy: {
72
+ '/kora-sync': {
73
+ target: 'ws://localhost:3001',
74
+ ws: true,
75
+ rewriteWsOrigin: true,
76
+ },
77
+ },
78
+ },
79
+ })
@@ -0,0 +1,62 @@
1
+ # {{projectName}}
2
+
3
+ An offline-first application built with [Kora.js](https://ehoneahobed.github.io/kora/).
4
+
5
+ ## Getting Started
6
+
7
+ ```bash
8
+ npm run dev
9
+ ```
10
+
11
+ - **App**: http://localhost:5173
12
+
13
+ ## Project Structure
14
+
15
+ ```
16
+ src/
17
+ schema.ts # Schema entry point
18
+ main.tsx # App initialization and Kora setup
19
+ App.tsx # Main application component
20
+ modules/
21
+ todos/
22
+ todo.schema.ts # Todo collection definition
23
+ todo.queries.ts # Reusable todo query builders
24
+ todo.mutations.ts # Todo mutation helpers
25
+ useTodos.ts # React hook connecting todos to the UI
26
+ kora-worker.ts # SQLite WASM web worker
27
+ index.css # Styles
28
+ kora.config.ts # Kora configuration
29
+ ```
30
+
31
+ In a feature module, `schema` defines data shape, `queries` contain reads only, `mutations` contain
32
+ writes, and `useTodos.ts` is the React binding that exposes todo data and actions to components.
33
+
34
+ ## Scripts
35
+
36
+ | Script | Description |
37
+ |--------|-------------|
38
+ | `npm run dev` | Start dev server |
39
+ | `npm run build` | Build for production |
40
+ | `npm run preview` | Preview production build |
41
+
42
+ ## Adding Sync
43
+
44
+ To enable real-time sync across devices, update `src/main.tsx`:
45
+
46
+ ```typescript
47
+ const app = createApp({
48
+ schema,
49
+ sync: {
50
+ url: import.meta.env.VITE_SYNC_URL || 'ws://localhost:3001',
51
+ },
52
+ // ...
53
+ })
54
+ ```
55
+
56
+ Then create a `server.ts` and install `@korajs/server`. See the [Sync Configuration guide](https://ehoneahobed.github.io/kora/guide/sync-configuration).
57
+
58
+ ## Learn More
59
+
60
+ - [Documentation](https://ehoneahobed.github.io/kora/)
61
+ - [Storage Configuration](https://ehoneahobed.github.io/kora/guide/storage-configuration)
62
+ - [Schema Design](https://ehoneahobed.github.io/kora/guide/schema-design)
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{projectName}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'korajs/config'
2
+
3
+ export default defineConfig({
4
+ schema: './src/schema.ts',
5
+ dev: {
6
+ port: 5173,
7
+ watch: {
8
+ enabled: true,
9
+ debounceMs: 300,
10
+ },
11
+ },
12
+ })
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "kora dev",
8
+ "build": "vite build",
9
+ "check": "svelte-check --tsconfig ./tsconfig.json",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "korajs": "{{koraVersion}}",
14
+ "@korajs/svelte": "{{koraVersion}}",
15
+ "@korajs/store": "{{koraVersion}}",
16
+ "@lucide/svelte": "^0.468.0",
17
+ "@sqlite.org/sqlite-wasm": ">=3.51.0-build1",
18
+ "svelte": "^5.0.0",
19
+ "yjs": "^13.6.30"
20
+ },
21
+ "devDependencies": {
22
+ "@korajs/cli": "{{koraVersion}}",
23
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
24
+ "@tailwindcss/vite": "^4.0.0",
25
+ "svelte-check": "^4.1.0",
26
+ "tailwindcss": "^4.0.0",
27
+ "typescript": "^5.7.0",
28
+ "vite": "^6.0.0"
29
+ }
30
+ }