lopata 0.19.2 → 0.20.0
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/types/bindings/analytics-engine-sql.d.ts +107 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/bindings/analytics-engine-sql.ts +1138 -0
- package/src/bindings/do-worker-env.ts +4 -0
- package/src/plugin.ts +37 -0
- package/src/worker-thread/thread-env.ts +4 -0
|
@@ -161,6 +161,10 @@ export function buildWorkerEnv(
|
|
|
161
161
|
// SQLITE_BUSY instead of waiting.
|
|
162
162
|
db.run('PRAGMA busy_timeout=5000')
|
|
163
163
|
runMigrations(db)
|
|
164
|
+
// Expose this thread's DB handle so the fetch patch (plugin.ts) can serve the
|
|
165
|
+
// intercepted Analytics Engine SQL API from this DO worker's data dir.
|
|
166
|
+
const threadGlobals = globalThis as { __lopata_db?: Database }
|
|
167
|
+
threadGlobals.__lopata_db = db
|
|
164
168
|
|
|
165
169
|
const env: Record<string, unknown> = {}
|
|
166
170
|
|
package/src/plugin.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { plugin } from 'bun'
|
|
2
|
+
import type { Database } from 'bun:sqlite'
|
|
3
|
+
import { buildAnalyticsEngineSqlResponse, isAnalyticsEngineSqlUrl, isLocalAnalyticsEngineToken } from './bindings/analytics-engine-sql'
|
|
2
4
|
import type { ImageTransformOptions, OutputOptions } from './bindings/images'
|
|
3
5
|
import { setupCloudflareGlobals } from './setup-globals'
|
|
4
6
|
import { getActiveContext } from './tracing/context'
|
|
@@ -158,6 +160,41 @@ async function applyCfImageTransform(response: Response, imageOpts: Record<strin
|
|
|
158
160
|
|
|
159
161
|
const _originalFetch = globalThis.fetch
|
|
160
162
|
globalThis.fetch = ((input: any, init?: any): Promise<Response> => {
|
|
163
|
+
// Intercept the Cloudflare Analytics Engine SQL API (no Worker binding exists
|
|
164
|
+
// for reads) and serve it from the local SQLite store — same code runs in dev
|
|
165
|
+
// and prod unchanged.
|
|
166
|
+
const aeMethod = String(init?.method ?? (input instanceof Request ? input.method : 'GET')).toUpperCase()
|
|
167
|
+
const aeUrl = typeof input === 'string'
|
|
168
|
+
? input
|
|
169
|
+
: input instanceof URL
|
|
170
|
+
? input.href
|
|
171
|
+
: input instanceof Request
|
|
172
|
+
? input.url
|
|
173
|
+
: undefined
|
|
174
|
+
// Gate on the bearer token: missing or `Bearer local` is served locally; a real
|
|
175
|
+
// token falls through to the actual Cloudflare API (prod).
|
|
176
|
+
const aeAuth = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined)).get('authorization')
|
|
177
|
+
if (aeMethod === 'POST' && typeof aeUrl === 'string' && isAnalyticsEngineSqlUrl(aeUrl) && isLocalAnalyticsEngineToken(aeAuth)) {
|
|
178
|
+
// Set per worker-thread by buildThreadEnv / buildDoWorkerEnv before user code
|
|
179
|
+
// runs. Fail loudly rather than fall back to a possibly-wrong data dir, which
|
|
180
|
+
// would return plausible but incorrect numbers.
|
|
181
|
+
const db = (globalThis as { __lopata_db?: Database }).__lopata_db
|
|
182
|
+
if (!db) throw new Error('Analytics Engine SQL API was intercepted before the worker-thread database was wired')
|
|
183
|
+
const aeReq = new Request(input, init)
|
|
184
|
+
if (!getActiveContext()) return aeReq.text().then(sql => buildAnalyticsEngineSqlResponse(db, sql))
|
|
185
|
+
return startSpan({
|
|
186
|
+
name: 'analytics_engine sql',
|
|
187
|
+
kind: 'client',
|
|
188
|
+
attributes: { 'http.method': 'POST', 'http.url': aeUrl },
|
|
189
|
+
}, async () => {
|
|
190
|
+
const sql = (await aeReq.text()).trim()
|
|
191
|
+
if (sql) setSpanAttribute('db.statement', sql)
|
|
192
|
+
const res = buildAnalyticsEngineSqlResponse(db, sql)
|
|
193
|
+
setSpanAttribute('http.status_code', res.status)
|
|
194
|
+
return res
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
161
198
|
const ctx = getActiveContext()
|
|
162
199
|
if (ctx) {
|
|
163
200
|
ctx.fetchStack.current = new Error()
|
|
@@ -67,6 +67,10 @@ export function buildThreadEnv({ config, baseDir, dataDir, rpc, envWsBridge, bro
|
|
|
67
67
|
db.run('PRAGMA journal_mode=WAL')
|
|
68
68
|
db.run('PRAGMA busy_timeout=5000')
|
|
69
69
|
runMigrations(db)
|
|
70
|
+
// Expose this thread's DB handle so the fetch patch (plugin.ts) can serve the
|
|
71
|
+
// intercepted Analytics Engine SQL API from the right data dir in multi-worker setups.
|
|
72
|
+
const threadGlobals = globalThis as { __lopata_db?: Database }
|
|
73
|
+
threadGlobals.__lopata_db = db
|
|
70
74
|
|
|
71
75
|
const env: Record<string, unknown> = {}
|
|
72
76
|
|