@africode/core 5.0.8 → 5.0.9

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.
Files changed (62) hide show
  1. package/AGENT_INSTRUCTIONS.md +595 -595
  2. package/COMPONENT_SCHEMA.json +1800 -991
  3. package/bin/create-africode.js +87 -25
  4. package/components/auth-form.js +154 -0
  5. package/components/index.js +10 -0
  6. package/components/kyc-upload.js +173 -0
  7. package/components/nav-drawer.js +217 -0
  8. package/components/transaction-ledger.js +138 -0
  9. package/components/wallet-balance.js +114 -0
  10. package/core/a2ui-schema-manager.js +1 -1
  11. package/core/a2ui.js +178 -1
  12. package/core/bun-runtime.js +122 -7
  13. package/core/cli/commands/build.js +30 -5
  14. package/core/cli/ui.js +13 -3
  15. package/core/compliance.js +201 -0
  16. package/core/middleware.js +80 -17
  17. package/core/patterns.js +168 -0
  18. package/core/request-analytics.js +254 -0
  19. package/core/request-identity.js +79 -29
  20. package/core/sdk.js +4 -1
  21. package/core/validation.js +13 -0
  22. package/dist/africode.js +858 -457
  23. package/dist/africode.js.map +14 -9
  24. package/dist/build-info.json +3 -3
  25. package/dist/components.js +784 -383
  26. package/dist/components.js.map +11 -6
  27. package/package.json +1 -1
  28. package/templates/starter/package.json +5 -5
  29. package/templates/starter/src/index.js +18 -0
  30. package/templates/starter/src/pages/index.js +1 -1
  31. package/templates/starter-3d/package.json +5 -5
  32. package/templates/starter-3d/src/pages/index.js +1 -1
  33. package/templates/starter-dashboard/.env.example +21 -0
  34. package/templates/starter-dashboard/africode.config.js +20 -0
  35. package/templates/starter-dashboard/package.json +14 -0
  36. package/templates/starter-dashboard/src/index.js +17 -0
  37. package/templates/starter-dashboard/src/pages/api/analytics.js +24 -0
  38. package/templates/starter-dashboard/src/pages/index.html +118 -0
  39. package/templates/starter-dashboard/src/pages/index.js +110 -0
  40. package/templates/starter-dashboard/src/styles/main.css +172 -0
  41. package/templates/starter-fintech/.env.example +28 -0
  42. package/templates/starter-fintech/africode.config.js +20 -0
  43. package/templates/starter-fintech/package.json +15 -0
  44. package/templates/starter-fintech/src/index.js +17 -0
  45. package/templates/starter-fintech/src/pages/api/auth.js +45 -0
  46. package/templates/starter-fintech/src/pages/api/transfer.js +65 -0
  47. package/templates/starter-fintech/src/pages/api/wallet.js +39 -0
  48. package/templates/starter-fintech/src/pages/api/webhooks/payment.js +32 -0
  49. package/templates/starter-fintech/src/pages/index.html +169 -0
  50. package/templates/starter-fintech/src/pages/index.js +161 -0
  51. package/templates/starter-fintech/src/styles/main.css +246 -0
  52. package/templates/starter-react/package.json +5 -5
  53. package/templates/starter-react/src/pages/index.js +1 -1
  54. package/templates/starter-tailwind/package.json +5 -5
  55. package/templates/starter-tailwind/src/pages/index.js +1 -1
  56. package/templates/starter-website/.env.example +18 -0
  57. package/templates/starter-website/africode.config.js +20 -0
  58. package/templates/starter-website/package.json +14 -0
  59. package/templates/starter-website/src/index.js +17 -0
  60. package/templates/starter-website/src/pages/index.html +124 -0
  61. package/templates/starter-website/src/pages/index.js +116 -0
  62. package/templates/starter-website/src/styles/main.css +195 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@africode/core",
3
- "version": "5.0.8",
3
+ "version": "5.0.9",
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",
@@ -4,11 +4,11 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "dev": "africode dev",
8
- "build": "africode build",
9
- "migrate": "africode migrate"
7
+ "dev": "bun run ./bin/africode.js dev",
8
+ "build": "bun run ./bin/africode.js build",
9
+ "migrate": "bun run ./bin/africode.js migrate"
10
10
  },
11
11
  "dependencies": {
12
- "@africode/core": "^5.0.2"
12
+ "@africode/core": "^5.0.9"
13
13
  }
14
- }
14
+ }
@@ -0,0 +1,18 @@
1
+ import { html, Layout } from '@africode/core';
2
+
3
+ export function loader() {
4
+ return { title: 'Welcome to AfriCode' };
5
+ }
6
+
7
+ export default function App({ data }) {
8
+ return Layout({
9
+ title: data?.title || 'Welcome to AfriCode',
10
+ children: html`
11
+ <main>
12
+ <h1>Welcome to AfriCode</h1>
13
+ <p>Your African-centric full-stack framework is ready.</p>
14
+ <p>Use the CLI to add pages and components, then edit this starter app.</p>
15
+ </main>
16
+ `
17
+ });
18
+ }
@@ -1,4 +1,4 @@
1
- import { html, Layout } from 'africode';
1
+ import { html, Layout } from '@africode/core';
2
2
 
3
3
  export function loader() {
4
4
  return { title: 'Welcome to AfriCode' };
@@ -4,12 +4,12 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "dev": "africode dev",
8
- "build": "africode build",
9
- "migrate": "africode migrate"
7
+ "dev": "bun run ./bin/africode.js dev",
8
+ "build": "bun run ./bin/africode.js build",
9
+ "migrate": "bun run ./bin/africode.js migrate"
10
10
  },
11
11
  "dependencies": {
12
- "@africode/core": "^5.0.2",
12
+ "@africode/core": "^5.0.9",
13
13
  "three": "^0.160.0"
14
14
  }
15
- }
15
+ }
@@ -1,4 +1,4 @@
1
- import { html, Layout } from 'africode';
1
+ import { html, Layout } from '@africode/core';
2
2
 
3
3
  /**
4
4
  * Home Page
@@ -0,0 +1,21 @@
1
+ # AfriCode Dashboard Starter Environment
2
+
3
+ # Server
4
+ PORT=3000
5
+ NODE_ENV=development
6
+ PUBLIC_API_URL=http://localhost:3000
7
+
8
+ # Local analytics data
9
+ DB_FILE=./data.db
10
+ ANALYTICS_SOURCE=mock
11
+ ANALYTICS_REFRESH_SECONDS=30
12
+
13
+ # Session and admin access
14
+ SESSION_SECRET=replace_this_with_a_long_random_string
15
+ JWT_SECRET=replace_this_with_another_long_random_string
16
+ ADMIN_EMAIL=admin@example.com
17
+ ADMIN_PASSWORD=change_me_before_production
18
+
19
+ # Optional reporting integrations
20
+ REPORTS_ENABLED=true
21
+ EXPORTS_DIR=./exports
@@ -0,0 +1,20 @@
1
+ /**
2
+ * AfriCode Dashboard Starter Configuration
3
+ */
4
+ export default {
5
+ entry: 'src/index.js',
6
+ server: {
7
+ port: process.env.PORT || 3000,
8
+ host: '0.0.0.0',
9
+ staticDir: 'public',
10
+ cors: true
11
+ },
12
+ directories: {
13
+ src: 'src',
14
+ pages: 'src/pages',
15
+ components: 'src/components',
16
+ styles: 'src/styles',
17
+ build: 'dist'
18
+ },
19
+ theme: { default: 'dark', toggleAttribute: 'data-theme' }
20
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "africode-dashboard-starter",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "bun run ./bin/africode.js dev",
8
+ "build": "bun run ./bin/africode.js build",
9
+ "migrate": "bun run ./bin/africode.js migrate"
10
+ },
11
+ "dependencies": {
12
+ "@africode/core": "^5.0.9"
13
+ }
14
+ }
@@ -0,0 +1,17 @@
1
+ import { html, Layout } from '@africode/core';
2
+
3
+ export function loader() {
4
+ return { title: 'AfriCode Dashboard Starter' };
5
+ }
6
+
7
+ export default function App({ data }) {
8
+ return Layout({
9
+ title: data?.title || 'AfriCode Dashboard Starter',
10
+ children: html`
11
+ <main>
12
+ <h1>Dashboard starter ready</h1>
13
+ <p>Use this template for admin panels, analytics, and internal tools.</p>
14
+ </main>
15
+ `
16
+ });
17
+ }
@@ -0,0 +1,24 @@
1
+ function json(data, status = 200) {
2
+ return new Response(JSON.stringify(data), {
3
+ status,
4
+ headers: { 'Content-Type': 'application/json' }
5
+ });
6
+ }
7
+
8
+ export function GET() {
9
+ return json({
10
+ ok: true,
11
+ source: process.env.ANALYTICS_SOURCE || 'mock',
12
+ refreshedAt: new Date().toISOString(),
13
+ metrics: [
14
+ { id: 'revenue', label: 'Revenue', value: 48250, unit: 'USD', change: 12.4 },
15
+ { id: 'users', label: 'Active users', value: 18420, unit: 'count', change: 8.1 },
16
+ { id: 'conversion', label: 'Conversion', value: 6.8, unit: 'percent', change: -1.2 }
17
+ ],
18
+ activity: [
19
+ { id: 'act_001', title: 'New enterprise account', status: 'review', timestamp: '2026-07-07T08:30:00.000Z' },
20
+ { id: 'act_002', title: 'Revenue report exported', status: 'complete', timestamp: '2026-07-07T09:20:00.000Z' },
21
+ { id: 'act_003', title: 'Usage threshold reached', status: 'attention', timestamp: '2026-07-07T10:05:00.000Z' }
22
+ ]
23
+ });
24
+ }
@@ -0,0 +1,118 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>AfriCode Dashboard Starter</title>
7
+ <script type="importmap">
8
+ {
9
+ "imports": {
10
+ "africode": "/node_modules/@africode/core/core/sdk-browser.js",
11
+ "africode/components": "/node_modules/@africode/core/components/index.js"
12
+ }
13
+ }
14
+ </script>
15
+ <link rel="stylesheet" href="/node_modules/@africode/core/styles/africanity.css" />
16
+ <link rel="stylesheet" href="/styles/main.css" />
17
+ <script type="module" src="/node_modules/@africode/core/core/sdk-browser.js"></script>
18
+ </head>
19
+ <body>
20
+ <main class="dashboard-shell">
21
+ <section class="topbar">
22
+ <div>
23
+ <span class="eyebrow">AfriCode Dashboard Starter</span>
24
+ <h1>Operations overview</h1>
25
+ </div>
26
+ <button class="button" id="theme-toggle" type="button">Toggle theme</button>
27
+ </section>
28
+
29
+ <section class="grid">
30
+ <div>
31
+ <section class="metrics" id="metrics"></section>
32
+ <section class="panel" style="margin-top: 16px;">
33
+ <h2>Weekly signal</h2>
34
+ <div class="chart" id="chart" aria-label="Weekly analytics chart"></div>
35
+ </section>
36
+ </div>
37
+
38
+ <aside class="panel">
39
+ <h2>Activity</h2>
40
+ <div class="activity-list" id="activity-list"></div>
41
+ </aside>
42
+ </section>
43
+ </main>
44
+
45
+ <script type="module">
46
+ const formatMetric = (metric) => {
47
+ if (metric.unit === 'USD') return '$' + Number(metric.value).toLocaleString('en-US');
48
+ if (metric.unit === 'percent') return Number(metric.value).toFixed(1) + '%';
49
+ return Number(metric.value).toLocaleString('en-US');
50
+ };
51
+
52
+ function renderMetrics(metrics) {
53
+ const root = document.querySelector('#metrics');
54
+ root.replaceChildren(...metrics.map((metric) => {
55
+ const card = document.createElement('article');
56
+ const label = document.createElement('span');
57
+ const value = document.createElement('strong');
58
+ const trend = document.createElement('span');
59
+
60
+ card.className = 'metric';
61
+ label.className = 'label';
62
+ value.className = 'value';
63
+ trend.className = metric.change >= 0 ? 'trend good' : 'trend bad';
64
+ label.textContent = metric.label;
65
+ value.textContent = formatMetric(metric);
66
+ trend.textContent = (metric.change >= 0 ? '+' : '') + metric.change + '%';
67
+
68
+ card.append(label, value, trend);
69
+ return card;
70
+ }));
71
+ }
72
+
73
+ function renderActivity(items) {
74
+ const root = document.querySelector('#activity-list');
75
+ root.replaceChildren(...items.map((item) => {
76
+ const row = document.createElement('article');
77
+ const title = document.createElement('strong');
78
+ const meta = document.createElement('span');
79
+
80
+ row.className = 'activity-row';
81
+ meta.className = 'meta';
82
+ title.textContent = item.title;
83
+ meta.textContent = item.status + ' - ' + new Date(item.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
84
+
85
+ row.append(title, meta);
86
+ return row;
87
+ }));
88
+ }
89
+
90
+ function renderChart(metrics) {
91
+ const values = [42, 58, 51, 66, 72, 69, 84];
92
+ const root = document.querySelector('#chart');
93
+ root.replaceChildren(...values.map((value) => {
94
+ const bar = document.createElement('div');
95
+ bar.className = 'bar';
96
+ bar.style.height = value + '%';
97
+ bar.title = value + '%';
98
+ return bar;
99
+ }));
100
+ }
101
+
102
+ async function loadAnalytics() {
103
+ const response = await fetch('/api/analytics');
104
+ const data = await response.json();
105
+ renderMetrics(data.metrics);
106
+ renderActivity(data.activity);
107
+ renderChart(data.metrics);
108
+ }
109
+
110
+ document.querySelector('#theme-toggle').addEventListener('click', () => {
111
+ const root = document.documentElement;
112
+ root.dataset.theme = root.dataset.theme === 'dark' ? 'light' : 'dark';
113
+ });
114
+
115
+ loadAnalytics();
116
+ </script>
117
+ </body>
118
+ </html>
@@ -0,0 +1,110 @@
1
+ import { html, Layout } from '@africode/core';
2
+
3
+ export function loader() {
4
+ return { title: 'AfriCode Dashboard Starter' };
5
+ }
6
+
7
+ export default function DashboardPage({ data }) {
8
+ return Layout({
9
+ title: data?.title || 'AfriCode Dashboard Starter',
10
+ children: html`
11
+ <main class="dashboard-shell">
12
+ <section class="topbar">
13
+ <div>
14
+ <span class="eyebrow">AfriCode Dashboard Starter</span>
15
+ <h1>Operations overview</h1>
16
+ </div>
17
+ <button class="button" id="theme-toggle" type="button">Toggle theme</button>
18
+ </section>
19
+
20
+ <section class="grid">
21
+ <div>
22
+ <section class="metrics" id="metrics"></section>
23
+ <section class="panel" style="margin-top: 16px;">
24
+ <h2>Weekly signal</h2>
25
+ <div class="chart" id="chart" aria-label="Weekly analytics chart"></div>
26
+ </section>
27
+ </div>
28
+
29
+ <aside class="panel">
30
+ <h2>Activity</h2>
31
+ <div class="activity-list" id="activity-list"></div>
32
+ </aside>
33
+ </section>
34
+ </main>
35
+
36
+ <script type="module">
37
+ const formatMetric = (metric) => {
38
+ if (metric.unit === 'USD') return '$' + Number(metric.value).toLocaleString('en-US');
39
+ if (metric.unit === 'percent') return Number(metric.value).toFixed(1) + '%';
40
+ return Number(metric.value).toLocaleString('en-US');
41
+ };
42
+
43
+ function renderMetrics(metrics) {
44
+ const root = document.querySelector('#metrics');
45
+ root.replaceChildren(...metrics.map((metric) => {
46
+ const card = document.createElement('article');
47
+ const label = document.createElement('span');
48
+ const value = document.createElement('strong');
49
+ const trend = document.createElement('span');
50
+
51
+ card.className = 'metric';
52
+ label.className = 'label';
53
+ value.className = 'value';
54
+ trend.className = metric.change >= 0 ? 'trend good' : 'trend bad';
55
+ label.textContent = metric.label;
56
+ value.textContent = formatMetric(metric);
57
+ trend.textContent = (metric.change >= 0 ? '+' : '') + metric.change + '%';
58
+
59
+ card.append(label, value, trend);
60
+ return card;
61
+ }));
62
+ }
63
+
64
+ function renderActivity(items) {
65
+ const root = document.querySelector('#activity-list');
66
+ root.replaceChildren(...items.map((item) => {
67
+ const row = document.createElement('article');
68
+ const title = document.createElement('strong');
69
+ const meta = document.createElement('span');
70
+
71
+ row.className = 'activity-row';
72
+ meta.className = 'meta';
73
+ title.textContent = item.title;
74
+ meta.textContent = item.status + ' - ' + new Date(item.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
75
+
76
+ row.append(title, meta);
77
+ return row;
78
+ }));
79
+ }
80
+
81
+ function renderChart(metrics) {
82
+ const values = [42, 58, 51, 66, 72, 69, 84];
83
+ const root = document.querySelector('#chart');
84
+ root.replaceChildren(...values.map((value) => {
85
+ const bar = document.createElement('div');
86
+ bar.className = 'bar';
87
+ bar.style.height = value + '%';
88
+ bar.title = value + '%';
89
+ return bar;
90
+ }));
91
+ }
92
+
93
+ async function loadAnalytics() {
94
+ const response = await fetch('/api/analytics');
95
+ const data = await response.json();
96
+ renderMetrics(data.metrics);
97
+ renderActivity(data.activity);
98
+ renderChart(data.metrics);
99
+ }
100
+
101
+ document.querySelector('#theme-toggle').addEventListener('click', () => {
102
+ const root = document.documentElement;
103
+ root.dataset.theme = root.dataset.theme === 'dark' ? 'light' : 'dark';
104
+ });
105
+
106
+ loadAnalytics();
107
+ </script>
108
+ `
109
+ });
110
+ }
@@ -0,0 +1,172 @@
1
+ :root {
2
+ color-scheme: light;
3
+ --bg: #f5f7fb;
4
+ --surface: #ffffff;
5
+ --surface-muted: #eef3f8;
6
+ --text: #172033;
7
+ --muted: #647084;
8
+ --border: #d7deea;
9
+ --primary: #2563eb;
10
+ --good: #087443;
11
+ --warn: #b7791f;
12
+ --danger: #b42318;
13
+ --shadow: 0 14px 34px rgba(23, 32, 51, 0.08);
14
+ }
15
+
16
+ [data-theme="dark"] {
17
+ color-scheme: dark;
18
+ --bg: #07111f;
19
+ --surface: #101b2d;
20
+ --surface-muted: #17243a;
21
+ --text: #f8fafc;
22
+ --muted: #a7b4c8;
23
+ --border: #26364f;
24
+ --primary: #60a5fa;
25
+ --good: #34d399;
26
+ --warn: #f5b84b;
27
+ --danger: #fb7185;
28
+ --shadow: 0 18px 44px rgba(0, 0, 0, 0.24);
29
+ }
30
+
31
+ * {
32
+ box-sizing: border-box;
33
+ }
34
+
35
+ body {
36
+ min-height: 100vh;
37
+ margin: 0;
38
+ background: var(--bg);
39
+ color: var(--text);
40
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
41
+ }
42
+
43
+ button {
44
+ font: inherit;
45
+ }
46
+
47
+ .dashboard-shell {
48
+ width: min(1180px, calc(100% - 32px));
49
+ margin: 0 auto;
50
+ padding: 28px 0;
51
+ }
52
+
53
+ .topbar,
54
+ .metric,
55
+ .panel {
56
+ background: var(--surface);
57
+ border: 1px solid var(--border);
58
+ border-radius: 8px;
59
+ box-shadow: var(--shadow);
60
+ }
61
+
62
+ .topbar {
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: space-between;
66
+ gap: 16px;
67
+ padding: 18px 20px;
68
+ }
69
+
70
+ .eyebrow,
71
+ .label,
72
+ .meta,
73
+ .status {
74
+ color: var(--muted);
75
+ font-size: 0.84rem;
76
+ }
77
+
78
+ h1,
79
+ h2,
80
+ h3,
81
+ p {
82
+ margin: 0;
83
+ }
84
+
85
+ h1 {
86
+ font-size: clamp(1.45rem, 3vw, 2.2rem);
87
+ line-height: 1.1;
88
+ }
89
+
90
+ .button {
91
+ min-height: 40px;
92
+ border: 1px solid var(--border);
93
+ border-radius: 6px;
94
+ padding: 0 14px;
95
+ background: var(--surface-muted);
96
+ color: var(--text);
97
+ cursor: pointer;
98
+ }
99
+
100
+ .grid {
101
+ display: grid;
102
+ grid-template-columns: minmax(0, 1fr) 360px;
103
+ gap: 16px;
104
+ margin-top: 16px;
105
+ }
106
+
107
+ .metrics {
108
+ display: grid;
109
+ grid-template-columns: repeat(3, minmax(0, 1fr));
110
+ gap: 16px;
111
+ }
112
+
113
+ .metric,
114
+ .panel {
115
+ padding: 18px;
116
+ }
117
+
118
+ .value {
119
+ display: block;
120
+ margin-top: 10px;
121
+ font-size: 1.6rem;
122
+ font-weight: 760;
123
+ }
124
+
125
+ .trend.good {
126
+ color: var(--good);
127
+ }
128
+
129
+ .trend.bad {
130
+ color: var(--danger);
131
+ }
132
+
133
+ .activity-list {
134
+ display: grid;
135
+ gap: 10px;
136
+ margin-top: 14px;
137
+ }
138
+
139
+ .activity-row {
140
+ display: grid;
141
+ gap: 4px;
142
+ padding: 12px;
143
+ border-radius: 6px;
144
+ background: var(--surface-muted);
145
+ }
146
+
147
+ .chart {
148
+ display: grid;
149
+ align-items: end;
150
+ grid-template-columns: repeat(7, 1fr);
151
+ gap: 8px;
152
+ height: 220px;
153
+ margin-top: 18px;
154
+ padding: 12px;
155
+ border-radius: 8px;
156
+ background: var(--surface-muted);
157
+ }
158
+
159
+ .bar {
160
+ min-height: 18px;
161
+ border-radius: 5px 5px 0 0;
162
+ background: var(--primary);
163
+ }
164
+
165
+ @media (max-width: 860px) {
166
+ .topbar,
167
+ .grid,
168
+ .metrics {
169
+ display: grid;
170
+ grid-template-columns: 1fr;
171
+ }
172
+ }
@@ -0,0 +1,28 @@
1
+ # AfriCode Fintech Starter Environment
2
+
3
+ # Server
4
+ PORT=3000
5
+ NODE_ENV=development
6
+ PUBLIC_API_URL=http://localhost:3000
7
+
8
+ # Local data
9
+ DB_FILE=./data.db
10
+
11
+ # Session and signing secrets
12
+ SESSION_SECRET=replace_this_with_a_long_random_string
13
+ JWT_SECRET=replace_this_with_another_long_random_string
14
+ WEBHOOK_SIGNING_SECRET=whsec_mock_africode_fintech
15
+
16
+ # Sandbox payment and identity placeholders
17
+ PAYMENT_PROVIDER=sandbox
18
+ PAYMENT_PUBLIC_KEY=pk_test_africode
19
+ PAYMENT_SECRET_KEY=sk_test_africode
20
+ NIDA_CIG_BASE_URL=https://sandbox.nida.local
21
+ NIDA_CLIENT_ID=africode_sandbox_client
22
+ NIDA_PRIVATE_KEY_PATH=./secrets/nida-private.pem
23
+ TIPS_BASE_URL=https://sandbox.tips.local
24
+ TIPS_MERCHANT_ID=AFRICODE_SANDBOX_MERCHANT
25
+
26
+ # Compliance simulation
27
+ AML_DAILY_LIMIT_TZS=50000000
28
+ KYC_REQUIRED=true
@@ -0,0 +1,20 @@
1
+ /**
2
+ * AfriCode Fintech Starter Configuration
3
+ */
4
+ export default {
5
+ entry: 'src/index.js',
6
+ server: {
7
+ port: process.env.PORT || 3000,
8
+ host: '0.0.0.0',
9
+ staticDir: 'public',
10
+ cors: true
11
+ },
12
+ directories: {
13
+ src: 'src',
14
+ pages: 'src/pages',
15
+ components: 'src/components',
16
+ styles: 'src/styles',
17
+ build: 'dist'
18
+ },
19
+ theme: { default: 'dark', toggleAttribute: 'data-theme' }
20
+ };
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "africode-fintech-starter",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "bun run ./bin/africode.js dev",
8
+ "build": "bun run ./bin/africode.js build",
9
+ "migrate": "bun run ./bin/africode.js migrate"
10
+ },
11
+ "dependencies": {
12
+ "@africode/core": "^5.0.9",
13
+ "chart.js": "^4.0.0"
14
+ }
15
+ }
@@ -0,0 +1,17 @@
1
+ import { html, Layout } from '@africode/core';
2
+
3
+ export function loader() {
4
+ return { title: 'AfriCode Fintech Starter' };
5
+ }
6
+
7
+ export default function App({ data }) {
8
+ return Layout({
9
+ title: data?.title || 'AfriCode Fintech Starter',
10
+ children: html`
11
+ <main>
12
+ <h1>Fintech starter ready</h1>
13
+ <p>Use this template for wallets, payments, KYC, and merchant flows.</p>
14
+ </main>
15
+ `
16
+ });
17
+ }