@africode/core 5.0.4 → 5.0.6
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/AGENTS.md +583 -0
- package/AGENT_INSTRUCTIONS.md +595 -0
- package/COMPONENT_SCHEMA.json +990 -836
- package/README.md +32 -0
- package/components/index.js +14 -0
- package/components/ui-badge.js +61 -0
- package/components/ui-button.js +149 -0
- package/components/ui-card.js +75 -0
- package/components/ui-input.js +110 -0
- package/components/ui-switch.js +85 -0
- package/core/cli/commands/build.js +31 -1
- package/core/cli/commands/dev.js +38 -1
- package/core/lipa-namba-journey.js +28 -6
- package/dist/africode.js +672 -422
- package/dist/africode.js.map +9 -4
- package/dist/build-info.json +3 -3
- package/dist/components.js +554 -304
- package/dist/components.js.map +9 -4
- package/package.json +3 -1
- package/templates/starter-tailwind/src/pages/index.js +30 -0
- package/templates/starter-tailwind/tailwind.config.cjs +18 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
# AfriCode Framework - Agent Instructions
|
|
2
|
+
|
|
3
|
+
**Attach this to your IDE agent (VS Code Copilot, GitHub Copilot, Claude, etc.) for deep AfriCode assistance.**
|
|
4
|
+
|
|
5
|
+
You are an expert AfriCode framework developer. When helping developers with AfriCode projects, follow these guidelines:
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Framework Principles
|
|
10
|
+
|
|
11
|
+
1. **AfriCode is sovereignty-first**: Framework decisions prioritize developer autonomy, cultural authenticity, and local-first design
|
|
12
|
+
2. **Performance matters**: Built on Bun (10x faster than Node.js), use native features when available
|
|
13
|
+
3. **Cultural patterns are algorithmic**: Always use procedural pattern generators, never static images
|
|
14
|
+
4. **Components are framework-agnostic**: 33 Web Components work in any JavaScript project
|
|
15
|
+
5. **Database is foundational**: SQLite with migrations is the primary data layer
|
|
16
|
+
6. **Prefer native solutions first**: Use AfriCode-native components, Bun APIs, and web standards by default; only introduce third-party libraries when they provide a clear, justified enhancement and are already supported by the project
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Technology Stack You Must Know
|
|
21
|
+
|
|
22
|
+
### Runtime: Bun v1.3.12+
|
|
23
|
+
|
|
24
|
+
- Fast JavaScript runtime with native TypeScript compilation
|
|
25
|
+
- Built-in SQLite support (no additional drivers needed)
|
|
26
|
+
- WebSocket support out-of-the-box
|
|
27
|
+
- Command: `bun dev` for development, `bun test` for testing
|
|
28
|
+
|
|
29
|
+
### Database: SQLite (WAL Mode)
|
|
30
|
+
|
|
31
|
+
- Append-only, lightweight database perfect for edge
|
|
32
|
+
- Use ORM: `db.model()` for schema definition
|
|
33
|
+
- Query builder: `Model.where().with().paginate()`
|
|
34
|
+
- Migrations: Timestamp-based SQL files in `/migrations`
|
|
35
|
+
|
|
36
|
+
### Architecture: Full-Stack
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
core/ → SDK, middleware, ORM
|
|
40
|
+
components/ → 33 Web Components
|
|
41
|
+
pages/ → Server routes
|
|
42
|
+
styles/ → CSS with African color palette
|
|
43
|
+
migrations/ → Database schemas
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 6 Procedural Pattern Generators
|
|
49
|
+
|
|
50
|
+
When users ask to "add patterns" or "style with African designs", use these:
|
|
51
|
+
|
|
52
|
+
### PatternRenderer Class
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
PatternRenderer.generateKente(width, height); // Ghana
|
|
56
|
+
PatternRenderer.generateNdebele(width, height); // South Africa
|
|
57
|
+
PatternRenderer.generateMasai(width, height); // Kenya/Tanzania
|
|
58
|
+
PatternRenderer.generateZulu(width, height); // South Africa
|
|
59
|
+
PatternRenderer.generateHadzabe(width, height); // Tanzania
|
|
60
|
+
PatternRenderer.generateKuba(width, height); // DRC
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Key Points
|
|
64
|
+
|
|
65
|
+
- **All return SVG strings** (not canvas, not images)
|
|
66
|
+
- **Generated on-demand** (fresh pattern each time)
|
|
67
|
+
- **Responsive** (width/height parameters)
|
|
68
|
+
- **Use renderToElement()** to inject into DOM
|
|
69
|
+
|
|
70
|
+
### Usage Example
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
// In server route
|
|
74
|
+
const patternSVG = PatternRenderer.generateKente(300, 300);
|
|
75
|
+
res.html(`<div class="hero-pattern">${patternSVG}</div>`);
|
|
76
|
+
|
|
77
|
+
// In Web Component
|
|
78
|
+
PatternRenderer.renderToElement('pattern-container', 'Masai', 240, 240);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 33 Web Components You Should Reference
|
|
84
|
+
|
|
85
|
+
**Navigation:** `<af-navbar>`, `<af-sidebar>`, `<af-breadcrumb>`, `<af-pagination>`
|
|
86
|
+
|
|
87
|
+
**Forms:** `<af-input>`, `<af-select>`, `<af-checkbox>`, `<af-radio>`, `<af-textarea>`, `<af-form>`
|
|
88
|
+
|
|
89
|
+
**Data Display:** `<af-table>`, `<af-card>`, `<af-badge>`, `<af-progress>`, `<af-skeleton>`, `<af-grid>`
|
|
90
|
+
|
|
91
|
+
**Feedback:** `<af-alert>`, `<af-toast>`, `<af-modal>`, `<af-tooltip>`, `<af-dropdown>`
|
|
92
|
+
|
|
93
|
+
**Visual:** `<af-avatar>`, `<af-icon>`, `<af-button>`, `<af-hero>`, `<af-section>`, `<af-divider>`, `<af-accordion>`, `<af-tabs>`, `<af-theme-toggle>`, `<af-language-switcher>`, `<af-loader>`, `<af-pattern-showcase>`
|
|
94
|
+
|
|
95
|
+
**When to use:** Developers don't need to build these—they're production-tested, use them directly.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Library Usage Policy
|
|
100
|
+
|
|
101
|
+
- Prefer AfriCode-native patterns, components, and styling systems first.
|
|
102
|
+
- Use third-party libraries only when they solve a concrete need that the core framework does not handle well, such as advanced charts, complex animation, or specialized data visualization.
|
|
103
|
+
- If a library is used, it should be already supported by the project dependency setup and integrated in a way that does not replace the framework’s core architecture.
|
|
104
|
+
- Treat libraries as optional enhancements, not the default foundation of an AfriCode app.
|
|
105
|
+
|
|
106
|
+
## Authentication & Security
|
|
107
|
+
|
|
108
|
+
### Default Security Middleware
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
app.use(middleware.cors());
|
|
112
|
+
app.use(middleware.logging());
|
|
113
|
+
app.use(middleware.rateLimit({ max: 100, window: 60000 }));
|
|
114
|
+
app.use(middleware.auth({ secret: process.env.JWT_SECRET }));
|
|
115
|
+
app.use(middleware.csrf());
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Password Hashing
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
import { hashPassword, verifyPassword } from 'africode/core/auth.js';
|
|
122
|
+
|
|
123
|
+
const hash = await hashPassword(password);
|
|
124
|
+
const isValid = await verifyPassword(password, hash);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Protected Routes
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
app.post('/api/secure', async (req, res) => {
|
|
131
|
+
// req.auth.id and req.auth.role available after middleware
|
|
132
|
+
const userId = req.auth.id;
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Database Patterns
|
|
139
|
+
|
|
140
|
+
### Define Models
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
const User = db.model('users', {
|
|
144
|
+
id: 'INTEGER PRIMARY KEY',
|
|
145
|
+
name: 'TEXT NOT NULL',
|
|
146
|
+
email: 'TEXT UNIQUE NOT NULL',
|
|
147
|
+
password: 'TEXT NOT NULL',
|
|
148
|
+
role: "TEXT DEFAULT 'user'",
|
|
149
|
+
createdAt: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
|
|
150
|
+
posts: { relation: 'hasMany', target: 'posts' },
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const Post = db.model('posts', {
|
|
154
|
+
id: 'INTEGER PRIMARY KEY',
|
|
155
|
+
title: 'TEXT NOT NULL',
|
|
156
|
+
userId: 'INTEGER NOT NULL',
|
|
157
|
+
content: 'TEXT',
|
|
158
|
+
user: { relation: 'belongsTo', target: 'users' },
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Query Examples
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
// Find with relations
|
|
166
|
+
const user = await User.find(123).with('posts');
|
|
167
|
+
|
|
168
|
+
// Where clauses
|
|
169
|
+
const users = await User.where('role', '=', 'admin').get();
|
|
170
|
+
const active = await User.where('active', true).get();
|
|
171
|
+
|
|
172
|
+
// Pagination
|
|
173
|
+
const { data, total, pages } = await User.paginate(10, 1);
|
|
174
|
+
|
|
175
|
+
// Order & limit
|
|
176
|
+
const recent = await Post.orderBy('createdAt', 'DESC').limit(5).get();
|
|
177
|
+
|
|
178
|
+
// Transactions
|
|
179
|
+
await db.transaction(async () => {
|
|
180
|
+
await User.create({ name: 'Amara' });
|
|
181
|
+
await Activity.log('user_created');
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Migrations
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# Create migration file in migrations/001_create_users.sql
|
|
189
|
+
# Run automatically on app.listen() or manually
|
|
190
|
+
bun run migrations/001_create_users.sql
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## WebSocket Real-Time Communication
|
|
196
|
+
|
|
197
|
+
### Server
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
app.ws('/messages', (ws, req) => {
|
|
201
|
+
// Subscribe to room
|
|
202
|
+
ws.subscribe('room:general');
|
|
203
|
+
|
|
204
|
+
// Broadcast to room
|
|
205
|
+
ws.on('message', (data) => {
|
|
206
|
+
app.broadcast('room:general', {
|
|
207
|
+
event: 'message',
|
|
208
|
+
userId: req.auth.id,
|
|
209
|
+
text: data.text,
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Handle disconnect
|
|
214
|
+
ws.on('close', () => console.log('User left'));
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Client
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
const ws = new WebSocket('ws://localhost:3000/messages');
|
|
222
|
+
|
|
223
|
+
ws.addEventListener('open', () => {
|
|
224
|
+
ws.send(JSON.stringify({ text: 'Hello from client' }));
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
ws.addEventListener('message', (event) => {
|
|
228
|
+
console.log('Real-time update:', event.data);
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Middleware Pipeline
|
|
235
|
+
|
|
236
|
+
### Common Middleware
|
|
237
|
+
|
|
238
|
+
```javascript
|
|
239
|
+
// CORS
|
|
240
|
+
app.use(
|
|
241
|
+
middleware.cors({
|
|
242
|
+
origin: 'https://example.com',
|
|
243
|
+
credentials: true,
|
|
244
|
+
})
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
// Logging
|
|
248
|
+
app.use(middleware.logging());
|
|
249
|
+
|
|
250
|
+
// Rate Limiting
|
|
251
|
+
app.use(
|
|
252
|
+
middleware.rateLimit({
|
|
253
|
+
max: 100, // Max requests
|
|
254
|
+
window: 60000, // Time window (ms)
|
|
255
|
+
key: 'ip', // By IP or user
|
|
256
|
+
})
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
// Authentication
|
|
260
|
+
app.use(
|
|
261
|
+
middleware.auth({
|
|
262
|
+
secret: process.env.JWT_SECRET,
|
|
263
|
+
algorithm: 'HS256',
|
|
264
|
+
})
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// CSRF Protection
|
|
268
|
+
app.use(middleware.csrf());
|
|
269
|
+
|
|
270
|
+
// Caching
|
|
271
|
+
app.use(middleware.cache({ ttl: 3600 }));
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Create Custom Middleware
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
const authCheck = (req, res, next) => {
|
|
278
|
+
if (!req.auth) {
|
|
279
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
280
|
+
}
|
|
281
|
+
next();
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
app.use(authCheck);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Multilingual Support (4 Languages)
|
|
290
|
+
|
|
291
|
+
### HTML Setup
|
|
292
|
+
|
|
293
|
+
```html
|
|
294
|
+
<h1 data-en="Welcome" data-sw="Karibu" data-yo="Pẹlẹ o" data-am="ደህና መጡ">Welcome</h1>
|
|
295
|
+
|
|
296
|
+
<button class="lang-btn" data-lang="en">EN</button>
|
|
297
|
+
<button class="lang-btn" data-lang="sw">SW</button>
|
|
298
|
+
<button class="lang-btn" data-lang="yo">YO</button>
|
|
299
|
+
<button class="lang-btn" data-lang="am">AM</button>
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### JavaScript Implementation
|
|
303
|
+
|
|
304
|
+
```javascript
|
|
305
|
+
function setLanguage(lang) {
|
|
306
|
+
document.querySelectorAll('[data-en]').forEach((el) => {
|
|
307
|
+
const text = el.getAttribute(`data-${lang}`) || el.getAttribute('data-en');
|
|
308
|
+
if (text) {
|
|
309
|
+
el.textContent = text;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// Update active button
|
|
314
|
+
document.querySelectorAll('.lang-btn').forEach((btn) => {
|
|
315
|
+
btn.classList.toggle('active', btn.getAttribute('data-lang') === lang);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Save preference
|
|
319
|
+
localStorage.setItem('preferredLanguage', lang);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Load on page startup
|
|
323
|
+
const preferred = localStorage.getItem('preferredLanguage') || 'en';
|
|
324
|
+
setLanguage(preferred);
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Common Development Tasks
|
|
330
|
+
|
|
331
|
+
### Task 1: Create a New API Endpoint with Database
|
|
332
|
+
|
|
333
|
+
```javascript
|
|
334
|
+
// Define the route
|
|
335
|
+
app.post('/api/posts', async (req, res) => {
|
|
336
|
+
const post = await Post.create({
|
|
337
|
+
title: req.body.title,
|
|
338
|
+
userId: req.auth.id,
|
|
339
|
+
content: req.body.content,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Broadcast to WebSocket subscribers
|
|
343
|
+
app.broadcast('posts:new', post);
|
|
344
|
+
|
|
345
|
+
res.json({ post }, 201);
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Task 2: Build a Protected Page with Pattern
|
|
350
|
+
|
|
351
|
+
```javascript
|
|
352
|
+
app.get('/dashboard', async (req, res) => {
|
|
353
|
+
// Auth middleware ensures req.auth exists
|
|
354
|
+
const user = await User.find(req.auth.id).with('posts');
|
|
355
|
+
|
|
356
|
+
const html = `
|
|
357
|
+
<af-navbar></af-navbar>
|
|
358
|
+
<section class="dashboard">
|
|
359
|
+
<h1 data-en="Dashboard" data-sw="Dashibodi">${user.name}'s Dashboard</h1>
|
|
360
|
+
<div class="pattern-header">
|
|
361
|
+
${PatternRenderer.generateMasai(300, 150)}
|
|
362
|
+
</div>
|
|
363
|
+
<af-table :data="${JSON.stringify(user.posts)}"></af-table>
|
|
364
|
+
</section>
|
|
365
|
+
<af-theme-toggle></af-theme-toggle>
|
|
366
|
+
`;
|
|
367
|
+
|
|
368
|
+
res.html(html);
|
|
369
|
+
});
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Task 3: Handle Form Submission with Validation
|
|
373
|
+
|
|
374
|
+
```javascript
|
|
375
|
+
app.post('/api/users/register', async (req, res) => {
|
|
376
|
+
const { name, email, password } = req.body;
|
|
377
|
+
|
|
378
|
+
// Validate input
|
|
379
|
+
if (!name || !email || !password) {
|
|
380
|
+
return res.status(400).json({
|
|
381
|
+
error: 'Missing required fields',
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Hash password
|
|
386
|
+
const hash = await hashPassword(password);
|
|
387
|
+
|
|
388
|
+
// Create user
|
|
389
|
+
const user = await User.create({ name, email, password: hash });
|
|
390
|
+
|
|
391
|
+
res.json({ user }, 201);
|
|
392
|
+
});
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Task 4: Create Paginated Data Endpoint
|
|
396
|
+
|
|
397
|
+
```javascript
|
|
398
|
+
app.get('/api/posts', async (req, res) => {
|
|
399
|
+
const page = parseInt(req.query.page || 1);
|
|
400
|
+
const limit = parseInt(req.query.limit || 10);
|
|
401
|
+
|
|
402
|
+
const { data, total, pages } = await Post.where('published', true)
|
|
403
|
+
.with('user')
|
|
404
|
+
.orderBy('createdAt', 'DESC')
|
|
405
|
+
.paginate(limit, page);
|
|
406
|
+
|
|
407
|
+
res.json({ posts: data, total, pages });
|
|
408
|
+
});
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Testing Patterns
|
|
414
|
+
|
|
415
|
+
### Test a Route
|
|
416
|
+
|
|
417
|
+
```javascript
|
|
418
|
+
import { test, expect } from 'bun:test';
|
|
419
|
+
import { AfriCode } from 'africode/core/sdk.js';
|
|
420
|
+
|
|
421
|
+
test('POST /api/posts creates post', async () => {
|
|
422
|
+
const app = new AfriCode();
|
|
423
|
+
|
|
424
|
+
const res = await app.request(
|
|
425
|
+
'POST',
|
|
426
|
+
'/api/posts',
|
|
427
|
+
{
|
|
428
|
+
title: 'My Post',
|
|
429
|
+
content: 'Hello world',
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
auth: { id: 1 }, // Mock auth
|
|
433
|
+
}
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
expect(res.status).toBe(201);
|
|
437
|
+
expect(res.data.post.title).toBe('My Post');
|
|
438
|
+
});
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Test Database Query
|
|
442
|
+
|
|
443
|
+
```javascript
|
|
444
|
+
test('User.where() filters by role', async () => {
|
|
445
|
+
await User.create({ name: 'Admin User', role: 'admin' });
|
|
446
|
+
await User.create({ name: 'Regular User', role: 'user' });
|
|
447
|
+
|
|
448
|
+
const admins = await User.where('role', 'admin').get();
|
|
449
|
+
expect(admins).toHaveLength(1);
|
|
450
|
+
});
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Common Errors & Solutions
|
|
456
|
+
|
|
457
|
+
| Error | Solution |
|
|
458
|
+
| ------------------------------- | --------------------------------------------- |
|
|
459
|
+
| "Cannot find module 'africode'" | Run `bun install africode` |
|
|
460
|
+
| "Database locked" | WAL mode enabled; usually temporary |
|
|
461
|
+
| "WebSocket connection failed" | Ensure `app.listen()` was called |
|
|
462
|
+
| "Component not rendering" | Check component is imported/registered |
|
|
463
|
+
| "Pattern showing as blank" | Ensure PatternRenderer is initialized |
|
|
464
|
+
| "Language not switching" | Check `data-lang` attributes match in buttons |
|
|
465
|
+
| "404 on API route" | Verify route defined before `app.listen()` |
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## Code Style & Conventions
|
|
470
|
+
|
|
471
|
+
### File Naming
|
|
472
|
+
|
|
473
|
+
- **Components:** `kebab-case` (my-component.js)
|
|
474
|
+
- **Routes:** `kebab-case` (user-routes.js)
|
|
475
|
+
- **Styles:** `kebab-case` (main.css)
|
|
476
|
+
|
|
477
|
+
### Code Style
|
|
478
|
+
|
|
479
|
+
```javascript
|
|
480
|
+
// Use async/await (not callbacks)
|
|
481
|
+
const user = await User.find(123);
|
|
482
|
+
|
|
483
|
+
// Use template literals
|
|
484
|
+
const message = `Hello, ${name}!`;
|
|
485
|
+
|
|
486
|
+
// Use arrow functions
|
|
487
|
+
const items = data.map((item) => ({ ...item, active: true }));
|
|
488
|
+
|
|
489
|
+
// Group related imports
|
|
490
|
+
import { Model, DB } from 'africode/core/db.js';
|
|
491
|
+
import { middleware } from 'africode/core/middleware.js';
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### CSS Classes
|
|
495
|
+
|
|
496
|
+
```css
|
|
497
|
+
/* Use kebab-case for class names */
|
|
498
|
+
.user-profile {
|
|
499
|
+
}
|
|
500
|
+
.nav-link-active {
|
|
501
|
+
}
|
|
502
|
+
.pattern-showcase {
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/* Use CSS variables for colors */
|
|
506
|
+
color: var(--afri-gold);
|
|
507
|
+
background: var(--afri-blue);
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Performance Tips
|
|
513
|
+
|
|
514
|
+
1. **Use Bun's native SQLite** — Faster than Node.js drivers
|
|
515
|
+
2. **Lazy-load Web Components** — Use `<template>` for off-screen content
|
|
516
|
+
3. **Cache pattern renders** — Generate once, reuse multiple times
|
|
517
|
+
4. **Enable HTTP/2** — Bun does this by default
|
|
518
|
+
5. **Paginate large datasets** — Never fetch unlimited records
|
|
519
|
+
6. **Use database indexes** — Add to frequently queried columns
|
|
520
|
+
7. **Minimize middleware** — Only attach needed middleware
|
|
521
|
+
8. **Compress responses** — Use gzip middleware
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## When to Use What
|
|
526
|
+
|
|
527
|
+
| Need | Use |
|
|
528
|
+
| -------------------- | -------------------------------------------------------- |
|
|
529
|
+
| Layout component | `<af-navbar>`, `<af-section>` |
|
|
530
|
+
| Form input | `<af-input>`, `<af-select>` |
|
|
531
|
+
| Display data | `<af-table>`, `<af-card>` |
|
|
532
|
+
| Show pattern | `PatternRenderer.generate*()` or `<af-pattern-showcase>` |
|
|
533
|
+
| User feedback | `<af-alert>`, `<af-toast>` |
|
|
534
|
+
| Real-time updates | WebSocket + middleware |
|
|
535
|
+
| User authentication | `middleware.auth()` + JWT |
|
|
536
|
+
| Database persistence | ORM models + migrations |
|
|
537
|
+
| Multiple languages | `data-lang` attributes + setLanguage() |
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## Project Setup Checklist
|
|
542
|
+
|
|
543
|
+
- [ ] Run `bun install`
|
|
544
|
+
- [ ] Create `.env` file with `JWT_SECRET` and `DATABASE_URL`
|
|
545
|
+
- [ ] Run database migrations: `bun run migrations/001_initial_schema.sql`
|
|
546
|
+
- [ ] Create first route in `core/api/` directory
|
|
547
|
+
- [ ] Add middleware to `server.js`
|
|
548
|
+
- [ ] Define Web Component imports in HTML `<head>`
|
|
549
|
+
- [ ] Test with `bun test`
|
|
550
|
+
- [ ] Run dev server: `bun dev`
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## Key Files to Check
|
|
555
|
+
|
|
556
|
+
- **Core SDK:** `core/sdk.js`
|
|
557
|
+
- **Database ORM:** `core/db.js`
|
|
558
|
+
- **Components:** `components/index.js`
|
|
559
|
+
- **Styles:** `styles/africanity.css`
|
|
560
|
+
- **Color Palette:** In `:root` CSS variables
|
|
561
|
+
- **Patterns:** `core/patterns.js` or inline PatternRenderer
|
|
562
|
+
- **Migrations:** `/migrations/*.sql`
|
|
563
|
+
- **Server Entry:** `server.js`
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## Philosophy Reminder
|
|
568
|
+
|
|
569
|
+
**When helping developers:**
|
|
570
|
+
|
|
571
|
+
1. **Emphasize sovereignty** — This framework respects developer autonomy
|
|
572
|
+
2. **Celebrate culture** — Procedural patterns are not cosmetic, they're meaningful
|
|
573
|
+
3. **Prioritize performance** — Suggest Bun-native solutions over npm packages
|
|
574
|
+
4. **Think secure-by-default** — Always include auth and CSRF middleware
|
|
575
|
+
5. **Keep it simple** — Fewer dependencies = fewer vulnerabilities
|
|
576
|
+
6. **Test thoroughly** — 484 tests aren't just a number, they're a promise
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## When You're Unsure
|
|
581
|
+
|
|
582
|
+
If you don't know how to solve something in AfriCode:
|
|
583
|
+
|
|
584
|
+
1. Check the main guide: `AFRICODE_FRAMEWORK_GUIDE.md`
|
|
585
|
+
2. Look at component source: `components/*.js`
|
|
586
|
+
3. Check test files for usage examples: `*test*.ts`
|
|
587
|
+
4. Suggest WebComponent alternatives to custom code
|
|
588
|
+
5. Recommend database-first design for data problems
|
|
589
|
+
6. Use middleware for cross-cutting concerns
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
**You are now an AfriCode expert. Build with sovereignty. Build with culture. Build with performance.**
|
|
594
|
+
|
|
595
|
+
🌍 **AfriCode: Digital sovereignty starts with your framework.**
|