orez 0.2.26 → 0.2.29
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/dist/cf-do/worker.d.ts.map +1 -1
- package/dist/cf-do/worker.js +9 -1
- package/dist/cf-do/worker.js.map +1 -1
- package/dist/pg-proxy-do-backend.d.ts +2 -0
- package/dist/pg-proxy-do-backend.d.ts.map +1 -1
- package/dist/pg-proxy-do-backend.js +49 -7
- package/dist/pg-proxy-do-backend.js.map +1 -1
- package/dist/pg-sqlite-compiler/catalog/seed.d.ts +67 -0
- package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/catalog/seed.js +436 -0
- package/dist/pg-sqlite-compiler/catalog/seed.js.map +1 -0
- package/dist/pg-sqlite-compiler/index.d.ts +12 -0
- package/dist/pg-sqlite-compiler/index.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/index.js +59 -0
- package/dist/pg-sqlite-compiler/index.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +48 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.js +93 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/catalog.d.ts +34 -0
- package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/catalog.js +30 -0
- package/dist/pg-sqlite-compiler/passes/catalog.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/datetime.d.ts +21 -0
- package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/datetime.js +53 -0
- package/dist/pg-sqlite-compiler/passes/datetime.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/index.d.ts +21 -0
- package/dist/pg-sqlite-compiler/passes/index.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/index.js +39 -0
- package/dist/pg-sqlite-compiler/passes/index.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/types.d.ts +41 -0
- package/dist/pg-sqlite-compiler/passes/types.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/types.js +103 -0
- package/dist/pg-sqlite-compiler/passes/types.js.map +1 -0
- package/dist/pg-sqlite-compiler/test/oracle.d.ts +34 -0
- package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/test/oracle.js +204 -0
- package/dist/pg-sqlite-compiler/test/oracle.js.map +1 -0
- package/dist/pg-sqlite-compiler/types.d.ts +55 -0
- package/dist/pg-sqlite-compiler/types.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/types.js +2 -0
- package/dist/pg-sqlite-compiler/types.js.map +1 -0
- package/package.json +8 -4
- package/src/admin/admin-data.test.ts +0 -348
- package/src/admin/http-proxy.ts +0 -252
- package/src/admin/log-store.ts +0 -192
- package/src/admin/server.ts +0 -471
- package/src/admin/ui.ts +0 -1322
- package/src/bench/proxy-throughput.bench.ts +0 -343
- package/src/bench/serial-mutations.bench.ts +0 -270
- package/src/browser.ts +0 -203
- package/src/cf-do/.wrangler/cache/cf.json +0 -1
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0f0f3bdf0abda097eb6f1246db4657d9fc622081362d894d82c1a1ce067b05b6.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/1ddd3a4a48a11b51658444f5458a1fb175194b1d5b6a5bda20ef3fe3205b900c.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/204a39120310d37e972c5914cfd71ad55c151bdb9e8ed289a5f8c5b052dd60e4.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/3835f242df9728adba3d127a238793fd054ed3e51df3f60749ee744c469bf2a2.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/4aa9c80eb716cf55b8995ccf7afab0b36c683e6da07d7c37a3f9c570136036df.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/533e2fd1d6ea46e7a9a0017916ef341802d438d72583462755f2c1f8225e9bf2.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/5ffa1aced1225ecaeac6366f7586aa3de92761cdff8711d81fbd81f248076abd.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/686c3a9f0d7e59ed2ab607efd4b76d779c97cafeb3818380033bf7c7eb86c819.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/6e8214e8dcfadd0deb52d64e5e9ca85c6b329ace11193909845995396914c473.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/78d9ec9ff873d3fe3507ff53c2a6f6dfc408b4268eb0db3f2a146c0678965366.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/7eff9f0ed7e27ad0d3f9d923de0682fab1928591172c1ba336c5f79a134a5d85.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/836cda5b995b25867d722ed4f4c2292167e80351a3c6038db626648eb247dd8b.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/91ef63b112209ab30172763acd8a0935106c248f7f1bcae5545ce37a9f201551.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/a66ea4293a5f5938bc6d116edfa2522bb85bc37aea3541fbc09c3b613b9b32c0.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/ceb2ab26b80590840b65651deb6e948d3bf81565c6751f3a58752cf4bf4aecae.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
- package/src/cf-do/ARCHITECTURE.md +0 -83
- package/src/cf-do/watermark.test.ts +0 -103
- package/src/cf-do/watermark.ts +0 -118
- package/src/cf-do/worker.ts +0 -1033
- package/src/cf-do/wrangler.toml +0 -11
- package/src/cf-pglite/README.md +0 -19
- package/src/change-tracking.ts +0 -25
- package/src/child-process.test.ts +0 -147
- package/src/child-process.ts +0 -90
- package/src/cli-entry.ts +0 -72
- package/src/cli.test.ts +0 -38
- package/src/cli.ts +0 -1214
- package/src/config.ts +0 -150
- package/src/do-sql-tracking.test.ts +0 -19
- package/src/do-sql-tracking.ts +0 -19
- package/src/index.ts +0 -1215
- package/src/integration/integration.test.ts +0 -517
- package/src/integration/native-binary.guard.test.ts +0 -13
- package/src/integration/native-startup.test.ts +0 -44
- package/src/integration/replication-latency.test.ts +0 -428
- package/src/integration/restore-live-stress.test.ts +0 -433
- package/src/integration/restore-reset.test.ts +0 -400
- package/src/integration/restore.test.ts +0 -274
- package/src/integration/test-permissions.ts +0 -147
- package/src/load-config.ts +0 -46
- package/src/log.ts +0 -96
- package/src/mutex.ts +0 -47
- package/src/pg-proxy-browser.singledb.test.ts +0 -233
- package/src/pg-proxy-browser.ts +0 -2022
- package/src/pg-proxy-do-backend.test.ts +0 -3890
- package/src/pg-proxy-do-backend.ts +0 -7157
- package/src/pg-proxy.ts +0 -1087
- package/src/pglite-ipc.test.ts +0 -116
- package/src/pglite-ipc.ts +0 -266
- package/src/pglite-manager.ts +0 -557
- package/src/pglite-web-proxy.test.ts +0 -57
- package/src/pglite-web-proxy.ts +0 -221
- package/src/pglite-web-worker.ts +0 -152
- package/src/pglite-worker-thread.ts +0 -253
- package/src/port.ts +0 -25
- package/src/process-title.ts +0 -9
- package/src/recovery.ts +0 -155
- package/src/replication/change-tracker.test.ts +0 -357
- package/src/replication/change-tracker.ts +0 -279
- package/src/replication/handler.test.ts +0 -511
- package/src/replication/handler.ts +0 -1190
- package/src/replication/pgoutput-encoder.test.ts +0 -697
- package/src/replication/pgoutput-encoder.ts +0 -373
- package/src/replication/tcp-replication.test.ts +0 -876
- package/src/replication/zero-compat.test.ts +0 -1150
- package/src/restore-stress.test.ts +0 -188
- package/src/s3-local.ts +0 -203
- package/src/shim/hooks.mjs +0 -120
- package/src/shim/register.mjs +0 -4
- package/src/sqlite-mode/apply-mode.ts +0 -224
- package/src/sqlite-mode/index.ts +0 -15
- package/src/sqlite-mode/native-binary.ts +0 -89
- package/src/sqlite-mode/package-resolve.ts +0 -17
- package/src/sqlite-mode/resolve-mode.ts +0 -80
- package/src/sqlite-mode/shim-template.ts +0 -159
- package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
- package/src/sqlite-mode/types.ts +0 -30
- package/src/vite-plugin.ts +0 -67
- package/src/wasm-sqlite.test.ts +0 -537
- package/src/worker/browser-admin.ts +0 -52
- package/src/worker/browser-build-config.test.ts +0 -71
- package/src/worker/browser-build-config.ts +0 -109
- package/src/worker/browser-embed-admin.test.ts +0 -75
- package/src/worker/browser-embed.ts +0 -345
- package/src/worker/cf-patches.ts +0 -384
- package/src/worker/embed-integration.test.ts +0 -321
- package/src/worker/index.ts +0 -138
- package/src/worker/shims/fastify.test.ts +0 -255
- package/src/worker/shims/fastify.ts +0 -306
- package/src/worker/shims/http-service.test.ts +0 -355
- package/src/worker/shims/http-service.ts +0 -293
- package/src/worker/shims/node-stub.ts +0 -290
- package/src/worker/shims/oxfmt.ts +0 -3
- package/src/worker/shims/postgres-browser.ts +0 -59
- package/src/worker/shims/postgres-socket.test.ts +0 -576
- package/src/worker/shims/postgres-socket.ts +0 -310
- package/src/worker/shims/postgres.test.ts +0 -364
- package/src/worker/shims/postgres.ts +0 -1454
- package/src/worker/shims/sqlite-browser.test.ts +0 -233
- package/src/worker/shims/sqlite-browser.ts +0 -175
- package/src/worker/shims/sqlite.test.ts +0 -786
- package/src/worker/shims/sqlite.ts +0 -978
- package/src/worker/shims/stream-browser.ts +0 -15
- package/src/worker/shims/ws-browser.test.ts +0 -205
- package/src/worker/shims/ws-browser.ts +0 -248
- package/src/worker/shims/ws.test.ts +0 -288
- package/src/worker/shims/ws.ts +0 -467
- package/src/worker/shims/zero-process-env.ts +0 -11
- package/src/worker/types.ts +0 -75
- package/src/worker/worker-integration.test.ts +0 -223
- package/src/worker/worker.test.ts +0 -136
- package/src/worker/zero-cache-embed-cf.ts +0 -463
- package/src/worker/zero-cache-embed.ts +0 -277
package/src/admin/http-proxy.ts
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import { createServer, connect, type Socket, type Server } from 'node:net'
|
|
2
|
-
|
|
3
|
-
import type { ZeroLiteConfig } from '../config.js'
|
|
4
|
-
import type { LogStore } from './log-store.js'
|
|
5
|
-
|
|
6
|
-
export interface HttpLogEntry {
|
|
7
|
-
id: number
|
|
8
|
-
ts: number
|
|
9
|
-
method: string
|
|
10
|
-
path: string
|
|
11
|
-
status: number
|
|
12
|
-
duration: number
|
|
13
|
-
reqSize: number
|
|
14
|
-
resSize: number
|
|
15
|
-
reqHeaders: Record<string, string>
|
|
16
|
-
resHeaders: Record<string, string>
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface HttpLogStore {
|
|
20
|
-
push(entry: Omit<HttpLogEntry, 'id'>): void
|
|
21
|
-
query(opts?: { since?: number; path?: string }): {
|
|
22
|
-
entries: HttpLogEntry[]
|
|
23
|
-
cursor: number
|
|
24
|
-
}
|
|
25
|
-
clear(): void
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const MAX_ENTRIES = 10_000
|
|
29
|
-
const TRIM_BATCH = Math.floor(MAX_ENTRIES * 0.1)
|
|
30
|
-
|
|
31
|
-
export function createHttpLogStore(): HttpLogStore {
|
|
32
|
-
const entries: HttpLogEntry[] = []
|
|
33
|
-
let nextId = 1
|
|
34
|
-
|
|
35
|
-
function push(entry: Omit<HttpLogEntry, 'id'>) {
|
|
36
|
-
const full: HttpLogEntry = { ...entry, id: nextId++ }
|
|
37
|
-
entries.push(full)
|
|
38
|
-
if (entries.length > MAX_ENTRIES + TRIM_BATCH) {
|
|
39
|
-
entries.splice(0, entries.length - MAX_ENTRIES)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function query(opts?: { since?: number; path?: string }) {
|
|
44
|
-
let result: HttpLogEntry[] = entries
|
|
45
|
-
if (opts?.since) {
|
|
46
|
-
const since = opts.since
|
|
47
|
-
let lo = 0
|
|
48
|
-
let hi = result.length
|
|
49
|
-
while (lo < hi) {
|
|
50
|
-
const mid = (lo + hi) >>> 1
|
|
51
|
-
if (result[mid].id <= since) lo = mid + 1
|
|
52
|
-
else hi = mid
|
|
53
|
-
}
|
|
54
|
-
result = result.slice(lo)
|
|
55
|
-
}
|
|
56
|
-
if (opts?.path) {
|
|
57
|
-
const p = opts.path
|
|
58
|
-
result = result.filter((e) => e.path.includes(p))
|
|
59
|
-
}
|
|
60
|
-
return {
|
|
61
|
-
entries: result,
|
|
62
|
-
cursor: entries.length > 0 ? entries[entries.length - 1].id : 0,
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function clear() {
|
|
67
|
-
entries.length = 0
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return { push, query, clear }
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function parseHeaders(raw: string): Record<string, string> {
|
|
74
|
-
const out: Record<string, string> = {}
|
|
75
|
-
const lines = raw.split('\r\n')
|
|
76
|
-
for (let i = 1; i < lines.length; i++) {
|
|
77
|
-
if (lines[i] === '') break
|
|
78
|
-
const idx = lines[i].indexOf(': ')
|
|
79
|
-
if (idx > 0) {
|
|
80
|
-
out[lines[i].slice(0, idx).toLowerCase()] = lines[i].slice(idx + 2)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return out
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// public API routes served directly by the proxy (read-only, no auth)
|
|
87
|
-
// these are available at the sprite's public URL under /__orez/
|
|
88
|
-
const CORS =
|
|
89
|
-
'Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: GET, OPTIONS\r\nAccess-Control-Allow-Headers: *'
|
|
90
|
-
|
|
91
|
-
function httpResponse(
|
|
92
|
-
status: number,
|
|
93
|
-
body: string,
|
|
94
|
-
contentType = 'application/json'
|
|
95
|
-
): Buffer {
|
|
96
|
-
const headers = `HTTP/1.1 ${status} ${status === 200 ? 'OK' : 'Error'}\r\nContent-Type: ${contentType}\r\nContent-Length: ${Buffer.byteLength(body)}\r\n${CORS}\r\nConnection: close\r\n\r\n`
|
|
97
|
-
return Buffer.from(headers + body)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function handleOrezRoute(
|
|
101
|
-
path: string,
|
|
102
|
-
method: string,
|
|
103
|
-
logStore?: LogStore,
|
|
104
|
-
config?: ZeroLiteConfig,
|
|
105
|
-
startTime?: number
|
|
106
|
-
): Buffer | null {
|
|
107
|
-
if (method === 'OPTIONS') {
|
|
108
|
-
return httpResponse(200, '')
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (method !== 'GET') {
|
|
112
|
-
return httpResponse(405, JSON.stringify({ error: 'method not allowed' }))
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const url = new URL(path, 'http://localhost')
|
|
116
|
-
const route = url.pathname.replace(/^\/__orez/, '')
|
|
117
|
-
|
|
118
|
-
if (route === '/api/logs' && logStore) {
|
|
119
|
-
const source = url.searchParams.get('source') || undefined
|
|
120
|
-
const level = url.searchParams.get('level') || undefined
|
|
121
|
-
const sinceStr = url.searchParams.get('since')
|
|
122
|
-
const limitStr = url.searchParams.get('limit')
|
|
123
|
-
const since = sinceStr ? Number(sinceStr) : undefined
|
|
124
|
-
const limit = limitStr ? Number(limitStr) : undefined
|
|
125
|
-
return httpResponse(
|
|
126
|
-
200,
|
|
127
|
-
JSON.stringify(logStore.query({ source, level, since, limit }))
|
|
128
|
-
)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (route === '/api/status' && config) {
|
|
132
|
-
return httpResponse(
|
|
133
|
-
200,
|
|
134
|
-
JSON.stringify({
|
|
135
|
-
uptime: Math.floor((Date.now() - (startTime || Date.now())) / 1000),
|
|
136
|
-
logLevel: config.logLevel,
|
|
137
|
-
sqliteMode: config.disableWasmSqlite ? 'native' : 'wasm',
|
|
138
|
-
})
|
|
139
|
-
)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return httpResponse(404, JSON.stringify({ error: 'not found' }))
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// raw tcp proxy that avoids bun's broken node:http upgrade handling.
|
|
146
|
-
// bun silently drops socket.write() data in http server upgrade events,
|
|
147
|
-
// so we do everything at the net level instead.
|
|
148
|
-
//
|
|
149
|
-
// intercepts /__orez/* paths to serve read-only API (logs, status)
|
|
150
|
-
// directly without forwarding to zero-cache.
|
|
151
|
-
export function startHttpProxy(opts: {
|
|
152
|
-
listenPort: number
|
|
153
|
-
targetPort: number
|
|
154
|
-
httpLog: HttpLogStore
|
|
155
|
-
logStore?: LogStore
|
|
156
|
-
config?: ZeroLiteConfig
|
|
157
|
-
startTime?: number
|
|
158
|
-
}): Promise<Server> {
|
|
159
|
-
const { listenPort, targetPort, httpLog, logStore, config, startTime } = opts
|
|
160
|
-
|
|
161
|
-
const server = createServer((client: Socket) => {
|
|
162
|
-
const start = Date.now()
|
|
163
|
-
|
|
164
|
-
let logged = false
|
|
165
|
-
let reqMethod = ''
|
|
166
|
-
let reqPath = ''
|
|
167
|
-
let reqHeaders: Record<string, string> = {}
|
|
168
|
-
|
|
169
|
-
// intercept first client chunk to extract request info
|
|
170
|
-
client.once('data', (chunk: Buffer) => {
|
|
171
|
-
const str = chunk.toString('utf8')
|
|
172
|
-
const firstLine = str.split('\r\n')[0] || ''
|
|
173
|
-
const parts = firstLine.split(' ')
|
|
174
|
-
reqMethod = parts[0] || 'GET'
|
|
175
|
-
reqPath = parts[1] || '/'
|
|
176
|
-
reqHeaders = parseHeaders(str)
|
|
177
|
-
|
|
178
|
-
// intercept /__orez/ paths — serve directly, don't forward to zero-cache
|
|
179
|
-
// check char 0 first to skip the startsWith on hot-path sync/ws traffic
|
|
180
|
-
if (
|
|
181
|
-
reqPath.charCodeAt(0) === 47 &&
|
|
182
|
-
reqPath.charCodeAt(1) === 95 &&
|
|
183
|
-
reqPath.startsWith('/__orez/')
|
|
184
|
-
) {
|
|
185
|
-
const response = handleOrezRoute(reqPath, reqMethod, logStore, config, startTime)
|
|
186
|
-
if (response) {
|
|
187
|
-
client.write(response)
|
|
188
|
-
client.end()
|
|
189
|
-
httpLog.push({
|
|
190
|
-
ts: start,
|
|
191
|
-
method: reqMethod,
|
|
192
|
-
path: reqPath,
|
|
193
|
-
status: 200,
|
|
194
|
-
duration: Date.now() - start,
|
|
195
|
-
reqSize: chunk.length,
|
|
196
|
-
resSize: response.length,
|
|
197
|
-
reqHeaders,
|
|
198
|
-
resHeaders: {},
|
|
199
|
-
})
|
|
200
|
-
return
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// forward to zero-cache
|
|
205
|
-
const target = connect(targetPort, '127.0.0.1')
|
|
206
|
-
|
|
207
|
-
target.setKeepAlive(true, 30_000)
|
|
208
|
-
target.setTimeout(0)
|
|
209
|
-
client.setKeepAlive(true, 30_000)
|
|
210
|
-
client.setTimeout(0)
|
|
211
|
-
|
|
212
|
-
target.write(chunk)
|
|
213
|
-
client.pipe(target)
|
|
214
|
-
|
|
215
|
-
// intercept first target chunk to extract response info and log
|
|
216
|
-
target.once('data', (resChunk: Buffer) => {
|
|
217
|
-
const resStr = resChunk.toString('utf8')
|
|
218
|
-
const resFirstLine = resStr.split('\r\n')[0] || ''
|
|
219
|
-
const status = parseInt(resFirstLine.split(' ')[1]) || 0
|
|
220
|
-
const resHeaders = parseHeaders(resStr)
|
|
221
|
-
|
|
222
|
-
if (!logged) {
|
|
223
|
-
logged = true
|
|
224
|
-
httpLog.push({
|
|
225
|
-
ts: start,
|
|
226
|
-
method: status === 101 ? 'WS' : reqMethod,
|
|
227
|
-
path: reqPath,
|
|
228
|
-
status,
|
|
229
|
-
duration: Date.now() - start,
|
|
230
|
-
reqSize: 0,
|
|
231
|
-
resSize: resChunk.length,
|
|
232
|
-
reqHeaders,
|
|
233
|
-
resHeaders,
|
|
234
|
-
})
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
client.write(resChunk)
|
|
238
|
-
target.pipe(client)
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
target.on('error', () => client.destroy())
|
|
242
|
-
client.on('error', () => target.destroy())
|
|
243
|
-
target.on('close', () => client.destroy())
|
|
244
|
-
client.on('close', () => target.destroy())
|
|
245
|
-
})
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
return new Promise((resolve, reject) => {
|
|
249
|
-
server.listen(listenPort, '127.0.0.1', () => resolve(server as any))
|
|
250
|
-
server.on('error', reject)
|
|
251
|
-
})
|
|
252
|
-
}
|
package/src/admin/log-store.ts
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, appendFile, stat, rename, unlink, readdirSync } from 'node:fs'
|
|
2
|
-
import { join } from 'node:path'
|
|
3
|
-
|
|
4
|
-
export interface LogEntry {
|
|
5
|
-
id: number
|
|
6
|
-
ts: number
|
|
7
|
-
source: string
|
|
8
|
-
level: string
|
|
9
|
-
msg: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface LogStore {
|
|
13
|
-
push(source: string, level: string, msg: string): void
|
|
14
|
-
query(opts?: { source?: string; level?: string; since?: number; limit?: number }): {
|
|
15
|
-
entries: LogEntry[]
|
|
16
|
-
cursor: number
|
|
17
|
-
}
|
|
18
|
-
getAll(): LogEntry[]
|
|
19
|
-
clear(): void
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const ANSI_RE = /\x1b\[[0-9;]*m/g
|
|
23
|
-
const MAX_ENTRIES = 20_000
|
|
24
|
-
// trim in batches of 10% to avoid O(n) splice on every single push
|
|
25
|
-
const TRIM_BATCH = Math.floor(MAX_ENTRIES * 0.1)
|
|
26
|
-
const MAX_FILE_SIZE = 2 * 1024 * 1024
|
|
27
|
-
const MAX_QUERY_LIMIT = 5000
|
|
28
|
-
const LEVEL_PRIORITY: Record<string, number> = { error: 0, warn: 1, info: 2, debug: 3 }
|
|
29
|
-
const VALID_SOURCES = new Set(['orez', 'zero', 'pglite', 'proxy', 's3'])
|
|
30
|
-
const VALID_LEVELS = new Set(['error', 'warn', 'info', 'debug'])
|
|
31
|
-
|
|
32
|
-
export function createLogStore(
|
|
33
|
-
dataDir: string,
|
|
34
|
-
writeToDisk = true,
|
|
35
|
-
maxFileSize = MAX_FILE_SIZE
|
|
36
|
-
): LogStore {
|
|
37
|
-
const entries: LogEntry[] = []
|
|
38
|
-
let nextId = 1
|
|
39
|
-
|
|
40
|
-
const logsDir = join(dataDir, 'logs')
|
|
41
|
-
|
|
42
|
-
if (writeToDisk) {
|
|
43
|
-
mkdirSync(logsDir, { recursive: true })
|
|
44
|
-
// clean up old rotated log files on startup
|
|
45
|
-
try {
|
|
46
|
-
for (const f of readdirSync(logsDir)) {
|
|
47
|
-
if (/\.log\.\d+$/.test(f)) {
|
|
48
|
-
unlink(join(logsDir, f), () => {})
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
} catch {}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// track file sizes and rotation state per-source
|
|
55
|
-
const fileSizes: Record<string, number> = {}
|
|
56
|
-
const rotating: Record<string, boolean> = {}
|
|
57
|
-
|
|
58
|
-
// buffered async disk writes — avoids appendFileSync blocking the event loop
|
|
59
|
-
const writeBuffers: Record<string, string[]> = {}
|
|
60
|
-
const MAX_BUFFER_SIZE = 10_000
|
|
61
|
-
const FLUSH_INTERVAL_MS = 3000
|
|
62
|
-
|
|
63
|
-
function getLogFile(source: string): string {
|
|
64
|
-
return join(logsDir, `${source}.log`)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function rotateIfNeeded(source: string) {
|
|
68
|
-
if (!writeToDisk || rotating[source]) return
|
|
69
|
-
rotating[source] = true
|
|
70
|
-
const logFile = getLogFile(source)
|
|
71
|
-
stat(logFile, (err, stats) => {
|
|
72
|
-
if (err) {
|
|
73
|
-
rotating[source] = false
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
fileSizes[source] = stats.size
|
|
77
|
-
if (stats.size > maxFileSize) {
|
|
78
|
-
// delete old backup first, then rename current
|
|
79
|
-
unlink(logFile + '.1', () => {
|
|
80
|
-
rename(logFile, logFile + '.1', () => {
|
|
81
|
-
fileSizes[source] = 0
|
|
82
|
-
rotating[source] = false
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
} else {
|
|
86
|
-
rotating[source] = false
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function flushBuffers() {
|
|
92
|
-
for (const source in writeBuffers) {
|
|
93
|
-
const buf = writeBuffers[source]
|
|
94
|
-
if (buf.length === 0) continue
|
|
95
|
-
const data = buf.join('')
|
|
96
|
-
buf.length = 0
|
|
97
|
-
const logFile = getLogFile(source)
|
|
98
|
-
appendFile(logFile, data, (err) => {
|
|
99
|
-
if (err) return
|
|
100
|
-
fileSizes[source] = (fileSizes[source] || 0) + data.length
|
|
101
|
-
if (fileSizes[source] > maxFileSize) {
|
|
102
|
-
rotateIfNeeded(source)
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (writeToDisk) {
|
|
109
|
-
const timer = setInterval(flushBuffers, FLUSH_INTERVAL_MS)
|
|
110
|
-
if (timer.unref) timer.unref()
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function push(source: string, level: string, msg: string) {
|
|
114
|
-
const entry: LogEntry = {
|
|
115
|
-
id: nextId++,
|
|
116
|
-
ts: Date.now(),
|
|
117
|
-
source,
|
|
118
|
-
level,
|
|
119
|
-
msg: msg.replace(ANSI_RE, ''),
|
|
120
|
-
}
|
|
121
|
-
entries.push(entry)
|
|
122
|
-
// trim in batches to amortize the O(n) splice cost — instead of shifting
|
|
123
|
-
// 50k elements on every push, we shift once every ~5k pushes
|
|
124
|
-
if (entries.length > MAX_ENTRIES + TRIM_BATCH) {
|
|
125
|
-
entries.splice(0, entries.length - MAX_ENTRIES)
|
|
126
|
-
}
|
|
127
|
-
if (writeToDisk) {
|
|
128
|
-
const ts = new Date(entry.ts).toISOString()
|
|
129
|
-
const line = `[${ts}] [${level}] ${entry.msg}\n`
|
|
130
|
-
if (!writeBuffers[source]) writeBuffers[source] = []
|
|
131
|
-
const buf = writeBuffers[source]
|
|
132
|
-
buf.push(line)
|
|
133
|
-
// cap buffer size to prevent unbounded growth if flushBuffers is delayed
|
|
134
|
-
if (buf.length > MAX_BUFFER_SIZE) {
|
|
135
|
-
buf.splice(0, buf.length - MAX_BUFFER_SIZE)
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function query(opts?: {
|
|
141
|
-
source?: string
|
|
142
|
-
level?: string
|
|
143
|
-
since?: number
|
|
144
|
-
limit?: number
|
|
145
|
-
}) {
|
|
146
|
-
let result = entries
|
|
147
|
-
// clamp limit to prevent oversized responses
|
|
148
|
-
const limit = Math.min(Math.max(opts?.limit ?? 1000, 1), MAX_QUERY_LIMIT)
|
|
149
|
-
|
|
150
|
-
if (opts?.since) {
|
|
151
|
-
const since = opts.since
|
|
152
|
-
let lo = 0
|
|
153
|
-
let hi = result.length
|
|
154
|
-
while (lo < hi) {
|
|
155
|
-
const mid = (lo + hi) >>> 1
|
|
156
|
-
if (result[mid].id <= since) lo = mid + 1
|
|
157
|
-
else hi = mid
|
|
158
|
-
}
|
|
159
|
-
result = result.slice(lo)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (opts?.source && VALID_SOURCES.has(opts.source)) {
|
|
163
|
-
const source = opts.source
|
|
164
|
-
result = result.filter((e) => e.source === source)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (opts?.level && VALID_LEVELS.has(opts.level)) {
|
|
168
|
-
const maxPriority = LEVEL_PRIORITY[opts.level] ?? 3
|
|
169
|
-
result = result.filter((e) => (LEVEL_PRIORITY[e.level] ?? 3) <= maxPriority)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// limit results to prevent UI slowdown
|
|
173
|
-
if (result.length > limit) {
|
|
174
|
-
result = result.slice(-limit)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
entries: result,
|
|
179
|
-
cursor: entries.length > 0 ? entries[entries.length - 1].id : 0,
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function getAll() {
|
|
184
|
-
return [...entries]
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function clear() {
|
|
188
|
-
entries.length = 0
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return { push, query, getAll, clear }
|
|
192
|
-
}
|