thatcher 1.0.7 → 1.0.34

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 (174) hide show
  1. package/README.md +4 -16
  2. package/package.json +12 -18
  3. package/src/adapters/google-auth.js +4 -43
  4. package/src/adapters/google-drive.js +12 -67
  5. package/src/app/api/audit/dashboard/route.js +14 -11
  6. package/src/app/api/audit/logs/route.js +2 -2
  7. package/src/app/api/audit/permissions/[id]/route.js +6 -3
  8. package/src/app/api/audit/permissions/route.js +10 -7
  9. package/src/app/api/audit/permissions/stats/route.js +8 -5
  10. package/src/app/api/audit/route.js +4 -4
  11. package/src/app/api/audit/stats/route.js +1 -1
  12. package/src/app/api/auth/google/callback/route.js +5 -2
  13. package/src/app/api/auth/google/route.js +4 -1
  14. package/src/app/api/auth/login/route.js +5 -2
  15. package/src/app/api/auth/mwr-bridge/route.js +11 -21
  16. package/src/app/api/auth/password-reset/route.js +15 -14
  17. package/src/app/api/cron/trigger/route.js +4 -1
  18. package/src/app/api/debug/[[...path]]/route.js +6 -5
  19. package/src/app/api/debug/sqlite/route.js +7 -12
  20. package/src/app/api/debug/sync/route.js +5 -5
  21. package/src/app/api/domains/route.js +4 -1
  22. package/src/app/api/email/allocate/batch/route.js +19 -29
  23. package/src/app/api/email/allocate/route.js +16 -17
  24. package/src/app/api/email/receive/route.js +31 -42
  25. package/src/app/api/email/send/route.js +28 -16
  26. package/src/app/api/email/unallocated/route.js +10 -13
  27. package/src/app/api/files/[id]/route.js +4 -1
  28. package/src/app/api/formula/[[...path]]/route.js +0 -4
  29. package/src/app/api/health/route.js +10 -9
  30. package/src/app/api/metrics/route.js +1 -1
  31. package/src/cli.js +4 -4
  32. package/src/config/config-loader.js +1 -21
  33. package/src/config/constants.js +0 -35
  34. package/src/config/env.js +0 -30
  35. package/src/config/spec-helpers.js +6 -95
  36. package/src/engine.js +23 -0
  37. package/src/engine.server.js +8 -78
  38. package/src/index.js +42 -67
  39. package/src/lib/alert-manager.js +7 -3
  40. package/src/lib/api-helpers.js +4 -3
  41. package/src/lib/api.js +10 -10
  42. package/src/lib/audit-logger-enhanced.js +77 -83
  43. package/src/lib/auth-middleware.js +1 -32
  44. package/src/lib/auth-route-helpers.js +5 -0
  45. package/src/lib/busybase-adapter.js +0 -5
  46. package/src/lib/busybase-audit-reads.js +142 -0
  47. package/src/lib/busybase-audit.js +41 -0
  48. package/src/lib/busybase-lucia-adapter.js +72 -0
  49. package/src/lib/busybase-store.js +338 -0
  50. package/src/lib/config-field-helpers.js +0 -17
  51. package/src/lib/config-generator-engine.js +19 -36
  52. package/src/lib/config-helpers.js +0 -21
  53. package/src/lib/crud-action-helpers.js +2 -2
  54. package/src/lib/crud-factory.js +1 -16
  55. package/src/lib/crud-handlers.js +17 -52
  56. package/src/lib/csrf-protection.js +5 -2
  57. package/src/lib/date-utils.js +3 -127
  58. package/src/lib/debug-registry.js +0 -5
  59. package/src/lib/domain-loader.js +4 -1
  60. package/src/lib/email-sender.js +25 -23
  61. package/src/lib/error-handler.js +5 -41
  62. package/src/lib/error-recovery.js +10 -7
  63. package/src/lib/error-resilience.js +7 -5
  64. package/src/lib/events-engine.js +9 -6
  65. package/src/lib/export-sink.js +0 -5
  66. package/src/lib/field-iterator.js +7 -76
  67. package/src/lib/field-registry.js +0 -22
  68. package/src/lib/generic-crud-handler.js +0 -16
  69. package/src/lib/hook-engine.js +5 -53
  70. package/src/lib/hot-reload/checkpoint.js +5 -2
  71. package/src/lib/hot-reload/promise-container.js +7 -3
  72. package/src/lib/hot-reload/route-wrapper.js +4 -1
  73. package/src/lib/hot-reload/supervisor.js +6 -3
  74. package/src/lib/hyperformula-service.js +0 -5
  75. package/src/lib/id-helpers.js +10 -0
  76. package/src/lib/index.js +4 -7
  77. package/src/lib/log-aggregator.js +0 -1
  78. package/src/lib/logger.js +0 -15
  79. package/src/lib/metrics-collector.js +2 -46
  80. package/src/lib/next-compat.js +15 -1
  81. package/src/lib/observability-bootstrap.js +0 -5
  82. package/src/lib/perf-monitor.js +4 -1
  83. package/src/lib/perf-profiler.js +0 -5
  84. package/src/lib/query-string-adapter.js +5 -47
  85. package/src/lib/realtime-server.js +4 -26
  86. package/src/lib/request-tracing.js +0 -5
  87. package/src/lib/resource-monitor.js +4 -1
  88. package/src/lib/response-formatter.js +0 -38
  89. package/src/lib/route-helpers.js +3 -1
  90. package/src/lib/safe-json.js +5 -1
  91. package/src/lib/server-bootstrap.js +10 -9
  92. package/src/lib/stage-pipeline.js +2 -2
  93. package/src/lib/state-transport-client.js +4 -1
  94. package/src/lib/static-server.js +2 -2
  95. package/src/lib/status-helpers.js +2 -51
  96. package/src/lib/tracing.js +0 -5
  97. package/src/lib/utils.js +6 -67
  98. package/src/lib/validate.js +5 -88
  99. package/src/lib/validators.js +0 -38
  100. package/src/lib/with-audit-logging.js +1 -1
  101. package/src/lib/workflow-engine.js +10 -77
  102. package/src/lib/xstate-workflow-engine.js +3 -9
  103. package/src/plugins/index.js +5 -3
  104. package/src/server/server.js +26 -27
  105. package/src/services/collaborator-role.service.js +21 -77
  106. package/src/services/email-sender.js +7 -34
  107. package/src/services/notification-engine.js +12 -33
  108. package/src/services/permission.service.js +10 -73
  109. package/src/ui/advanced-search-renderer.js +3 -2
  110. package/src/ui/advanced-widgets.js +1 -1
  111. package/src/ui/auth-pages.js +10 -9
  112. package/src/ui/auth-styles.js +1 -1
  113. package/src/ui/client.js +1 -1
  114. package/src/ui/component-engine.js +1 -1
  115. package/src/ui/dashboard-renderer.js +5 -5
  116. package/src/ui/dialog-engine.js +6 -9
  117. package/src/ui/dialog-factory.js +2 -2
  118. package/src/ui/engagement-cards.js +7 -7
  119. package/src/ui/engagement-dialogs.js +2 -3
  120. package/src/ui/engagement-grid-renderer.js +20 -19
  121. package/src/ui/entity-renderer.js +56 -42
  122. package/src/ui/file-dialogs.js +25 -9
  123. package/src/ui/flexup-report-renderer.js +21 -18
  124. package/src/ui/format-helpers.js +27 -28
  125. package/src/ui/highlight-threading-renderer.js +9 -9
  126. package/src/ui/job-management-renderer.js +18 -16
  127. package/src/ui/layout.js +51 -25
  128. package/src/ui/ml-console-renderer.js +15 -16
  129. package/src/ui/monitoring-dashboard-client.js +9 -8
  130. package/src/ui/notifications-renderer.js +11 -10
  131. package/src/ui/page-handler-admin.js +32 -23
  132. package/src/ui/page-handler-helpers.js +27 -23
  133. package/src/ui/page-handler-reviews.js +71 -35
  134. package/src/ui/page-handler-rfi.js +28 -14
  135. package/src/ui/page-handler.js +65 -48
  136. package/src/ui/picker-dialogs.js +26 -19
  137. package/src/ui/render-helpers.js +84 -46
  138. package/src/ui/review-comparison-renderer.js +8 -7
  139. package/src/ui/review-detail-panels.js +8 -9
  140. package/src/ui/review-detail-renderer.js +3 -3
  141. package/src/ui/review-detail-script.js +13 -12
  142. package/src/ui/review-mwr-renderer.js +6 -5
  143. package/src/ui/review-renderer.js +7 -6
  144. package/src/ui/review-widgets.js +3 -3
  145. package/src/ui/rfi-detail-renderer.js +37 -20
  146. package/src/ui/rfi-report-renderer.js +14 -9
  147. package/src/ui/settings-renderer-advanced.js +21 -18
  148. package/src/ui/settings-renderer-advanced2.js +20 -19
  149. package/src/ui/settings-renderer-teams.js +4 -4
  150. package/src/ui/settings-renderer.js +4 -4
  151. package/src/ui/spacing-system.js +5 -2
  152. package/src/ui/standalone-login.js +2 -2
  153. package/src/ui/styles.css +2 -1
  154. package/src/ui/styles2.css +75 -5
  155. package/src/ui/test-page.js +4 -4
  156. package/src/ui/widgets.js +20 -15
  157. package/src/lib/audit-logger.js +0 -193
  158. package/src/lib/database-core.js +0 -255
  159. package/src/lib/database-migrations.js +0 -350
  160. package/src/lib/db-backup.js +0 -97
  161. package/src/lib/error-boundary.js +0 -134
  162. package/src/lib/errors.js +0 -69
  163. package/src/lib/health-monitor.js +0 -134
  164. package/src/lib/index-optimizer.js +0 -96
  165. package/src/lib/query-engine-write.js +0 -221
  166. package/src/lib/query-engine.js +0 -446
  167. package/src/ui/collaboration-dialogs.js +0 -31
  168. package/src/ui/global-tags.js +0 -144
  169. package/src/ui/idle-logout.js +0 -156
  170. package/src/ui/letter-dialogs.js +0 -37
  171. package/src/ui/password-reset-page.js +0 -146
  172. package/src/ui/perf-renderer.js +0 -71
  173. package/src/ui/rfi-renderer.js +0 -194
  174. package/src/ui/validation-ui.js +0 -147
@@ -1,28 +1,18 @@
1
- /**
2
- * Auth Engine - Lucia session management and Google OAuth
3
- * Simplified extraction from moonlanding/src/engine.server.js
4
- */
5
-
1
+ // Simplified extraction from moonlanding/src/engine.server.js
6
2
  import { Lucia } from 'lucia';
7
- import { BetterSqlite3Adapter } from '@lucia-auth/adapter-sqlite';
8
3
  import { Google } from 'arctic';
9
4
  import bcrypt from 'bcrypt';
10
- import { getDatabase } from './lib/database-core.js';
5
+ import { BusyBaseLuciaAdapter } from './lib/busybase-lucia-adapter.js';
11
6
  import { buildConfig, hasGoogleAuth } from './config/env.js';
12
7
 
13
8
  let _lucia = null;
14
9
  let _google = null;
15
10
  let _adapter = null;
16
11
 
17
- /**
18
- * Initialize auth engine with database and config
19
- * @param {object} [config] - Optional config override
20
- */
21
12
  export function initAuth(config = null) {
22
13
  const cfg = config || buildConfig();
23
- const db = getDatabase(cfg.db.path);
24
14
 
25
- _adapter = new BetterSqlite3Adapter(db, { user: 'users', session: 'sessions' });
15
+ _adapter = new BusyBaseLuciaAdapter();
26
16
 
27
17
  _lucia = new Lucia(_adapter, {
28
18
  sessionCookie: {
@@ -54,29 +44,16 @@ export function initAuth(config = null) {
54
44
  return { lucia: _lucia, google: _google };
55
45
  }
56
46
 
57
- /**
58
- * Get Lucia instance
59
- * @returns {Lucia}
60
- */
61
47
  export function getLucia() {
62
48
  if (!_lucia) initAuth();
63
49
  return _lucia;
64
50
  }
65
51
 
66
- /**
67
- * Get Google OAuth client
68
- * @returns {Google|null}
69
- */
70
52
  export function getGoogle() {
71
53
  if (!_google) initAuth();
72
54
  return _google;
73
55
  }
74
56
 
75
- /**
76
- * Parse cookies from header
77
- * @param {string} cookieHeader
78
- * @returns {object}
79
- */
80
57
  function parseCookies(cookieHeader) {
81
58
  const cookies = {};
82
59
  if (!cookieHeader) return cookies;
@@ -89,18 +66,10 @@ function parseCookies(cookieHeader) {
89
66
 
90
67
  let _currentRequest = null;
91
68
 
92
- /**
93
- * Set current request context (for auth)
94
- * @param {object} req
95
- */
96
69
  export function setCurrentRequest(req) {
97
70
  _currentRequest = req;
98
71
  }
99
72
 
100
- /**
101
- * Get current user from session
102
- * @returns {object|null}
103
- */
104
73
  export async function getUser() {
105
74
  try {
106
75
  const request = _currentRequest;
@@ -120,21 +89,12 @@ export async function getUser() {
120
89
  }
121
90
  }
122
91
 
123
- /**
124
- * Require user (throw if not authenticated)
125
- * @returns {Promise<object>}
126
- */
127
92
  export async function requireUser() {
128
93
  const user = await getUser();
129
94
  if (!user) throw new Error('Unauthorized');
130
95
  return user;
131
96
  }
132
97
 
133
- /**
134
- * Create a new session
135
- * @param {string} userId
136
- * @returns {{session: object, sessionCookie: object}}
137
- */
138
98
  export async function createSession(userId) {
139
99
  const lucia = getLucia();
140
100
  // Note: cookies() requires Next.js polyfill in moonlanding; for SDK we return session data
@@ -143,66 +103,36 @@ export async function createSession(userId) {
143
103
  return { session, sessionCookie };
144
104
  }
145
105
 
146
- /**
147
- * Invalidate current session
148
- */
149
106
  export async function invalidateSession() {
150
107
  const lucia = getLucia();
151
108
  // Simplified - caller handles cookie clearing
152
109
  // In full app this uses @lucia-auth/adapter-sqlite's cookie management
153
110
  }
154
111
 
155
- /**
156
- * Hash password with bcrypt
157
- * @param {string} password
158
- * @returns {Promise<string>}
159
- */
160
112
  export async function hashPassword(password) {
161
113
  return bcrypt.hash(password, 12);
162
114
  }
163
115
 
164
- /**
165
- * Verify password
166
- * @param {string} password
167
- * @param {string} hash
168
- * @returns {Promise<boolean>}
169
- */
170
116
  export async function verifyPassword(password, hash) {
171
117
  return bcrypt.compare(password, hash);
172
118
  }
173
119
 
174
- /**
175
- * Get user by email
176
- * @param {string} email
177
- * @returns {object|null}
178
- */
179
- export function getUserByEmail(email) {
180
- const db = getDatabase();
181
- return db.prepare('SELECT * FROM users WHERE email = ?').get(email);
120
+ export async function getUserByEmail(email) {
121
+ const { getBy } = await import('./lib/busybase-store.js');
122
+ return getBy('user', 'email', email);
182
123
  }
183
124
 
184
- /**
185
- * Create a new user
186
- * @param {object} userData
187
- * @returns {object}
188
- */
189
125
  export async function createUser(userData) {
190
126
  const hashedPassword = await hashPassword(userData.password);
191
- const { create } = await import('./query-engine-write.js');
127
+ const { create } = await import('./lib/busybase-store.js');
192
128
  return create('user', {
193
129
  ...userData,
194
130
  password: hashedPassword,
195
131
  }, { id: 'system' });
196
132
  }
197
133
 
198
- /**
199
- * Authenticate user with credentials
200
- * @param {string} email
201
- * @param {string} password
202
- * @returns {object|null} User if valid, null otherwise
203
- */
204
134
  export async function authenticate(email, password) {
205
- const user = getUserByEmail(email);
135
+ const user = await getUserByEmail(email);
206
136
  if (!user) return null;
207
137
 
208
138
  const valid = await verifyPassword(password, user.password);
package/src/index.js CHANGED
@@ -1,12 +1,10 @@
1
- /**
2
- * Thatcher SDK - Configuration-Driven Application Framework
3
- */
4
-
5
1
  import fs from 'fs';
6
2
  import path from 'path';
7
3
  import { fileURLToPath, pathToFileURL } from 'url';
4
+ import { createLogger } from './lib/logger.js';
8
5
 
9
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const log = createLogger('[Thatcher]');
10
8
 
11
9
  // Forward-declare so we can set later
12
10
  let _configEngine = null;
@@ -15,19 +13,18 @@ let _pluginsLoaded = false;
15
13
  let _server = null;
16
14
  let _hotReloadWatchers = [];
17
15
 
18
- /**
19
- * Resolve a path relative to this package's src directory as an import URL
20
- * @param {string} relative - Path like './src/lib/database-core.js'
21
- * @returns {string} file:// URL
22
- */
23
16
  function resolveModule(relative) {
24
17
  const abs = path.resolve(__dirname, relative);
25
18
  return pathToFileURL(abs).href;
26
19
  }
27
20
 
28
- /**
29
- * Thatcher class - main API
30
- */
21
+ // busybase stores data in a directory. Callers pass `databasePath` which may be
22
+ // either a directory or a path ending in a db filename (.db/.sqlite); reduce the
23
+ // latter to its containing directory.
24
+ function databasePathToDir(p) {
25
+ return /\.(db|sqlite|sqlite3)$/i.test(p) ? path.dirname(p) : p;
26
+ }
27
+
31
28
  export class Thatcher {
32
29
  constructor(options = {}) {
33
30
  this.options = this.normalizeOptions(options);
@@ -51,9 +48,6 @@ export class Thatcher {
51
48
  };
52
49
  }
53
50
 
54
- /**
55
- * Initialize the system (config, DB, plugins)
56
- */
57
51
  async init() {
58
52
  if (this.initialized) return this;
59
53
 
@@ -78,9 +72,6 @@ export class Thatcher {
78
72
  return this;
79
73
  }
80
74
 
81
- /**
82
- * Load master configuration
83
- */
84
75
  async loadConfig() {
85
76
  const { config } = this.options;
86
77
  let masterConfig;
@@ -138,21 +129,31 @@ export class Thatcher {
138
129
  this.config = masterConfig;
139
130
  }
140
131
 
141
- /**
142
- * Initialize database with schema from config
143
- */
144
132
  async initDatabase() {
145
133
  if (_databaseInitialized) return;
146
- const { migrate } = await import(resolveModule('./lib/database-core.js'));
147
- migrate(_configEngine);
148
- const { getDatabase } = await import(resolveModule('./lib/database-core.js'));
149
- getDatabase(); // prime the connection
134
+ const { createEmbedded } = await import('busybase/embedded');
135
+ // Honour the caller's chosen location. `databasePath` is the documented
136
+ // option; busybase wants a directory, so a value that looks like a db file
137
+ // is reduced to its dirname. `dataDir` and BUSYBASE_DIR remain fallbacks.
138
+ const dir = this.options.dataDir
139
+ || (this.options.databasePath ? databasePathToDir(this.options.databasePath) : null)
140
+ || process.env.BUSYBASE_DIR
141
+ || 'busybase_data';
142
+ const client = await createEmbedded({ dir });
143
+
144
+ const store = await import(resolveModule('./lib/busybase-store.js'));
145
+ store.setBusyBaseClient(client);
146
+ const audit = await import(resolveModule('./lib/busybase-audit.js'));
147
+ audit.setBusyBaseClient(client);
148
+
149
+ this.busybase = client;
150
+ globalThis.__thatcherBusyBase = client;
151
+ if (globalThis.__debug__) {
152
+ globalThis.__debug__.expose('datastore', { kind: () => 'busybase', dir: () => dir }, 'Data Store');
153
+ }
150
154
  _databaseInitialized = true;
151
155
  }
152
156
 
153
- /**
154
- * Load .plugin.js files
155
- */
156
157
  async loadPlugins() {
157
158
  if (_pluginsLoaded) return;
158
159
 
@@ -179,7 +180,7 @@ export class Thatcher {
179
180
  _configEngine.registerPlugin(plugin.entityName, plugin);
180
181
  }
181
182
  } catch (e) {
182
- console.error(`[plugins] Failed to load ${file}:`, e.message);
183
+ log.error(`plugins: failed to load ${file}: ${e.message}`);
183
184
  }
184
185
  }
185
186
  }
@@ -188,9 +189,6 @@ export class Thatcher {
188
189
  _pluginsLoaded = true;
189
190
  }
190
191
 
191
- /**
192
- * Setup hot reload watchers
193
- */
194
192
  setupHotReload() {
195
193
  const dirs = [
196
194
  path.resolve(process.cwd(), 'config'),
@@ -204,13 +202,13 @@ export class Thatcher {
204
202
  try {
205
203
  const watcher = fs.watch(dir, { recursive: true }, (_, filename) => {
206
204
  if (filename && (filename.endsWith('.js') || filename.endsWith('.jsx') || filename.endsWith('.yml'))) {
207
- console.log(`[HotReload] ${filename} changed`);
205
+ log.info(`hot reload: ${filename} changed`);
208
206
  this.invalidateCache();
209
207
  }
210
208
  });
211
209
  _hotReloadWatchers.push(watcher);
212
210
  } catch (err) {
213
- console.warn(`[HotReload] Failed to watch ${dir}:`, err.message);
211
+ log.warn(`hot reload: failed to watch ${dir}: ${err.message}`);
214
212
  }
215
213
  }
216
214
  }
@@ -221,9 +219,6 @@ export class Thatcher {
221
219
  }
222
220
  }
223
221
 
224
- /**
225
- * Start HTTP server
226
- */
227
222
  async startServer(opts = {}) {
228
223
  if (!this.initialized) throw new Error('Call init() first');
229
224
 
@@ -241,15 +236,12 @@ export class Thatcher {
241
236
 
242
237
  return new Promise((resolve) => {
243
238
  _server.listen(port, host, () => {
244
- console.log(`\n Thatcher Server\n- Local: http://localhost:${port}\n Ready\n`);
239
+ console.log(`\n> Thatcher Server\n- Local: http://localhost:${port}\n[OK] Ready\n`);
245
240
  resolve(_server);
246
241
  });
247
242
  });
248
243
  }
249
244
 
250
- /**
251
- * Stop server
252
- */
253
245
  async stop() {
254
246
  if (_server) {
255
247
  _server.close();
@@ -259,30 +251,18 @@ export class Thatcher {
259
251
  _hotReloadWatchers = [];
260
252
  }
261
253
 
262
- /**
263
- * Get config engine
264
- */
265
254
  getConfigEngine() {
266
255
  return _configEngine;
267
256
  }
268
257
 
269
- /**
270
- * Get entity spec
271
- */
272
258
  getEntitySpec(name) {
273
259
  return _configEngine?.generateEntitySpec(name);
274
260
  }
275
261
 
276
- /**
277
- * List all entities
278
- */
279
262
  getAllEntities() {
280
263
  return _configEngine?.getAllEntities() || [];
281
264
  }
282
265
 
283
- /**
284
- * Get workflow definition
285
- */
286
266
  getWorkflow(name) {
287
267
  return _configEngine?.getWorkflow(name);
288
268
  }
@@ -290,32 +270,32 @@ export class Thatcher {
290
270
  // === CRUD operations ===
291
271
 
292
272
  async list(entity, where = {}, opts = {}) {
293
- const { list } = await import(resolveModule('./lib/query-engine.js'));
273
+ const { list } = await import(resolveModule('./lib/busybase-store.js'));
294
274
  return list(entity, where, opts);
295
275
  }
296
276
 
297
277
  async get(entity, id) {
298
- const { get } = await import(resolveModule('./lib/query-engine.js'));
278
+ const { get } = await import(resolveModule('./lib/busybase-store.js'));
299
279
  return get(entity, id);
300
280
  }
301
281
 
302
282
  async create(entity, data, user) {
303
- const { create } = await import(resolveModule('./lib/query-engine-write.js'));
283
+ const { create } = await import(resolveModule('./lib/busybase-store.js'));
304
284
  return create(entity, data, user);
305
285
  }
306
286
 
307
287
  async update(entity, id, data, user) {
308
- const { update } = await import(resolveModule('./lib/query-engine-write.js'));
288
+ const { update } = await import(resolveModule('./lib/busybase-store.js'));
309
289
  return update(entity, id, data, user);
310
290
  }
311
291
 
312
292
  async delete(entity, id) {
313
- const { remove } = await import(resolveModule('./lib/query-engine-write.js'));
293
+ const { remove } = await import(resolveModule('./lib/busybase-store.js'));
314
294
  return remove(entity, id);
315
295
  }
316
296
 
317
297
  async search(entity, query, where = {}, opts = {}) {
318
- const { search } = await import(resolveModule('./lib/query-engine.js'));
298
+ const { search } = await import(resolveModule('./lib/busybase-store.js'));
319
299
  return search(entity, query, where, opts);
320
300
  }
321
301
 
@@ -353,21 +333,16 @@ export class Thatcher {
353
333
  // === Database transaction ===
354
334
 
355
335
  async withTransaction(callback) {
356
- const { withTransaction } = await import(resolveModule('./lib/query-engine.js'));
357
- return withTransaction(callback);
336
+ // busybase has no transaction primitive; the old query-engine's withTransaction
337
+ // was a pass-through, so preserve that contract rather than import a dead file.
338
+ return callback();
358
339
  }
359
340
  }
360
341
 
361
- /**
362
- * Create thatcher instance
363
- */
364
342
  export function createThatcher(options) {
365
343
  return new Thatcher(options);
366
344
  }
367
345
 
368
- /**
369
- * Quick-start helper
370
- */
371
346
  export async function startThatcher(options = {}) {
372
347
  const t = new Thatcher(options);
373
348
  await t.init();
@@ -1,3 +1,7 @@
1
+ import { createLogger } from './logger.js';
2
+
3
+ const log = createLogger('[AlertManager]');
4
+
1
5
  const alerts = []
2
6
  const alertHandlers = []
3
7
  const thresholds = {
@@ -105,13 +109,13 @@ function triggerAlert(type, message, metadata = {}) {
105
109
  alerts.push(alert)
106
110
  if (alerts.length > 1000) alerts.shift()
107
111
 
108
- console.error(`[ALERT] [${alert.severity.toUpperCase()}] ${type}: ${message}`, metadata)
112
+ log.error(`[${alert.severity.toUpperCase()}] ${type}: ${message}`, metadata)
109
113
 
110
114
  for (const handler of alertHandlers) {
111
115
  try {
112
116
  handler(alert)
113
117
  } catch (err) {
114
- console.error('[AlertManager] Handler error:', err)
118
+ log.error('handler error:', { message: err.message })
115
119
  }
116
120
  }
117
121
  }
@@ -133,7 +137,7 @@ function checkAllThresholds(allMetrics) {
133
137
  checkResources(allMetrics.resources)
134
138
  checkDatabase(allMetrics.database)
135
139
  } catch (err) {
136
- console.error('[AlertManager] Check error:', err)
140
+ log.error('check error:', { message: err.message })
137
141
  }
138
142
  }
139
143
 
@@ -4,16 +4,17 @@ import { getSpec } from '@/config/spec-helpers';
4
4
  import { migrate } from '@/engine';
5
5
  import { getUser } from '@/engine.server';
6
6
  import { can } from '@/services/permission.service';
7
- import { logger } from '@/lib/logger';
7
+ import { createLogger } from '@/lib/logger';
8
8
  import { HTTP } from '@/config/constants';
9
9
  import { ERROR_MESSAGES } from '@/config';
10
10
 
11
+ const log = createLogger('[API]');
12
+
11
13
  let dbInit = false;
12
14
  export function ensureDb() {
13
15
  if (!dbInit) {
14
16
  migrate();
15
17
  dbInit = true;
16
- } else {
17
18
  }
18
19
  }
19
20
 
@@ -41,7 +42,7 @@ export async function withEntityAccess(entity, action, handler) {
41
42
  if (!can(user, spec, action)) return unauthorized(action);
42
43
  return await handler(spec, user);
43
44
  } catch (e) {
44
- logger.apiError(action, entity, e);
45
+ log.error(`${action} ${entity}: ${e.message}`);
45
46
  return serverError(e.message);
46
47
  }
47
48
  }
package/src/lib/api.js CHANGED
@@ -2,7 +2,7 @@ import { getUser } from '@/engine.server';
2
2
  import { getSpec } from '@/config/spec-helpers';
3
3
  import { API_ENDPOINTS } from '@/config';
4
4
  import { can } from '@/services/permission.service';
5
- import { list, get, create, update, remove, listWithPagination, search } from '@/lib/query-engine';
5
+ import { get, create, update, remove, listWithPagination, search } from '@/lib/busybase-store';
6
6
  import { validateEntity, validateUpdate, hasErrors } from '@/lib/validate';
7
7
  import { broadcastUpdate } from '@/lib/realtime-server';
8
8
  import { UnauthorizedError, PermissionError, NotFoundError, ValidationError, AppError } from '@/lib/error-handler';
@@ -24,16 +24,16 @@ const createHandler = (entity, action) => async (request, { params, searchParams
24
24
 
25
25
  if (action === 'list') {
26
26
  if (q) {
27
- const items = search(entity, q);
27
+ const items = await search(entity, q);
28
28
  return ok({ items });
29
29
  }
30
- const { items, pagination } = listWithPagination(entity, {}, page, pageSize);
30
+ const { items, pagination } = await listWithPagination(entity, {}, page, pageSize);
31
31
  return paginated(items, pagination);
32
32
  }
33
33
 
34
34
  if (action === 'get') {
35
35
  if (!id) throw new AppError('ID required', 'BAD_REQUEST', HTTP.BAD_REQUEST);
36
- const item = get(entity, id);
36
+ const item = await get(entity, id);
37
37
  if (!item) throw NotFoundError(entity, id);
38
38
  return ok(item);
39
39
  }
@@ -43,31 +43,31 @@ const createHandler = (entity, action) => async (request, { params, searchParams
43
43
  const errors = await validateEntity(spec, data);
44
44
  if (hasErrors(errors)) throw new ValidationError('Validation failed', errors);
45
45
 
46
- const result = create(entity, data, user);
46
+ const result = await create(entity, data, user);
47
47
  broadcastUpdate(API_ENDPOINTS.entity(entity), 'create', result);
48
48
  return created(result);
49
49
  }
50
50
 
51
51
  if (action === 'update') {
52
52
  if (!id) throw new AppError('ID required', 'BAD_REQUEST', HTTP.BAD_REQUEST);
53
- const prev = get(entity, id);
53
+ const prev = await get(entity, id);
54
54
  if (!prev) throw NotFoundError(entity, id);
55
55
 
56
56
  const data = await request.json();
57
57
  const errors = await validateUpdate(spec, id, data);
58
58
  if (hasErrors(errors)) throw new ValidationError('Validation failed', errors);
59
59
 
60
- update(entity, id, data, user);
61
- const result = get(entity, id);
60
+ await update(entity, id, data, user);
61
+ const result = await get(entity, id);
62
62
  broadcastUpdate(API_ENDPOINTS.entityId(entity, id), 'update', result);
63
63
  return ok(result);
64
64
  }
65
65
 
66
66
  if (action === 'delete') {
67
67
  if (!id) throw new AppError('ID required', 'BAD_REQUEST', HTTP.BAD_REQUEST);
68
- if (!get(entity, id)) throw NotFoundError(entity, id);
68
+ if (!(await get(entity, id))) throw NotFoundError(entity, id);
69
69
 
70
- remove(entity, id);
70
+ await remove(entity, id);
71
71
  broadcastUpdate(API_ENDPOINTS.entityId(entity, id), 'delete', { id });
72
72
  return ok({ success: true });
73
73
  }