clawfire 0.1.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.
- package/README.md +182 -0
- package/dist/admin.cjs +309 -0
- package/dist/admin.cjs.map +1 -0
- package/dist/admin.d.cts +93 -0
- package/dist/admin.d.ts +93 -0
- package/dist/admin.js +274 -0
- package/dist/admin.js.map +1 -0
- package/dist/auth-DQ3cifhb.d.cts +55 -0
- package/dist/auth-DtnUPbXT.d.ts +55 -0
- package/dist/chunk-37Y2XI7X.js +75 -0
- package/dist/chunk-YGIPORYL.js +339 -0
- package/dist/cli.js +241 -0
- package/dist/client.cjs +97 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +4 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +68 -0
- package/dist/client.js.map +1 -0
- package/dist/codegen.cjs +648 -0
- package/dist/codegen.cjs.map +1 -0
- package/dist/codegen.d.cts +25 -0
- package/dist/codegen.d.ts +25 -0
- package/dist/codegen.js +617 -0
- package/dist/codegen.js.map +1 -0
- package/dist/config-QMBJRn9G.d.cts +46 -0
- package/dist/config-QMBJRn9G.d.ts +46 -0
- package/dist/dev-server-QAVWINAT.js +973 -0
- package/dist/dev.cjs +1388 -0
- package/dist/dev.cjs.map +1 -0
- package/dist/dev.d.cts +111 -0
- package/dist/dev.d.ts +111 -0
- package/dist/dev.js +1349 -0
- package/dist/dev.js.map +1 -0
- package/dist/discover-BPMAZFBD.js +9 -0
- package/dist/discover-DYNqz_ym.d.cts +28 -0
- package/dist/discover-DYNqz_ym.d.ts +28 -0
- package/dist/errors-s_mP7rs9.d.cts +33 -0
- package/dist/errors-s_mP7rs9.d.ts +33 -0
- package/dist/functions.cjs +1156 -0
- package/dist/functions.cjs.map +1 -0
- package/dist/functions.d.cts +115 -0
- package/dist/functions.d.ts +115 -0
- package/dist/functions.js +1108 -0
- package/dist/functions.js.map +1 -0
- package/dist/hosting-7WVFHAYJ.js +85 -0
- package/dist/html-PCUCJGBH.js +7 -0
- package/dist/index.cjs +349 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +312 -0
- package/dist/index.js.map +1 -0
- package/dist/playground.cjs +364 -0
- package/dist/playground.cjs.map +1 -0
- package/dist/playground.d.cts +12 -0
- package/dist/playground.d.ts +12 -0
- package/dist/playground.js +337 -0
- package/dist/playground.js.map +1 -0
- package/dist/router-BVB_I-tu.d.ts +65 -0
- package/dist/router-Cikk8Heq.d.cts +65 -0
- package/dist/schema-BJsictSV.d.cts +172 -0
- package/dist/schema-BJsictSV.d.ts +172 -0
- package/package.json +150 -0
- package/templates/CLAUDE.md +71 -0
- package/templates/app/routes/auth/login.ts +35 -0
- package/templates/app/routes/health.ts +20 -0
- package/templates/app/schemas/user.ts +26 -0
- package/templates/clawfire.config.ts +25 -0
- package/templates/functions/index.ts +43 -0
- package/templates/starter/.claude/skills/clawfire-api/SKILL.md +131 -0
- package/templates/starter/.claude/skills/clawfire-auth/SKILL.md +111 -0
- package/templates/starter/.claude/skills/clawfire-deploy/SKILL.md +95 -0
- package/templates/starter/.claude/skills/clawfire-diagnose/SKILL.md +99 -0
- package/templates/starter/.claude/skills/clawfire-model/SKILL.md +128 -0
- package/templates/starter/CLAUDE.md +227 -0
- package/templates/starter/app/routes/health.ts +20 -0
- package/templates/starter/app/routes/todos/create.ts +25 -0
- package/templates/starter/app/routes/todos/delete.ts +20 -0
- package/templates/starter/app/routes/todos/list.ts +26 -0
- package/templates/starter/app/routes/todos/update.ts +32 -0
- package/templates/starter/app/schemas/todo.ts +16 -0
- package/templates/starter/app/store.ts +56 -0
- package/templates/starter/clawfire.config.ts +25 -0
- package/templates/starter/dev.ts +12 -0
- package/templates/starter/package.json +19 -0
- package/templates/starter/public/index.html +365 -0
- package/templates/starter/tsconfig.json +17 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Clawfire Project
|
|
2
|
+
|
|
3
|
+
This is a **Clawfire** project — an AI-First Firebase app framework.
|
|
4
|
+
**"Speak. Build. Deploy."**
|
|
5
|
+
|
|
6
|
+
사용자가 자연어로 요청하면 Claude가 자율적으로 API, 모델, 인증, 배포를 수행합니다.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Stack (Fixed — do not change)
|
|
11
|
+
|
|
12
|
+
| Layer | Technology |
|
|
13
|
+
|-------|-----------|
|
|
14
|
+
| DB | Firestore |
|
|
15
|
+
| Auth | Firebase Auth |
|
|
16
|
+
| Hosting | Firebase Hosting |
|
|
17
|
+
| Backend | Firebase Functions |
|
|
18
|
+
| Schema | Zod |
|
|
19
|
+
| Language | TypeScript |
|
|
20
|
+
| Framework | clawfire |
|
|
21
|
+
|
|
22
|
+
**이 스택은 고정입니다. 다른 DB, 다른 프레임워크를 제안하지 마세요.**
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Project Structure
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
app/
|
|
30
|
+
store.ts ← 인메모리 데이터 저장소 (Firebase 없이 동작)
|
|
31
|
+
routes/ ← API 라우트 핸들러 (파일 기반 라우팅)
|
|
32
|
+
health.ts ← 서버 상태 확인
|
|
33
|
+
todos/ ← Todo CRUD
|
|
34
|
+
list.ts
|
|
35
|
+
create.ts
|
|
36
|
+
update.ts
|
|
37
|
+
delete.ts
|
|
38
|
+
schemas/ ← Firestore 모델 정의
|
|
39
|
+
todo.ts
|
|
40
|
+
public/
|
|
41
|
+
index.html ← 프론트엔드 (바닐라 JS, 빌드 불필요)
|
|
42
|
+
generated/ ← 자동 생성 파일 (DO NOT EDIT)
|
|
43
|
+
functions/
|
|
44
|
+
index.ts ← Firebase Functions 진입점 (배포용)
|
|
45
|
+
dev.ts ← Dev 서버 진입점
|
|
46
|
+
clawfire.config.ts ← Clawfire 설정
|
|
47
|
+
firebase.json ← Firebase 설정
|
|
48
|
+
firestore.rules ← Firestore 보안 규칙
|
|
49
|
+
firestore.indexes.json ← Firestore 인덱스
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm run dev # 개발 서버 시작 (http://localhost:3456)
|
|
58
|
+
npm run build # TypeScript 체크
|
|
59
|
+
npm run deploy # Firebase 배포
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## AI Skills (slash commands)
|
|
65
|
+
|
|
66
|
+
| Command | Description |
|
|
67
|
+
|---------|-------------|
|
|
68
|
+
| `/clawfire-api` | API 생성/수정 → 라우트 + 클라이언트 자동 생성 |
|
|
69
|
+
| `/clawfire-model` | 모델 생성/수정 → Rules + 인덱스 자동 생성 |
|
|
70
|
+
| `/clawfire-auth` | 인증 정책 설정 → Guards 자동 반영 |
|
|
71
|
+
| `/clawfire-deploy` | Firebase 배포 (명시적 요청 필요) |
|
|
72
|
+
| `/clawfire-diagnose` | 문제 탐지 및 수정 제안 |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Core Pattern: defineAPI
|
|
77
|
+
|
|
78
|
+
모든 API는 이 패턴을 따릅니다:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { defineAPI, z } from "clawfire";
|
|
82
|
+
|
|
83
|
+
export default defineAPI({
|
|
84
|
+
input: z.object({ ... }), // Zod 입력 스키마
|
|
85
|
+
output: z.object({ ... }), // Zod 출력 스키마
|
|
86
|
+
meta: {
|
|
87
|
+
description: "...", // 필수 — AI/Playground가 읽음
|
|
88
|
+
auth: "public", // "public" | "authenticated" | "role" | "reauth"
|
|
89
|
+
tags: ["..."], // 그룹화용
|
|
90
|
+
},
|
|
91
|
+
handler: async (input, ctx) => { // 비즈니스 로직
|
|
92
|
+
return { ... };
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**절대 규칙:**
|
|
98
|
+
- 모든 API는 **POST만** 사용
|
|
99
|
+
- `input`과 `output`은 반드시 `z.object({})`
|
|
100
|
+
- `meta.description`은 필수
|
|
101
|
+
- `handler`는 반드시 `async`
|
|
102
|
+
- import는 `from "clawfire"` (NOT `"clawfire"`)
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Core Pattern: defineModel
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { defineModel } from "clawfire";
|
|
110
|
+
|
|
111
|
+
export const Todo = defineModel({
|
|
112
|
+
collection: "todos",
|
|
113
|
+
fields: {
|
|
114
|
+
title: { type: "string", required: true },
|
|
115
|
+
completed: { type: "boolean", required: true },
|
|
116
|
+
},
|
|
117
|
+
timestamps: true,
|
|
118
|
+
rules: {
|
|
119
|
+
read: "public",
|
|
120
|
+
create: "authenticated",
|
|
121
|
+
update: "authenticated",
|
|
122
|
+
delete: "role",
|
|
123
|
+
deleteRoles: ["admin"],
|
|
124
|
+
ownerField: "userId",
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## File-Based Routing
|
|
132
|
+
|
|
133
|
+
파일 경로 = API 경로:
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
app/routes/health.ts → POST /api/health
|
|
137
|
+
app/routes/todos/list.ts → POST /api/todos/list
|
|
138
|
+
app/routes/products/[id]/get.ts → POST /api/products/:id/get
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Error Handling
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { Errors } from "clawfire";
|
|
147
|
+
|
|
148
|
+
throw Errors.notFound("Not found"); // 404
|
|
149
|
+
throw Errors.validation("Bad input", err); // 400
|
|
150
|
+
throw Errors.forbidden("No access"); // 403
|
|
151
|
+
throw Errors.unauthorized("Login required");// 401
|
|
152
|
+
throw Errors.conflict("Already exists"); // 409
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Response Format
|
|
158
|
+
|
|
159
|
+
성공: `{ "data": { ... } }`
|
|
160
|
+
실패: `{ "error": { "code": "NOT_FOUND", "message": "..." } }`
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Auth Levels
|
|
165
|
+
|
|
166
|
+
| Level | 의미 | Header |
|
|
167
|
+
|-------|------|--------|
|
|
168
|
+
| `public` | 인증 불필요 | 없음 |
|
|
169
|
+
| `authenticated` | 로그인 필수 | `Authorization: Bearer <token>` |
|
|
170
|
+
| `role` | 특정 역할 필요 | Bearer + role 매칭 |
|
|
171
|
+
| `reauth` | 최근 인증 필요 (5분) | Bearer + 최신 토큰 |
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Adding a New Resource (Step-by-Step)
|
|
176
|
+
|
|
177
|
+
사용자가 "상품 API 만들어줘"라고 하면:
|
|
178
|
+
|
|
179
|
+
### Step 1: Store 추가 (인메모리 모드)
|
|
180
|
+
|
|
181
|
+
`app/store.ts`에 새 Map + CRUD 메서드 추가.
|
|
182
|
+
|
|
183
|
+
### Step 2: Schema 생성
|
|
184
|
+
|
|
185
|
+
`app/schemas/product.ts` 생성 → `defineModel(...)`.
|
|
186
|
+
|
|
187
|
+
### Step 3: Routes 생성
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
app/routes/products/
|
|
191
|
+
list.ts ← 목록 조회
|
|
192
|
+
create.ts ← 생성
|
|
193
|
+
update.ts ← 수정
|
|
194
|
+
delete.ts ← 삭제
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
각 파일에 `defineAPI(...)` with 적절한 input/output/handler.
|
|
198
|
+
|
|
199
|
+
### Step 4: Dev 서버 확인
|
|
200
|
+
|
|
201
|
+
`npm run dev` → 라우트 자동 발견, 핫 리로드.
|
|
202
|
+
|
|
203
|
+
### Step 5: Frontend 업데이트
|
|
204
|
+
|
|
205
|
+
`public/index.html`에 UI 추가하거나, 사용자에게 별도 프론트엔드 구현 안내.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Security Defaults (변경하지 마세요)
|
|
210
|
+
|
|
211
|
+
- Input validation: **ON** (Zod 스키마 자동 검증)
|
|
212
|
+
- CORS: **Deny all** (dev에서만 허용)
|
|
213
|
+
- Rate limit: **100 req/min/IP**
|
|
214
|
+
- XSS sanitization: **ON** (`<` `>` 자동 이스케이프)
|
|
215
|
+
- Safe headers: **ON** (X-Frame-Options, X-Content-Type-Options)
|
|
216
|
+
- Log masking: **ON** (password, token 등 자동 마스킹)
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Important: What NOT to do
|
|
221
|
+
|
|
222
|
+
- HTTP method routing 사용 금지 (GET, PUT, DELETE 등) — 모든 것은 POST
|
|
223
|
+
- Express, Fastify 등 다른 프레임워크 사용 금지
|
|
224
|
+
- `from "clawfire"` import 금지 — 반드시 `from "clawfire"`
|
|
225
|
+
- `generated/` 폴더 내 파일 직접 수정 금지
|
|
226
|
+
- Firestore 보안 규칙을 수동으로 작성하지 마세요 — `/clawfire-model` 사용
|
|
227
|
+
- 불필요한 보안 규칙 완화 금지 (최소 권한 원칙)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineAPI, z } from "clawfire";
|
|
2
|
+
|
|
3
|
+
export default defineAPI({
|
|
4
|
+
input: z.object({}),
|
|
5
|
+
output: z.object({
|
|
6
|
+
status: z.string(),
|
|
7
|
+
timestamp: z.string(),
|
|
8
|
+
version: z.string(),
|
|
9
|
+
}),
|
|
10
|
+
meta: {
|
|
11
|
+
description: "Health check endpoint",
|
|
12
|
+
auth: "public",
|
|
13
|
+
tags: ["system"],
|
|
14
|
+
},
|
|
15
|
+
handler: async () => ({
|
|
16
|
+
status: "ok",
|
|
17
|
+
timestamp: new Date().toISOString(),
|
|
18
|
+
version: "1.0.0",
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defineAPI, z } from "clawfire";
|
|
2
|
+
import { todoStore } from "../../store.js";
|
|
3
|
+
|
|
4
|
+
export default defineAPI({
|
|
5
|
+
input: z.object({
|
|
6
|
+
title: z.string().min(1).max(200),
|
|
7
|
+
}),
|
|
8
|
+
output: z.object({
|
|
9
|
+
todo: z.object({
|
|
10
|
+
id: z.string(),
|
|
11
|
+
title: z.string(),
|
|
12
|
+
completed: z.boolean(),
|
|
13
|
+
createdAt: z.string(),
|
|
14
|
+
}),
|
|
15
|
+
}),
|
|
16
|
+
meta: {
|
|
17
|
+
description: "Create a new todo",
|
|
18
|
+
auth: "public",
|
|
19
|
+
tags: ["todos"],
|
|
20
|
+
},
|
|
21
|
+
handler: async (input) => {
|
|
22
|
+
const todo = todoStore.create(input.title);
|
|
23
|
+
return { todo };
|
|
24
|
+
},
|
|
25
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineAPI, z } from "clawfire";
|
|
2
|
+
import { todoStore } from "../../store.js";
|
|
3
|
+
|
|
4
|
+
export default defineAPI({
|
|
5
|
+
input: z.object({
|
|
6
|
+
id: z.string(),
|
|
7
|
+
}),
|
|
8
|
+
output: z.object({
|
|
9
|
+
success: z.boolean(),
|
|
10
|
+
}),
|
|
11
|
+
meta: {
|
|
12
|
+
description: "Delete a todo",
|
|
13
|
+
auth: "public",
|
|
14
|
+
tags: ["todos"],
|
|
15
|
+
},
|
|
16
|
+
handler: async (input) => {
|
|
17
|
+
const success = todoStore.delete(input.id);
|
|
18
|
+
return { success };
|
|
19
|
+
},
|
|
20
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineAPI, z } from "clawfire";
|
|
2
|
+
import { todoStore } from "../../store.js";
|
|
3
|
+
|
|
4
|
+
export default defineAPI({
|
|
5
|
+
input: z.object({}),
|
|
6
|
+
output: z.object({
|
|
7
|
+
todos: z.array(
|
|
8
|
+
z.object({
|
|
9
|
+
id: z.string(),
|
|
10
|
+
title: z.string(),
|
|
11
|
+
completed: z.boolean(),
|
|
12
|
+
createdAt: z.string(),
|
|
13
|
+
}),
|
|
14
|
+
),
|
|
15
|
+
count: z.number(),
|
|
16
|
+
}),
|
|
17
|
+
meta: {
|
|
18
|
+
description: "List all todos",
|
|
19
|
+
auth: "public",
|
|
20
|
+
tags: ["todos"],
|
|
21
|
+
},
|
|
22
|
+
handler: async () => {
|
|
23
|
+
const todos = todoStore.list();
|
|
24
|
+
return { todos, count: todos.length };
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { defineAPI, z } from "clawfire";
|
|
2
|
+
import { todoStore } from "../../store.js";
|
|
3
|
+
|
|
4
|
+
export default defineAPI({
|
|
5
|
+
input: z.object({
|
|
6
|
+
id: z.string(),
|
|
7
|
+
title: z.string().min(1).max(200).optional(),
|
|
8
|
+
completed: z.boolean().optional(),
|
|
9
|
+
}),
|
|
10
|
+
output: z.object({
|
|
11
|
+
todo: z
|
|
12
|
+
.object({
|
|
13
|
+
id: z.string(),
|
|
14
|
+
title: z.string(),
|
|
15
|
+
completed: z.boolean(),
|
|
16
|
+
createdAt: z.string(),
|
|
17
|
+
})
|
|
18
|
+
.nullable(),
|
|
19
|
+
}),
|
|
20
|
+
meta: {
|
|
21
|
+
description: "Update a todo (toggle complete, edit title)",
|
|
22
|
+
auth: "public",
|
|
23
|
+
tags: ["todos"],
|
|
24
|
+
},
|
|
25
|
+
handler: async (input) => {
|
|
26
|
+
const todo = todoStore.update(input.id, {
|
|
27
|
+
title: input.title,
|
|
28
|
+
completed: input.completed,
|
|
29
|
+
});
|
|
30
|
+
return { todo };
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineModel } from "clawfire";
|
|
2
|
+
|
|
3
|
+
export const Todo = defineModel({
|
|
4
|
+
collection: "todos",
|
|
5
|
+
fields: {
|
|
6
|
+
title: { type: "string", required: true, description: "Todo title" },
|
|
7
|
+
completed: { type: "boolean", required: true, description: "Completion status" },
|
|
8
|
+
},
|
|
9
|
+
timestamps: true,
|
|
10
|
+
rules: {
|
|
11
|
+
read: "public",
|
|
12
|
+
create: "public",
|
|
13
|
+
update: "public",
|
|
14
|
+
delete: "public",
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory Todo Store
|
|
3
|
+
*
|
|
4
|
+
* Firebase 없이 동작하는 간단한 인메모리 저장소.
|
|
5
|
+
* 나중에 Firestore로 교체할 수 있습니다.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface Todo {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
completed: boolean;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const todos: Map<string, Todo> = new Map();
|
|
16
|
+
let idCounter = 0;
|
|
17
|
+
|
|
18
|
+
function generateId(): string {
|
|
19
|
+
return `todo_${++idCounter}_${Date.now()}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
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(),
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
get(id: string): Todo | undefined {
|
|
30
|
+
return todos.get(id);
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
create(title: string): Todo {
|
|
34
|
+
const todo: Todo = {
|
|
35
|
+
id: generateId(),
|
|
36
|
+
title,
|
|
37
|
+
completed: false,
|
|
38
|
+
createdAt: new Date().toISOString(),
|
|
39
|
+
};
|
|
40
|
+
todos.set(todo.id, todo);
|
|
41
|
+
return todo;
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
update(id: string, data: Partial<Pick<Todo, "title" | "completed">>): Todo | null {
|
|
45
|
+
const todo = todos.get(id);
|
|
46
|
+
if (!todo) return null;
|
|
47
|
+
if (data.title !== undefined) todo.title = data.title;
|
|
48
|
+
if (data.completed !== undefined) todo.completed = data.completed;
|
|
49
|
+
todos.set(id, todo);
|
|
50
|
+
return todo;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
delete(id: string): boolean {
|
|
54
|
+
return todos.delete(id);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { configureClawfire } from "clawfire";
|
|
2
|
+
|
|
3
|
+
export default configureClawfire({
|
|
4
|
+
firebase: {
|
|
5
|
+
apiKey: "YOUR_API_KEY",
|
|
6
|
+
authDomain: "YOUR_PROJECT.firebaseapp.com",
|
|
7
|
+
projectId: "YOUR_PROJECT_ID",
|
|
8
|
+
storageBucket: "YOUR_PROJECT.firebasestorage.app",
|
|
9
|
+
appId: "YOUR_APP_ID",
|
|
10
|
+
},
|
|
11
|
+
server: {
|
|
12
|
+
region: "us-central1",
|
|
13
|
+
cors: [],
|
|
14
|
+
rateLimit: 100,
|
|
15
|
+
},
|
|
16
|
+
security: {
|
|
17
|
+
validateInput: true,
|
|
18
|
+
safeHeaders: true,
|
|
19
|
+
maskLogs: true,
|
|
20
|
+
},
|
|
21
|
+
playground: {
|
|
22
|
+
enabled: true,
|
|
23
|
+
path: "/__playground",
|
|
24
|
+
},
|
|
25
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-clawfire-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "npx tsx dev.ts",
|
|
8
|
+
"build": "tsc --noEmit",
|
|
9
|
+
"deploy": "npx clawfire deploy"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"clawfire": "latest"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"tsx": "^4.0.0",
|
|
16
|
+
"typescript": "^5.5.0",
|
|
17
|
+
"@types/node": "^20.0.0"
|
|
18
|
+
}
|
|
19
|
+
}
|