thatcher 1.0.4

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 (221) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +398 -0
  3. package/package.json +73 -0
  4. package/src/adapters/google-auth.js +148 -0
  5. package/src/adapters/google-drive.js +209 -0
  6. package/src/app/api/[entity]/[[...path]]/route.js +33 -0
  7. package/src/app/api/audit/dashboard/route.js +77 -0
  8. package/src/app/api/audit/logs/route.js +46 -0
  9. package/src/app/api/audit/permissions/[id]/route.js +37 -0
  10. package/src/app/api/audit/permissions/route.js +93 -0
  11. package/src/app/api/audit/permissions/stats/route.js +29 -0
  12. package/src/app/api/audit/route.js +79 -0
  13. package/src/app/api/audit/stats/route.js +18 -0
  14. package/src/app/api/auth/google/callback/route.js +94 -0
  15. package/src/app/api/auth/google/route.js +58 -0
  16. package/src/app/api/auth/login/route.js +121 -0
  17. package/src/app/api/auth/logout/route.js +52 -0
  18. package/src/app/api/auth/me/route.js +16 -0
  19. package/src/app/api/auth/mwr-bridge/route.js +77 -0
  20. package/src/app/api/auth/password-reset/route.js +65 -0
  21. package/src/app/api/cron/trigger/route.js +58 -0
  22. package/src/app/api/csrf-token/route.js +8 -0
  23. package/src/app/api/debug/config/route.js +22 -0
  24. package/src/app/api/debug/hooks/route.js +16 -0
  25. package/src/app/api/debug/plugins/route.js +20 -0
  26. package/src/app/api/debug/sqlite/route.js +21 -0
  27. package/src/app/api/debug/sync/route.js +29 -0
  28. package/src/app/api/debug/workflow/route.js +20 -0
  29. package/src/app/api/domains/[domain]/route.js +26 -0
  30. package/src/app/api/domains/route.js +21 -0
  31. package/src/app/api/email/allocate/batch/route.js +106 -0
  32. package/src/app/api/email/allocate/route.js +141 -0
  33. package/src/app/api/email/receive/route.js +158 -0
  34. package/src/app/api/email/route.js +3 -0
  35. package/src/app/api/email/send/route.js +77 -0
  36. package/src/app/api/email/unallocated/route.js +47 -0
  37. package/src/app/api/files/[id]/route.js +38 -0
  38. package/src/app/api/health/route.js +95 -0
  39. package/src/app/api/metrics/route.js +73 -0
  40. package/src/app/api/monitoring/dashboard/route.js +18 -0
  41. package/src/cli.js +243 -0
  42. package/src/config/config-loader.js +112 -0
  43. package/src/config/constants.js +127 -0
  44. package/src/config/env.js +164 -0
  45. package/src/config/spec-helpers.js +232 -0
  46. package/src/engine.server.js +212 -0
  47. package/src/index.js +368 -0
  48. package/src/lib/accessibility.js +162 -0
  49. package/src/lib/action-factory.js +34 -0
  50. package/src/lib/action-utils.js +21 -0
  51. package/src/lib/alert-manager.js +189 -0
  52. package/src/lib/api-error-wrapper.js +125 -0
  53. package/src/lib/api-helpers.js +53 -0
  54. package/src/lib/api.js +82 -0
  55. package/src/lib/audit-logger-enhanced.js +117 -0
  56. package/src/lib/audit-logger.js +193 -0
  57. package/src/lib/auth-middleware.js +102 -0
  58. package/src/lib/auth-route-helpers.js +83 -0
  59. package/src/lib/business-rules-engine.js +86 -0
  60. package/src/lib/compression.js +44 -0
  61. package/src/lib/config-field-helpers.js +91 -0
  62. package/src/lib/config-generator-engine.js +445 -0
  63. package/src/lib/config-helpers.js +120 -0
  64. package/src/lib/connection-guard.js +79 -0
  65. package/src/lib/crud-action-helpers.js +34 -0
  66. package/src/lib/crud-factory.js +83 -0
  67. package/src/lib/crud-handlers.js +244 -0
  68. package/src/lib/csrf-protection.js +63 -0
  69. package/src/lib/database-core.js +258 -0
  70. package/src/lib/database-migrations.js +96 -0
  71. package/src/lib/date-utils.js +159 -0
  72. package/src/lib/db-backup.js +97 -0
  73. package/src/lib/db-monitor.js +127 -0
  74. package/src/lib/domain-loader.js +82 -0
  75. package/src/lib/email-sender.js +100 -0
  76. package/src/lib/error-boundary.js +134 -0
  77. package/src/lib/error-handler.js +84 -0
  78. package/src/lib/error-recovery.js +190 -0
  79. package/src/lib/error-resilience.js +130 -0
  80. package/src/lib/errors.js +69 -0
  81. package/src/lib/events-engine.js +182 -0
  82. package/src/lib/field-iterator.js +50 -0
  83. package/src/lib/field-registry.js +68 -0
  84. package/src/lib/field-types.js +154 -0
  85. package/src/lib/generic-crud-handler.js +32 -0
  86. package/src/lib/health-monitor.js +134 -0
  87. package/src/lib/hook-engine.js +169 -0
  88. package/src/lib/hot-reload/cache-invalidator.js +115 -0
  89. package/src/lib/hot-reload/checkpoint.js +95 -0
  90. package/src/lib/hot-reload/debug-exposure.js +67 -0
  91. package/src/lib/hot-reload/directory-watcher.js +96 -0
  92. package/src/lib/hot-reload/index.js +50 -0
  93. package/src/lib/hot-reload/mutex.js +75 -0
  94. package/src/lib/hot-reload/promise-container.js +66 -0
  95. package/src/lib/hot-reload/route-wrapper.js +46 -0
  96. package/src/lib/hot-reload/safe-error.js +51 -0
  97. package/src/lib/hot-reload/supervisor.js +161 -0
  98. package/src/lib/hot-reload/timeout-wrapper.js +52 -0
  99. package/src/lib/http-methods-factory.js +25 -0
  100. package/src/lib/index-optimizer.js +96 -0
  101. package/src/lib/index.js +35 -0
  102. package/src/lib/list-data-transform.js +39 -0
  103. package/src/lib/log-aggregator.js +116 -0
  104. package/src/lib/logger.js +55 -0
  105. package/src/lib/metrics-collector.js +102 -0
  106. package/src/lib/minifier.js +19 -0
  107. package/src/lib/monitoring-init.js +67 -0
  108. package/src/lib/next-compat.js +80 -0
  109. package/src/lib/next-polyfills.js +135 -0
  110. package/src/lib/perf-monitor.js +91 -0
  111. package/src/lib/progress-components.js +181 -0
  112. package/src/lib/query-cache.js +126 -0
  113. package/src/lib/query-engine-write.js +221 -0
  114. package/src/lib/query-engine.js +399 -0
  115. package/src/lib/query-perf.js +117 -0
  116. package/src/lib/query-string-adapter.js +75 -0
  117. package/src/lib/realtime-server.js +67 -0
  118. package/src/lib/render-cache.js +61 -0
  119. package/src/lib/request-tracker.js +43 -0
  120. package/src/lib/resource-hints.js +29 -0
  121. package/src/lib/resource-monitor.js +117 -0
  122. package/src/lib/response-formatter.js +80 -0
  123. package/src/lib/route-helpers.js +33 -0
  124. package/src/lib/route-resolver.js +142 -0
  125. package/src/lib/safe-json.js +8 -0
  126. package/src/lib/server-bootstrap.js +71 -0
  127. package/src/lib/stage-pipeline.js +153 -0
  128. package/src/lib/state-protocol.js +171 -0
  129. package/src/lib/state-transport-client.js +169 -0
  130. package/src/lib/state-transport-reconnect.js +121 -0
  131. package/src/lib/state-transport-server.js +181 -0
  132. package/src/lib/static-server.js +97 -0
  133. package/src/lib/status-helpers.js +98 -0
  134. package/src/lib/universal-handler.js +7 -0
  135. package/src/lib/utils.js +93 -0
  136. package/src/lib/validate.js +197 -0
  137. package/src/lib/validation/business-validators.js +61 -0
  138. package/src/lib/validation/csrf.js +51 -0
  139. package/src/lib/validation/file-validators.js +34 -0
  140. package/src/lib/validation/format-validators.js +106 -0
  141. package/src/lib/validation/index.js +19 -0
  142. package/src/lib/validation/rate-limit.js +31 -0
  143. package/src/lib/validation/security-validators.js +78 -0
  144. package/src/lib/validation-middleware.js +133 -0
  145. package/src/lib/validators.js +105 -0
  146. package/src/lib/with-audit-logging.js +63 -0
  147. package/src/lib/with-error-handler.js +31 -0
  148. package/src/lib/workflow-engine.js +250 -0
  149. package/src/server/server.js +305 -0
  150. package/src/services/collaborator-role.service.js +205 -0
  151. package/src/services/email-sender.js +105 -0
  152. package/src/services/notification-engine.js +110 -0
  153. package/src/services/permission.service.js +181 -0
  154. package/src/ui/advanced-search-renderer.js +42 -0
  155. package/src/ui/advanced-widgets.js +47 -0
  156. package/src/ui/auth-pages.js +114 -0
  157. package/src/ui/auth-styles.js +53 -0
  158. package/src/ui/client.js +99 -0
  159. package/src/ui/collaboration-dialogs.js +31 -0
  160. package/src/ui/common-handlers.js +156 -0
  161. package/src/ui/component-engine.js +103 -0
  162. package/src/ui/dashboard-renderer.js +150 -0
  163. package/src/ui/dialog-engine.js +147 -0
  164. package/src/ui/dialog-factory.js +38 -0
  165. package/src/ui/engagement-cards.js +76 -0
  166. package/src/ui/engagement-dialogs.js +100 -0
  167. package/src/ui/engagement-grid-renderer.js +109 -0
  168. package/src/ui/entity-renderer.js +176 -0
  169. package/src/ui/event-delegation.js +108 -0
  170. package/src/ui/fetch-json.js +24 -0
  171. package/src/ui/file-dialogs.js +81 -0
  172. package/src/ui/flexup-report-renderer.js +173 -0
  173. package/src/ui/format-helpers.js +102 -0
  174. package/src/ui/global-tags.js +144 -0
  175. package/src/ui/highlight-threading-renderer.js +176 -0
  176. package/src/ui/idle-logout.js +156 -0
  177. package/src/ui/job-management-renderer.js +61 -0
  178. package/src/ui/layout.js +219 -0
  179. package/src/ui/letter-dialogs.js +37 -0
  180. package/src/ui/ml-console-renderer.js +108 -0
  181. package/src/ui/monitoring-dashboard-client.js +136 -0
  182. package/src/ui/monitoring-dashboard.js +134 -0
  183. package/src/ui/notifications-renderer.js +51 -0
  184. package/src/ui/page-handler-admin.js +121 -0
  185. package/src/ui/page-handler-helpers.js +111 -0
  186. package/src/ui/page-handler-reviews.js +165 -0
  187. package/src/ui/page-handler-rfi.js +42 -0
  188. package/src/ui/page-handler.js +210 -0
  189. package/src/ui/password-reset-page.js +146 -0
  190. package/src/ui/perf-helpers.js +96 -0
  191. package/src/ui/perf-renderer.js +71 -0
  192. package/src/ui/permissions-ui.js +163 -0
  193. package/src/ui/picker-dialogs.js +100 -0
  194. package/src/ui/render-helpers.js +124 -0
  195. package/src/ui/renderer.js +35 -0
  196. package/src/ui/review-comparison-renderer.js +58 -0
  197. package/src/ui/review-detail-panels.js +71 -0
  198. package/src/ui/review-detail-renderer.js +170 -0
  199. package/src/ui/review-detail-script.js +95 -0
  200. package/src/ui/review-mwr-renderer.js +113 -0
  201. package/src/ui/review-renderer.js +202 -0
  202. package/src/ui/review-widgets.js +88 -0
  203. package/src/ui/review-zone-nav.js +12 -0
  204. package/src/ui/rfi-detail-renderer.js +191 -0
  205. package/src/ui/rfi-renderer.js +194 -0
  206. package/src/ui/rfi-report-renderer.js +56 -0
  207. package/src/ui/rippleui.css +1 -0
  208. package/src/ui/settings-renderer-advanced.js +195 -0
  209. package/src/ui/settings-renderer-advanced2.js +158 -0
  210. package/src/ui/settings-renderer-teams.js +112 -0
  211. package/src/ui/settings-renderer.js +166 -0
  212. package/src/ui/spacing-system.js +155 -0
  213. package/src/ui/standalone-login.js +109 -0
  214. package/src/ui/styles.css +2530 -0
  215. package/src/ui/styles2.css +1602 -0
  216. package/src/ui/test-page.js +23 -0
  217. package/src/ui/validation-rules.js +73 -0
  218. package/src/ui/validation-ui.js +147 -0
  219. package/src/ui/virtual-scroll.js +107 -0
  220. package/src/ui/webjsx.js +61 -0
  221. package/src/ui/widgets.js +152 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Structured Logger - Consistent logging across the SDK
3
+ * Simple wrapper with prefix support
4
+ */
5
+
6
+ const LOG_PREFIXES = {
7
+ API: '[API]',
8
+ DB: '[DB]',
9
+ AUTH: '[AUTH]',
10
+ CONFIG: '[Config]',
11
+ SERVICE: '[Service]',
12
+ system: '[System]',
13
+ email: '[Email]',
14
+ validation: '[Validation]',
15
+ database: '[DB]',
16
+ hook: '[Hook]',
17
+ workflow: '[Workflow]',
18
+ };
19
+
20
+ /**
21
+ * Create a logger with prefix
22
+ * @param {string} prefix - Log prefix (from LOG_PREFIXES or custom)
23
+ * @returns {object} Logger with methods
24
+ */
25
+ export function createLogger(prefix) {
26
+ const logger = {
27
+ error: (msg, meta = {}) => {
28
+ console.error(`${prefix} ${msg}`, meta);
29
+ },
30
+ warn: (msg, meta = {}) => {
31
+ console.warn(`${prefix} ${msg}`, meta);
32
+ },
33
+ info: (msg, meta = {}) => {
34
+ console.info(`${prefix} ${msg}`, meta);
35
+ },
36
+ debug: (msg, meta = {}) => {
37
+ if (process.env.DEBUG === 'true') {
38
+ console.debug(`${prefix} ${msg}`, meta);
39
+ }
40
+ },
41
+ };
42
+
43
+ return logger;
44
+ }
45
+
46
+ /**
47
+ * Get prefixed logger
48
+ * @param {string} key - Key from LOG_PREFIXES
49
+ * @returns {object}
50
+ */
51
+ export function getLogger(key) {
52
+ return createLogger(LOG_PREFIXES[key] || `[${key.toUpperCase()}]`);
53
+ }
54
+
55
+ export { LOG_PREFIXES };
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Metrics Collector - Request metrics and stats
3
+ */
4
+
5
+ const metrics = {
6
+ requests: [],
7
+ slowQueries: [],
8
+ errors: [],
9
+ startTime: Date.now(),
10
+ };
11
+
12
+ let _requestCount = 0;
13
+
14
+ /**
15
+ * Record a request
16
+ * @param {string} endpoint
17
+ * @param {string} method
18
+ * @param {number} durationMs
19
+ * @param {number} statusCode
20
+ */
21
+ export function recordRequest(endpoint, method, durationMs, statusCode) {
22
+ _requestCount++;
23
+
24
+ const record = {
25
+ timestamp: Date.now(),
26
+ endpoint,
27
+ method,
28
+ durationMs,
29
+ statusCode,
30
+ };
31
+
32
+ metrics.requests.push(record);
33
+
34
+ // Keep last 1000 requests
35
+ if (metrics.requests.length > 1000) {
36
+ metrics.requests.shift();
37
+ }
38
+
39
+ // Track slow queries (>500ms)
40
+ if (durationMs > 500) {
41
+ metrics.slowQueries.push(record);
42
+ }
43
+
44
+ // Track errors
45
+ if (statusCode >= 400) {
46
+ metrics.errors.push(record);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Get all metrics
52
+ * @returns {object}
53
+ */
54
+ export function getMetrics() {
55
+ const now = Date.now();
56
+ const uptimeSec = Math.floor((now - metrics.startTime) / 1000);
57
+
58
+ const requests = metrics.requests;
59
+ const recent = requests.filter(r => now - r.timestamp < 60000); // last minute
60
+
61
+ const avgDuration = recent.length
62
+ ? recent.reduce((sum, r) => sum + r.durationMs, 0) / recent.length
63
+ : 0;
64
+
65
+ const errorRate = recent.length
66
+ ? recent.filter(r => r.statusCode >= 400).length / recent.length
67
+ : 0;
68
+
69
+ return {
70
+ uptime_seconds: uptimeSec,
71
+ total_requests: _requestCount,
72
+ requests_per_minute: recent.length,
73
+ average_response_time_ms: Math.round(avgDuration),
74
+ error_rate_percent: Math.round(errorRate * 100),
75
+ slow_queries: metrics.slowQueries.slice(-10),
76
+ recent_errors: metrics.errors.slice(-10),
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Get summary statistics
82
+ * @returns {object}
83
+ */
84
+ export function getSummary() {
85
+ const m = getMetrics();
86
+ return {
87
+ requests: m.total_requests,
88
+ avgMs: m.average_response_time_ms,
89
+ errors: m.recent_errors.length,
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Reset metrics (for testing)
95
+ */
96
+ export function resetMetrics() {
97
+ metrics.requests = [];
98
+ metrics.slowQueries = [];
99
+ metrics.errors = [];
100
+ _requestCount = 0;
101
+ metrics.startTime = Date.now();
102
+ }
@@ -0,0 +1,19 @@
1
+ export function minifyJS(code) {
2
+ return code
3
+ .replace(/\/\/.*$/gm, '')
4
+ .replace(/\/\*[\s\S]*?\*\//g, '')
5
+ .replace(/^\s+/gm, '')
6
+ .replace(/\s+$/gm, '')
7
+ .replace(/\n+/g, '\n')
8
+ .replace(/\s*([{}()\[\];:,=<>!+\-*\/&|?])\s*/g, '$1')
9
+ .trim();
10
+ }
11
+
12
+ export function minifyCSS(css) {
13
+ return css
14
+ .replace(/\/\*[\s\S]*?\*\//g, '')
15
+ .replace(/\s+/g, ' ')
16
+ .replace(/\s*([{}:;,>~+])\s*/g, '$1')
17
+ .replace(/;}/g, '}')
18
+ .trim();
19
+ }
@@ -0,0 +1,67 @@
1
+ import { startMonitoring as startResourceMonitoring } from '@/lib/resource-monitor.js'
2
+ import { checkAllThresholds, registerAlertHandler } from '@/lib/alert-manager.js'
3
+ import { getAllMetrics } from '@/lib/metrics-collector.js'
4
+ import { info, warn, error } from '@/lib/log-aggregator.js'
5
+ import path from 'path'
6
+ import { fileURLToPath } from 'url'
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
+ let alertCheckInterval = null
10
+ let initialized = false
11
+
12
+ function initializeMonitoring(config = {}) {
13
+ if (initialized) {
14
+ warn('Monitoring already initialized')
15
+ return
16
+ }
17
+
18
+ const {
19
+ resourceInterval = 5000,
20
+ alertCheckInterval: alertInterval = 10000,
21
+ dbPath = path.join(__dirname, '../../data/app.db')
22
+ } = config
23
+
24
+ startResourceMonitoring(resourceInterval, dbPath)
25
+ info('Resource monitoring started', { interval: resourceInterval })
26
+
27
+ alertCheckInterval = setInterval(() => {
28
+ try {
29
+ const metrics = getAllMetrics()
30
+ checkAllThresholds(metrics)
31
+ } catch (err) {
32
+ error('Alert check failed', { error: err.message })
33
+ }
34
+ }, alertInterval)
35
+
36
+ info('Alert checking started', { interval: alertInterval })
37
+
38
+ registerAlertHandler((alert) => {
39
+ if (alert.severity === 'critical') {
40
+ error(`CRITICAL ALERT: ${alert.message}`, alert.metadata)
41
+ } else if (alert.severity === 'warning') {
42
+ warn(`WARNING: ${alert.message}`, alert.metadata)
43
+ }
44
+ })
45
+
46
+ info('Alert handlers registered')
47
+
48
+ initialized = true
49
+ info('Monitoring system initialized')
50
+ }
51
+
52
+ async function shutdownMonitoring() {
53
+ if (!initialized) return
54
+
55
+ if (alertCheckInterval) {
56
+ clearInterval(alertCheckInterval)
57
+ alertCheckInterval = null
58
+ }
59
+
60
+ const { stopMonitoring } = await import('@/lib/resource-monitor.js')
61
+ stopMonitoring()
62
+
63
+ initialized = false
64
+ info('Monitoring system shutdown')
65
+ }
66
+
67
+ export { initializeMonitoring, shutdownMonitoring }
@@ -0,0 +1,80 @@
1
+ const MAX_BODY_SIZE = 10 * 1024 * 1024;
2
+
3
+ export class NextRequest {
4
+ constructor(req, body, url) {
5
+ this.method = req.method;
6
+ this.headers = req.headers;
7
+ this.url = url;
8
+ this.body = body;
9
+ }
10
+
11
+ async json() {
12
+ return this.body;
13
+ }
14
+
15
+ async text() {
16
+ return typeof this.body === 'string' ? this.body : JSON.stringify(this.body);
17
+ }
18
+ }
19
+
20
+ export class NextResponse {
21
+ constructor(body, init = {}) {
22
+ this.body = body;
23
+ this.status = init.status || 200;
24
+ this.headers = new Map(Object.entries(init.headers || {}));
25
+ }
26
+
27
+ async json() {
28
+ return this.body;
29
+ }
30
+
31
+ static json(body, init = {}) {
32
+ return new NextResponse(body, init);
33
+ }
34
+ }
35
+
36
+ export async function readBody(req) {
37
+ return new Promise((resolve, reject) => {
38
+ let data = '';
39
+ let size = 0;
40
+ req.on('data', (chunk) => {
41
+ size += chunk.length;
42
+ if (size > MAX_BODY_SIZE) {
43
+ req.destroy();
44
+ reject(new Error('Request body too large'));
45
+ return;
46
+ }
47
+ data += chunk;
48
+ });
49
+ req.on('end', () => {
50
+ try {
51
+ resolve(data ? JSON.parse(data) : {});
52
+ } catch {
53
+ resolve(data);
54
+ }
55
+ });
56
+ req.on('error', (err) => reject(err));
57
+ });
58
+ }
59
+
60
+ const HEADER_MAP = {
61
+ 'content-type': 'Content-Type',
62
+ 'content-length': 'Content-Length',
63
+ 'set-cookie': 'Set-Cookie',
64
+ 'cache-control': 'Cache-Control',
65
+ 'expires': 'Expires',
66
+ 'etag': 'ETag',
67
+ 'last-modified': 'Last-Modified',
68
+ 'location': 'Location',
69
+ 'date': 'Date',
70
+ 'connection': 'Connection',
71
+ };
72
+
73
+ export function normalizeHeaderName(key) {
74
+ return HEADER_MAP[key.toLowerCase()] || key;
75
+ }
76
+
77
+ export function registerGlobals() {
78
+ globalThis.NextRequest = NextRequest;
79
+ globalThis.NextResponse = NextResponse;
80
+ }
@@ -0,0 +1,135 @@
1
+ import { AsyncLocalStorage } from 'async_hooks';
2
+
3
+ const requestContext = new AsyncLocalStorage();
4
+
5
+ export class NextResponse {
6
+ constructor(body, init = {}) {
7
+ this.body = body;
8
+ this.status = init.status || 200;
9
+ this.headers = new Map(Object.entries(init.headers || {}));
10
+ }
11
+
12
+ async json() {
13
+ return this.body;
14
+ }
15
+
16
+ static json(body, init = {}) {
17
+ return new NextResponse(body, init);
18
+ }
19
+
20
+ static redirect(url, status = 307) {
21
+ return new NextResponse(null, {
22
+ status,
23
+ headers: { Location: url }
24
+ });
25
+ }
26
+ }
27
+
28
+ let fallbackRequest = null;
29
+ let fallbackResponse = null;
30
+
31
+ export function setCurrentRequest(req) {
32
+ fallbackRequest = req;
33
+ }
34
+
35
+ export function setCurrentResponse(res) {
36
+ fallbackResponse = res;
37
+ }
38
+
39
+ export function runWithContext(req, res, fn) {
40
+ return requestContext.run({ req, res }, fn);
41
+ }
42
+
43
+ function getRequest() {
44
+ const store = requestContext.getStore();
45
+ return store?.req || fallbackRequest;
46
+ }
47
+
48
+ function getResponse() {
49
+ const store = requestContext.getStore();
50
+ return store?.res || fallbackResponse;
51
+ }
52
+
53
+ export async function cookies() {
54
+ if (typeof window !== 'undefined') {
55
+ throw new Error('cookies() should only be called on the server side');
56
+ }
57
+
58
+ const req = getRequest();
59
+ const res = getResponse();
60
+ const cookieHeader = req?.headers?.cookie || '';
61
+ const cookieMap = {};
62
+
63
+ if (cookieHeader) {
64
+ cookieHeader.split(';').forEach(cookie => {
65
+ const [name, value] = cookie.split('=').map(s => s.trim());
66
+ if (name) cookieMap[name] = decodeURIComponent(value || '');
67
+ });
68
+ }
69
+
70
+ return {
71
+ get: (name) => {
72
+ const value = cookieMap[name];
73
+ return value ? { name, value } : undefined;
74
+ },
75
+ set: (name, value, options = {}) => {
76
+ if (!res) return;
77
+ let setCookieValue = `${name}=${encodeURIComponent(value)}`;
78
+ if (options.path) setCookieValue += `; Path=${options.path}`;
79
+ if (options.maxAge) setCookieValue += `; Max-Age=${options.maxAge}`;
80
+ if (options.expires) setCookieValue += `; Expires=${options.expires}`;
81
+ if (options.secure) setCookieValue += '; Secure';
82
+ if (options.httpOnly) setCookieValue += '; HttpOnly';
83
+ if (options.sameSite) setCookieValue += `; SameSite=${options.sameSite}`;
84
+ const existing = res.getHeader('Set-Cookie') || [];
85
+ const setCookies = Array.isArray(existing) ? existing : [existing];
86
+ res.setHeader('Set-Cookie', [...setCookies, setCookieValue]);
87
+ },
88
+ delete: (name) => {
89
+ if (!res) return;
90
+ const setCookieValue = `${name}=; Path=/; Max-Age=0`;
91
+ const existing = res.getHeader('Set-Cookie') || [];
92
+ const setCookies = Array.isArray(existing) ? existing : [existing];
93
+ res.setHeader('Set-Cookie', [...setCookies, setCookieValue]);
94
+ },
95
+ getAll: () => {
96
+ return Object.entries(cookieMap).map(([name, value]) => ({ name, value }));
97
+ },
98
+ has: (name) => {
99
+ return name in cookieMap;
100
+ },
101
+ };
102
+ }
103
+
104
+ export function headers() {
105
+ return {
106
+ get: (name) => null,
107
+ getSetCookie: () => [],
108
+ has: (name) => false,
109
+ entries: () => [],
110
+ };
111
+ }
112
+
113
+ export function revalidatePath() {}
114
+
115
+ export function revalidateTag() {}
116
+
117
+ export function redirect(path, status = 302) {
118
+ if (typeof window !== 'undefined') {
119
+ window.location.href = path;
120
+ } else {
121
+ const error = new Error(`Redirect to ${path}`);
122
+ error.type = 'redirect';
123
+ error.location = path;
124
+ error.status = status;
125
+ throw error;
126
+ }
127
+ }
128
+
129
+ export function notFound() {
130
+ if (typeof window !== 'undefined') {
131
+ window.location.href = '/404';
132
+ } else {
133
+ throw new Error('notFound()');
134
+ }
135
+ }
@@ -0,0 +1,91 @@
1
+ const metrics = new Map()
2
+ const thresholds = { render: 100, query: 50, api: 200, total: 500 }
3
+ let enabled = process.env.PERF_MONITOR !== 'false'
4
+
5
+ export function startTimer(key) {
6
+ if (!enabled) return null
7
+ const start = process.hrtime.bigint()
8
+ return { key, start }
9
+ }
10
+
11
+ export function endTimer(timer) {
12
+ if (!enabled || !timer) return 0
13
+ const end = process.hrtime.bigint()
14
+ const ms = Number(end - timer.start) / 1000000
15
+ recordMetric(timer.key, ms)
16
+ return ms
17
+ }
18
+
19
+ export function recordMetric(key, value) {
20
+ if (!enabled) return
21
+ if (!metrics.has(key)) metrics.set(key, [])
22
+ const arr = metrics.get(key)
23
+ arr.push({ value, ts: Date.now() })
24
+ if (arr.length > 1000) arr.shift()
25
+ }
26
+
27
+ export function getMetrics(key) {
28
+ if (!key) return Object.fromEntries(metrics.entries())
29
+ return metrics.get(key) || []
30
+ }
31
+
32
+ export function getStats(key) {
33
+ const data = metrics.get(key) || []
34
+ if (data.length === 0) return null
35
+ const values = data.map(d => d.value)
36
+ values.sort((a, b) => a - b)
37
+ return {
38
+ count: values.length,
39
+ min: values[0],
40
+ max: values[values.length - 1],
41
+ avg: values.reduce((a, b) => a + b, 0) / values.length,
42
+ p50: values[Math.floor(values.length * 0.5)],
43
+ p95: values[Math.floor(values.length * 0.95)],
44
+ p99: values[Math.floor(values.length * 0.99)]
45
+ }
46
+ }
47
+
48
+ export function checkThreshold(key, value) {
49
+ if (!enabled) return true
50
+ const limit = thresholds[key]
51
+ if (limit && value > limit) {
52
+ console.warn(`[PERF] ${key} exceeded threshold: ${value.toFixed(2)}ms > ${limit}ms`)
53
+ return false
54
+ }
55
+ return true
56
+ }
57
+
58
+ export function setThreshold(key, ms) {
59
+ thresholds[key] = ms
60
+ }
61
+
62
+ export function clearMetrics() {
63
+ metrics.clear()
64
+ }
65
+
66
+ export function enable() {
67
+ enabled = true
68
+ }
69
+
70
+ export function disable() {
71
+ enabled = false
72
+ }
73
+
74
+ export function measure(key, fn) {
75
+ const timer = startTimer(key)
76
+ try {
77
+ const result = fn()
78
+ if (result instanceof Promise) {
79
+ return result.finally(() => {
80
+ const ms = endTimer(timer)
81
+ checkThreshold(key, ms)
82
+ })
83
+ }
84
+ const ms = endTimer(timer)
85
+ checkThreshold(key, ms)
86
+ return result
87
+ } catch (err) {
88
+ endTimer(timer)
89
+ throw err
90
+ }
91
+ }