lopata 0.5.0 → 0.5.2
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/package.json +1 -1
- package/src/config.ts +1 -45
- package/src/env.ts +0 -3
- package/src/generation-manager.ts +10 -8
- package/src/plugin.ts +19 -1
- package/src/tracing/global.d.ts +50 -0
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -80,8 +80,7 @@ export async function loadConfig(path: string, envName?: string): Promise<Wrangl
|
|
|
80
80
|
if (path.endsWith('.toml')) {
|
|
81
81
|
config = parseTOML(raw) as unknown as WranglerConfig
|
|
82
82
|
} else {
|
|
83
|
-
|
|
84
|
-
config = JSON.parse(stripJsoncComments(raw))
|
|
83
|
+
config = Bun.JSONC.parse(raw) as WranglerConfig
|
|
85
84
|
}
|
|
86
85
|
return applyEnvOverrides(config, envName)
|
|
87
86
|
}
|
|
@@ -120,46 +119,3 @@ function applyEnvOverrides(config: WranglerConfig, envName?: string): WranglerCo
|
|
|
120
119
|
}
|
|
121
120
|
return merged
|
|
122
121
|
}
|
|
123
|
-
|
|
124
|
-
// ─── JSONC Comment Stripping ───────────────────────────────────────────────
|
|
125
|
-
|
|
126
|
-
function stripJsoncComments(input: string): string {
|
|
127
|
-
let result = ''
|
|
128
|
-
let i = 0
|
|
129
|
-
while (i < input.length) {
|
|
130
|
-
// String literal — copy as-is
|
|
131
|
-
if (input[i] === '"') {
|
|
132
|
-
result += '"'
|
|
133
|
-
i++
|
|
134
|
-
while (i < input.length && input[i] !== '"') {
|
|
135
|
-
if (input[i] === '\\') {
|
|
136
|
-
result += input[i]! + (input[i + 1] ?? '')
|
|
137
|
-
i += 2
|
|
138
|
-
} else {
|
|
139
|
-
result += input[i]!
|
|
140
|
-
i++
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
if (i < input.length) {
|
|
144
|
-
result += '"'
|
|
145
|
-
i++
|
|
146
|
-
}
|
|
147
|
-
continue
|
|
148
|
-
}
|
|
149
|
-
// Single-line comment
|
|
150
|
-
if (input[i] === '/' && input[i + 1] === '/') {
|
|
151
|
-
while (i < input.length && input[i] !== '\n') i++
|
|
152
|
-
continue
|
|
153
|
-
}
|
|
154
|
-
// Block comment
|
|
155
|
-
if (input[i] === '/' && input[i + 1] === '*') {
|
|
156
|
-
i += 2
|
|
157
|
-
while (i < input.length && !(input[i] === '*' && input[i + 1] === '/')) i++
|
|
158
|
-
i += 2
|
|
159
|
-
continue
|
|
160
|
-
}
|
|
161
|
-
result += input[i]!
|
|
162
|
-
i++
|
|
163
|
-
}
|
|
164
|
-
return result
|
|
165
|
-
}
|
package/src/env.ts
CHANGED
|
@@ -146,10 +146,7 @@ export class GenerationManager {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
private async _doReload(): Promise<Generation> {
|
|
149
|
-
// 1.
|
|
150
|
-
const workerModule = await import(`${this.workerPath}?v=${Date.now()}`)
|
|
151
|
-
|
|
152
|
-
// 1b. Configure executor factory with module/config paths (for isolated mode)
|
|
149
|
+
// 1. Configure executor factory with module/config paths (for isolated mode)
|
|
153
150
|
if (this.executorFactory && 'configure' in this.executorFactory) {
|
|
154
151
|
;(this.executorFactory as any).configure(this.workerPath, this._configPath)
|
|
155
152
|
}
|
|
@@ -157,14 +154,19 @@ export class GenerationManager {
|
|
|
157
154
|
// 2. Build new env with fresh binding instances (same underlying DB)
|
|
158
155
|
const { env, registry } = buildEnv(this.config, this.baseDir, this.executorFactory, this.browserConfig)
|
|
159
156
|
|
|
160
|
-
// 3.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// 4. Update globalEnv for cloudflare:workers env export (main worker only)
|
|
157
|
+
// 3. Update globalEnv BEFORE importing the worker module so that
|
|
158
|
+
// top-level `import { env } from "cloudflare:workers"` sees bindings
|
|
159
|
+
// during module evaluation (main worker only).
|
|
164
160
|
if (this.isMain) {
|
|
165
161
|
setGlobalEnv(env)
|
|
166
162
|
}
|
|
167
163
|
|
|
164
|
+
// 4. Import fresh worker module using cache-busting query string
|
|
165
|
+
const workerModule = await import(`${this.workerPath}?v=${Date.now()}`)
|
|
166
|
+
|
|
167
|
+
// 5. Wire DO and Workflow class references
|
|
168
|
+
wireClassRefs(registry, workerModule, env, this.workerRegistry)
|
|
169
|
+
|
|
168
170
|
// 5. Validate default export (or service worker fetch handler)
|
|
169
171
|
const defaultExport = workerModule.default
|
|
170
172
|
const classBasedExport = isEntrypointClass(defaultExport)
|
package/src/plugin.ts
CHANGED
|
@@ -15,7 +15,21 @@ import { globalEnv } from './env'
|
|
|
15
15
|
import { getActiveExecutionContext } from './execution-context'
|
|
16
16
|
import { getActiveContext } from './tracing/context'
|
|
17
17
|
import { instrumentBinding } from './tracing/instrument'
|
|
18
|
-
import { addSpanEvent, setSpanAttribute, startSpan } from './tracing/span'
|
|
18
|
+
import { addSpanEvent, persistError, setSpanAttribute, startSpan } from './tracing/span' // ─── Userland tracing API ────────────────────────────────────────────
|
|
19
|
+
// Exposes a lightweight global that user code can call to create custom
|
|
20
|
+
// spans visible in the Lopata dashboard. In production (without Lopata)
|
|
21
|
+
// the global is simply absent, so the user's thin wrapper becomes a no-op.
|
|
22
|
+
;(globalThis as any).__lopata = {
|
|
23
|
+
trace<T>(name: string, attrsOrFn: Record<string, unknown> | (() => T | Promise<T>), maybeFn?: () => T | Promise<T>): Promise<T> {
|
|
24
|
+
const fn = typeof attrsOrFn === 'function' ? attrsOrFn : maybeFn!
|
|
25
|
+
const attributes = typeof attrsOrFn === 'function' ? undefined : attrsOrFn
|
|
26
|
+
return startSpan({ name, attributes }, fn)
|
|
27
|
+
},
|
|
28
|
+
setAttribute: setSpanAttribute,
|
|
29
|
+
addEvent(name: string, message?: string, attrs?: Record<string, unknown>): void {
|
|
30
|
+
addSpanEvent(name, 'info', message ?? '', attrs)
|
|
31
|
+
},
|
|
32
|
+
}
|
|
19
33
|
|
|
20
34
|
// Register global `caches` object (CacheStorage) with tracing
|
|
21
35
|
const rawCacheStorage = new SqliteCacheStorage(getDatabase())
|
|
@@ -151,6 +165,10 @@ for (const method of consoleMethods) {
|
|
|
151
165
|
if (!ctx) return
|
|
152
166
|
const message = args.map(formatConsoleArg).join(' ')
|
|
153
167
|
addSpanEvent(`console.${method}`, method, message)
|
|
168
|
+
if (method === 'error') {
|
|
169
|
+
const errorArg = args.find((a) => a instanceof Error)
|
|
170
|
+
persistError(errorArg ?? new Error(message), 'console.error')
|
|
171
|
+
}
|
|
154
172
|
}
|
|
155
173
|
}
|
|
156
174
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lopata userland tracing API.
|
|
3
|
+
*
|
|
4
|
+
* Available on `globalThis.__lopata` when running under Lopata dev server.
|
|
5
|
+
* In production (Cloudflare Workers) the global is `undefined` — wrap calls
|
|
6
|
+
* in a thin helper that falls back to a no-op:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // app/lib/trace.ts
|
|
10
|
+
* type TraceFn = <T>(name: string, fn: () => T | Promise<T>) => Promise<T>
|
|
11
|
+
*
|
|
12
|
+
* export const trace: TraceFn = (name, fn) =>
|
|
13
|
+
* globalThis.__lopata?.trace(name, fn) ?? fn()
|
|
14
|
+
*
|
|
15
|
+
* export const setAttribute = (key: string, value: unknown) =>
|
|
16
|
+
* globalThis.__lopata?.setAttribute(key, value)
|
|
17
|
+
*
|
|
18
|
+
* export const addEvent = (name: string, message?: string) =>
|
|
19
|
+
* globalThis.__lopata?.addEvent(name, message)
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Add this file to your tsconfig types to get autocomplete:
|
|
23
|
+
* ```json
|
|
24
|
+
* { "compilerOptions": { "types": ["lopata/src/tracing/global"] } }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
interface LopataTracing {
|
|
29
|
+
/**
|
|
30
|
+
* Create a traced span around `fn`. The span is visible in the Lopata
|
|
31
|
+
* dashboard and becomes a child of the currently active span (if any).
|
|
32
|
+
*/
|
|
33
|
+
trace<T>(name: string, fn: () => T | Promise<T>): Promise<T>
|
|
34
|
+
/**
|
|
35
|
+
* Create a traced span with custom attributes.
|
|
36
|
+
*/
|
|
37
|
+
trace<T>(name: string, attrs: Record<string, unknown>, fn: () => T | Promise<T>): Promise<T>
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set an attribute on the currently active span.
|
|
41
|
+
*/
|
|
42
|
+
setAttribute(key: string, value: unknown): void
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Add an event (log entry) to the currently active span.
|
|
46
|
+
*/
|
|
47
|
+
addEvent(name: string, message?: string, attrs?: Record<string, unknown>): void
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
declare var __lopata: LopataTracing | undefined
|