create-elit 3.6.5 → 3.6.7
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/README.md +32 -68
- package/dist/index.js +121 -8
- package/dist/templates/{elit.config.ts → auth-fullstack-example/elit.config.ts} +51 -0
- package/dist/templates/auth-fullstack-example/package.json +26 -0
- package/dist/templates/auth-fullstack-example/src/native-screen.ts +10 -0
- package/dist/templates/auth-fullstack-example/wapkignore +10 -0
- package/dist/templates/auth-fullstack-example/wapkpatch +1 -0
- package/dist/templates/basic-example/README.md +39 -0
- package/dist/templates/basic-example/elit.config.ts +114 -0
- package/dist/templates/basic-example/gitignore +7 -0
- package/dist/templates/basic-example/package.json +26 -0
- package/dist/templates/basic-example/public/favicon.svg +22 -0
- package/dist/templates/basic-example/public/index.html +14 -0
- package/dist/templates/basic-example/src/client.ts +15 -0
- package/dist/templates/basic-example/src/main.ts +89 -0
- package/dist/templates/basic-example/src/mobile.ts +35 -0
- package/dist/templates/basic-example/src/styles.ts +273 -0
- package/dist/templates/basic-example/tsconfig.json +24 -0
- package/dist/templates/basic-example/wapkignore +10 -0
- package/dist/templates/basic-example/wapkpatch +1 -0
- package/dist/templates/todo-fullstack-example/README.md +39 -0
- package/dist/templates/todo-fullstack-example/databases/todo.ts +41 -0
- package/dist/templates/todo-fullstack-example/elit.config.ts +123 -0
- package/dist/templates/todo-fullstack-example/gitignore +7 -0
- package/dist/templates/todo-fullstack-example/package.json +26 -0
- package/dist/templates/todo-fullstack-example/public/favicon.svg +22 -0
- package/dist/templates/todo-fullstack-example/public/index.html +14 -0
- package/dist/templates/todo-fullstack-example/src/client.ts +15 -0
- package/dist/templates/todo-fullstack-example/src/components/AppFooter.ts +16 -0
- package/dist/templates/todo-fullstack-example/src/components/AppHeader.ts +23 -0
- package/dist/templates/todo-fullstack-example/src/main.ts +7 -0
- package/dist/templates/todo-fullstack-example/src/mobile.ts +36 -0
- package/dist/templates/todo-fullstack-example/src/pages/TodoPage.ts +491 -0
- package/dist/templates/todo-fullstack-example/src/router.ts +16 -0
- package/dist/templates/todo-fullstack-example/src/server.ts +226 -0
- package/dist/templates/todo-fullstack-example/src/styles.ts +768 -0
- package/dist/templates/todo-fullstack-example/src/todo-types.ts +19 -0
- package/dist/templates/todo-fullstack-example/src/web.ts +16 -0
- package/dist/templates/todo-fullstack-example/tsconfig.json +24 -0
- package/dist/templates/todo-fullstack-example/wapkignore +10 -0
- package/dist/templates/todo-fullstack-example/wapkpatch +1 -0
- package/package.json +1 -1
- package/dist/templates/package.json +0 -17
- package/dist/templates/src/client.test.ts +0 -292
- package/dist/templates/src/components/Footer.test.ts +0 -226
- package/dist/templates/src/components/Header.test.ts +0 -493
- package/dist/templates/src/pages/ChatListPage.test.ts +0 -603
- package/dist/templates/src/pages/ChatPage.test.ts +0 -530
- package/dist/templates/src/pages/ForgotPasswordPage.test.ts +0 -484
- package/dist/templates/src/pages/HomePage.test.ts +0 -601
- package/dist/templates/src/pages/LoginPage.test.ts +0 -619
- package/dist/templates/src/pages/PrivateChatPage.test.ts +0 -556
- package/dist/templates/src/pages/ProfilePage.test.ts +0 -628
- package/dist/templates/src/pages/RegisterPage.test.ts +0 -661
- /package/dist/templates/{README.md → auth-fullstack-example/README.md} +0 -0
- /package/dist/templates/{databases → auth-fullstack-example/databases}/users.ts +0 -0
- /package/dist/templates/{gitignore → auth-fullstack-example/gitignore} +0 -0
- /package/dist/templates/{public → auth-fullstack-example/public}/favicon.svg +0 -0
- /package/dist/templates/{public → auth-fullstack-example/public}/index.html +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/client.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/components/Footer.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/components/Header.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/components/index.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/main.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/pages/ChatListPage.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/pages/ChatPage.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/pages/ForgotPasswordPage.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/pages/HomePage.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/pages/LoginPage.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/pages/PrivateChatPage.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/pages/ProfilePage.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/pages/RegisterPage.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/router.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/server.ts +0 -0
- /package/dist/templates/{src → auth-fullstack-example/src}/styles.ts +0 -0
- /package/dist/templates/{tsconfig.json → auth-fullstack-example/tsconfig.json} +0 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { Database } from 'elit/database';
|
|
2
|
+
import { ServerRouter, json, type ServerRouteContext } from 'elit/server';
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
import type { TodoItem, TodoPriority, TodoSummary } from './todo-types';
|
|
5
|
+
|
|
6
|
+
const priorityWeight: Record<TodoPriority, number> = {
|
|
7
|
+
high: 0,
|
|
8
|
+
medium: 1,
|
|
9
|
+
low: 2
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const router = new ServerRouter();
|
|
13
|
+
|
|
14
|
+
const db = new Database({
|
|
15
|
+
dir: resolve(process.cwd(), 'databases'),
|
|
16
|
+
language: 'ts'
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
function normalizePriority(value: unknown): TodoPriority {
|
|
20
|
+
if (value === 'high' || value === 'low') {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return 'medium';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function summarizeTodos(items: TodoItem[]): TodoSummary {
|
|
28
|
+
const completed = items.filter((todo) => todo.completed).length;
|
|
29
|
+
const active = items.length - completed;
|
|
30
|
+
const highPriority = items.filter((todo) => !todo.completed && todo.priority === 'high').length;
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
total: items.length,
|
|
34
|
+
active,
|
|
35
|
+
completed,
|
|
36
|
+
highPriority
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sortTodos(items: TodoItem[]): TodoItem[] {
|
|
41
|
+
return [...items].sort((left, right) =>
|
|
42
|
+
Number(left.completed) - Number(right.completed)
|
|
43
|
+
|| priorityWeight[left.priority] - priorityWeight[right.priority]
|
|
44
|
+
|| right.updatedAt.localeCompare(left.updatedAt)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function readTodos(): Promise<TodoItem[]> {
|
|
49
|
+
const result = await db.execute(`
|
|
50
|
+
import { todos } from '@db/todo';
|
|
51
|
+
console.log(JSON.stringify(todos));
|
|
52
|
+
`);
|
|
53
|
+
|
|
54
|
+
const payload = result.logs.find((entry: { type: string }) => entry.type === 'log')?.args?.[0];
|
|
55
|
+
|
|
56
|
+
if (typeof payload === 'string') {
|
|
57
|
+
return sortTodos(JSON.parse(payload) as TodoItem[]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (Array.isArray(payload)) {
|
|
61
|
+
return sortTodos(payload as TodoItem[]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function writeTodos(items: TodoItem[]): TodoItem[] {
|
|
68
|
+
const sortedTodos = sortTodos(items);
|
|
69
|
+
db.update('todo', 'todos', sortedTodos);
|
|
70
|
+
return sortedTodos;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function createTodoId(): string {
|
|
74
|
+
return `todo_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getTitle(body: any): string {
|
|
78
|
+
return typeof body?.title === 'string' ? body.title.trim() : '';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getNotes(body: any): string {
|
|
82
|
+
return typeof body?.notes === 'string' ? body.notes.trim() : '';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function sendTodoPayload(ctx: ServerRouteContext, todos: TodoItem[], status = 200, extras: Record<string, unknown> = {}) {
|
|
86
|
+
return json(ctx.res, {
|
|
87
|
+
...extras,
|
|
88
|
+
todos,
|
|
89
|
+
summary: summarizeTodos(todos)
|
|
90
|
+
}, status);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
router.get('/api/health', async (ctx: ServerRouteContext) => {
|
|
94
|
+
json(ctx.res, {
|
|
95
|
+
ok: true,
|
|
96
|
+
storage: 'elit/database',
|
|
97
|
+
file: 'databases/todo.ts'
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
router.get('/api/todos', async (ctx: ServerRouteContext) => {
|
|
102
|
+
const todos = await readTodos();
|
|
103
|
+
sendTodoPayload(ctx, todos);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
router.post('/api/todos', async (ctx: ServerRouteContext) => {
|
|
107
|
+
const title = getTitle(ctx.body);
|
|
108
|
+
const notes = getNotes(ctx.body);
|
|
109
|
+
const priority = normalizePriority(ctx.body?.priority);
|
|
110
|
+
|
|
111
|
+
if (!title) {
|
|
112
|
+
return json(ctx.res, { error: 'Add a task title before saving.' }, 400);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (title.length > 120) {
|
|
116
|
+
return json(ctx.res, { error: 'Keep task titles under 120 characters.' }, 400);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (notes.length > 280) {
|
|
120
|
+
return json(ctx.res, { error: 'Notes should stay under 280 characters.' }, 400);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const now = new Date().toISOString();
|
|
124
|
+
const todo: TodoItem = {
|
|
125
|
+
id: createTodoId(),
|
|
126
|
+
title,
|
|
127
|
+
notes,
|
|
128
|
+
priority,
|
|
129
|
+
completed: false,
|
|
130
|
+
createdAt: now,
|
|
131
|
+
updatedAt: now
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const nextTodos = writeTodos([todo, ...(await readTodos())]);
|
|
135
|
+
|
|
136
|
+
return sendTodoPayload(ctx, nextTodos, 201, {
|
|
137
|
+
message: 'Task added to databases/todo.ts.',
|
|
138
|
+
todo
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
router.patch('/api/todos/:id', async (ctx: ServerRouteContext) => {
|
|
143
|
+
const todoId = ctx.params.id;
|
|
144
|
+
const currentTodos = await readTodos();
|
|
145
|
+
const todoIndex = currentTodos.findIndex((todo) => todo.id === todoId);
|
|
146
|
+
|
|
147
|
+
if (todoIndex === -1) {
|
|
148
|
+
return json(ctx.res, { error: 'Task not found.' }, 404);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const currentTodo = currentTodos[todoIndex];
|
|
152
|
+
const nextTitle = typeof ctx.body?.title === 'string' ? ctx.body.title.trim() : currentTodo.title;
|
|
153
|
+
const nextNotes = typeof ctx.body?.notes === 'string' ? ctx.body.notes.trim() : currentTodo.notes;
|
|
154
|
+
const nextPriority = ctx.body && 'priority' in ctx.body
|
|
155
|
+
? normalizePriority(ctx.body.priority)
|
|
156
|
+
: currentTodo.priority;
|
|
157
|
+
const nextCompleted = typeof ctx.body?.completed === 'boolean'
|
|
158
|
+
? ctx.body.completed
|
|
159
|
+
: currentTodo.completed;
|
|
160
|
+
|
|
161
|
+
if (!nextTitle) {
|
|
162
|
+
return json(ctx.res, { error: 'Task title cannot be empty.' }, 400);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (nextTitle.length > 120) {
|
|
166
|
+
return json(ctx.res, { error: 'Keep task titles under 120 characters.' }, 400);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (nextNotes.length > 280) {
|
|
170
|
+
return json(ctx.res, { error: 'Notes should stay under 280 characters.' }, 400);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const updatedTodo: TodoItem = {
|
|
174
|
+
...currentTodo,
|
|
175
|
+
title: nextTitle,
|
|
176
|
+
notes: nextNotes,
|
|
177
|
+
priority: nextPriority,
|
|
178
|
+
completed: nextCompleted,
|
|
179
|
+
updatedAt: new Date().toISOString()
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const nextTodos = writeTodos(currentTodos.map((todo) =>
|
|
183
|
+
todo.id === todoId ? updatedTodo : todo
|
|
184
|
+
));
|
|
185
|
+
|
|
186
|
+
return sendTodoPayload(ctx, nextTodos, 200, {
|
|
187
|
+
message: updatedTodo.completed ? 'Task marked complete.' : 'Task updated.',
|
|
188
|
+
todo: updatedTodo
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
router.delete('/api/todos/completed', async (ctx: ServerRouteContext) => {
|
|
193
|
+
const currentTodos = await readTodos();
|
|
194
|
+
const nextTodos = currentTodos.filter((todo) => !todo.completed);
|
|
195
|
+
|
|
196
|
+
if (nextTodos.length === currentTodos.length) {
|
|
197
|
+
return json(ctx.res, { error: 'There are no completed tasks to clear.' }, 400);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const removedCount = currentTodos.length - nextTodos.length;
|
|
201
|
+
const savedTodos = writeTodos(nextTodos);
|
|
202
|
+
|
|
203
|
+
return sendTodoPayload(ctx, savedTodos, 200, {
|
|
204
|
+
message: `Cleared ${removedCount} completed task${removedCount === 1 ? '' : 's'}.`,
|
|
205
|
+
removedCount
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
router.delete('/api/todos/:id', async (ctx: ServerRouteContext) => {
|
|
210
|
+
const todoId = ctx.params.id;
|
|
211
|
+
const currentTodos = await readTodos();
|
|
212
|
+
const todo = currentTodos.find((entry) => entry.id === todoId);
|
|
213
|
+
|
|
214
|
+
if (!todo) {
|
|
215
|
+
return json(ctx.res, { error: 'Task not found.' }, 404);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const nextTodos = writeTodos(currentTodos.filter((entry) => entry.id !== todoId));
|
|
219
|
+
|
|
220
|
+
return sendTodoPayload(ctx, nextTodos, 200, {
|
|
221
|
+
message: 'Task removed from the board.',
|
|
222
|
+
todo
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
export const server = router;
|