clawfire 0.6.0 → 0.6.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawfire",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "AI-First Firebase app framework — Speak. Build. Deploy.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -79,7 +79,7 @@
79
79
  }
80
80
  },
81
81
  "bin": {
82
- "clawfire": "./dist/cli.js"
82
+ "clawfire": "dist/cli.js"
83
83
  },
84
84
  "files": [
85
85
  "dist",
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "main": "dist/index.js",
10
10
  "engines": {
11
- "node": "20"
11
+ "node": ">=18"
12
12
  },
13
13
  "dependencies": {
14
14
  "clawfire": "latest",
@@ -19,7 +19,7 @@ export default defineAPI({
19
19
  tags: ["todos"],
20
20
  },
21
21
  handler: async (input) => {
22
- const todo = todoStore.create(input.title);
22
+ const todo = await todoStore.create(input.title);
23
23
  return { todo };
24
24
  },
25
25
  });
@@ -14,7 +14,7 @@ export default defineAPI({
14
14
  tags: ["todos"],
15
15
  },
16
16
  handler: async (input) => {
17
- const success = todoStore.delete(input.id);
17
+ const success = await todoStore.delete(input.id);
18
18
  return { success };
19
19
  },
20
20
  });
@@ -20,7 +20,7 @@ export default defineAPI({
20
20
  tags: ["todos"],
21
21
  },
22
22
  handler: async () => {
23
- const todos = todoStore.list();
23
+ const todos = await todoStore.list();
24
24
  return { todos, count: todos.length };
25
25
  },
26
26
  });
@@ -23,7 +23,7 @@ export default defineAPI({
23
23
  tags: ["todos"],
24
24
  },
25
25
  handler: async (input) => {
26
- const todo = todoStore.update(input.id, {
26
+ const todo = await todoStore.update(input.id, {
27
27
  title: input.title,
28
28
  completed: input.completed,
29
29
  });
@@ -1,8 +1,9 @@
1
1
  /**
2
- * In-memory Todo Store
2
+ * Todo Store — Dual Mode (In-Memory + Firestore)
3
3
  *
4
- * Firebase 없이 동작하는 간단한 인메모리 저장소.
5
- * 나중에 Firestore 교체할 있습니다.
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).
6
7
  */
7
8
 
8
9
  export interface Todo {
@@ -12,45 +13,118 @@ export interface Todo {
12
13
  createdAt: string;
13
14
  }
14
15
 
15
- const todos: Map<string, Todo> = new Map();
16
+ // ─── In-Memory Store ──────────────────────────────────────────
17
+ const memoryStore = new Map<string, Todo>();
16
18
  let idCounter = 0;
17
19
 
18
- function generateId(): string {
19
- return `todo_${++idCounter}_${Date.now()}`;
20
+ // ─── Firestore State (lazy-initialized) ──────────────────────
21
+ let initialized = false;
22
+ let useFirestore = false;
23
+ let firestoreDb: any = null;
24
+ let firestoreMethods: any = null;
25
+
26
+ async function ensureInit(): Promise<void> {
27
+ if (initialized) return;
28
+ initialized = true;
29
+
30
+ const configJson = process.env.CLAWFIRE_FIREBASE_CONFIG;
31
+ if (!configJson) return;
32
+
33
+ try {
34
+ const config = JSON.parse(configJson);
35
+ if (!config.apiKey || config.apiKey.startsWith("YOUR_")) return;
36
+
37
+ const firebaseApp = await import("firebase/app");
38
+ const firebaseFirestore = await import("firebase/firestore");
39
+
40
+ const app =
41
+ firebaseApp.getApps().length === 0
42
+ ? firebaseApp.initializeApp(config)
43
+ : firebaseApp.getApps()[0];
44
+
45
+ firestoreDb = firebaseFirestore.getFirestore(app);
46
+ firestoreMethods = firebaseFirestore;
47
+ useFirestore = true;
48
+ console.log(" [store] Firestore mode enabled");
49
+ } catch (err) {
50
+ console.warn(" [store] Firestore init failed, using in-memory:", err);
51
+ }
20
52
  }
21
53
 
54
+ // ─── Exported Store (async methods) ──────────────────────────
22
55
  export const todoStore = {
23
- list(): Todo[] {
24
- return Array.from(todos.values()).sort(
25
- (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
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(),
26
70
  );
27
71
  },
28
72
 
29
- get(id: string): Todo | undefined {
30
- return todos.get(id);
31
- },
32
-
33
- create(title: string): Todo {
73
+ async create(title: string): Promise<Todo> {
74
+ await ensureInit();
34
75
  const todo: Todo = {
35
- id: generateId(),
76
+ id: "",
36
77
  title,
37
78
  completed: false,
38
79
  createdAt: new Date().toISOString(),
39
80
  };
40
- todos.set(todo.id, todo);
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
+ }
41
93
  return todo;
42
94
  },
43
95
 
44
- update(id: string, data: Partial<Pick<Todo, "title" | "completed">>): Todo | null {
45
- const todo = todos.get(id);
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);
46
114
  if (!todo) return null;
47
115
  if (data.title !== undefined) todo.title = data.title;
48
116
  if (data.completed !== undefined) todo.completed = data.completed;
49
- todos.set(id, todo);
117
+ memoryStore.set(id, todo);
50
118
  return todo;
51
119
  },
52
120
 
53
- delete(id: string): boolean {
54
- return todos.delete(id);
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);
55
129
  },
56
130
  };
@@ -11,7 +11,8 @@
11
11
  "postinstall": "cd functions && npm install"
12
12
  },
13
13
  "dependencies": {
14
- "clawfire": "latest"
14
+ "clawfire": "latest",
15
+ "firebase": "^11.0.0"
15
16
  },
16
17
  "devDependencies": {
17
18
  "tsx": "^4.0.0",