lopata 0.8.2 → 0.8.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.
package/package.json
CHANGED
package/src/bindings/workflow.ts
CHANGED
|
@@ -1154,7 +1154,7 @@ export class SqliteWorkflowBinding {
|
|
|
1154
1154
|
;(async () => {
|
|
1155
1155
|
let workflowTraceId: string | undefined
|
|
1156
1156
|
try {
|
|
1157
|
-
const instance = new workflowClass({}, env)
|
|
1157
|
+
const instance = new workflowClass({ waitUntil: () => {} }, env)
|
|
1158
1158
|
const step = new WorkflowStepImpl(abortController.signal, db, id, resolvedLimits, resolvedClock)
|
|
1159
1159
|
const event = { payload: params, timestamp: new Date(createdAt ?? resolvedClock.now()), instanceId: id }
|
|
1160
1160
|
const result = await startSpan({
|
|
@@ -49,7 +49,7 @@ export function configPlugin(envName: string): Plugin {
|
|
|
49
49
|
watch: {
|
|
50
50
|
usePolling: true,
|
|
51
51
|
interval: 500,
|
|
52
|
-
ignored: ['**/.lopata/**', '**/.wrangler/**', '**/.react-router/**'],
|
|
52
|
+
ignored: ['**/.lopata/**', '**/.wrangler/**', '**/.react-router/**', '**/*.tmp.*'],
|
|
53
53
|
},
|
|
54
54
|
},
|
|
55
55
|
environments: {
|
|
@@ -130,6 +130,75 @@ export function devServerPlugin(options: DevServerPluginOptions): Plugin {
|
|
|
130
130
|
return currentModule ?? workerModule
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Dispatch a request through the worker's fetch() handler with tracing
|
|
135
|
+
* and generation tracking. Throws on HMR race conditions so the caller
|
|
136
|
+
* can retry.
|
|
137
|
+
*/
|
|
138
|
+
async function handleWorkerFetch(req: IncomingMessage, res: ServerResponse, next: Function): Promise<void> {
|
|
139
|
+
const activeModule = await ensureWorkerModule()
|
|
140
|
+
const genId = currentGenerationId
|
|
141
|
+
genActiveRequests.set(genId, (genActiveRequests.get(genId) ?? 0) + 1)
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const request = nodeReqToRequest(req)
|
|
145
|
+
const parsedUrl = new URL(request.url)
|
|
146
|
+
|
|
147
|
+
const handler = activeModule.default as Record<string, unknown>
|
|
148
|
+
if (!handler || typeof handler.fetch !== 'function') {
|
|
149
|
+
console.error('[lopata:vite] Worker module default export has no fetch() method')
|
|
150
|
+
next()
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Capture caller stack before entering the worker (for async stack stitching)
|
|
155
|
+
const callerStack = new Error()
|
|
156
|
+
|
|
157
|
+
const ctx = new ExecutionContext()
|
|
158
|
+
const response = await (startSpan as Function)({
|
|
159
|
+
name: `${request.method} ${parsedUrl.pathname}`,
|
|
160
|
+
kind: 'server',
|
|
161
|
+
attributes: { 'http.method': request.method, 'http.url': request.url, 'lopata.generation_id': genId },
|
|
162
|
+
}, () =>
|
|
163
|
+
runWithExecutionContext(ctx, async () => {
|
|
164
|
+
try {
|
|
165
|
+
const resp = await (handler.fetch as Function).call(handler, request, env, ctx) as Response
|
|
166
|
+
;(setSpanAttribute as Function)('http.status_code', resp.status)
|
|
167
|
+
|
|
168
|
+
// Intercept React Router error boundary responses with lopata error page
|
|
169
|
+
const routeError = (globalThis as any).__lopata_routeError
|
|
170
|
+
delete (globalThis as any).__lopata_routeError
|
|
171
|
+
if (routeError) {
|
|
172
|
+
if (routeError instanceof Error) {
|
|
173
|
+
stitchAsyncStack(routeError, callerStack)
|
|
174
|
+
}
|
|
175
|
+
console.error('[lopata:vite] Route error:\n' + (routeError instanceof Error ? routeError.stack : String(routeError)))
|
|
176
|
+
return (renderErrorPage as Function)(routeError, request, env, config)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
ctx._awaitAll().catch(() => {})
|
|
180
|
+
return resp
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if (isHmrRaceError(err)) {
|
|
183
|
+
currentModule = null
|
|
184
|
+
throw err
|
|
185
|
+
}
|
|
186
|
+
if (err instanceof Error) {
|
|
187
|
+
stitchAsyncStack(err, callerStack)
|
|
188
|
+
}
|
|
189
|
+
console.error('[lopata:vite] Request error:\n' + (err instanceof Error ? err.stack : String(err)))
|
|
190
|
+
return (renderErrorPage as Function)(err, request, env, config)
|
|
191
|
+
}
|
|
192
|
+
})) as Response
|
|
193
|
+
|
|
194
|
+
writeResponse(response, res)
|
|
195
|
+
} finally {
|
|
196
|
+
const count = genActiveRequests.get(genId) ?? 1
|
|
197
|
+
if (count <= 1) genActiveRequests.delete(genId)
|
|
198
|
+
else genActiveRequests.set(genId, count - 1)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
133
202
|
return {
|
|
134
203
|
name: 'lopata:dev-server',
|
|
135
204
|
|
|
@@ -356,67 +425,18 @@ export function devServerPlugin(options: DevServerPluginOptions): Plugin {
|
|
|
356
425
|
}
|
|
357
426
|
|
|
358
427
|
try {
|
|
359
|
-
|
|
360
|
-
const genId = currentGenerationId
|
|
361
|
-
genActiveRequests.set(genId, (genActiveRequests.get(genId) ?? 0) + 1)
|
|
362
|
-
|
|
363
|
-
try {
|
|
364
|
-
const request = nodeReqToRequest(req)
|
|
365
|
-
const parsedUrl = new URL(request.url)
|
|
366
|
-
|
|
367
|
-
const handler = activeModule.default as Record<string, unknown>
|
|
368
|
-
if (!handler || typeof handler.fetch !== 'function') {
|
|
369
|
-
console.error('[lopata:vite] Worker module default export has no fetch() method')
|
|
370
|
-
return next()
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Capture caller stack before entering the worker (for async stack stitching)
|
|
374
|
-
const callerStack = new Error()
|
|
375
|
-
|
|
376
|
-
const ctx = new ExecutionContext()
|
|
377
|
-
const response = await (startSpan as Function)({
|
|
378
|
-
name: `${request.method} ${parsedUrl.pathname}`,
|
|
379
|
-
kind: 'server',
|
|
380
|
-
attributes: { 'http.method': request.method, 'http.url': request.url, 'lopata.generation_id': genId },
|
|
381
|
-
}, () =>
|
|
382
|
-
runWithExecutionContext(ctx, async () => {
|
|
383
|
-
try {
|
|
384
|
-
const resp = await (handler.fetch as Function).call(handler, request, env, ctx) as Response
|
|
385
|
-
;(setSpanAttribute as Function)('http.status_code', resp.status)
|
|
386
|
-
|
|
387
|
-
// Intercept React Router error boundary responses with lopata error page
|
|
388
|
-
const routeError = (globalThis as any).__lopata_routeError
|
|
389
|
-
delete (globalThis as any).__lopata_routeError
|
|
390
|
-
if (routeError) {
|
|
391
|
-
if (routeError instanceof Error) {
|
|
392
|
-
stitchAsyncStack(routeError, callerStack)
|
|
393
|
-
}
|
|
394
|
-
console.error('[lopata:vite] Route error:\n' + (routeError instanceof Error ? routeError.stack : String(routeError)))
|
|
395
|
-
return (renderErrorPage as Function)(routeError, request, env, config)
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
ctx._awaitAll().catch(() => {})
|
|
399
|
-
return resp
|
|
400
|
-
} catch (err) {
|
|
401
|
-
if (err instanceof Error) {
|
|
402
|
-
stitchAsyncStack(err, callerStack)
|
|
403
|
-
}
|
|
404
|
-
console.error('[lopata:vite] Request error:\n' + (err instanceof Error ? err.stack : String(err)))
|
|
405
|
-
return (renderErrorPage as Function)(err, request, env, config)
|
|
406
|
-
}
|
|
407
|
-
})) as Response
|
|
408
|
-
|
|
409
|
-
writeResponse(response, res)
|
|
410
|
-
} finally {
|
|
411
|
-
const count = genActiveRequests.get(genId) ?? 1
|
|
412
|
-
if (count <= 1) genActiveRequests.delete(genId)
|
|
413
|
-
else genActiveRequests.set(genId, count - 1)
|
|
414
|
-
}
|
|
428
|
+
await handleWorkerFetch(req, res, next)
|
|
415
429
|
} catch (err) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
430
|
+
if (!isHmrRaceError(err)) {
|
|
431
|
+
writeRequestError(res, err)
|
|
432
|
+
return
|
|
433
|
+
}
|
|
434
|
+
// Retry once after a short delay — module graph may be mid-evaluation during HMR
|
|
435
|
+
await new Promise((resolve) => setTimeout(resolve, 200))
|
|
436
|
+
try {
|
|
437
|
+
await handleWorkerFetch(req, res, next)
|
|
438
|
+
} catch (retryErr) {
|
|
439
|
+
writeRequestError(res, retryErr)
|
|
420
440
|
}
|
|
421
441
|
}
|
|
422
442
|
})
|
|
@@ -622,6 +642,19 @@ export function devServerPlugin(options: DevServerPluginOptions): Plugin {
|
|
|
622
642
|
}
|
|
623
643
|
}
|
|
624
644
|
|
|
645
|
+
/** Detect transient TypeError from Vite module graph being mid-evaluation during HMR */
|
|
646
|
+
function isHmrRaceError(err: unknown): boolean {
|
|
647
|
+
return err instanceof TypeError && err.message.includes('not be null or undefined')
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function writeRequestError(res: ServerResponse, err: unknown): void {
|
|
651
|
+
console.error('[lopata:vite] Request error:', err)
|
|
652
|
+
if (!res.headersSent) {
|
|
653
|
+
res.writeHead(500, { 'content-type': 'text/plain' })
|
|
654
|
+
res.end(err instanceof Error ? err.stack ?? err.message : String(err))
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
625
658
|
function stitchAsyncStack(err: Error, callerError: Error | null): void {
|
|
626
659
|
if (!callerError) return
|
|
627
660
|
if (!err.stack || !callerError.stack) return
|