@africode/core 5.0.5 → 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/README.md +2 -0
- package/dist/build-info.json +3 -3
- package/package.json +3 -1
package/AGENTS.md
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
# 🤖 AGENTS.md — AfriCode v5.0.0 Robot-First Specification
|
|
2
|
+
|
|
3
|
+
**Purpose**: This document defines the canonical technology stack, coding conventions, and framework grammar for all AI agents building on AfriCode. It ensures generated code is "native" and secure.
|
|
4
|
+
|
|
5
|
+
**Last Updated**: May 2, 2026
|
|
6
|
+
**Framework Version**: 5.0.0
|
|
7
|
+
**Target Deployment**: Bun Runtime (v1.3.12+) on AMD AI Cloud & Alibaba Cloud Assets
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 🏗️ Canonical Technology Stack
|
|
12
|
+
|
|
13
|
+
### Runtime & Build
|
|
14
|
+
- **Runtime**: Bun v1.3.12+ (native, all-in-one: runtime + bundler + test runner)
|
|
15
|
+
- **Language**: ES2024 JavaScript (native, no transpilation needed for modern syntax)
|
|
16
|
+
- **Module Format**: ESM (native Bun import/export)
|
|
17
|
+
- **Build System**: Bun.build() with custom plugins for SVG optimization
|
|
18
|
+
- **Development Server**: Bun.serve() with FileSystemRouter for automatic routing
|
|
19
|
+
|
|
20
|
+
### UI & Components
|
|
21
|
+
- **Component Model**: Web Components (Custom Elements + Shadow DOM + Slots)
|
|
22
|
+
- **State Management**: Proxy-based reactivity (core/state.js) — zero virtual DOM
|
|
23
|
+
- **Styling**: CSS Variables + Logical Properties (CSS Containment Level 3)
|
|
24
|
+
- **Pattern Generation**: Procedural SVG (no asset uploads needed)
|
|
25
|
+
- **Message Protocol**: A2UI v1.0 (core/a2ui.js) for AI-generated interfaces
|
|
26
|
+
|
|
27
|
+
### Data & Persistence
|
|
28
|
+
- **Primary Store**: Bun.SQLite (synchronous API, ultra-fast)
|
|
29
|
+
- **Migration System**: Bun-native SQL scripts in migrations/ folder
|
|
30
|
+
- **Query Pattern**: Direct SQL with type-safe parameter binding
|
|
31
|
+
- **Cache Layer**: In-memory Proxy objects with automatic expiry
|
|
32
|
+
|
|
33
|
+
### Security & Compliance
|
|
34
|
+
- **Authentication**: Session-based (HttpOnly cookies + SameSite=Strict)
|
|
35
|
+
- **Hashing**: Argon2id (built into Bun)
|
|
36
|
+
- **Fintech Compliance**: NIDA CIG + TIPS + AML/FIU middleware
|
|
37
|
+
- **Cryptography**: Ed25519 for message signing (Bun.crypto)
|
|
38
|
+
- **CORS**: Frame-based origin isolation via Web Components
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 📐 Canonical Directory Structure
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
AfriCode/
|
|
46
|
+
├── bin/ # CLI entry points
|
|
47
|
+
│ ├── africode.js # Main CLI
|
|
48
|
+
│ └── create-africode.js # Project scaffolder
|
|
49
|
+
├── core/ # Framework internals (do not modify)
|
|
50
|
+
│ ├── sdk.js # Main export
|
|
51
|
+
│ ├── state.js # Reactive state engine
|
|
52
|
+
│ ├── store.js # Global store singleton
|
|
53
|
+
│ ├── a2ui.js # AI message protocol
|
|
54
|
+
│ ├── compliance.js # Fintech middleware
|
|
55
|
+
│ ├── bun-runtime.js # Bun server initialization
|
|
56
|
+
│ ├── router.js # FileSystemRouter
|
|
57
|
+
│ ├── html.js # HTML template utilities
|
|
58
|
+
│ ├── patterns.js # SVG procedural generation
|
|
59
|
+
│ ├── validation.js # Input validation schemas
|
|
60
|
+
│ ├── server/ # Server utilities
|
|
61
|
+
│ │ ├── auth.js # Session management
|
|
62
|
+
│ │ ├── db.js # Database layer
|
|
63
|
+
│ │ ├── render.js # SSR template rendering
|
|
64
|
+
│ │ └── router.js # Route handler utilities
|
|
65
|
+
│ └── cli/ # CLI implementation
|
|
66
|
+
│ └── commands/ # Command handlers
|
|
67
|
+
├── components/ # Web Components (pre-approved)
|
|
68
|
+
│ ├── index.js # Component registry & exports
|
|
69
|
+
│ ├── base.js # BaseElement class
|
|
70
|
+
│ ├── button.js # Button component
|
|
71
|
+
│ ├── card.js # Card component
|
|
72
|
+
│ ├── cultural-card.js # Cultural card variant
|
|
73
|
+
│ ├── kanga-card.js # Kanga pattern card
|
|
74
|
+
│ ├── form.js # Form wrapper
|
|
75
|
+
│ ├── input.js # Input control
|
|
76
|
+
│ ├── select.js # Select dropdown
|
|
77
|
+
│ ├── modal.js # Modal dialog
|
|
78
|
+
│ ├── navbar.js # Navigation bar
|
|
79
|
+
│ └── [33 total components]
|
|
80
|
+
├── patterns/ # Procedural SVG generators
|
|
81
|
+
│ ├── kente.js # Kente weaving patterns
|
|
82
|
+
│ ├── shuka.js # Maasai Shuka tartan
|
|
83
|
+
│ ├── ndebele.js # Ndebele geometric
|
|
84
|
+
│ ├── kitenge.js # Kitenge florals
|
|
85
|
+
│ └── [8 regional patterns]
|
|
86
|
+
├── samples/ # 🆕 Hand-curated LLM examples
|
|
87
|
+
│ ├── README.md # Usage guide for agents
|
|
88
|
+
│ ├── basic-app/ # Minimal app (5 files)
|
|
89
|
+
│ │ ├── package.json
|
|
90
|
+
│ │ ├── pages/index.html
|
|
91
|
+
│ │ ├── styles/theme.css
|
|
92
|
+
│ │ └── core/app.js
|
|
93
|
+
│ ├── fintech-kiosk/ # Payment flow (Lipa Namba)
|
|
94
|
+
│ │ ├── pages/
|
|
95
|
+
│ │ │ ├── index.html # Entry
|
|
96
|
+
│ │ │ ├── [payment-id].html # Dynamic route
|
|
97
|
+
│ │ │ └── api/payment.js
|
|
98
|
+
│ │ ├── components/
|
|
99
|
+
│ │ │ ├── payment-form.js
|
|
100
|
+
│ │ │ ├── nida-verify.js
|
|
101
|
+
│ │ │ └── confirmation.js
|
|
102
|
+
│ │ └── core/
|
|
103
|
+
│ │ ├── merchant-service.js
|
|
104
|
+
│ │ └── compliance-check.js
|
|
105
|
+
│ ├── cultural-showcase/ # Pattern display
|
|
106
|
+
│ │ ├── pages/
|
|
107
|
+
│ │ ├── components/
|
|
108
|
+
│ │ └── core/pattern-service.js
|
|
109
|
+
│ ├── admin-dashboard/ # Multi-user app
|
|
110
|
+
│ │ ├── pages/auth/login.html
|
|
111
|
+
│ │ ├── pages/dashboard/
|
|
112
|
+
│ │ ├── components/data-table.js
|
|
113
|
+
│ │ └── middleware/auth-check.js
|
|
114
|
+
│ └── [8+ regional examples]
|
|
115
|
+
├── migrations/ # SQL schema versioning
|
|
116
|
+
│ └── 001_initial_schema.sql # Base schema with compliance fields
|
|
117
|
+
├── styles/ # Design tokens
|
|
118
|
+
│ ├── africanity.css # Main theme
|
|
119
|
+
│ └── typography.css # Type scale
|
|
120
|
+
├── tests/ # Test pyramid (70/20/10)
|
|
121
|
+
│ ├── unit/ # 70% unit tests
|
|
122
|
+
│ │ ├── state.test.js
|
|
123
|
+
│ │ ├── patterns.test.js
|
|
124
|
+
│ │ ├── validation.test.js
|
|
125
|
+
│ │ └── [compliance tests]
|
|
126
|
+
│ ├── integration/ # 20% integration tests
|
|
127
|
+
│ │ ├── a2ui.test.js
|
|
128
|
+
│ │ ├── router.test.js
|
|
129
|
+
│ │ ├── nida-flow.test.js # NIDA verification
|
|
130
|
+
│ │ ├── tips-payment.test.js # TIPS payment
|
|
131
|
+
│ │ └── aml-screening.test.js
|
|
132
|
+
│ └── e2e/ # 10% critical journeys
|
|
133
|
+
│ ├── signup-nida.test.js # Register + verify
|
|
134
|
+
│ ├── lipa-namba.test.js # Payment flow
|
|
135
|
+
│ └── merchant-checkout.test.js
|
|
136
|
+
├── package.json # Main entry point metadata
|
|
137
|
+
├── bunfig.toml # Bun configuration
|
|
138
|
+
├── tsconfig.json # TypeScript settings (optional)
|
|
139
|
+
├── AGENTS.md # 🆕 This file — AI training spec
|
|
140
|
+
├── COMPONENT_SCHEMA.json # 🆕 Auto-generated component manifest
|
|
141
|
+
└── README.md # User documentation
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 🎯 Coding Conventions (AI Must Follow)
|
|
147
|
+
|
|
148
|
+
### File Naming & Structure
|
|
149
|
+
- **Component files**: `kebab-case.js` (e.g., `payment-form.js`)
|
|
150
|
+
- **Utility/service files**: `kebab-case.js` (e.g., `merchant-service.js`)
|
|
151
|
+
- **Test files**: `*.test.js` (e.g., `nida-flow.test.js`)
|
|
152
|
+
- **One component per file** (no monoliths)
|
|
153
|
+
- **Named exports for utilities**, default export for components
|
|
154
|
+
|
|
155
|
+
### JavaScript Patterns
|
|
156
|
+
|
|
157
|
+
#### ✅ CORRECT
|
|
158
|
+
```javascript
|
|
159
|
+
// components/payment-form.js
|
|
160
|
+
export class PaymentForm extends HTMLElement {
|
|
161
|
+
constructor() {
|
|
162
|
+
super();
|
|
163
|
+
this.attachShadow({ mode: 'open' });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
connectedCallback() {
|
|
167
|
+
this.render();
|
|
168
|
+
this.setupEventListeners();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
render() {
|
|
172
|
+
this.shadowRoot.innerHTML = `...`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
setupEventListeners() {
|
|
176
|
+
this.addEventListener('submit', (e) => this.handleSubmit(e));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async handleSubmit(e) {
|
|
180
|
+
e.preventDefault();
|
|
181
|
+
const result = await this.validate();
|
|
182
|
+
if (result.ok) this.dispatchEvent(new CustomEvent('success', { detail: result }));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
customElements.define('af-payment-form', PaymentForm);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### ❌ AVOID
|
|
190
|
+
```javascript
|
|
191
|
+
// DON'T use React JSX
|
|
192
|
+
const PaymentForm = () => <form>...</form>;
|
|
193
|
+
|
|
194
|
+
// DON'T use Vue/Svelte syntax
|
|
195
|
+
<script setup>
|
|
196
|
+
const form = ref(null);
|
|
197
|
+
</script>
|
|
198
|
+
|
|
199
|
+
// DON'T use external state libraries (Redux, Zustand)
|
|
200
|
+
import { store } from '@stores/payment';
|
|
201
|
+
|
|
202
|
+
// DON'T use DOM selectors without scoping
|
|
203
|
+
document.querySelector('.form'); // ❌ Global scope
|
|
204
|
+
|
|
205
|
+
// DO scope to Shadow DOM
|
|
206
|
+
this.shadowRoot.querySelector('.form'); // ✅ Encapsulated
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### State Management Pattern
|
|
210
|
+
```javascript
|
|
211
|
+
// core/app-state.js (CORRECT)
|
|
212
|
+
import { createReactiveState } from 'africode/core/state.js';
|
|
213
|
+
|
|
214
|
+
export const appState = createReactiveState({
|
|
215
|
+
user: null,
|
|
216
|
+
payment: { amount: 0, status: 'idle' },
|
|
217
|
+
nida: { verified: false, nin: null }
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Subscribe to changes
|
|
221
|
+
appState.subscribe('payment.status', (newStatus) => {
|
|
222
|
+
console.log('Payment status changed:', newStatus);
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### API Route Pattern
|
|
227
|
+
```javascript
|
|
228
|
+
// pages/api/payment.js (CORRECT)
|
|
229
|
+
export async function POST(request) {
|
|
230
|
+
const body = await request.json();
|
|
231
|
+
|
|
232
|
+
// Validate input
|
|
233
|
+
const validation = validatePayload(body, paymentSchema);
|
|
234
|
+
if (!validation.ok) return new Response(validation.error, { status: 400 });
|
|
235
|
+
|
|
236
|
+
// Check compliance
|
|
237
|
+
const amlResult = await checkAMLScreening(body.amount);
|
|
238
|
+
if (!amlResult.ok) return new Response('Transaction blocked', { status: 403 });
|
|
239
|
+
|
|
240
|
+
// Process payment
|
|
241
|
+
const tx = await processPayment(body);
|
|
242
|
+
return new Response(JSON.stringify(tx), { status: 200 });
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Component Composition
|
|
247
|
+
```javascript
|
|
248
|
+
// pages/checkout.html (CORRECT)
|
|
249
|
+
<af-payment-form id="form">
|
|
250
|
+
<af-nida-verify slot="identity"></af-nida-verify>
|
|
251
|
+
<af-card slot="summary">
|
|
252
|
+
<h2>Order Summary</h2>
|
|
253
|
+
<af-price amount="50000"></af-price>
|
|
254
|
+
</af-card>
|
|
255
|
+
</af-payment-form>
|
|
256
|
+
|
|
257
|
+
<script type="module">
|
|
258
|
+
import 'africode/components/index.js';
|
|
259
|
+
|
|
260
|
+
const form = document.querySelector('af-payment-form');
|
|
261
|
+
form.addEventListener('success', (e) => {
|
|
262
|
+
console.log('Payment confirmed:', e.detail);
|
|
263
|
+
});
|
|
264
|
+
</script>
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Error Handling
|
|
268
|
+
```javascript
|
|
269
|
+
// Middleware pattern
|
|
270
|
+
export function complianceCheck(handler) {
|
|
271
|
+
return async (request) => {
|
|
272
|
+
try {
|
|
273
|
+
const compliant = await validateCompliance(request);
|
|
274
|
+
if (!compliant) throw new Error('Compliance failed');
|
|
275
|
+
return await handler(request);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error('Compliance error:', error);
|
|
278
|
+
return new Response(
|
|
279
|
+
JSON.stringify({ error: error.message, code: 'COMPLIANCE_ERROR' }),
|
|
280
|
+
{ status: 403 }
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## 🔐 Fintech Compliance Grammar
|
|
290
|
+
|
|
291
|
+
### NIDA Verification Flow
|
|
292
|
+
```javascript
|
|
293
|
+
// core/server/nida-flow.js
|
|
294
|
+
export async function verifyNIDA(nin, pin) {
|
|
295
|
+
// Step 1: Sign request with Ed25519 private key
|
|
296
|
+
const signature = await signRequest({ nin, timestamp: Date.now() }, privateKey);
|
|
297
|
+
|
|
298
|
+
// Step 2: Call CIG endpoint over TLS 1.3
|
|
299
|
+
const response = await fetch('https://api.nida.go.tz/cig/verify', {
|
|
300
|
+
method: 'POST',
|
|
301
|
+
headers: {
|
|
302
|
+
'Content-Type': 'application/json',
|
|
303
|
+
'X-Signature': signature,
|
|
304
|
+
'X-Client-Id': 'africode-v5'
|
|
305
|
+
},
|
|
306
|
+
body: JSON.stringify({ nin, pin })
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Step 3: Validate response signature
|
|
310
|
+
const data = await response.json();
|
|
311
|
+
const isValid = await verifySignature(data, data.signature, nidaPublicKey);
|
|
312
|
+
|
|
313
|
+
if (!isValid) throw new Error('Invalid signature from NIDA');
|
|
314
|
+
return data;
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### TIPS Payment Flow
|
|
319
|
+
```javascript
|
|
320
|
+
// core/server/tips-flow.js
|
|
321
|
+
export async function initiateP2PPayment(sender, receiver, amount) {
|
|
322
|
+
// Validate both parties exist in NIDA
|
|
323
|
+
await verifyNIDA(sender.nin);
|
|
324
|
+
await verifyNIDA(receiver.nin);
|
|
325
|
+
|
|
326
|
+
// Create TIPS transaction
|
|
327
|
+
const tipsPayload = {
|
|
328
|
+
transactionType: 'P2P',
|
|
329
|
+
senderNIN: sender.nin,
|
|
330
|
+
receiverNIN: receiver.nin,
|
|
331
|
+
amount: amount,
|
|
332
|
+
currency: 'TZS',
|
|
333
|
+
reference: generateReference(),
|
|
334
|
+
timestamp: new Date().toISOString()
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// Sign and send to TIPS
|
|
338
|
+
const tipsResponse = await callTIPS('/p2p/initiate', tipsPayload);
|
|
339
|
+
|
|
340
|
+
// Log for AML/FIU reporting
|
|
341
|
+
await logAMLTransaction(tipsResponse.transactionId, tipsPayload);
|
|
342
|
+
|
|
343
|
+
return tipsResponse;
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### AML Screening
|
|
348
|
+
```javascript
|
|
349
|
+
// core/server/aml-check.js
|
|
350
|
+
export async function screenTransaction(amount, nin) {
|
|
351
|
+
// Query FIU's AML database
|
|
352
|
+
const suspicious = await queryFIUList(nin);
|
|
353
|
+
if (suspicious) return { ok: false, reason: 'SUSPICIOUS_PARTY' };
|
|
354
|
+
|
|
355
|
+
// Check transaction limits
|
|
356
|
+
const dailyTotal = await getDailyTransactionTotal(nin);
|
|
357
|
+
if (dailyTotal + amount > 50_000_000) { // 50M TZS daily limit
|
|
358
|
+
return { ok: false, reason: 'AMOUNT_EXCEEDS_LIMIT' };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Queue for AML reporting (24-hour window)
|
|
362
|
+
await queueAMLReport({
|
|
363
|
+
transactionId: generateId(),
|
|
364
|
+
amount,
|
|
365
|
+
nin,
|
|
366
|
+
timestamp: new Date()
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
return { ok: true };
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## 📦 Component Manifest (COMPONENT_SCHEMA.json)
|
|
376
|
+
|
|
377
|
+
**Purpose**: Auto-generated JSON schema that whitelists safe components for AI generation. Prevents hallucinated HTML and enforces A2UI compliance.
|
|
378
|
+
|
|
379
|
+
**Generated by**: `scripts/generate-component-schema.js`
|
|
380
|
+
|
|
381
|
+
```json
|
|
382
|
+
{
|
|
383
|
+
"version": "5.0.0",
|
|
384
|
+
"timestamp": "2026-05-02T00:00:00Z",
|
|
385
|
+
"components": [
|
|
386
|
+
{
|
|
387
|
+
"tagName": "af-button",
|
|
388
|
+
"filePath": "components/button.js",
|
|
389
|
+
"slots": ["default"],
|
|
390
|
+
"attributes": {
|
|
391
|
+
"variant": { "type": "string", "enum": ["primary", "secondary", "ghost"], "default": "primary" },
|
|
392
|
+
"size": { "type": "string", "enum": ["sm", "md", "lg"], "default": "md" },
|
|
393
|
+
"disabled": { "type": "boolean", "default": false }
|
|
394
|
+
},
|
|
395
|
+
"events": [
|
|
396
|
+
{ "name": "click", "detail": { "type": "object" } }
|
|
397
|
+
],
|
|
398
|
+
"cssCustomProperties": [
|
|
399
|
+
"--af-button-bg",
|
|
400
|
+
"--af-button-text",
|
|
401
|
+
"--af-button-border"
|
|
402
|
+
],
|
|
403
|
+
"example": "<af-button variant=\"primary\" size=\"lg\">Click Me</af-button>",
|
|
404
|
+
"securityNotes": "Sanitizes slot content"
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
"tagName": "af-nida-verify",
|
|
408
|
+
"filePath": "components/nida-verify.js",
|
|
409
|
+
"slots": [],
|
|
410
|
+
"attributes": {
|
|
411
|
+
"nin": { "type": "string", "required": true, "pattern": "^[0-9]{20}$" },
|
|
412
|
+
"pin": { "type": "string", "required": true }
|
|
413
|
+
},
|
|
414
|
+
"events": [
|
|
415
|
+
{ "name": "verified", "detail": { "verified": true, "user": {} } }
|
|
416
|
+
],
|
|
417
|
+
"complianceNotes": "NIDA CIG v2.1 compliant, Ed25519 signed requests"
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
"tagName": "af-payment-form",
|
|
421
|
+
"filePath": "components/payment-form.js",
|
|
422
|
+
"slots": ["identity", "summary"],
|
|
423
|
+
"attributes": {
|
|
424
|
+
"merchant-id": { "type": "string", "required": true },
|
|
425
|
+
"amount": { "type": "number", "required": true }
|
|
426
|
+
},
|
|
427
|
+
"events": [
|
|
428
|
+
{ "name": "success", "detail": { "transactionId": "", "status": "" } },
|
|
429
|
+
{ "name": "error", "detail": { "error": "", "code": "" } }
|
|
430
|
+
],
|
|
431
|
+
"complianceNotes": "Auto-screens AML, enforces TIPS standards"
|
|
432
|
+
}
|
|
433
|
+
],
|
|
434
|
+
"forbiddenPatterns": [
|
|
435
|
+
"eval()",
|
|
436
|
+
"innerHTML = userInput",
|
|
437
|
+
"fetch without CORS headers",
|
|
438
|
+
"localStorage for sensitive data"
|
|
439
|
+
]
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## ⚡ Bun-Native Optimization Rules
|
|
446
|
+
|
|
447
|
+
### 🎯 Inversion of Control
|
|
448
|
+
```javascript
|
|
449
|
+
// ❌ OLD: Library pattern (user calls your code)
|
|
450
|
+
import { PaymentForm } from 'africode/components';
|
|
451
|
+
const form = new PaymentForm();
|
|
452
|
+
|
|
453
|
+
// ✅ NEW: Framework pattern (framework owns lifecycle)
|
|
454
|
+
// Just add file: pages/checkout.html
|
|
455
|
+
// Bun.serve + FileSystemRouter auto-discovers and serves it
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### 🎯 Asset Optimization (Bun Plugins)
|
|
459
|
+
```javascript
|
|
460
|
+
// bunfig.toml - Bun configuration
|
|
461
|
+
[build]
|
|
462
|
+
plugins = ["./build-plugins/svg-optimizer.js"]
|
|
463
|
+
|
|
464
|
+
// build-plugins/svg-optimizer.js
|
|
465
|
+
export default {
|
|
466
|
+
name: 'svg-optimizer',
|
|
467
|
+
setup(build) {
|
|
468
|
+
build.onLoad({ filter: /\.svg$/ }, async (args) => {
|
|
469
|
+
const svg = await Bun.file(args.path).text();
|
|
470
|
+
const minified = minifySVG(svg);
|
|
471
|
+
return {
|
|
472
|
+
contents: `export default "${minified}"`,
|
|
473
|
+
loader: 'js'
|
|
474
|
+
};
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### 🎯 5ms Startup Target
|
|
481
|
+
- No lazy imports in critical paths
|
|
482
|
+
- Bun.SQLite ready before route handling
|
|
483
|
+
- All validators pre-compiled at startup
|
|
484
|
+
- Compliance certificates pre-loaded in memory
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## 🧪 Test Pyramid (70/20/10)
|
|
489
|
+
|
|
490
|
+
### Unit Tests (70%) — core/*/
|
|
491
|
+
```bash
|
|
492
|
+
bun test tests/unit/*.test.js
|
|
493
|
+
# Focus: individual functions, validators, pattern generators
|
|
494
|
+
# Example: patterns.test.js — verify Kente weave algorithm
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Integration Tests (20%) — pages/api/*/
|
|
498
|
+
```bash
|
|
499
|
+
bun test tests/integration/*.test.js
|
|
500
|
+
# Focus: A2UI rendering, router matching, compliance middleware
|
|
501
|
+
# Example: nida-flow.test.js — full verification ceremony
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### E2E Tests (10%) — critical user journeys
|
|
505
|
+
```bash
|
|
506
|
+
bun test tests/e2e/*.test.js
|
|
507
|
+
# Focus: signup-nida, lipa-namba payment, merchant-checkout
|
|
508
|
+
# Example: lipa-namba.test.js — real TIPS transaction simulation
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## 🔥 Hot-Reload Middleware
|
|
514
|
+
|
|
515
|
+
```javascript
|
|
516
|
+
// core/hmr.js (CORRECT)
|
|
517
|
+
export function createHMRServer() {
|
|
518
|
+
return new WebSocket('ws://localhost:3001/hmr');
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// In Bun.serve, call with --hot flag:
|
|
522
|
+
// bun run bin/africode.js dev --hot
|
|
523
|
+
// This enables:
|
|
524
|
+
// - File watching via Bun.watch()
|
|
525
|
+
// - Soft reloads (no session loss)
|
|
526
|
+
// - Database connection persistence
|
|
527
|
+
// - In-memory state retained
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
## 📋 AI Agent Constraints
|
|
533
|
+
|
|
534
|
+
When building apps with this spec, AI agents MUST:
|
|
535
|
+
|
|
536
|
+
1. **Only use pre-approved components** from COMPONENT_SCHEMA.json
|
|
537
|
+
2. **Never generate raw HTML** — use af-* Web Components
|
|
538
|
+
3. **Prefer AfriCode-native patterns first**; use external libraries only when they provide a clear, justified enhancement and are already supported by the project
|
|
539
|
+
4. **Always validate inputs** with schemas in core/validation.js
|
|
540
|
+
5. **Always call compliance middleware** before processing payments
|
|
541
|
+
6. **Always sign NIDA requests** with Ed25519 keys
|
|
542
|
+
7. **Always log transactions** for AML/FIU reporting
|
|
543
|
+
8. **Always use Bun-native APIs** (no Node.js polyfills)
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## 🚀 Quick Verification
|
|
548
|
+
|
|
549
|
+
To verify an AI-generated app is "native" to AfriCode:
|
|
550
|
+
|
|
551
|
+
```bash
|
|
552
|
+
# Check component usage
|
|
553
|
+
grep -r "<af-" pages/ components/ | wc -l # Should be > 0
|
|
554
|
+
|
|
555
|
+
# Check for forbidden patterns
|
|
556
|
+
grep -r "eval(" pages/ components/ # Should be empty
|
|
557
|
+
grep -r "innerHTML =" pages/ components/ # Should only be in Shadow DOM
|
|
558
|
+
|
|
559
|
+
# Check Bun-native compliance
|
|
560
|
+
grep -r "fetch(" pages/ | grep -v "CORS" # Should all have headers
|
|
561
|
+
|
|
562
|
+
# Run test pyramid
|
|
563
|
+
bun test tests/unit/ # 70% should pass instantly
|
|
564
|
+
bun test tests/integration/ # 20% should pass in < 5s
|
|
565
|
+
bun test tests/e2e/ # 10% critical journeys
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## 📚 Resources
|
|
571
|
+
|
|
572
|
+
- **Framework API**: https://github.com/africode/framework
|
|
573
|
+
- **Bun Docs**: https://bun.sh/docs
|
|
574
|
+
- **Web Components**: https://developer.mozilla.org/en-US/docs/Web/Web_Components
|
|
575
|
+
- **NIDA CIG v2.1**: https://nida.go.tz/cig/docs
|
|
576
|
+
- **TIPS Standards**: https://tips.go.tz/standards
|
|
577
|
+
- **AML/FIU**: https://fiu.go.tz/reporting
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
**Last Reviewed**: May 2, 2026
|
|
582
|
+
**Next Review**: August 2, 2026 (quarterly)
|
|
583
|
+
**Contact**: framework@africode.dev
|
|
@@ -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.**
|
package/README.md
CHANGED
|
@@ -83,6 +83,8 @@ my-app/
|
|
|
83
83
|
|
|
84
84
|
### AI-Safe Development
|
|
85
85
|
|
|
86
|
+
The published package includes the agent guidance files so AI assistants can discover the framework rules when the package is installed. These files are bundled as [AGENTS.md](AGENTS.md) and [AGENT_INSTRUCTIONS.md](AGENT_INSTRUCTIONS.md).
|
|
87
|
+
|
|
86
88
|
```typescript
|
|
87
89
|
// pages/index.ts
|
|
88
90
|
import { html } from '@africode/core';
|
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"timestamp": "2026-06-28T11:
|
|
3
|
-
"version": "5.0.
|
|
2
|
+
"timestamp": "2026-06-28T11:53:06.111Z",
|
|
3
|
+
"version": "5.0.6",
|
|
4
4
|
"bundles": {
|
|
5
5
|
"core": {
|
|
6
6
|
"file": "africode.js",
|
|
@@ -19,5 +19,5 @@
|
|
|
19
19
|
]
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
|
-
"buildTime": "
|
|
22
|
+
"buildTime": "130.11ms"
|
|
23
23
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@africode/core",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.6",
|
|
4
4
|
"description": "Bun-native full-stack framework with generative AI, fintech compliance, and real-time performance - built for Tanzanian digital economy",
|
|
5
5
|
"module": "core/sdk.js",
|
|
6
6
|
"main": "core/sdk.js",
|
|
@@ -105,6 +105,8 @@
|
|
|
105
105
|
"styles",
|
|
106
106
|
"templates",
|
|
107
107
|
"dist",
|
|
108
|
+
"AGENTS.md",
|
|
109
|
+
"AGENT_INSTRUCTIONS.md",
|
|
108
110
|
"AFRICODE_FRAMEWORK_GUIDE.md",
|
|
109
111
|
"docs/IDE-Guide.md",
|
|
110
112
|
"COMPONENT_SCHEMA.json",
|