thatcher 1.0.41 → 1.0.42

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 (122) hide show
  1. package/README.md +27 -0
  2. package/package.json +2 -5
  3. package/src/app/api/audit/dashboard/route.js +2 -2
  4. package/src/app/api/audit/logs/route.js +2 -2
  5. package/src/app/api/audit/permissions/[id]/route.js +2 -2
  6. package/src/app/api/audit/permissions/route.js +3 -3
  7. package/src/app/api/audit/permissions/stats/route.js +2 -2
  8. package/src/app/api/audit/route.js +2 -3
  9. package/src/app/api/audit/stats/route.js +2 -2
  10. package/src/app/api/auth/google/callback/route.js +1 -1
  11. package/src/app/api/auth/google/route.js +1 -1
  12. package/src/app/api/auth/login/route.js +1 -1
  13. package/src/app/api/auth/logout/route.js +1 -1
  14. package/src/app/api/auth/me/route.js +1 -1
  15. package/src/app/api/auth/mwr-bridge/route.js +1 -1
  16. package/src/app/api/auth/password-reset/route.js +1 -1
  17. package/src/app/api/csrf-token/route.js +1 -1
  18. package/src/app/api/debug/[[...path]]/route.js +2 -2
  19. package/src/app/api/debug/config/route.js +1 -1
  20. package/src/app/api/debug/hooks/route.js +1 -1
  21. package/src/app/api/debug/plugins/route.js +1 -1
  22. package/src/app/api/debug/sqlite/route.js +1 -1
  23. package/src/app/api/debug/sync/route.js +1 -1
  24. package/src/app/api/debug/workflow/route.js +1 -1
  25. package/src/app/api/domains/[domain]/route.js +1 -2
  26. package/src/app/api/domains/route.js +1 -1
  27. package/src/app/api/email/allocate/batch/route.js +1 -1
  28. package/src/app/api/email/allocate/route.js +1 -1
  29. package/src/app/api/email/receive/route.js +1 -1
  30. package/src/app/api/email/send/route.js +1 -1
  31. package/src/app/api/email/unallocated/route.js +1 -1
  32. package/src/app/api/files/[id]/route.js +1 -1
  33. package/src/app/api/health/route.js +1 -2
  34. package/src/app/api/metrics/route.js +1 -2
  35. package/src/engine.js +3 -3
  36. package/src/engine.server.js +3 -3
  37. package/src/index.js +9 -9
  38. package/src/lib/action-factory.js +1 -1
  39. package/src/lib/api-helpers.js +1 -1
  40. package/src/lib/api.js +3 -4
  41. package/src/lib/audit-logger-enhanced.js +1 -1
  42. package/src/lib/auth-middleware.js +1 -1
  43. package/src/lib/auth-route-helpers.js +1 -1
  44. package/src/lib/{busybase-adapter.js → busybase/adapter.js} +3 -3
  45. package/src/lib/{busybase-audit-reads.js → busybase/audit-reads.js} +1 -1
  46. package/src/lib/{busybase-audit.js → busybase/audit.js} +2 -2
  47. package/src/lib/{busybase-lucia-adapter.js → busybase/lucia-adapter.js} +1 -1
  48. package/src/lib/{busybase-store.js → busybase/store.js} +3 -3
  49. package/src/lib/config-generator-engine.js +13 -1
  50. package/src/lib/crud-action-helpers.js +1 -1
  51. package/src/lib/crud-handlers.js +4 -4
  52. package/src/lib/email-sender.js +1 -1
  53. package/src/lib/errors/index.js +3 -0
  54. package/src/lib/{error-recovery.js → errors/recovery.js} +120 -4
  55. package/src/lib/{error-handler.js → errors/types.js} +1 -1
  56. package/src/lib/errors/wrap.js +225 -0
  57. package/src/lib/events-engine.js +1 -1
  58. package/src/lib/hot-reload/index.js +34 -0
  59. package/src/lib/index.js +5 -5
  60. package/src/lib/keyed-cache.js +112 -0
  61. package/src/lib/monitor.js +285 -0
  62. package/src/lib/next-shim.js +294 -0
  63. package/src/lib/observability-init.js +213 -0
  64. package/src/lib/perf.js +468 -0
  65. package/src/lib/query-cache.js +40 -46
  66. package/src/lib/realtime-server.js +18 -0
  67. package/src/lib/render-cache.js +14 -28
  68. package/src/lib/{request-tracing.js → request-trace.js} +57 -2
  69. package/src/lib/response-formatter.js +18 -0
  70. package/src/lib/route-helpers.js +1 -1
  71. package/src/lib/state-protocol.js +13 -0
  72. package/src/lib/state-transport-client.js +6 -0
  73. package/src/lib/state-transport-reconnect.js +5 -0
  74. package/src/lib/state-transport-server.js +8 -0
  75. package/src/lib/utils.js +1 -1
  76. package/src/lib/{validate.js → validation/entity-validators.js} +9 -8
  77. package/src/lib/validation/index.js +1 -1
  78. package/src/lib/validation/security-validators.js +1 -1
  79. package/src/lib/validation-middleware.js +2 -2
  80. package/src/lib/workflow-engine.js +23 -3
  81. package/src/lib/xstate-workflow-engine.js +18 -2
  82. package/src/services/collaborator-role.service.js +2 -2
  83. package/src/services/notification-engine.js +5 -5
  84. package/src/services/permission.service.js +1 -1
  85. package/src/ui/format-helpers.js +1 -4
  86. package/src/ui/highlight-threading-renderer.js +1 -1
  87. package/src/ui/page-handler-admin.js +6 -5
  88. package/src/ui/page-handler-helpers.js +2 -2
  89. package/src/ui/page-handler-reviews.js +4 -4
  90. package/src/ui/page-handler-rfi.js +1 -1
  91. package/src/ui/page-handler.js +2 -2
  92. package/src/ui/render-helpers.js +1 -1
  93. package/src/ui/renderer.js +1 -1
  94. package/src/ui/{review-comparison-renderer.js → review/comparison.js} +1 -1
  95. package/src/ui/{review-detail-renderer.js → review/detail-renderer.js} +3 -3
  96. package/src/ui/review/index.js +32 -0
  97. package/src/ui/{review-mwr-renderer.js → review/mwr.js} +1 -1
  98. package/src/ui/{review-renderer.js → review/renderer.js} +1 -1
  99. package/src/ui/{settings-renderer.js → settings/home.js} +7 -34
  100. package/src/ui/{settings-renderer-advanced.js → settings/review.js} +117 -56
  101. package/src/ui/settings/shared.js +45 -0
  102. package/src/ui/{settings-renderer-teams.js → settings/teams.js} +1 -2
  103. package/src/ui/settings/templates.js +116 -0
  104. package/src/lib/api-error-wrapper.js +0 -125
  105. package/src/lib/db-monitor.js +0 -127
  106. package/src/lib/error-resilience.js +0 -132
  107. package/src/lib/monitoring-init.js +0 -67
  108. package/src/lib/next-compat.js +0 -94
  109. package/src/lib/next-polyfills.js +0 -135
  110. package/src/lib/observability-bootstrap.js +0 -116
  111. package/src/lib/perf-monitor.js +0 -94
  112. package/src/lib/perf-profiler.js +0 -233
  113. package/src/lib/query-perf.js +0 -117
  114. package/src/lib/request-tracker.js +0 -43
  115. package/src/lib/resource-monitor.js +0 -120
  116. package/src/lib/validators.js +0 -67
  117. package/src/lib/with-error-handler.js +0 -31
  118. package/src/ui/settings-renderer-advanced2.js +0 -159
  119. /package/src/ui/{review-detail-panels.js → review/detail-panels.js} +0 -0
  120. /package/src/ui/{review-detail-script.js → review/detail-script.js} +0 -0
  121. /package/src/ui/{review-widgets.js → review/widgets.js} +0 -0
  122. /package/src/ui/{review-zone-nav.js → review/zone-nav.js} +0 -0
package/README.md CHANGED
@@ -380,6 +380,33 @@ Thatcher is published to npm as `thatcher`. Every push to `main` triggers:
380
380
 
381
381
  CI/CD ready via included GitHub Actions workflow.
382
382
 
383
+ ## Runtime: Bun vs Node
384
+
385
+ Thatcher targets Bun as its primary runtime (`engines.bun` in `package.json`,
386
+ a `bun.lock` in the repo root, and `src/cli.js` carries a `#!/usr/bin/env bun`
387
+ shebang), but most of the codebase is plain ES modules with no Bun-only API
388
+ calls, so a Node-only consumer can run everything except the two `bun run`
389
+ scripts as-is.
390
+
391
+ | Script | Command | Runtime |
392
+ |--------|---------|---------|
393
+ | `npm run dev` | `bun run src/cli.js dev` | **Bun required** — invoked via `bun run`; `thatcher dev` also enables hot reload (`server.hotReload: true`), and the CLI's own shebang is `#!/usr/bin/env bun`. |
394
+ | `npm run start` | `bun run src/cli.js start` | **Bun required** — same `bun run` invocation as `dev`, just without hot reload. |
395
+ | `npm test` | `node test.js` | Plain Node — runs fine under `node test.js` directly. |
396
+ | `npm run lint` | `eslint src/` | Tool-agnostic — standard ESLint CLI, no runtime dependency. |
397
+ | `npm run typecheck` | `tsc --noEmit` | Tool-agnostic — standard TypeScript CLI. |
398
+
399
+ In practice, `src/cli.js` itself has no Bun-only API calls (no `Bun.*`
400
+ globals) — the `bun run` wrapping in `dev`/`start` is a process-launcher
401
+ choice, not a hard code dependency, so `node src/cli.js start` / `node
402
+ src/cli.js dev` also work for a Node-only deployer. The one place Bun is
403
+ harder to avoid is the `busybase` dependency, whose published `src/*.js` are
404
+ bun-build outputs (per its own build process) rather than hand-authored
405
+ Node-targeted files. A Node-only consumer/deployer can therefore run
406
+ `test`/`lint`/`typecheck` and the CLI itself with plain `node`, but should
407
+ expect the `dev`/`start` npm scripts as published to require Bun on `PATH`
408
+ unless invoking `src/cli.js` directly with `node`.
409
+
383
410
  ## License
384
411
 
385
412
  MIT — Extracted from moonlanding with all functionality preserved.
package/package.json CHANGED
@@ -1,16 +1,13 @@
1
1
  {
2
2
  "name": "thatcher",
3
- "version": "1.0.41",
3
+ "version": "1.0.42",
4
4
  "description": "A config-driven application framework for building data-intensive web apps without code.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
+ "_exportsCuratedNote": "This map is intentionally curated, not a blanket ./lib/* + ./ui/* re-export of every internal file. Rationale (2026-07): grepping thatcher's own source found zero internal self-imports via the 'thatcher/lib/*' or 'thatcher/ui/*' package specifier (internal code uses relative paths / resolveModule file URLs instead), and grepping casey (the primary real external consumer, at ../casey) found it imports only the package root ('thatcher' -> createThatcher). README.md documents exactly two public entry shapes: the root ('thatcher') and 'thatcher/adapters/google-drive'. './engine.server' (auth) was already a named, deliberate entry point distinct from the internal lib dump. './config/*', './services/*', './validation/*', './plugins/*' are kept even though no consumer was evidenced, because they are small, purposeful, plausibly-public directories (plugin-authoring in particular mirrors casey's own plugins/case-tools pattern) and erring toward NOT breaking an unseen consumer outweighs the surface-area savings of dropping them. The wide-open './lib/*' and './ui/*' globs (70+ and 40+ internal implementation files respectively, covering error handling, rendering internals, busybase adapters, etc.) were removed because nothing -- internal or external -- was found to reference them via the package-specifier form; any of thatcher's own modules that genuinely need to reach another lib file already do so via a relative import, which this map does not gate. If a real external consumer is later found needing a specific lib/ui file, add that ONE subpath explicitly rather than reopening the blanket glob.",
7
8
  "exports": {
8
9
  ".": "./src/index.js",
9
10
  "./engine.server": "./src/engine.server.js",
10
- "./lib/*": "./src/lib/*.js",
11
- "./lib/*.js": "./src/lib/*.js",
12
- "./ui/*": "./src/ui/*.js",
13
- "./ui/*.js": "./src/ui/*.js",
14
11
  "./config/*": "./src/config/*.js",
15
12
  "./config/*.js": "./src/config/*.js",
16
13
  "./services/*": "./src/services/*.js",
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
 
4
4
  const log = createLogger('[AuditDashboardAPI]');
@@ -6,7 +6,7 @@ import { getUser, setCurrentRequest } from '@/engine.server';
6
6
  import {
7
7
  getPermissionAuditTrail, getPermissionAuditStats, getPermissionAuditBreakdown,
8
8
  getPermissionAuditByDateRange, searchPermissionAudit, getPermissionDiff,
9
- } from '@/lib/busybase-audit-reads';
9
+ } from '@/lib/busybase/audit-reads';
10
10
 
11
11
  export async function GET(request) {
12
12
  setCurrentRequest(request);
@@ -1,7 +1,7 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { requireAuth } from '@/lib/auth-middleware';
3
3
  import { searchLogs, getLogStats, rotateLogsOlderThan } from '@/lib/audit-logger-enhanced';
4
- import { withErrorHandler } from '@/lib/with-error-handler';
4
+ import { withErrorHandler } from '@/lib/errors';
5
5
 
6
6
  export const GET = withErrorHandler(async (request) => {
7
7
  const user = await requireAuth();
@@ -1,9 +1,9 @@
1
- import { NextResponse, cookies } from '@/lib/next-polyfills';
1
+ import { NextResponse, cookies } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
 
4
4
  const log = createLogger('[AuditDetailAPI]');
5
5
  import { lucia } from '@/engine.server';
6
- import { getPermissionAuditById, getPermissionDiff } from '@/lib/busybase-audit-reads';
6
+ import { getPermissionAuditById, getPermissionDiff } from '@/lib/busybase/audit-reads';
7
7
 
8
8
  async function getUser() {
9
9
  const sessionId = (await cookies()).get(lucia.sessionCookieName)?.value ?? null;
@@ -1,13 +1,13 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
 
4
4
  const log = createLogger('[AuditAPI]');
5
5
  import { lucia } from '@/engine.server';
6
- import { cookies } from '@/lib/next-polyfills';
6
+ import { cookies } from '@/lib/next-shim';
7
7
  import {
8
8
  logPermissionChange, getPermissionAuditTrail, searchPermissionAudit,
9
9
  getPermissionAuditByDateRange, exportPermissionAuditCSV,
10
- } from '@/lib/busybase-audit-reads';
10
+ } from '@/lib/busybase/audit-reads';
11
11
 
12
12
  async function getUser() {
13
13
  const sessionId = (await cookies()).get(lucia.sessionCookieName)?.value ?? null;
@@ -1,9 +1,9 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
 
4
4
  const log = createLogger('[AuditStatsAPI]');
5
5
  import { getUser, setCurrentRequest } from '@/engine.server';
6
- import { getPermissionAuditStats, getPermissionAuditBreakdown } from '@/lib/busybase-audit-reads';
6
+ import { getPermissionAuditStats, getPermissionAuditBreakdown } from '@/lib/busybase/audit-reads';
7
7
 
8
8
  export async function GET(request) {
9
9
  setCurrentRequest(request);
@@ -1,9 +1,8 @@
1
1
  import { requireAuth, requirePermission } from '@/lib/auth-middleware';
2
- import { getAuditHistory, getActionStats, getUserStats } from '@/lib/busybase-audit-reads';
2
+ import { getAuditHistory, getActionStats, getUserStats } from '@/lib/busybase/audit-reads';
3
3
  import { getSpec } from '@/config/spec-helpers';
4
4
  import { paginated, ok } from '@/lib/response-formatter';
5
- import { withErrorHandler } from '@/lib/with-error-handler';
6
- import { AppError } from '@/lib/error-handler';
5
+ import { withErrorHandler, AppError } from '@/lib/errors';
7
6
  import { HTTP } from '@/config/constants';
8
7
  import { parse as parseQuery } from '@/lib/query-string-adapter';
9
8
 
@@ -1,7 +1,7 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { requireAuth } from '@/lib/auth-middleware';
3
3
  import { getLogStats } from '@/lib/audit-logger-enhanced';
4
- import { withErrorHandler } from '@/lib/with-error-handler';
4
+ import { withErrorHandler } from '@/lib/errors';
5
5
 
6
6
  export const GET = withErrorHandler(async (request) => {
7
7
  const user = await requireAuth();
@@ -8,7 +8,7 @@ import { GOOGLE_APIS } from '@/config/constants';
8
8
  import { config } from '@/config';
9
9
  import { getConfigEngine } from '@/lib/config-generator-engine';
10
10
  import { globalManager } from '@/lib/hot-reload/mutex';
11
- import { NextResponse } from '@/lib/next-polyfills';
11
+ import { NextResponse } from '@/lib/next-shim';
12
12
  import {
13
13
  validateOAuthProvider,
14
14
  getOAuthCookie,
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
 
4
4
  const log = createLogger('[OAuth]');
@@ -3,7 +3,7 @@ import { getBy, verifyPassword, migrate } from '@/engine';
3
3
 
4
4
  const log = createLogger('[Login]');
5
5
  import { initializeSystemConfig } from '@/config/system-config-loader';
6
- import { withErrorHandler } from '@/lib/with-error-handler';
6
+ import { withErrorHandler } from '@/lib/errors';
7
7
  import { lucia } from '@/engine.server';
8
8
 
9
9
  let initialized = false;
@@ -1,5 +1,5 @@
1
1
  import { setCurrentRequest as setEngineRequest, invalidateSession } from '@/engine.server';
2
- import { setCurrentRequest, setCurrentResponse } from '@/lib/next-polyfills';
2
+ import { setCurrentRequest, setCurrentResponse } from '@/lib/next-shim';
3
3
 
4
4
  function buildLogoutHeaders(headerMap) {
5
5
  const setCookies = headerMap.get('Set-Cookie');
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { getUser, setCurrentRequest } from '@/engine.server';
3
3
  import { HTTP } from '@/config/constants';
4
4
 
@@ -1,7 +1,7 @@
1
1
  import { getBy, list, update } from '@/engine';
2
2
  import { lucia } from '@/engine.server';
3
3
  import { now } from '@/lib/id-helpers';
4
- import { withErrorHandler } from '@/lib/with-error-handler';
4
+ import { withErrorHandler } from '@/lib/errors';
5
5
  import crypto from 'crypto';
6
6
 
7
7
  export const POST = withErrorHandler(async (request) => {
@@ -1,6 +1,6 @@
1
1
  import { getBy, hashPassword, list, create, update, remove } from '@/engine';
2
2
  import { genId, now } from '@/lib/id-helpers';
3
- import { withErrorHandler } from '@/lib/with-error-handler';
3
+ import { withErrorHandler } from '@/lib/errors';
4
4
  import crypto from 'crypto';
5
5
 
6
6
  export const POST = withErrorHandler(async (request) => {
@@ -1,6 +1,6 @@
1
1
  import { generateToken } from '@/lib/csrf-protection';
2
2
  import { ok } from '@/lib/response-formatter';
3
- import { withErrorHandler } from '@/lib/with-error-handler';
3
+ import { withErrorHandler } from '@/lib/errors';
4
4
 
5
5
  export const GET = withErrorHandler(async (request) => {
6
6
  const token = generateToken();
@@ -1,10 +1,10 @@
1
1
  import { tracer } from '@/lib/tracing.js';
2
- import { perfProfiler } from '@/lib/perf-profiler.js';
2
+ import { perfProfiler } from '@/lib/perf.js';
3
3
  import { debugRegistry } from '@/lib/debug-registry.js';
4
4
  import { getExportSink } from '@/lib/export-sink.js';
5
5
  import { getXStateWorkflowEngine } from '@/lib/xstate-workflow-engine.js';
6
6
  import { getHyperFormulaService } from '@/lib/hyperformula-service.js';
7
- import { getBusyBaseAdapter } from '@/lib/busybase-adapter.js';
7
+ import { getBusyBaseAdapter } from '@/lib/busybase/adapter.js';
8
8
  import { hookEngine } from '@/lib/hook-engine.js';
9
9
  import { createLogger } from '@/lib/logger.js';
10
10
 
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
 
3
3
  export async function GET() {
4
4
  if (process.env.NODE_ENV === 'production') {
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
 
3
3
  export async function GET() {
4
4
  if (process.env.NODE_ENV === 'production') {
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
 
3
3
  export async function GET() {
4
4
  if (process.env.NODE_ENV === 'production') {
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
 
3
3
  // The data layer is busybase (LanceDB), not SQLite — the old pragma/page-count probe
4
4
  // no longer applies. This endpoint now reports the configured store kind.
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { NextResponse } from '@/lib/next-polyfills';
3
+ import { NextResponse } from '@/lib/next-shim';
4
4
  import { count } from '@/engine';
5
5
 
6
6
  export async function GET() {
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
 
3
3
  export async function GET() {
4
4
  if (process.env.NODE_ENV === 'production') {
@@ -1,8 +1,7 @@
1
1
  import { getDomainLoader } from '@/lib/domain-loader';
2
2
  import { getConfigEngine } from '@/lib/config-generator-engine';
3
3
  import { ok } from '@/lib/response-formatter';
4
- import { withErrorHandler } from '@/lib/with-error-handler';
5
- import { AppError, NotFoundError } from '@/lib/error-handler';
4
+ import { withErrorHandler, AppError, NotFoundError } from '@/lib/errors';
6
5
  import { HTTP } from '@/config/constants';
7
6
 
8
7
  export const GET = withErrorHandler(async (request, context) => {
@@ -3,7 +3,7 @@ import { getDomainLoader } from '@/lib/domain-loader';
3
3
 
4
4
  const log = createLogger('[DomainsAPI]');
5
5
  import { ok } from '@/lib/response-formatter';
6
- import { withErrorHandler } from '@/lib/with-error-handler';
6
+ import { withErrorHandler } from '@/lib/errors';
7
7
  import { getConfigEngine } from '@/lib/config-generator-engine';
8
8
 
9
9
  export const GET = withErrorHandler(async (request) => {
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
 
4
4
  const log = createLogger('[EmailBatchAllocate]');
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
  import { genId, now } from '@/lib/id-helpers';
4
4
  import { get, create } from '@/engine';
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
  import { genId, now } from '@/lib/id-helpers';
4
4
  import { create } from '@/engine';
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
  import { now } from '@/lib/id-helpers';
4
4
  import { timingSafeEqual } from 'node:crypto';
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
  import { list } from '@/engine';
4
4
 
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { createLogger } from '@/lib/logger.js';
3
3
 
4
4
  const log = createLogger('[FilesAPI]');
@@ -5,8 +5,7 @@ const log = createLogger('[Health]');
5
5
  import path from 'path'
6
6
  import { count } from '@/engine'
7
7
  import { getAllMetrics } from '@/lib/metrics-collector.js'
8
- import { getDatabaseStats } from '@/lib/db-monitor.js'
9
- import { getCurrentResources } from '@/lib/resource-monitor.js'
8
+ import { getDatabaseStats, getCurrentResources } from '@/lib/monitor.js'
10
9
  import { getRecentAlerts } from '@/lib/alert-manager.js'
11
10
 
12
11
  function getLastSyncAt() {
@@ -1,7 +1,6 @@
1
1
  import { getAllMetrics, clearMetrics } from '@/lib/metrics-collector.js'
2
2
  import { getRecentAlerts } from '@/lib/alert-manager.js'
3
- import { getDatabaseStats } from '@/lib/db-monitor.js'
4
- import { getCurrentResources } from '@/lib/resource-monitor.js'
3
+ import { getDatabaseStats, getCurrentResources } from '@/lib/monitor.js'
5
4
  import { getLogs } from '@/lib/log-aggregator.js'
6
5
 
7
6
  export const GET = async (request) => {
package/src/engine.js CHANGED
@@ -1,12 +1,12 @@
1
1
  // Previously `@/engine` resolved to a non-existent module; this barrel makes it
2
- // concrete and points the data layer at busybase-store.
2
+ // concrete and points the data layer at busybase/store.
3
3
 
4
4
  // Data layer (busybase)
5
5
  export {
6
6
  list, get, getBy, count, listWithPagination, searchWithPagination, search,
7
7
  create, update, remove, bulkCreate, getChildren, batchGetChildren,
8
8
  setBusyBaseClient,
9
- } from '@/lib/busybase-store.js';
9
+ } from '@/lib/busybase/store.js';
10
10
 
11
11
  // IDs / time
12
12
  export { genId, now } from '@/lib/id-helpers.js';
@@ -15,7 +15,7 @@ export { genId, now } from '@/lib/id-helpers.js';
15
15
  export { hashPassword, verifyPassword } from '@/engine.server.js';
16
16
 
17
17
  // Audit
18
- export { logAction } from '@/lib/busybase-audit.js';
18
+ export { logAction } from '@/lib/busybase/audit.js';
19
19
 
20
20
  // Legacy SQLite shims (data layer is busybase now). busybase is schemaless so there
21
21
  // is no migration step; migrate() is a no-op kept for callers that still invoke it.
@@ -2,7 +2,7 @@
2
2
  import { Lucia } from 'lucia';
3
3
  import { Google } from 'arctic';
4
4
  import bcrypt from 'bcrypt';
5
- import { BusyBaseLuciaAdapter } from './lib/busybase-lucia-adapter.js';
5
+ import { BusyBaseLuciaAdapter } from './lib/busybase/lucia-adapter.js';
6
6
  import { buildConfig, hasGoogleAuth } from './config/env.js';
7
7
 
8
8
  let _lucia = null;
@@ -118,13 +118,13 @@ export async function verifyPassword(password, hash) {
118
118
  }
119
119
 
120
120
  export async function getUserByEmail(email) {
121
- const { getBy } = await import('./lib/busybase-store.js');
121
+ const { getBy } = await import('./lib/busybase/store.js');
122
122
  return getBy('user', 'email', email);
123
123
  }
124
124
 
125
125
  export async function createUser(userData) {
126
126
  const hashedPassword = await hashPassword(userData.password);
127
- const { create } = await import('./lib/busybase-store.js');
127
+ const { create } = await import('./lib/busybase/store.js');
128
128
  return create('user', {
129
129
  ...userData,
130
130
  password: hashedPassword,
package/src/index.js CHANGED
@@ -141,9 +141,9 @@ export class Thatcher {
141
141
  || 'busybase_data';
142
142
  const client = await createEmbedded({ dir });
143
143
 
144
- const store = await import(resolveModule('./lib/busybase-store.js'));
144
+ const store = await import(resolveModule('./lib/busybase/store.js'));
145
145
  store.setBusyBaseClient(client);
146
- const audit = await import(resolveModule('./lib/busybase-audit.js'));
146
+ const audit = await import(resolveModule('./lib/busybase/audit.js'));
147
147
  audit.setBusyBaseClient(client);
148
148
 
149
149
  this.busybase = client;
@@ -270,38 +270,38 @@ export class Thatcher {
270
270
  // === CRUD operations ===
271
271
 
272
272
  async list(entity, where = {}, opts = {}) {
273
- const { list } = await import(resolveModule('./lib/busybase-store.js'));
273
+ const { list } = await import(resolveModule('./lib/busybase/store.js'));
274
274
  return list(entity, where, opts);
275
275
  }
276
276
 
277
277
  async get(entity, id) {
278
- const { get } = await import(resolveModule('./lib/busybase-store.js'));
278
+ const { get } = await import(resolveModule('./lib/busybase/store.js'));
279
279
  return get(entity, id);
280
280
  }
281
281
 
282
282
  async create(entity, data, user) {
283
- const { create } = await import(resolveModule('./lib/busybase-store.js'));
283
+ const { create } = await import(resolveModule('./lib/busybase/store.js'));
284
284
  return create(entity, data, user);
285
285
  }
286
286
 
287
287
  // opts.expectedVersion: optional optimistic-concurrency guard (see
288
- // busybase-store.js update()) -- a caller that read the row's _version can
288
+ // busybase/store.js update()) -- a caller that read the row's _version can
289
289
  // pass it here to detect (via a thrown {code:'conflict'} error) a concurrent
290
290
  // writer landing in between, instead of silently clobbering it. `user` stays
291
291
  // its own positional param (unused by the store today, kept for API
292
292
  // stability with existing callers); opts is a new, optional 5th param.
293
293
  async update(entity, id, data, user, opts = {}) {
294
- const { update } = await import(resolveModule('./lib/busybase-store.js'));
294
+ const { update } = await import(resolveModule('./lib/busybase/store.js'));
295
295
  return update(entity, id, data, opts);
296
296
  }
297
297
 
298
298
  async delete(entity, id) {
299
- const { remove } = await import(resolveModule('./lib/busybase-store.js'));
299
+ const { remove } = await import(resolveModule('./lib/busybase/store.js'));
300
300
  return remove(entity, id);
301
301
  }
302
302
 
303
303
  async search(entity, query, where = {}, opts = {}) {
304
- const { search } = await import(resolveModule('./lib/busybase-store.js'));
304
+ const { search } = await import(resolveModule('./lib/busybase/store.js'));
305
305
  return search(entity, query, where, opts);
306
306
  }
307
307
 
@@ -3,7 +3,7 @@
3
3
  import { create, update, remove } from '@/engine';
4
4
  import { requireUser, check } from '@/engine.server';
5
5
  import { getSpec } from '@/config/spec-helpers';
6
- import { revalidatePath } from '@/lib/next-polyfills';
6
+ import { revalidatePath } from '@/lib/next-shim';
7
7
 
8
8
  export async function serverCreateEntity(entityName, data) {
9
9
  const user = await requireUser();
@@ -1,5 +1,5 @@
1
1
 
2
- import { NextResponse } from '@/lib/next-polyfills';
2
+ import { NextResponse } from '@/lib/next-shim';
3
3
  import { getSpec } from '@/config/spec-helpers';
4
4
  import { migrate } from '@/engine';
5
5
  import { getUser } from '@/engine.server';
package/src/lib/api.js CHANGED
@@ -2,13 +2,12 @@ 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 { get, create, update, remove, listWithPagination, search } from '@/lib/busybase-store';
6
- import { validateEntity, validateUpdate, hasErrors } from '@/lib/validate';
5
+ import { get, create, update, remove, listWithPagination, search } from '@/lib/busybase/store';
6
+ import { validateEntity, validateUpdate, hasErrors } from '@/lib/validation/entity-validators';
7
7
  import { broadcastUpdate } from '@/lib/realtime-server';
8
- import { UnauthorizedError, PermissionError, NotFoundError, ValidationError, AppError } from '@/lib/error-handler';
8
+ import { UnauthorizedError, PermissionError, NotFoundError, ValidationError, AppError, withErrorHandler } from '@/lib/errors';
9
9
  import { ok, created, paginated } from '@/lib/response-formatter';
10
10
  import { QueryAdapter } from '@/lib/query-string-adapter';
11
- import { withErrorHandler } from '@/lib/with-error-handler';
12
11
  import { HTTP } from '@/config/constants';
13
12
 
14
13
  const createHandler = (entity, action) => async (request, { params, searchParams }) => {
@@ -2,7 +2,7 @@ import { createLogger } from './logger.js';
2
2
  import { genId, now } from '@/lib/id-helpers';
3
3
 
4
4
  const log = createLogger('[AuditEnhanced]');
5
- import { list, create, remove } from '@/lib/busybase-store';
5
+ import { list, create, remove } from '@/lib/busybase/store';
6
6
 
7
7
  export const LOG_LEVELS = { DEBUG: 'debug', INFO: 'info', WARN: 'warn', ERROR: 'error' };
8
8
  export const OPERATION_TYPES = { CREATE: 'create', UPDATE: 'update', DELETE: 'delete', READ: 'read', AUTH: 'auth', AUTHZ: 'authz' };
@@ -3,7 +3,7 @@
3
3
  import { getUser, getLucia } from '../engine.server.js';
4
4
  import { getSpec } from '../config/spec-helpers.js';
5
5
  import { can } from '../services/permission.service.js';
6
- import { UnauthorizedError, PermissionError, NotFoundError } from './error-handler.js';
6
+ import { UnauthorizedError, PermissionError, NotFoundError } from './errors/index.js';
7
7
 
8
8
  const actionMap = {
9
9
  list: 'list',
@@ -1,4 +1,4 @@
1
- import { NextResponse } from '@/lib/next-polyfills';
1
+ import { NextResponse } from '@/lib/next-shim';
2
2
  import { SESSION } from '@/config/auth-config';
3
3
  import { HTTP } from '@/config/constants';
4
4
 
@@ -1,5 +1,5 @@
1
- import { createLogger } from './logger.js';
2
- import { hookEngine } from './hook-engine.js';
1
+ import { createLogger } from '../logger.js';
2
+ import { hookEngine } from '../hook-engine.js';
3
3
 
4
4
  const logger = createLogger('[BusyBase]');
5
5
 
@@ -154,4 +154,4 @@ export function isBusyBaseAvailable() {
154
154
  return _bbInstance?.initialized === true;
155
155
  }
156
156
 
157
- export default BusyBaseAdapter;
157
+ export default BusyBaseAdapter;
@@ -4,7 +4,7 @@
4
4
  * audit_logs and permission_audit tables.
5
5
  */
6
6
 
7
- import { list, create } from '@/lib/busybase-store.js';
7
+ import { list, create } from '@/lib/busybase/store.js';
8
8
  import { genId, now } from '@/lib/id-helpers.js';
9
9
 
10
10
  const parseJson = (val) => { try { return val ? JSON.parse(val) : null; } catch { return null; } };
@@ -6,8 +6,8 @@
6
6
  * forget: it returns immediately and swallows/logs write errors rather than throwing.
7
7
  */
8
8
 
9
- import { createLogger } from './logger.js';
10
- import { genId, now } from './id-helpers.js';
9
+ import { createLogger } from '../logger.js';
10
+ import { genId, now } from '../id-helpers.js';
11
11
 
12
12
  const log = createLogger('[BusyBaseAudit]');
13
13
 
@@ -7,7 +7,7 @@
7
7
  * receives back; busybase stores it as an epoch-ms number, so we convert at the boundary.
8
8
  */
9
9
 
10
- import { list, get, create, update, remove } from '@/lib/busybase-store.js';
10
+ import { list, get, create, update, remove } from '@/lib/busybase/store.js';
11
11
 
12
12
  const SESSION = 'sessions';
13
13
  const USER = 'users';
@@ -14,9 +14,9 @@
14
14
  * Requires Bun (busybase embedded uses Bun.password + the vectordb native binding).
15
15
  */
16
16
 
17
- import { getSpec } from '../config/spec-helpers.js';
18
- import { RECORD_STATUS } from '../config/constants.js';
19
- import { genId, now } from './id-helpers.js';
17
+ import { getSpec } from '../../config/spec-helpers.js';
18
+ import { RECORD_STATUS } from '../../config/constants.js';
19
+ import { genId, now } from '../id-helpers.js';
20
20
 
21
21
  let _client = null;
22
22