@korajs/cli 0.4.0 → 0.5.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 (103) hide show
  1. package/dist/bin.cjs +1609 -643
  2. package/dist/bin.cjs.map +1 -1
  3. package/dist/bin.js +1086 -139
  4. package/dist/bin.js.map +1 -1
  5. package/dist/{chunk-CMSX76KM.js → chunk-3WNFM3QB.js} +103 -96
  6. package/dist/chunk-3WNFM3QB.js.map +1 -0
  7. package/dist/{chunk-MVP5PDT4.js → chunk-PSRM56B7.js} +2 -2
  8. package/dist/{chunk-YGVO4POI.js → chunk-SOTZIWIF.js} +27 -15
  9. package/dist/chunk-SOTZIWIF.js.map +1 -0
  10. package/dist/create.cjs +54 -47
  11. package/dist/create.cjs.map +1 -1
  12. package/dist/create.js +2 -2
  13. package/dist/index.cjs +78 -59
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +5 -3
  16. package/dist/index.d.ts +5 -3
  17. package/dist/index.js +2 -2
  18. package/package.json +9 -3
  19. package/templates/react-basic/README.md.hbs +10 -1
  20. package/templates/react-basic/src/App.tsx +112 -126
  21. package/templates/react-basic/src/index.css +165 -159
  22. package/templates/react-basic/src/kora-worker.ts +0 -1
  23. package/templates/react-basic/src/main.tsx +12 -12
  24. package/templates/react-basic/src/modules/todos/todo.mutations.ts +25 -0
  25. package/templates/react-basic/src/modules/todos/todo.queries.ts +5 -0
  26. package/templates/react-basic/src/modules/todos/todo.schema.ts +10 -0
  27. package/templates/react-basic/src/modules/todos/useTodos.ts +31 -0
  28. package/templates/react-basic/src/schema.ts +6 -12
  29. package/templates/react-basic/src/vite-env.d.ts +17 -0
  30. package/templates/react-basic/{tsconfig.json → tsconfig.json.hbs} +1 -2
  31. package/templates/react-basic/vite.config.ts +57 -50
  32. package/templates/react-sync/.env.example +19 -0
  33. package/templates/react-sync/README.md.hbs +43 -1
  34. package/templates/react-sync/package.json.hbs +3 -0
  35. package/templates/react-sync/server.ts +90 -26
  36. package/templates/react-sync/src/App.tsx +132 -130
  37. package/templates/react-sync/src/auth.ts +25 -0
  38. package/templates/react-sync/src/index.css +212 -165
  39. package/templates/react-sync/src/kora-worker.ts +0 -1
  40. package/templates/react-sync/src/main.tsx +34 -18
  41. package/templates/react-sync/src/modules/todos/todo.mutations.ts +25 -0
  42. package/templates/react-sync/src/modules/todos/todo.queries.ts +5 -0
  43. package/templates/react-sync/src/modules/todos/todo.schema.ts +10 -0
  44. package/templates/react-sync/src/modules/todos/useTodos.ts +31 -0
  45. package/templates/react-sync/src/schema.ts +6 -12
  46. package/templates/react-sync/src/vite-env.d.ts +17 -0
  47. package/templates/react-sync/{tsconfig.json → tsconfig.json.hbs} +1 -2
  48. package/templates/react-sync/vite.config.ts +67 -60
  49. package/templates/react-tailwind/README.md.hbs +10 -1
  50. package/templates/react-tailwind/src/App.tsx +186 -197
  51. package/templates/react-tailwind/src/index.css +3 -3
  52. package/templates/react-tailwind/src/kora-worker.ts +0 -1
  53. package/templates/react-tailwind/src/main.tsx +19 -12
  54. package/templates/react-tailwind/src/modules/todos/todo.mutations.ts +25 -0
  55. package/templates/react-tailwind/src/modules/todos/todo.queries.ts +5 -0
  56. package/templates/react-tailwind/src/modules/todos/todo.schema.ts +10 -0
  57. package/templates/react-tailwind/src/modules/todos/useTodos.ts +31 -0
  58. package/templates/react-tailwind/src/schema.ts +6 -12
  59. package/templates/react-tailwind/src/vite-env.d.ts +17 -0
  60. package/templates/react-tailwind/{tsconfig.json → tsconfig.json.hbs} +1 -2
  61. package/templates/react-tailwind/vite.config.ts +57 -50
  62. package/templates/react-tailwind-sync/.env.example +19 -0
  63. package/templates/react-tailwind-sync/README.md.hbs +43 -1
  64. package/templates/react-tailwind-sync/package.json.hbs +3 -0
  65. package/templates/react-tailwind-sync/server.ts +90 -26
  66. package/templates/react-tailwind-sync/src/App.tsx +246 -222
  67. package/templates/react-tailwind-sync/src/auth.ts +25 -0
  68. package/templates/react-tailwind-sync/src/index.css +3 -3
  69. package/templates/react-tailwind-sync/src/kora-worker.ts +0 -1
  70. package/templates/react-tailwind-sync/src/main.tsx +48 -18
  71. package/templates/react-tailwind-sync/src/modules/todos/todo.mutations.ts +25 -0
  72. package/templates/react-tailwind-sync/src/modules/todos/todo.queries.ts +5 -0
  73. package/templates/react-tailwind-sync/src/modules/todos/todo.schema.ts +10 -0
  74. package/templates/react-tailwind-sync/src/modules/todos/useTodos.ts +31 -0
  75. package/templates/react-tailwind-sync/src/schema.ts +6 -12
  76. package/templates/react-tailwind-sync/src/vite-env.d.ts +17 -0
  77. package/templates/react-tailwind-sync/{tsconfig.json → tsconfig.json.hbs} +1 -2
  78. package/templates/react-tailwind-sync/vite.config.ts +67 -60
  79. package/templates/tauri-react/.env.example +20 -0
  80. package/templates/tauri-react/README.md.hbs +36 -1
  81. package/templates/tauri-react/dev.ts +11 -11
  82. package/templates/tauri-react/package.json.hbs +2 -0
  83. package/templates/tauri-react/server.ts +86 -12
  84. package/templates/tauri-react/src/App.tsx +769 -544
  85. package/templates/tauri-react/src/AppShell.tsx +125 -83
  86. package/templates/tauri-react/src/SetupScreen.tsx +257 -208
  87. package/templates/tauri-react/src/auth.ts +45 -0
  88. package/templates/tauri-react/src/main.tsx +3 -3
  89. package/templates/tauri-react/src/modules/todos/todo.mutations.ts +25 -0
  90. package/templates/tauri-react/src/modules/todos/todo.queries.ts +5 -0
  91. package/templates/tauri-react/src/modules/todos/todo.schema.ts +10 -0
  92. package/templates/tauri-react/src/modules/todos/useTodos.ts +31 -0
  93. package/templates/tauri-react/src/schema.ts +6 -12
  94. package/templates/tauri-react/src/sync-config.ts +47 -47
  95. package/templates/tauri-react/src/updater.ts +15 -15
  96. package/templates/tauri-react/src/vite-env.d.ts +17 -0
  97. package/templates/tauri-react/src-tauri/tauri.conf.json +1 -1
  98. package/templates/tauri-react/tsconfig.json.hbs +14 -0
  99. package/templates/tauri-react/vite.config.ts +9 -9
  100. package/dist/chunk-CMSX76KM.js.map +0 -1
  101. package/dist/chunk-YGVO4POI.js.map +0 -1
  102. package/templates/tauri-react/tsconfig.json +0 -15
  103. /package/dist/{chunk-MVP5PDT4.js.map → chunk-PSRM56B7.js.map} +0 -0
@@ -1,292 +1,339 @@
1
1
  *,
2
2
  *::before,
3
3
  *::after {
4
- box-sizing: border-box;
5
- margin: 0;
6
- padding: 0;
4
+ box-sizing: border-box;
5
+ margin: 0;
6
+ padding: 0;
7
7
  }
8
8
 
9
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;
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
24
  }
25
25
 
26
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;
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
32
  }
33
33
 
34
34
  .app {
35
- max-width: 640px;
36
- margin: 0 auto;
37
- padding: 48px 16px;
35
+ max-width: 640px;
36
+ margin: 0 auto;
37
+ padding: 48px 16px;
38
38
  }
39
39
 
40
40
  .header {
41
- display: flex;
42
- align-items: center;
43
- justify-content: space-between;
44
- margin-bottom: 32px;
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: space-between;
44
+ margin-bottom: 32px;
45
45
  }
46
46
 
47
47
  .header h1 {
48
- font-size: 1.5rem;
49
- font-weight: 700;
48
+ font-size: 1.5rem;
49
+ font-weight: 700;
50
+ }
51
+
52
+ .header-actions {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 8px;
50
56
  }
51
57
 
52
58
  .sync-badge {
53
- display: inline-flex;
54
- align-items: center;
55
- gap: 6px;
56
- padding: 4px 12px;
57
- border-radius: 999px;
58
- background: var(--color-surface);
59
- font-size: 0.8rem;
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;
60
66
  }
61
67
 
62
68
  .sync-dot {
63
- width: 8px;
64
- height: 8px;
65
- border-radius: 50%;
69
+ width: 8px;
70
+ height: 8px;
71
+ border-radius: 50%;
66
72
  }
67
73
 
68
74
  .sync-dot.connected,
69
- .sync-dot.synced { background: var(--color-success); }
70
- .sync-dot.syncing { background: var(--color-warning); }
71
- .sync-dot.offline { background: var(--color-text-muted); }
72
- .sync-dot.error { background: var(--color-danger); }
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
+ }
73
114
 
74
115
  .stats {
75
- display: grid;
76
- grid-template-columns: repeat(3, 1fr);
77
- gap: 12px;
78
- margin-bottom: 32px;
116
+ display: grid;
117
+ grid-template-columns: repeat(3, 1fr);
118
+ gap: 12px;
119
+ margin-bottom: 32px;
79
120
  }
80
121
 
81
122
  .stat-card {
82
- background: var(--color-surface);
83
- border: 1px solid var(--color-border);
84
- border-radius: var(--radius);
85
- padding: 16px;
123
+ background: var(--color-surface);
124
+ border: 1px solid var(--color-border);
125
+ border-radius: var(--radius);
126
+ padding: 16px;
86
127
  }
87
128
 
88
129
  .stat-card .label {
89
- font-size: 0.8rem;
90
- color: var(--color-text-muted);
130
+ font-size: 0.8rem;
131
+ color: var(--color-text-muted);
91
132
  }
92
133
 
93
134
  .stat-card .value {
94
- font-size: 1.5rem;
95
- font-weight: 700;
135
+ font-size: 1.5rem;
136
+ font-weight: 700;
96
137
  }
97
138
 
98
- .stat-card .value.muted { color: var(--color-text-muted); }
99
- .stat-card .value.warning { color: var(--color-warning); }
100
- .stat-card .value.success { color: var(--color-success); }
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
+ }
101
148
 
102
149
  .add-form {
103
- display: flex;
104
- gap: 12px;
105
- margin-bottom: 32px;
150
+ display: flex;
151
+ gap: 12px;
152
+ margin-bottom: 32px;
106
153
  }
107
154
 
108
155
  .add-form input {
109
- flex: 1;
110
- padding: 10px 16px;
111
- border: 1px solid var(--color-border);
112
- border-radius: var(--radius);
113
- background: var(--color-surface);
114
- color: var(--color-text);
115
- font-size: 0.95rem;
116
- outline: none;
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;
117
164
  }
118
165
 
119
166
  .add-form input:focus {
120
- border-color: var(--color-primary);
167
+ border-color: var(--color-primary);
121
168
  }
122
169
 
123
170
  .add-form input::placeholder {
124
- color: var(--color-text-muted);
171
+ color: var(--color-text-muted);
125
172
  }
126
173
 
127
174
  .add-form button {
128
- padding: 10px 20px;
129
- border: none;
130
- border-radius: var(--radius);
131
- background: var(--color-primary);
132
- color: white;
133
- font-size: 0.95rem;
134
- font-weight: 500;
135
- cursor: pointer;
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;
136
183
  }
137
184
 
138
185
  .add-form button:hover {
139
- background: var(--color-primary-hover);
186
+ background: var(--color-primary-hover);
140
187
  }
141
188
 
142
189
  .add-form button:disabled {
143
- opacity: 0.5;
144
- cursor: not-allowed;
190
+ opacity: 0.5;
191
+ cursor: not-allowed;
145
192
  }
146
193
 
147
194
  .filters {
148
- display: flex;
149
- gap: 8px;
150
- margin-bottom: 24px;
195
+ display: flex;
196
+ gap: 8px;
197
+ margin-bottom: 24px;
151
198
  }
152
199
 
153
200
  .filter-btn {
154
- padding: 6px 16px;
155
- border: none;
156
- border-radius: var(--radius);
157
- background: var(--color-surface);
158
- color: var(--color-text-muted);
159
- font-size: 0.85rem;
160
- font-weight: 500;
161
- cursor: pointer;
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;
162
209
  }
163
210
 
164
211
  .filter-btn:hover {
165
- background: var(--color-border-hover);
166
- color: var(--color-text);
212
+ background: var(--color-border-hover);
213
+ color: var(--color-text);
167
214
  }
168
215
 
169
216
  .filter-btn.active {
170
- background: var(--color-primary);
171
- color: white;
217
+ background: var(--color-primary);
218
+ color: white;
172
219
  }
173
220
 
174
221
  .filter-btn .badge {
175
- display: inline-block;
176
- margin-left: 6px;
177
- padding: 1px 6px;
178
- border-radius: 999px;
179
- font-size: 0.75rem;
180
- background: rgba(255, 255, 255, 0.15);
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);
181
228
  }
182
229
 
183
230
  .filter-btn:not(.active) .badge {
184
- background: var(--color-border);
231
+ background: var(--color-border);
185
232
  }
186
233
 
187
234
  .todo-list {
188
- display: flex;
189
- flex-direction: column;
190
- gap: 8px;
235
+ display: flex;
236
+ flex-direction: column;
237
+ gap: 8px;
191
238
  }
192
239
 
193
240
  .todo-item {
194
- display: flex;
195
- align-items: center;
196
- gap: 12px;
197
- padding: 12px 16px;
198
- border: 1px solid var(--color-border);
199
- border-radius: var(--radius);
200
- background: var(--color-surface);
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);
201
248
  }
202
249
 
203
250
  .todo-item:hover {
204
- border-color: var(--color-border-hover);
251
+ border-color: var(--color-border-hover);
205
252
  }
206
253
 
207
254
  .todo-item .toggle {
208
- background: none;
209
- border: 2px solid var(--color-text-muted);
210
- width: 20px;
211
- height: 20px;
212
- border-radius: 50%;
213
- cursor: pointer;
214
- display: flex;
215
- align-items: center;
216
- justify-content: center;
217
- color: transparent;
218
- font-size: 0.7rem;
219
- flex-shrink: 0;
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;
220
267
  }
221
268
 
222
269
  .todo-item .toggle.checked {
223
- border-color: var(--color-success);
224
- background: var(--color-success);
225
- color: white;
270
+ border-color: var(--color-success);
271
+ background: var(--color-success);
272
+ color: white;
226
273
  }
227
274
 
228
275
  .todo-item .title {
229
- flex: 1;
276
+ flex: 1;
230
277
  }
231
278
 
232
279
  .todo-item .title.done {
233
- text-decoration: line-through;
234
- color: var(--color-text-muted);
280
+ text-decoration: line-through;
281
+ color: var(--color-text-muted);
235
282
  }
236
283
 
237
284
  .todo-item .time {
238
- font-size: 0.75rem;
239
- color: var(--color-text-dim);
285
+ font-size: 0.75rem;
286
+ color: var(--color-text-dim);
240
287
  }
241
288
 
242
289
  .todo-item .delete-btn {
243
- background: none;
244
- border: none;
245
- color: var(--color-text-dim);
246
- cursor: pointer;
247
- padding: 4px;
248
- opacity: 0;
249
- transition: opacity 0.15s;
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;
250
297
  }
251
298
 
252
299
  .todo-item:hover .delete-btn {
253
- opacity: 1;
300
+ opacity: 1;
254
301
  }
255
302
 
256
303
  .todo-item .delete-btn:hover {
257
- color: var(--color-danger);
304
+ color: var(--color-danger);
258
305
  }
259
306
 
260
307
  .empty-state {
261
- padding: 48px 0;
262
- text-align: center;
263
- color: var(--color-text-dim);
264
- border: 1px dashed var(--color-border);
265
- border-radius: var(--radius);
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);
266
313
  }
267
314
 
268
315
  .footer {
269
- display: flex;
270
- justify-content: space-between;
271
- margin-top: 24px;
272
- font-size: 0.85rem;
273
- color: var(--color-text-muted);
316
+ display: flex;
317
+ justify-content: space-between;
318
+ margin-top: 24px;
319
+ font-size: 0.85rem;
320
+ color: var(--color-text-muted);
274
321
  }
275
322
 
276
323
  .footer button {
277
- background: none;
278
- border: none;
279
- color: var(--color-text-muted);
280
- cursor: pointer;
324
+ background: none;
325
+ border: none;
326
+ color: var(--color-text-muted);
327
+ cursor: pointer;
281
328
  }
282
329
 
283
330
  .footer button:hover {
284
- color: var(--color-text);
331
+ color: var(--color-text);
285
332
  }
286
333
 
287
334
  .branding {
288
- margin-top: 48px;
289
- text-align: center;
290
- font-size: 0.75rem;
291
- color: var(--color-text-dim);
335
+ margin-top: 48px;
336
+ text-align: center;
337
+ font-size: 0.75rem;
338
+ color: var(--color-text-dim);
292
339
  }
@@ -4,7 +4,6 @@
4
4
  // Import the WASM binary URL so Vite resolves it with the correct content hash.
5
5
  // Without this, production builds fail because sqlite3 looks for the unhashed filename.
6
6
  import sqliteWasmUrl from '@sqlite.org/sqlite-wasm/sqlite3.wasm?url'
7
-
8
7
  ;(globalThis as Record<string, unknown>).__KORA_SQLITE_WASM_URL = sqliteWasmUrl
9
8
 
10
9
  import '@korajs/store/sqlite-wasm/worker'
@@ -1,36 +1,52 @@
1
- import { createApp } from 'korajs'
1
+ import { createKoraAuthSync } from '@korajs/auth'
2
+ import { AuthProvider } from '@korajs/auth/react'
2
3
  import { KoraProvider } from '@korajs/react'
4
+ import { createApp } from 'korajs'
3
5
  import { StrictMode } from 'react'
4
6
  import { createRoot } from 'react-dom/client'
5
- import schema from './schema'
6
7
  import { App } from './App'
8
+ import { authClient, completeOAuthCallbackFromLocation } from './auth'
9
+ import schema from './schema'
7
10
  import './index.css'
8
11
  import koraWorkerUrl from './kora-worker.ts?worker&url'
9
12
 
10
13
  // Build sync URL: use env var if set, otherwise derive from current page host.
11
14
  // This allows the Vite proxy (/kora-sync → ws://localhost:3001) to work in dev,
12
15
  // and also works through any tunnel (ngrok, cloudflared) without extra configuration.
13
- const syncUrl = import.meta.env.VITE_SYNC_URL
14
- || `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/kora-sync`
16
+ const syncUrl =
17
+ import.meta.env.VITE_SYNC_URL ||
18
+ `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/kora-sync`
15
19
 
16
20
  const app = createApp({
17
- schema,
18
- sync: {
19
- url: syncUrl,
20
- },
21
- store: {
22
- workerUrl: koraWorkerUrl,
23
- },
24
- devtools: true,
21
+ schema,
22
+ sync: {
23
+ url: syncUrl,
24
+ authClient: createKoraAuthSync({ authClient, schema }),
25
+ },
26
+ store: {
27
+ workerUrl: koraWorkerUrl,
28
+ },
29
+ devtools: true,
25
30
  })
26
31
 
27
32
  // Connect to sync server once the app is ready
28
33
  app.ready.then(() => app.sync?.connect())
34
+ void completeOAuthCallbackFromLocation().catch((error) => {
35
+ console.error('[Kora Auth] OAuth callback failed:', error)
36
+ })
37
+
38
+ const rootElement = document.getElementById('root')
39
+
40
+ if (!rootElement) {
41
+ throw new Error('Root element not found')
42
+ }
29
43
 
30
- createRoot(document.getElementById('root')!).render(
31
- <StrictMode>
32
- <KoraProvider app={app} fallback={<div>Loading...</div>}>
33
- <App />
34
- </KoraProvider>
35
- </StrictMode>,
44
+ createRoot(rootElement).render(
45
+ <StrictMode>
46
+ <AuthProvider client={authClient} fallback={<div>Restoring session...</div>}>
47
+ <KoraProvider app={app} fallback={<div>Loading...</div>}>
48
+ <App />
49
+ </KoraProvider>
50
+ </AuthProvider>
51
+ </StrictMode>,
36
52
  )
@@ -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,31 @@
1
+ import { useCollection, useMutation, useQuery } from '@korajs/react'
2
+ import {
3
+ type CreateTodoInput,
4
+ type UpdateTodoStatusInput,
5
+ createTodo,
6
+ deleteTodo,
7
+ updateTodoStatus,
8
+ } from './todo.mutations'
9
+ import { orderedTodos } from './todo.queries'
10
+
11
+ export function useTodos() {
12
+ const todos = useCollection('todos')
13
+ const allTodos = useQuery(orderedTodos(todos))
14
+ const addTodo = useMutation((data: CreateTodoInput) => createTodo(todos, data))
15
+ const toggleTodo = useMutation((id: string, data: UpdateTodoStatusInput) =>
16
+ updateTodoStatus(todos, id, data),
17
+ )
18
+ const removeTodo = useMutation((id: string) => deleteTodo(todos, id))
19
+
20
+ const activeTodos = allTodos.filter((todo) => !todo.completed)
21
+ const completedTodos = allTodos.filter((todo) => !!todo.completed)
22
+
23
+ return {
24
+ allTodos,
25
+ activeTodos,
26
+ completedTodos,
27
+ addTodo,
28
+ toggleTodo,
29
+ deleteTodo: removeTodo,
30
+ }
31
+ }
@@ -1,15 +1,9 @@
1
- import { defineSchema, t } from 'korajs'
1
+ import { defineSchema } from 'korajs'
2
+ import { todos } from './modules/todos/todo.schema'
2
3
 
3
4
  export default defineSchema({
4
- version: 1,
5
- collections: {
6
- todos: {
7
- fields: {
8
- title: t.string(),
9
- completed: t.boolean().default(false),
10
- createdAt: t.timestamp().auto(),
11
- },
12
- indexes: ['completed'],
13
- },
14
- },
5
+ version: 1,
6
+ collections: {
7
+ todos,
8
+ },
15
9
  })