@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
@@ -0,0 +1,481 @@
1
+ /**
2
+ * AfriCode Advanced ORM
3
+ * Query builder, relationships, transactions, and migrations
4
+ *
5
+ * Features:
6
+ * - Fluent query builder (.where, .select, .join, .orderBy, .limit)
7
+ * - Relationship definitions (.hasMany, .belongsTo)
8
+ * - Transaction support for ACID operations
9
+ * - Migration runner for schema versioning
10
+ */
11
+
12
+ import { Database } from 'bun:sqlite';
13
+ import { query as dbQuery, get as dbGet, run as dbRun } from './db.js';
14
+
15
+ /**
16
+ * Query Builder
17
+ * Fluent interface for building SQL queries
18
+ */
19
+ export class QueryBuilder {
20
+ constructor(table, db = null) {
21
+ this.table = table;
22
+ this.db = db;
23
+ this.selectFields = ['*'];
24
+ this.whereConditions = [];
25
+ this.joinClauses = [];
26
+ this.orderByClause = null;
27
+ this.limitClause = null;
28
+ this.offsetClause = null;
29
+ this.bindings = [];
30
+ }
31
+
32
+ /**
33
+ * Select specific columns
34
+ */
35
+ select(...fields) {
36
+ this.selectFields = fields.length > 0 ? fields : ['*'];
37
+ return this;
38
+ }
39
+
40
+ /**
41
+ * Add WHERE condition
42
+ */
43
+ where(field, operator, value = undefined) {
44
+ if (value === undefined) {
45
+ // where(field, value) syntax
46
+ value = operator;
47
+ operator = '=';
48
+ }
49
+
50
+ this.whereConditions.push({ field, operator, value });
51
+ this.bindings.push(value);
52
+ return this;
53
+ }
54
+
55
+ /**
56
+ * Add OR WHERE condition
57
+ */
58
+ orWhere(field, operator, value = undefined) {
59
+ if (value === undefined) {
60
+ value = operator;
61
+ operator = '=';
62
+ }
63
+
64
+ this.whereConditions.push({ field, operator, value, or: true });
65
+ this.bindings.push(value);
66
+ return this;
67
+ }
68
+
69
+ /**
70
+ * Add JOIN clause
71
+ */
72
+ join(table, field1, operator, field2) {
73
+ this.joinClauses.push({ table, field1, operator, field2, type: 'INNER' });
74
+ return this;
75
+ }
76
+
77
+ /**
78
+ * Add LEFT JOIN clause
79
+ */
80
+ leftJoin(table, field1, operator, field2) {
81
+ this.joinClauses.push({ table, field1, operator, field2, type: 'LEFT' });
82
+ return this;
83
+ }
84
+
85
+ /**
86
+ * Order by column
87
+ */
88
+ orderBy(field, direction = 'ASC') {
89
+ this.orderByClause = `${field} ${direction}`;
90
+ return this;
91
+ }
92
+
93
+ /**
94
+ * Limit results
95
+ */
96
+ limit(count) {
97
+ this.limitClause = count;
98
+ return this;
99
+ }
100
+
101
+ /**
102
+ * Offset results
103
+ */
104
+ offset(count) {
105
+ this.offsetClause = count;
106
+ return this;
107
+ }
108
+
109
+ /**
110
+ * Build SQL query
111
+ */
112
+ build() {
113
+ let sql = `SELECT ${this.selectFields.join(', ')} FROM ${this.table}`;
114
+
115
+ // Add JOINs
116
+ for (const join of this.joinClauses) {
117
+ sql += ` ${join.type} JOIN ${join.table} ON ${join.field1} ${join.operator} ${join.field2}`;
118
+ }
119
+
120
+ // Add WHERE conditions
121
+ if (this.whereConditions.length > 0) {
122
+ const conditions = this.whereConditions.map((cond, idx) => {
123
+ const prefix = idx > 0 && cond.or ? 'OR' : idx > 0 ? 'AND' : '';
124
+ return `${prefix} ${cond.field} ${cond.operator} ?`.trim();
125
+ });
126
+ sql += ` WHERE ${conditions.join(' ')}`;
127
+ }
128
+
129
+ // Add ORDER BY
130
+ if (this.orderByClause) {
131
+ sql += ` ORDER BY ${this.orderByClause}`;
132
+ }
133
+
134
+ // Add LIMIT
135
+ if (this.limitClause !== null) {
136
+ sql += ` LIMIT ${this.limitClause}`;
137
+ }
138
+
139
+ // Add OFFSET
140
+ if (this.offsetClause !== null) {
141
+ sql += ` OFFSET ${this.offsetClause}`;
142
+ }
143
+
144
+ return { sql, bindings: this.bindings };
145
+ }
146
+
147
+ /**
148
+ * Execute query and get all results
149
+ */
150
+ all() {
151
+ const { sql, bindings } = this.build();
152
+ return dbQuery(sql, bindings);
153
+ }
154
+
155
+ /**
156
+ * Execute query and get first result
157
+ */
158
+ first() {
159
+ const { sql, bindings } = this.build();
160
+ return dbGet(sql, bindings);
161
+ }
162
+
163
+ /**
164
+ * Execute query and get count
165
+ */
166
+ count() {
167
+ const originalSelect = this.selectFields;
168
+ this.selectFields = ['COUNT(*) as count'];
169
+ const result = this.first();
170
+ this.selectFields = originalSelect;
171
+ return result?.count || 0;
172
+ }
173
+
174
+ /**
175
+ * Get SQL and bindings without executing
176
+ */
177
+ toSql() {
178
+ return this.build();
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Model Base Class
184
+ * Provides common ORM functionality
185
+ */
186
+ export class Model {
187
+ constructor(data = {}) {
188
+ this.data = data;
189
+ this.relationships = {};
190
+ this.isDirty = false;
191
+ }
192
+
193
+ /**
194
+ * Define hasMany relationship
195
+ */
196
+ static hasMany(relationName, foreignModel, foreignKey, localKey = 'id') {
197
+ return function() {
198
+ const localValue = this.data[localKey];
199
+ if (!localValue) return [];
200
+ const related = new foreignModel();
201
+ return new QueryBuilder(foreignModel.tableName)
202
+ .where(foreignKey, localValue)
203
+ .all();
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Define belongsTo relationship
209
+ */
210
+ static belongsTo(relationName, parentModel, foreignKey, parentKey = 'id') {
211
+ return function() {
212
+ const foreignValue = this.data[foreignKey];
213
+ if (!foreignValue) return null;
214
+ const parent = new parentModel();
215
+ return new QueryBuilder(parentModel.tableName)
216
+ .where(parentKey, foreignValue)
217
+ .first();
218
+ };
219
+ }
220
+
221
+ /**
222
+ * Get a relationship
223
+ */
224
+ getRelation(name) {
225
+ if (!this.relationships[name]) {
226
+ throw new Error(`Relationship "${name}" not defined on model`);
227
+ }
228
+ return this.relationships[name].call(this);
229
+ }
230
+
231
+ /**
232
+ * Query builder for this model
233
+ */
234
+ static query() {
235
+ return new QueryBuilder(this.tableName);
236
+ }
237
+
238
+ /**
239
+ * Find by ID
240
+ */
241
+ static findById(id) {
242
+ return new QueryBuilder(this.tableName)
243
+ .where('id', id)
244
+ .first();
245
+ }
246
+
247
+ /**
248
+ * Find all
249
+ */
250
+ static all() {
251
+ return new QueryBuilder(this.tableName).all();
252
+ }
253
+
254
+ /**
255
+ * Create new record
256
+ */
257
+ static create(data) {
258
+ const fields = Object.keys(data);
259
+ const values = Object.values(data);
260
+ const placeholders = fields.map(() => '?').join(', ');
261
+
262
+ const sql = `INSERT INTO ${this.tableName} (${fields.join(', ')}) VALUES (${placeholders})`;
263
+ const result = dbRun(sql, values);
264
+
265
+ return { id: result.lastInsertRowid, ...data };
266
+ }
267
+
268
+ /**
269
+ * Update record
270
+ */
271
+ static update(id, data) {
272
+ const fields = Object.keys(data);
273
+ const values = Object.values(data);
274
+ const setClause = fields.map(f => `${f} = ?`).join(', ');
275
+
276
+ const sql = `UPDATE ${this.tableName} SET ${setClause}, updated_at = CURRENT_TIMESTAMP WHERE id = ?`;
277
+ return dbRun(sql, [...values, id]);
278
+ }
279
+
280
+ /**
281
+ * Delete record
282
+ */
283
+ static delete(id) {
284
+ const sql = `DELETE FROM ${this.tableName} WHERE id = ?`;
285
+ return dbRun(sql, [id]);
286
+ }
287
+ }
288
+
289
+ /**
290
+ * Transaction Support
291
+ * Provides ACID-compliant multi-step operations
292
+ * Note: SQLite auto-manages transactions in WAL mode, so we batch operations
293
+ */
294
+ export class Transaction {
295
+ constructor(db) {
296
+ this.db = db;
297
+ this.isActive = false;
298
+ this.operations = [];
299
+ }
300
+
301
+ /**
302
+ * Begin transaction (queues operations)
303
+ */
304
+ begin() {
305
+ this.isActive = true;
306
+ this.operations = [];
307
+ return this;
308
+ }
309
+
310
+ /**
311
+ * Add operation to transaction
312
+ */
313
+ addOperation(sql, bindings = []) {
314
+ if (!this.isActive) {
315
+ throw new Error('Transaction not active');
316
+ }
317
+ this.operations.push({ sql, bindings });
318
+ return this;
319
+ }
320
+
321
+ /**
322
+ * Commit transaction (executes all operations)
323
+ */
324
+ commit() {
325
+ if (!this.isActive) {
326
+ throw new Error('No active transaction');
327
+ }
328
+
329
+ try {
330
+ // Execute all operations
331
+ for (const op of this.operations) {
332
+ dbRun(op.sql, op.bindings);
333
+ }
334
+ this.isActive = false;
335
+ this.operations = [];
336
+ return true;
337
+ } catch (error) {
338
+ this.rollback();
339
+ throw error;
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Rollback transaction (clear operations)
345
+ */
346
+ rollback() {
347
+ if (this.isActive) {
348
+ this.isActive = false;
349
+ this.operations = [];
350
+ }
351
+ }
352
+ }
353
+
354
+ /**
355
+ * Migration Runner
356
+ * Handles schema versioning and migrations
357
+ */
358
+ export class MigrationRunner {
359
+ constructor(db) {
360
+ this.db = db;
361
+ this.migrationsPath = new URL('../migrations', import.meta.url).pathname;
362
+ this.initMigrationsTable();
363
+ }
364
+
365
+ /**
366
+ * Initialize migrations tracking table
367
+ */
368
+ initMigrationsTable() {
369
+ const sql = `
370
+ CREATE TABLE IF NOT EXISTS migrations (
371
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
372
+ name TEXT UNIQUE NOT NULL,
373
+ executed_at DATETIME DEFAULT CURRENT_TIMESTAMP
374
+ )
375
+ `;
376
+ dbRun(sql);
377
+ }
378
+
379
+ /**
380
+ * Get list of executed migrations
381
+ */
382
+ getExecutedMigrations() {
383
+ const sql = 'SELECT name FROM migrations ORDER BY executed_at ASC';
384
+ const results = dbQuery(sql);
385
+ return results.map(r => r.name);
386
+ }
387
+
388
+ /**
389
+ * Get list of pending migrations
390
+ */
391
+ async getPendingMigrations() {
392
+ const executed = this.getExecutedMigrations();
393
+ const migrationsDir = Bun.file(this.migrationsPath);
394
+
395
+ if (!migrationsDir.exists) {
396
+ return [];
397
+ }
398
+
399
+ const allMigrations = [];
400
+ // In a real implementation, this would scan the migrations directory
401
+ return allMigrations.filter(m => !executed.includes(m));
402
+ }
403
+
404
+ /**
405
+ * Run a single migration
406
+ */
407
+ runMigration(name, migrationSQL) {
408
+ try {
409
+ // Execute migration SQL
410
+ if (migrationSQL && migrationSQL.trim()) {
411
+ dbRun(migrationSQL);
412
+ }
413
+ // Record migration
414
+ dbRun(
415
+ 'INSERT INTO migrations (name) VALUES (?)',
416
+ [name]
417
+ );
418
+ return true;
419
+ } catch (error) {
420
+ throw new Error(`Migration ${name} failed: ${error.message}`);
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Rollback last migration
426
+ */
427
+ rollbackMigration(name) {
428
+ const sql = 'DELETE FROM migrations WHERE name = ?';
429
+ dbRun(sql, [name]);
430
+ }
431
+
432
+ /**
433
+ * Run all pending migrations
434
+ */
435
+ async runPending() {
436
+ const pending = await this.getPendingMigrations();
437
+ const results = [];
438
+
439
+ for (const migration of pending) {
440
+ try {
441
+ this.runMigration(migration, ''); // SQL would come from file
442
+ results.push({ name: migration, status: 'success' });
443
+ } catch (error) {
444
+ results.push({ name: migration, status: 'failed', error: error.message });
445
+ }
446
+ }
447
+
448
+ return results;
449
+ }
450
+ }
451
+
452
+ /**
453
+ * Define model relationships dynamically
454
+ */
455
+ export function defineRelationships(Model, relationships) {
456
+ for (const [name, definition] of Object.entries(relationships)) {
457
+ if (definition.type === 'hasMany') {
458
+ Model.prototype[name] = Model.hasMany(
459
+ name,
460
+ definition.model,
461
+ definition.foreignKey,
462
+ definition.localKey
463
+ );
464
+ } else if (definition.type === 'belongsTo') {
465
+ Model.prototype[name] = Model.belongsTo(
466
+ name,
467
+ definition.model,
468
+ definition.foreignKey,
469
+ definition.parentKey
470
+ );
471
+ }
472
+ }
473
+ }
474
+
475
+ export default {
476
+ QueryBuilder,
477
+ Model,
478
+ Transaction,
479
+ MigrationRunner,
480
+ defineRelationships
481
+ };