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.
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * Todo Store — Dual Mode (In-Memory + Firestore)
3
3
  *
4
- * Mode is auto-detected from process.env.CLAWFIRE_FIREBASE_CONFIG.
5
- * If the env var contains valid Firebase config, Firestore mode is used.
6
- * Otherwise, falls back to in-memory mode (no setup needed).
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 memoryStore = new Map<string, Todo>();
30
+ const memoryMap = new Map<string, Todo>();
18
31
  let idCounter = 0;
19
32
 
20
- // ─── Firestore State (lazy-initialized) ──────────────────────
21
- let initialized = false;
22
- let useFirestore = false;
23
- let firestoreDb: any = null;
24
- let firestoreMethods: any = null;
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 ensureInit(): Promise<void> {
27
- if (initialized) return;
28
- initialized = true;
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) return;
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_")) return;
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
- firestoreDb = firebaseFirestore.getFirestore(app);
46
- firestoreMethods = firebaseFirestore;
47
- useFirestore = true;
48
- console.log(" [store] Firestore mode enabled");
98
+ fsDb = firebaseFirestore.getFirestore(app);
99
+ fsMethods = firebaseFirestore;
100
+ fsAvailable = true;
101
+ console.log(" [store] Firestore mode available");
49
102
  } catch (err) {
50
- console.warn(" [store] Firestore init failed, using in-memory:", err);
103
+ fsError = `Firestore init failed: ${err instanceof Error ? err.message : String(err)}`;
104
+ console.warn(" [store]", fsError);
51
105
  }
52
106
  }
53
107
 
54
- // ─── Exported Store (async methods) ──────────────────────────
55
- export const todoStore = {
56
- async list(): Promise<Todo[]> {
57
- await ensureInit();
58
- if (useFirestore) {
59
- const { collection, getDocs, query, orderBy } = firestoreMethods;
60
- const q = query(
61
- collection(firestoreDb, "todos"),
62
- orderBy("createdAt", "desc"),
63
- );
64
- const snap = await getDocs(q);
65
- return snap.docs.map((d: any) => ({ id: d.id, ...d.data() }) as Todo);
66
- }
67
- return Array.from(memoryStore.values()).sort(
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: string): Promise<Todo> {
74
- await ensureInit();
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
- if (useFirestore) {
82
- const { collection, addDoc } = firestoreMethods;
83
- const ref = await addDoc(collection(firestoreDb, "todos"), {
84
- title: todo.title,
85
- completed: todo.completed,
86
- createdAt: todo.createdAt,
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
- id: string,
98
- data: Partial<Pick<Todo, "title" | "completed">>,
99
- ): Promise<Todo | null> {
100
- await ensureInit();
101
- if (useFirestore) {
102
- const { doc, updateDoc, getDoc } = firestoreMethods;
103
- const ref = doc(firestoreDb, "todos", id);
104
- const updateData: Record<string, any> = {};
105
- if (data.title !== undefined) updateData.title = data.title;
106
- if (data.completed !== undefined) updateData.completed = data.completed;
107
- await updateDoc(ref, updateData);
108
- const snap = await getDoc(ref);
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: string): Promise<boolean> {
122
- await ensureInit();
123
- if (useFirestore) {
124
- const { doc, deleteDoc } = firestoreMethods;
125
- await deleteDoc(doc(firestoreDb, "todos", id));
126
- return true;
127
- }
128
- return memoryStore.delete(id);
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
  };
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2022",
4
- "module": "CommonJS",
5
- "moduleResolution": "node",
4
+ "module": "Node16",
5
+ "moduleResolution": "Node16",
6
6
  "esModuleInterop": true,
7
7
  "strict": true,
8
8
  "skipLibCheck": true,