thatcher 1.0.41 → 1.0.43
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.
- package/README.md +27 -0
- package/package.json +2 -5
- package/src/app/api/audit/dashboard/route.js +2 -2
- package/src/app/api/audit/logs/route.js +2 -2
- package/src/app/api/audit/permissions/[id]/route.js +2 -2
- package/src/app/api/audit/permissions/route.js +3 -3
- package/src/app/api/audit/permissions/stats/route.js +2 -2
- package/src/app/api/audit/route.js +2 -3
- package/src/app/api/audit/stats/route.js +2 -2
- package/src/app/api/auth/google/callback/route.js +1 -1
- package/src/app/api/auth/google/route.js +1 -1
- package/src/app/api/auth/login/route.js +1 -1
- package/src/app/api/auth/logout/route.js +1 -1
- package/src/app/api/auth/me/route.js +1 -1
- package/src/app/api/auth/mwr-bridge/route.js +1 -1
- package/src/app/api/auth/password-reset/route.js +1 -1
- package/src/app/api/csrf-token/route.js +1 -1
- package/src/app/api/debug/[[...path]]/route.js +2 -2
- package/src/app/api/debug/config/route.js +1 -1
- package/src/app/api/debug/hooks/route.js +1 -1
- package/src/app/api/debug/plugins/route.js +1 -1
- package/src/app/api/debug/sqlite/route.js +1 -1
- package/src/app/api/debug/sync/route.js +1 -1
- package/src/app/api/debug/workflow/route.js +1 -1
- package/src/app/api/domains/[domain]/route.js +1 -2
- package/src/app/api/domains/route.js +1 -1
- package/src/app/api/email/allocate/batch/route.js +1 -1
- package/src/app/api/email/allocate/route.js +1 -1
- package/src/app/api/email/receive/route.js +1 -1
- package/src/app/api/email/send/route.js +1 -1
- package/src/app/api/email/unallocated/route.js +1 -1
- package/src/app/api/files/[id]/route.js +1 -1
- package/src/app/api/health/route.js +1 -2
- package/src/app/api/metrics/route.js +1 -2
- package/src/engine.js +3 -3
- package/src/engine.server.js +3 -3
- package/src/index.js +35 -10
- package/src/lib/action-factory.js +1 -1
- package/src/lib/api-helpers.js +1 -1
- package/src/lib/api.js +3 -4
- package/src/lib/audit-logger-enhanced.js +1 -1
- package/src/lib/auth-middleware.js +1 -1
- package/src/lib/auth-route-helpers.js +1 -1
- package/src/lib/{busybase-adapter.js → busybase/adapter.js} +3 -3
- package/src/lib/{busybase-audit-reads.js → busybase/audit-reads.js} +1 -1
- package/src/lib/{busybase-audit.js → busybase/audit.js} +2 -2
- package/src/lib/{busybase-lucia-adapter.js → busybase/lucia-adapter.js} +1 -1
- package/src/lib/{busybase-store.js → busybase/store.js} +3 -3
- package/src/lib/config-generator-engine.js +13 -1
- package/src/lib/crud-action-helpers.js +1 -1
- package/src/lib/crud-handlers.js +4 -4
- package/src/lib/email-sender.js +1 -1
- package/src/lib/errors/index.js +3 -0
- package/src/lib/{error-recovery.js → errors/recovery.js} +120 -4
- package/src/lib/{error-handler.js → errors/types.js} +1 -1
- package/src/lib/errors/wrap.js +225 -0
- package/src/lib/events-engine.js +1 -1
- package/src/lib/hot-reload/index.js +34 -0
- package/src/lib/index.js +5 -5
- package/src/lib/keyed-cache.js +112 -0
- package/src/lib/monitor.js +285 -0
- package/src/lib/next-shim.js +294 -0
- package/src/lib/observability-init.js +213 -0
- package/src/lib/perf.js +468 -0
- package/src/lib/query-cache.js +40 -46
- package/src/lib/realtime-server.js +18 -0
- package/src/lib/render-cache.js +14 -28
- package/src/lib/{request-tracing.js → request-trace.js} +57 -2
- package/src/lib/response-formatter.js +18 -0
- package/src/lib/route-helpers.js +1 -1
- package/src/lib/state-protocol.js +13 -0
- package/src/lib/state-transport-client.js +6 -0
- package/src/lib/state-transport-reconnect.js +5 -0
- package/src/lib/state-transport-server.js +8 -0
- package/src/lib/utils.js +1 -1
- package/src/lib/{validate.js → validation/entity-validators.js} +9 -8
- package/src/lib/validation/index.js +1 -1
- package/src/lib/validation/security-validators.js +1 -1
- package/src/lib/validation-middleware.js +2 -2
- package/src/lib/workflow-engine.js +23 -3
- package/src/lib/xstate-workflow-engine.js +18 -2
- package/src/services/collaborator-role.service.js +2 -2
- package/src/services/notification-engine.js +5 -5
- package/src/services/permission.service.js +1 -1
- package/src/ui/format-helpers.js +1 -4
- package/src/ui/highlight-threading-renderer.js +1 -1
- package/src/ui/page-handler-admin.js +6 -5
- package/src/ui/page-handler-helpers.js +2 -2
- package/src/ui/page-handler-reviews.js +4 -4
- package/src/ui/page-handler-rfi.js +1 -1
- package/src/ui/page-handler.js +2 -2
- package/src/ui/render-helpers.js +1 -1
- package/src/ui/renderer.js +1 -1
- package/src/ui/{review-comparison-renderer.js → review/comparison.js} +1 -1
- package/src/ui/{review-detail-renderer.js → review/detail-renderer.js} +3 -3
- package/src/ui/review/index.js +32 -0
- package/src/ui/{review-mwr-renderer.js → review/mwr.js} +1 -1
- package/src/ui/{review-renderer.js → review/renderer.js} +1 -1
- package/src/ui/{settings-renderer.js → settings/home.js} +7 -34
- package/src/ui/{settings-renderer-advanced.js → settings/review.js} +117 -56
- package/src/ui/settings/shared.js +45 -0
- package/src/ui/{settings-renderer-teams.js → settings/teams.js} +1 -2
- package/src/ui/settings/templates.js +116 -0
- package/src/lib/api-error-wrapper.js +0 -125
- package/src/lib/db-monitor.js +0 -127
- package/src/lib/error-resilience.js +0 -132
- package/src/lib/monitoring-init.js +0 -67
- package/src/lib/next-compat.js +0 -94
- package/src/lib/next-polyfills.js +0 -135
- package/src/lib/observability-bootstrap.js +0 -116
- package/src/lib/perf-monitor.js +0 -94
- package/src/lib/perf-profiler.js +0 -233
- package/src/lib/query-perf.js +0 -117
- package/src/lib/request-tracker.js +0 -43
- package/src/lib/resource-monitor.js +0 -120
- package/src/lib/validators.js +0 -67
- package/src/lib/with-error-handler.js +0 -31
- package/src/ui/settings-renderer-advanced2.js +0 -159
- /package/src/ui/{review-detail-panels.js → review/detail-panels.js} +0 -0
- /package/src/ui/{review-detail-script.js → review/detail-script.js} +0 -0
- /package/src/ui/{review-widgets.js → review/widgets.js} +0 -0
- /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.
|
|
3
|
+
"version": "1.0.43",
|
|
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-
|
|
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
|
|
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-
|
|
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/
|
|
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-
|
|
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
|
|
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-
|
|
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-
|
|
6
|
+
import { cookies } from '@/lib/next-shim';
|
|
7
7
|
import {
|
|
8
8
|
logPermissionChange, getPermissionAuditTrail, searchPermissionAudit,
|
|
9
9
|
getPermissionAuditByDateRange, exportPermissionAuditCSV,
|
|
10
|
-
} from '@/lib/busybase
|
|
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-
|
|
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
|
|
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
|
|
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/
|
|
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-
|
|
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/
|
|
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-
|
|
11
|
+
import { NextResponse } from '@/lib/next-shim';
|
|
12
12
|
import {
|
|
13
13
|
validateOAuthProvider,
|
|
14
14
|
getOAuthCookie,
|
|
@@ -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/
|
|
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-
|
|
2
|
+
import { setCurrentRequest, setCurrentResponse } from '@/lib/next-shim';
|
|
3
3
|
|
|
4
4
|
function buildLogoutHeaders(headerMap) {
|
|
5
5
|
const setCookies = headerMap.get('Set-Cookie');
|
|
@@ -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/
|
|
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/
|
|
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/
|
|
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
|
|
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
|
|
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,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/
|
|
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/
|
|
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) => {
|
|
@@ -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/
|
|
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/
|
|
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
|
|
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
|
|
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
|
|
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.
|
package/src/engine.server.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
144
|
+
const store = await import(resolveModule('./lib/busybase/store.js'));
|
|
145
145
|
store.setBusyBaseClient(client);
|
|
146
|
-
const audit = await import(resolveModule('./lib/busybase
|
|
146
|
+
const audit = await import(resolveModule('./lib/busybase/audit.js'));
|
|
147
147
|
audit.setBusyBaseClient(client);
|
|
148
148
|
|
|
149
149
|
this.busybase = client;
|
|
@@ -248,7 +248,32 @@ export class Thatcher {
|
|
|
248
248
|
_server = null;
|
|
249
249
|
}
|
|
250
250
|
for (const w of _hotReloadWatchers) w.close();
|
|
251
|
-
_hotReloadWatchers = []
|
|
251
|
+
_hotReloadWatchers = []
|
|
252
|
+
// This never called the underlying database client's own close() at all
|
|
253
|
+
// -- busybase's createEmbedded() now exposes a real one (busybase's own
|
|
254
|
+
// embedded.ts fix), but nothing here called it, so a consumer reusing the
|
|
255
|
+
// SAME database file in the same process (a fresh Thatcher instance, a
|
|
256
|
+
// test harness) would silently open a SECOND native handle onto an
|
|
257
|
+
// already-open file rather than genuinely replacing the first --
|
|
258
|
+
// _databaseInitialized is a MODULE-level singleton (see initDatabase()'s
|
|
259
|
+
// own comment / AGENTS.md's documented cwd-bound handle caveat), so it
|
|
260
|
+
// must be reset here too or a subsequent initDatabase() call would
|
|
261
|
+
// silently skip re-opening and keep using the now-closed client.
|
|
262
|
+
// NOTE (live-witnessed, Windows): calling close() does NOT synchronously
|
|
263
|
+
// release the native libsql binding's OS-level file lock while THIS
|
|
264
|
+
// process keeps running -- the lock only actually clears once the owning
|
|
265
|
+
// process exits. A caller needing the underlying file/directory removable
|
|
266
|
+
// in the SAME still-running process (e.g. a per-run isolated-tmpdir test
|
|
267
|
+
// harness) cannot rely on this call alone for that and must retry the
|
|
268
|
+
// removal with backoff, or isolate each run in its own child process.
|
|
269
|
+
// close() is still correct and worth calling regardless -- it prevents
|
|
270
|
+
// the silent second-handle-fork above, which is a real, distinct benefit.
|
|
271
|
+
if (this.busybase) {
|
|
272
|
+
try { this.busybase.close?.() } catch { /* best-effort */ }
|
|
273
|
+
this.busybase = null
|
|
274
|
+
globalThis.__thatcherBusyBase = null
|
|
275
|
+
_databaseInitialized = false
|
|
276
|
+
}
|
|
252
277
|
}
|
|
253
278
|
|
|
254
279
|
getConfigEngine() {
|
|
@@ -270,38 +295,38 @@ export class Thatcher {
|
|
|
270
295
|
// === CRUD operations ===
|
|
271
296
|
|
|
272
297
|
async list(entity, where = {}, opts = {}) {
|
|
273
|
-
const { list } = await import(resolveModule('./lib/busybase
|
|
298
|
+
const { list } = await import(resolveModule('./lib/busybase/store.js'));
|
|
274
299
|
return list(entity, where, opts);
|
|
275
300
|
}
|
|
276
301
|
|
|
277
302
|
async get(entity, id) {
|
|
278
|
-
const { get } = await import(resolveModule('./lib/busybase
|
|
303
|
+
const { get } = await import(resolveModule('./lib/busybase/store.js'));
|
|
279
304
|
return get(entity, id);
|
|
280
305
|
}
|
|
281
306
|
|
|
282
307
|
async create(entity, data, user) {
|
|
283
|
-
const { create } = await import(resolveModule('./lib/busybase
|
|
308
|
+
const { create } = await import(resolveModule('./lib/busybase/store.js'));
|
|
284
309
|
return create(entity, data, user);
|
|
285
310
|
}
|
|
286
311
|
|
|
287
312
|
// opts.expectedVersion: optional optimistic-concurrency guard (see
|
|
288
|
-
// busybase
|
|
313
|
+
// busybase/store.js update()) -- a caller that read the row's _version can
|
|
289
314
|
// pass it here to detect (via a thrown {code:'conflict'} error) a concurrent
|
|
290
315
|
// writer landing in between, instead of silently clobbering it. `user` stays
|
|
291
316
|
// its own positional param (unused by the store today, kept for API
|
|
292
317
|
// stability with existing callers); opts is a new, optional 5th param.
|
|
293
318
|
async update(entity, id, data, user, opts = {}) {
|
|
294
|
-
const { update } = await import(resolveModule('./lib/busybase
|
|
319
|
+
const { update } = await import(resolveModule('./lib/busybase/store.js'));
|
|
295
320
|
return update(entity, id, data, opts);
|
|
296
321
|
}
|
|
297
322
|
|
|
298
323
|
async delete(entity, id) {
|
|
299
|
-
const { remove } = await import(resolveModule('./lib/busybase
|
|
324
|
+
const { remove } = await import(resolveModule('./lib/busybase/store.js'));
|
|
300
325
|
return remove(entity, id);
|
|
301
326
|
}
|
|
302
327
|
|
|
303
328
|
async search(entity, query, where = {}, opts = {}) {
|
|
304
|
-
const { search } = await import(resolveModule('./lib/busybase
|
|
329
|
+
const { search } = await import(resolveModule('./lib/busybase/store.js'));
|
|
305
330
|
return search(entity, query, where, opts);
|
|
306
331
|
}
|
|
307
332
|
|
|
@@ -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-
|
|
6
|
+
import { revalidatePath } from '@/lib/next-shim';
|
|
7
7
|
|
|
8
8
|
export async function serverCreateEntity(entityName, data) {
|
|
9
9
|
const user = await requireUser();
|
package/src/lib/api-helpers.js
CHANGED
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
|
|
6
|
-
import { validateEntity, validateUpdate, hasErrors } from '@/lib/
|
|
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/
|
|
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
|
|
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 './
|
|
6
|
+
import { UnauthorizedError, PermissionError, NotFoundError } from './errors/index.js';
|
|
7
7
|
|
|
8
8
|
const actionMap = {
|
|
9
9
|
list: 'list',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createLogger } from '
|
|
2
|
-
import { hookEngine } from '
|
|
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
|
|
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 '
|
|
10
|
-
import { genId, now } from '
|
|
9
|
+
import { createLogger } from '../logger.js';
|
|
10
|
+
import { genId, now } from '../id-helpers.js';
|
|
11
11
|
|
|
12
12
|
const log = createLogger('[BusyBaseAudit]');
|
|
13
13
|
|