clawfire 0.6.18 → 0.6.20
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/dist/cli.js +1 -1
- package/dist/{dev-server-54RXFRNZ.js → dev-server-MLT3ZO5A.js} +29 -28
- package/dist/dev.cjs +29 -28
- package/dist/dev.cjs.map +1 -1
- package/dist/dev.js +29 -28
- package/dist/dev.js.map +1 -1
- package/package.json +1 -1
- package/templates/starter/app/pages/todos/index.html +158 -40
- package/templates/starter/functions/package.json +1 -0
- package/templates/starter/functions/routes/todos/create.ts +4 -2
- package/templates/starter/functions/routes/todos/delete.ts +4 -2
- package/templates/starter/functions/routes/todos/list.ts +7 -4
- package/templates/starter/functions/routes/todos/update.ts +4 -2
- package/templates/starter/functions/store.ts +161 -80
- package/templates/starter/functions/tsconfig.json +2 -2
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Todo Store — Dual Mode (In-Memory + Firestore)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Two explicit stores are available:
|
|
5
|
+
* - memoryTodoStore: Pure in-memory Map, always works
|
|
6
|
+
* - firestoreTodoStore: Client Firebase SDK via CLAWFIRE_FIREBASE_CONFIG
|
|
7
|
+
*
|
|
8
|
+
* Use getStore("memory" | "firestore") to select at runtime.
|
|
9
|
+
* Legacy todoStore is kept for backward compatibility (auto-detect).
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
12
|
export interface Todo {
|
|
@@ -13,26 +16,76 @@ export interface Todo {
|
|
|
13
16
|
createdAt: string;
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
export interface TodoStore {
|
|
20
|
+
list(): Promise<Todo[]>;
|
|
21
|
+
create(title: string): Promise<Todo>;
|
|
22
|
+
update(
|
|
23
|
+
id: string,
|
|
24
|
+
data: Partial<Pick<Todo, "title" | "completed">>,
|
|
25
|
+
): Promise<Todo | null>;
|
|
26
|
+
delete(id: string): Promise<boolean>;
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
// ─── In-Memory Store ──────────────────────────────────────────
|
|
17
|
-
const
|
|
30
|
+
const memoryMap = new Map<string, Todo>();
|
|
18
31
|
let idCounter = 0;
|
|
19
32
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
export const memoryTodoStore: TodoStore = {
|
|
34
|
+
async list() {
|
|
35
|
+
return Array.from(memoryMap.values()).sort(
|
|
36
|
+
(a, b) =>
|
|
37
|
+
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async create(title) {
|
|
42
|
+
const todo: Todo = {
|
|
43
|
+
id: `todo_${++idCounter}_${Date.now()}`,
|
|
44
|
+
title,
|
|
45
|
+
completed: false,
|
|
46
|
+
createdAt: new Date().toISOString(),
|
|
47
|
+
};
|
|
48
|
+
memoryMap.set(todo.id, todo);
|
|
49
|
+
return todo;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
async update(id, data) {
|
|
53
|
+
const todo = memoryMap.get(id);
|
|
54
|
+
if (!todo) return null;
|
|
55
|
+
if (data.title !== undefined) todo.title = data.title;
|
|
56
|
+
if (data.completed !== undefined) todo.completed = data.completed;
|
|
57
|
+
memoryMap.set(id, todo);
|
|
58
|
+
return todo;
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async delete(id) {
|
|
62
|
+
return memoryMap.delete(id);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// ─── Firestore Store (lazy-initialized) ──────────────────────
|
|
67
|
+
let fsInitialized = false;
|
|
68
|
+
let fsAvailable = false;
|
|
69
|
+
let fsDb: any = null;
|
|
70
|
+
let fsMethods: any = null;
|
|
71
|
+
let fsError: string | null = null;
|
|
25
72
|
|
|
26
|
-
async function
|
|
27
|
-
if (
|
|
28
|
-
|
|
73
|
+
async function ensureFirestore(): Promise<void> {
|
|
74
|
+
if (fsInitialized) return;
|
|
75
|
+
fsInitialized = true;
|
|
29
76
|
|
|
30
77
|
const configJson = process.env.CLAWFIRE_FIREBASE_CONFIG;
|
|
31
|
-
if (!configJson)
|
|
78
|
+
if (!configJson) {
|
|
79
|
+
fsError = "Firestore not configured: CLAWFIRE_FIREBASE_CONFIG env var is not set.";
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
32
82
|
|
|
33
83
|
try {
|
|
34
84
|
const config = JSON.parse(configJson);
|
|
35
|
-
if (!config.apiKey || config.apiKey.startsWith("YOUR_"))
|
|
85
|
+
if (!config.apiKey || config.apiKey.startsWith("YOUR_")) {
|
|
86
|
+
fsError = "Firestore not configured: Firebase API key is missing or placeholder.";
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
36
89
|
|
|
37
90
|
const firebaseApp = await import("firebase/app");
|
|
38
91
|
const firebaseFirestore = await import("firebase/firestore");
|
|
@@ -42,89 +95,117 @@ async function ensureInit(): Promise<void> {
|
|
|
42
95
|
? firebaseApp.initializeApp(config)
|
|
43
96
|
: firebaseApp.getApps()[0];
|
|
44
97
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
console.log(" [store] Firestore mode
|
|
98
|
+
fsDb = firebaseFirestore.getFirestore(app);
|
|
99
|
+
fsMethods = firebaseFirestore;
|
|
100
|
+
fsAvailable = true;
|
|
101
|
+
console.log(" [store] Firestore mode available");
|
|
49
102
|
} catch (err) {
|
|
50
|
-
|
|
103
|
+
fsError = `Firestore init failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
104
|
+
console.warn(" [store]", fsError);
|
|
51
105
|
}
|
|
52
106
|
}
|
|
53
107
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
(a, b) =>
|
|
69
|
-
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
|
108
|
+
function requireFirestore() {
|
|
109
|
+
if (!fsAvailable) {
|
|
110
|
+
throw new Error(fsError || "Firestore is not available.");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const firestoreTodoStore: TodoStore = {
|
|
115
|
+
async list() {
|
|
116
|
+
await ensureFirestore();
|
|
117
|
+
requireFirestore();
|
|
118
|
+
const { collection, getDocs, query, orderBy } = fsMethods;
|
|
119
|
+
const q = query(
|
|
120
|
+
collection(fsDb, "todos"),
|
|
121
|
+
orderBy("createdAt", "desc"),
|
|
70
122
|
);
|
|
123
|
+
const snap = await getDocs(q);
|
|
124
|
+
return snap.docs.map((d: any) => ({ id: d.id, ...d.data() }) as Todo);
|
|
71
125
|
},
|
|
72
126
|
|
|
73
|
-
async create(title
|
|
74
|
-
await
|
|
127
|
+
async create(title) {
|
|
128
|
+
await ensureFirestore();
|
|
129
|
+
requireFirestore();
|
|
130
|
+
const { collection, addDoc } = fsMethods;
|
|
75
131
|
const todo: Todo = {
|
|
76
132
|
id: "",
|
|
77
133
|
title,
|
|
78
134
|
completed: false,
|
|
79
135
|
createdAt: new Date().toISOString(),
|
|
80
136
|
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
88
|
-
todo.id = ref.id;
|
|
89
|
-
} else {
|
|
90
|
-
todo.id = `todo_${++idCounter}_${Date.now()}`;
|
|
91
|
-
memoryStore.set(todo.id, todo);
|
|
92
|
-
}
|
|
137
|
+
const ref = await addDoc(collection(fsDb, "todos"), {
|
|
138
|
+
title: todo.title,
|
|
139
|
+
completed: todo.completed,
|
|
140
|
+
createdAt: todo.createdAt,
|
|
141
|
+
});
|
|
142
|
+
todo.id = ref.id;
|
|
93
143
|
return todo;
|
|
94
144
|
},
|
|
95
145
|
|
|
96
|
-
async update(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return snap.exists()
|
|
110
|
-
? ({ id: snap.id, ...snap.data() } as Todo)
|
|
111
|
-
: null;
|
|
112
|
-
}
|
|
113
|
-
const todo = memoryStore.get(id);
|
|
114
|
-
if (!todo) return null;
|
|
115
|
-
if (data.title !== undefined) todo.title = data.title;
|
|
116
|
-
if (data.completed !== undefined) todo.completed = data.completed;
|
|
117
|
-
memoryStore.set(id, todo);
|
|
118
|
-
return todo;
|
|
146
|
+
async update(id, data) {
|
|
147
|
+
await ensureFirestore();
|
|
148
|
+
requireFirestore();
|
|
149
|
+
const { doc, updateDoc, getDoc } = fsMethods;
|
|
150
|
+
const ref = doc(fsDb, "todos", id);
|
|
151
|
+
const updateData: Record<string, any> = {};
|
|
152
|
+
if (data.title !== undefined) updateData.title = data.title;
|
|
153
|
+
if (data.completed !== undefined) updateData.completed = data.completed;
|
|
154
|
+
await updateDoc(ref, updateData);
|
|
155
|
+
const snap = await getDoc(ref);
|
|
156
|
+
return snap.exists()
|
|
157
|
+
? ({ id: snap.id, ...snap.data() } as Todo)
|
|
158
|
+
: null;
|
|
119
159
|
},
|
|
120
160
|
|
|
121
|
-
async delete(id
|
|
122
|
-
await
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
161
|
+
async delete(id) {
|
|
162
|
+
await ensureFirestore();
|
|
163
|
+
requireFirestore();
|
|
164
|
+
const { doc, deleteDoc } = fsMethods;
|
|
165
|
+
await deleteDoc(doc(fsDb, "todos", id));
|
|
166
|
+
return true;
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// ─── Store Selector ──────────────────────────────────────────
|
|
171
|
+
export function getStore(mode?: string): TodoStore {
|
|
172
|
+
return mode === "firestore" ? firestoreTodoStore : memoryTodoStore;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ─── Legacy Export (auto-detect, backward compatible) ────────
|
|
176
|
+
let legacyInitialized = false;
|
|
177
|
+
let legacyUseFirestore = false;
|
|
178
|
+
|
|
179
|
+
async function ensureLegacyInit(): Promise<void> {
|
|
180
|
+
if (legacyInitialized) return;
|
|
181
|
+
legacyInitialized = true;
|
|
182
|
+
await ensureFirestore();
|
|
183
|
+
legacyUseFirestore = fsAvailable;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export const todoStore: TodoStore = {
|
|
187
|
+
async list() {
|
|
188
|
+
await ensureLegacyInit();
|
|
189
|
+
return legacyUseFirestore
|
|
190
|
+
? firestoreTodoStore.list()
|
|
191
|
+
: memoryTodoStore.list();
|
|
192
|
+
},
|
|
193
|
+
async create(title) {
|
|
194
|
+
await ensureLegacyInit();
|
|
195
|
+
return legacyUseFirestore
|
|
196
|
+
? firestoreTodoStore.create(title)
|
|
197
|
+
: memoryTodoStore.create(title);
|
|
198
|
+
},
|
|
199
|
+
async update(id, data) {
|
|
200
|
+
await ensureLegacyInit();
|
|
201
|
+
return legacyUseFirestore
|
|
202
|
+
? firestoreTodoStore.update(id, data)
|
|
203
|
+
: memoryTodoStore.update(id, data);
|
|
204
|
+
},
|
|
205
|
+
async delete(id) {
|
|
206
|
+
await ensureLegacyInit();
|
|
207
|
+
return legacyUseFirestore
|
|
208
|
+
? firestoreTodoStore.delete(id)
|
|
209
|
+
: memoryTodoStore.delete(id);
|
|
129
210
|
},
|
|
130
211
|
};
|