@africode/core 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/AFRICODE_FRAMEWORK_GUIDE.md +707 -0
  2. package/LICENSE +623 -0
  3. package/README.md +442 -0
  4. package/bin/africode.js +73 -0
  5. package/bin/africode.js.1758507140 +343 -0
  6. package/bin/cli.ts +83 -0
  7. package/bin/create-africode.js +158 -0
  8. package/bin/scaffold.ts +219 -0
  9. package/components/accordion.js +183 -0
  10. package/components/alert.js +131 -0
  11. package/components/auth.js +172 -0
  12. package/components/avatar.js +117 -0
  13. package/components/badge.js +104 -0
  14. package/components/base.d.ts +139 -0
  15. package/components/base.js +184 -0
  16. package/components/button.js +164 -0
  17. package/components/card.js +137 -0
  18. package/components/cultural-card.js +243 -0
  19. package/components/divider.js +83 -0
  20. package/components/dropdown.js +171 -0
  21. package/components/error-boundary.js +155 -0
  22. package/components/form.js +131 -0
  23. package/components/grid.js +273 -0
  24. package/components/hero.js +138 -0
  25. package/components/icon.js +36 -0
  26. package/components/index.js +57 -0
  27. package/components/input.js +256 -0
  28. package/components/kanga-card.js +185 -0
  29. package/components/language-switcher.js +108 -0
  30. package/components/loader.js +80 -0
  31. package/components/modal.js +262 -0
  32. package/components/motion.js +84 -0
  33. package/components/navbar.js +236 -0
  34. package/components/pattern-showcase.js +225 -0
  35. package/components/progress.js +134 -0
  36. package/components/react.js +111 -0
  37. package/components/section.js +54 -0
  38. package/components/select.js +322 -0
  39. package/components/sidebar.js +180 -0
  40. package/components/skeleton.js +85 -0
  41. package/components/table.js +181 -0
  42. package/components/tabs.js +202 -0
  43. package/components/theme-toggle.js +82 -0
  44. package/components/toast.js +139 -0
  45. package/components/tooltip.js +167 -0
  46. package/core/a2ui-schema-manager.js +344 -0
  47. package/core/a2ui.js +431 -0
  48. package/core/bun-runtime.js +799 -0
  49. package/core/cli/commands/add.js +23 -0
  50. package/core/cli/commands/audit.js +58 -0
  51. package/core/cli/commands/build.js +137 -0
  52. package/core/cli/commands/create-plugin.js +241 -0
  53. package/core/cli/commands/dev.js +228 -0
  54. package/core/cli/commands/lint.js +23 -0
  55. package/core/cli/commands/test.js +34 -0
  56. package/core/cli/migrator.js +71 -0
  57. package/core/cli/ui.js +46 -0
  58. package/core/compliance.js +628 -0
  59. package/core/config.js +263 -0
  60. package/core/db-advanced.js +481 -0
  61. package/core/db.js +284 -0
  62. package/core/enhanced-hmr.js +404 -0
  63. package/core/errors.js +222 -0
  64. package/core/file-router.js +290 -0
  65. package/core/heartbeat.js +64 -0
  66. package/core/hmr-client.js +204 -0
  67. package/core/hmr.js +196 -0
  68. package/core/html.d.ts +116 -0
  69. package/core/html.js +160 -0
  70. package/core/hydration.js +52 -0
  71. package/core/lipa-namba-journey.js +572 -0
  72. package/core/motion.js +106 -0
  73. package/core/nida-cig-middleware.js +455 -0
  74. package/core/patterns.d.ts +124 -0
  75. package/core/patterns.js +833 -0
  76. package/core/plugins/index.js +312 -0
  77. package/core/router.js +387 -0
  78. package/core/sdk-client.js +62 -0
  79. package/core/sdk.d.ts +133 -0
  80. package/core/sdk.js +123 -0
  81. package/core/seo.js +76 -0
  82. package/core/server/auth-endpoints.js +339 -0
  83. package/core/server/auth.js +180 -0
  84. package/core/server/csrf.js +206 -0
  85. package/core/server/db.js +39 -0
  86. package/core/server/middleware.js +324 -0
  87. package/core/server/rate-limit.js +238 -0
  88. package/core/server/render.js +69 -0
  89. package/core/server/router.js +120 -0
  90. package/core/shim.js +28 -0
  91. package/core/state.d.ts +86 -0
  92. package/core/state.js +242 -0
  93. package/core/store.d.ts +122 -0
  94. package/core/store.js +61 -0
  95. package/core/validation.d.ts +233 -0
  96. package/core/validation.js +590 -0
  97. package/core/websocket.js +639 -0
  98. package/dist/africode.js +2905 -0
  99. package/dist/africode.js.map +61 -0
  100. package/dist/build-info.json +23 -0
  101. package/dist/components.js +2888 -0
  102. package/dist/components.js.map +58 -0
  103. package/dist/styles/africanity.css +322 -0
  104. package/dist/styles/typography.css +141 -0
  105. package/docs/IDE-Guide.md +50 -0
  106. package/package.json +110 -0
  107. package/src/index.ts +196 -0
  108. package/styles/africanity.css +322 -0
  109. package/styles/typography.css +141 -0
  110. package/templates/starter/.env.example +15 -0
  111. package/templates/starter/africode.config.js +40 -0
  112. package/templates/starter/package.json +14 -0
  113. package/templates/starter/src/pages/index.html +46 -0
  114. package/templates/starter/src/pages/index.js +32 -0
  115. package/templates/starter/src/styles/main.css +4 -0
  116. package/templates/starter-3d/.env.example +7 -0
  117. package/templates/starter-3d/africode.config.js +29 -0
  118. package/templates/starter-3d/components/af-model-viewer.js +125 -0
  119. package/templates/starter-3d/package.json +15 -0
  120. package/templates/starter-3d/src/pages/index.html +46 -0
  121. package/templates/starter-3d/src/pages/index.js +50 -0
  122. package/templates/starter-3d/src/styles/main.css +4 -0
  123. package/templates/starter-react/.env.example +15 -0
  124. package/templates/starter-react/africode.config.js +40 -0
  125. package/templates/starter-react/package.json +16 -0
  126. package/templates/starter-react/src/pages/index.html +46 -0
  127. package/templates/starter-react/src/pages/index.js +68 -0
  128. package/templates/starter-react/src/styles/main.css +4 -0
  129. package/templates/starter-tailwind/.env.example +15 -0
  130. package/templates/starter-tailwind/africode.config.js +40 -0
  131. package/templates/starter-tailwind/package.json +20 -0
  132. package/templates/starter-tailwind/src/pages/index.html +46 -0
  133. package/templates/starter-tailwind/src/pages/index.js +37 -0
  134. package/templates/starter-tailwind/src/styles/main.css +4 -0
  135. package/templates/starter-tailwind/src/styles/tailwind.css +1 -0
  136. package/templates/starter-tailwind/src/tailwind-loader.js +30 -0
package/core/config.js ADDED
@@ -0,0 +1,263 @@
1
+ /**
2
+ * AfriCode Framework Configuration
3
+ *
4
+ * Centralized configuration for the entire framework.
5
+ * Provides createFramework() as the entry point for apps.
6
+ *
7
+ * Design:
8
+ * - Single config object controls all framework behavior
9
+ * - Defaults are strict and secure
10
+ * - Every opt-in behavior is explicit
11
+ * - Observability hooks are first-class
12
+ *
13
+ * @module core/config
14
+ */
15
+
16
+ /**
17
+ * @typedef {Object} AfriCodeConfig
18
+ *
19
+ * @property {Object} [validation] - Validation configuration
20
+ * @property {boolean} [validation.trimStrings=false] - Trim whitespace from string inputs
21
+ * @property {boolean} [validation.emptyAsNullDefault=false] - Convert "" → null for nullable fields (NOT recommended)
22
+ *
23
+ * @property {Object} [security] - Security configuration
24
+ * @property {Object} [security.csrf] - CSRF configuration
25
+ * @property {boolean} [security.csrf.enabled=true] - Enable CSRF protection
26
+ * @property {string[]} [security.csrf.excludePaths=[]] - Paths to exclude from CSRF
27
+ * @property {string} [security.csrf.sameSite='Strict'] - SameSite cookie policy
28
+ * @property {boolean} [security.csrf.secure=false] - Require HTTPS for cookies
29
+ * @property {Object} [security.rateLimit] - Rate limit configuration
30
+ * @property {boolean} [security.rateLimit.enabled=true] - Enable rate limiting
31
+ * @property {number} [security.rateLimit.windowMs=60000] - Window in milliseconds
32
+ * @property {number} [security.rateLimit.maxRequests=60] - Max requests per window
33
+ * @property {string[]} [security.rateLimit.excludePaths=[]] - Paths to exclude
34
+ *
35
+ * @property {Object} [hooks] - Observability hooks
36
+ * @property {Function} [hooks.onValidationError] - Called on validation failure
37
+ * @property {Function} [hooks.onSecurityViolation] - Called on CSRF/security failure
38
+ * @property {Function} [hooks.onRateLimit] - Called when rate limit is hit
39
+ * @property {Function} [hooks.onDatabaseError] - Called on database error
40
+ * @property {Function} [hooks.onRequest] - Called on every incoming request
41
+ */
42
+
43
+ /**
44
+ * Default configuration values.
45
+ * Strict by default. Secure by default.
46
+ */
47
+ const DEFAULT_CONFIG = {
48
+ validation: {
49
+ trimStrings: false,
50
+ emptyAsNullDefault: false
51
+ },
52
+ security: {
53
+ csrf: {
54
+ enabled: true,
55
+ excludePaths: [],
56
+ sameSite: 'Strict',
57
+ secure: false
58
+ },
59
+ rateLimit: {
60
+ enabled: true,
61
+ windowMs: 60 * 1000,
62
+ maxRequests: 60,
63
+ excludePaths: []
64
+ }
65
+ },
66
+ hooks: {
67
+ onValidationError: null,
68
+ onSecurityViolation: null,
69
+ onRateLimit: null,
70
+ onDatabaseError: null,
71
+ onRequest: null
72
+ }
73
+ };
74
+
75
+ /** @type {AfriCodeConfig} */
76
+ let _config = deepClone(DEFAULT_CONFIG);
77
+
78
+ /** @type {boolean} */
79
+ let _initialized = false;
80
+
81
+ /**
82
+ * Deep clone a plain object.
83
+ * @param {Object} obj
84
+ * @returns {Object}
85
+ */
86
+ function deepClone(obj) {
87
+ if (obj === null || typeof obj !== 'object') {return obj;}
88
+ if (Array.isArray(obj)) {return obj.map(deepClone);}
89
+ const cloned = {};
90
+ for (const key of Object.keys(obj)) {
91
+ cloned[key] = deepClone(obj[key]);
92
+ }
93
+ return cloned;
94
+ }
95
+
96
+ /**
97
+ * Deep merge source into target.
98
+ * @param {Object} target
99
+ * @param {Object} source
100
+ * @returns {Object}
101
+ */
102
+ function deepMerge(target, source) {
103
+ const result = { ...target };
104
+ for (const key of Object.keys(source)) {
105
+ if (
106
+ source[key] !== null &&
107
+ typeof source[key] === 'object' &&
108
+ !Array.isArray(source[key]) &&
109
+ typeof target[key] === 'object' &&
110
+ !Array.isArray(target[key])
111
+ ) {
112
+ result[key] = deepMerge(target[key] || {}, source[key]);
113
+ } else {
114
+ result[key] = source[key];
115
+ }
116
+ }
117
+ return result;
118
+ }
119
+
120
+ /**
121
+ * Initialize the AfriCode framework with configuration.
122
+ *
123
+ * This is the single entry point for framework configuration.
124
+ * Call once at app startup. All systems read from this config.
125
+ *
126
+ * @param {Partial<AfriCodeConfig>} [userConfig={}] - User overrides
127
+ * @returns {AfriCodeConfig} The resolved configuration
128
+ *
129
+ * @example
130
+ * import { createFramework } from 'africode/core/config';
131
+ *
132
+ * createFramework({
133
+ * validation: {
134
+ * trimStrings: true
135
+ * },
136
+ * security: {
137
+ * csrf: { enabled: true, secure: true },
138
+ * rateLimit: { maxRequests: 100 }
139
+ * },
140
+ * hooks: {
141
+ * onValidationError: (error) => logger.warn('Validation:', error),
142
+ * onSecurityViolation: (error) => logger.error('Security:', error),
143
+ * onRateLimit: (key) => metrics.increment('rate_limit_hit')
144
+ * }
145
+ * });
146
+ */
147
+ export function createFramework(userConfig = {}) {
148
+ _config = deepMerge(DEFAULT_CONFIG, userConfig);
149
+ _initialized = true;
150
+ return getConfig();
151
+ }
152
+
153
+ /**
154
+ * Get the current framework configuration.
155
+ * Returns a frozen copy to prevent accidental mutation.
156
+ *
157
+ * @returns {AfriCodeConfig}
158
+ */
159
+ export function getConfig() {
160
+ return Object.freeze(deepClone(_config));
161
+ }
162
+
163
+ /**
164
+ * Check if the framework has been initialized.
165
+ * @returns {boolean}
166
+ */
167
+ export function isInitialized() {
168
+ return _initialized;
169
+ }
170
+
171
+ /**
172
+ * Reset configuration to defaults.
173
+ * Primarily for testing.
174
+ */
175
+ export function resetConfig() {
176
+ _config = deepClone(DEFAULT_CONFIG);
177
+ _initialized = false;
178
+ }
179
+
180
+ // ─────────────────────────────────────────────────────────────
181
+ // Observability Hooks — fire-and-forget event emitters
182
+ //
183
+ // These are framework-level hooks for monitoring and logging.
184
+ // They never throw — errors in hooks are caught and logged.
185
+ // ─────────────────────────────────────────────────────────────
186
+
187
+ /**
188
+ * Emit a validation error event.
189
+ * @param {import('./errors.js').ValidationError} error
190
+ */
191
+ export function emitValidationError(error) {
192
+ const hook = _config.hooks?.onValidationError;
193
+ if (typeof hook === 'function') {
194
+ try { hook(error); } catch (e) {
195
+ console.error('[AfriCode] Hook error in onValidationError:', e);
196
+ }
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Emit a security violation event.
202
+ * @param {import('./errors.js').SecurityError} error
203
+ */
204
+ export function emitSecurityViolation(error) {
205
+ const hook = _config.hooks?.onSecurityViolation;
206
+ if (typeof hook === 'function') {
207
+ try { hook(error); } catch (e) {
208
+ console.error('[AfriCode] Hook error in onSecurityViolation:', e);
209
+ }
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Emit a rate limit event.
215
+ * @param {string} key - The client identifier that was rate limited
216
+ * @param {Request} [request] - The request that triggered the limit
217
+ */
218
+ export function emitRateLimit(key, request = null) {
219
+ const hook = _config.hooks?.onRateLimit;
220
+ if (typeof hook === 'function') {
221
+ try { hook(key, request); } catch (e) {
222
+ console.error('[AfriCode] Hook error in onRateLimit:', e);
223
+ }
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Emit a database error event.
229
+ * @param {import('./errors.js').DatabaseError} error
230
+ */
231
+ export function emitDatabaseError(error) {
232
+ const hook = _config.hooks?.onDatabaseError;
233
+ if (typeof hook === 'function') {
234
+ try { hook(error); } catch (e) {
235
+ console.error('[AfriCode] Hook error in onDatabaseError:', e);
236
+ }
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Emit a request event.
242
+ * @param {Request} request
243
+ */
244
+ export function emitRequest(request) {
245
+ const hook = _config.hooks?.onRequest;
246
+ if (typeof hook === 'function') {
247
+ try { hook(request); } catch (e) {
248
+ console.error('[AfriCode] Hook error in onRequest:', e);
249
+ }
250
+ }
251
+ }
252
+
253
+ export default {
254
+ createFramework,
255
+ getConfig,
256
+ isInitialized,
257
+ resetConfig,
258
+ emitValidationError,
259
+ emitSecurityViolation,
260
+ emitRateLimit,
261
+ emitDatabaseError,
262
+ emitRequest
263
+ };