@spfn/core 0.2.0-beta.42 → 0.2.0-beta.44
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/{boss-DI1r4kTS.d.ts → boss-Cxqc-Oiw.d.ts} +13 -0
- package/dist/event/sse/index.js +31 -9
- package/dist/event/sse/index.js.map +1 -1
- package/dist/job/index.d.ts +2 -2
- package/dist/job/index.js +58 -24
- package/dist/job/index.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +72 -33
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -438,15 +438,28 @@ function createSSEHandler(router, config = {}, tokenManager) {
|
|
|
438
438
|
subject: subject || void 0,
|
|
439
439
|
clientIp: c.req.header("x-forwarded-for") || c.req.header("x-real-ip")
|
|
440
440
|
});
|
|
441
|
+
c.header("X-Accel-Buffering", "no");
|
|
441
442
|
return streamSSE(c, async (stream) => {
|
|
442
443
|
const unsubscribes = [];
|
|
443
444
|
let messageId = 0;
|
|
445
|
+
let connectionDead = false;
|
|
446
|
+
let pingTimer;
|
|
447
|
+
const cleanup = () => {
|
|
448
|
+
if (connectionDead) return;
|
|
449
|
+
connectionDead = true;
|
|
450
|
+
clearInterval(pingTimer);
|
|
451
|
+
unsubscribes.forEach((fn) => fn());
|
|
452
|
+
sseLogger.info("SSE dead connection cleaned up", {
|
|
453
|
+
events: allowedEvents
|
|
454
|
+
});
|
|
455
|
+
};
|
|
444
456
|
for (const eventName of allowedEvents) {
|
|
445
457
|
const eventDef = router.events[eventName];
|
|
446
458
|
if (!eventDef) {
|
|
447
459
|
continue;
|
|
448
460
|
}
|
|
449
461
|
const unsubscribe = eventDef.subscribe((payload) => {
|
|
462
|
+
if (connectionDead) return;
|
|
450
463
|
if (subject && authConfig?.filter?.[eventName]) {
|
|
451
464
|
if (!authConfig.filter[eventName](subject, payload)) {
|
|
452
465
|
return;
|
|
@@ -461,10 +474,17 @@ function createSSEHandler(router, config = {}, tokenManager) {
|
|
|
461
474
|
event: eventName,
|
|
462
475
|
messageId
|
|
463
476
|
});
|
|
464
|
-
|
|
477
|
+
stream.writeSSE({
|
|
465
478
|
id: String(messageId),
|
|
466
479
|
event: eventName,
|
|
467
480
|
data: JSON.stringify(message)
|
|
481
|
+
}).catch((err) => {
|
|
482
|
+
sseLogger.warn("SSE write failed", {
|
|
483
|
+
event: eventName,
|
|
484
|
+
messageId,
|
|
485
|
+
error: err.message
|
|
486
|
+
});
|
|
487
|
+
cleanup();
|
|
468
488
|
});
|
|
469
489
|
});
|
|
470
490
|
unsubscribes.push(unsubscribe);
|
|
@@ -480,21 +500,23 @@ function createSSEHandler(router, config = {}, tokenManager) {
|
|
|
480
500
|
timestamp: Date.now()
|
|
481
501
|
})
|
|
482
502
|
});
|
|
483
|
-
|
|
484
|
-
|
|
503
|
+
pingTimer = setInterval(() => {
|
|
504
|
+
if (connectionDead) return;
|
|
505
|
+
stream.writeSSE({
|
|
485
506
|
event: "ping",
|
|
486
507
|
data: JSON.stringify({ timestamp: Date.now() })
|
|
508
|
+
}).catch((err) => {
|
|
509
|
+
sseLogger.warn("SSE ping failed", {
|
|
510
|
+
error: err.message
|
|
511
|
+
});
|
|
512
|
+
cleanup();
|
|
487
513
|
});
|
|
488
514
|
}, pingInterval);
|
|
489
515
|
const abortSignal = c.req.raw.signal;
|
|
490
|
-
while (!abortSignal.aborted) {
|
|
516
|
+
while (!abortSignal.aborted && !connectionDead) {
|
|
491
517
|
await stream.sleep(pingInterval);
|
|
492
518
|
}
|
|
493
|
-
|
|
494
|
-
unsubscribes.forEach((fn) => fn());
|
|
495
|
-
sseLogger.info("SSE connection closed", {
|
|
496
|
-
events: allowedEvents
|
|
497
|
-
});
|
|
519
|
+
cleanup();
|
|
498
520
|
}, async (err) => {
|
|
499
521
|
sseLogger.error("SSE stream error", {
|
|
500
522
|
error: err.message
|
|
@@ -1285,36 +1307,53 @@ async function registerJobs(router) {
|
|
|
1285
1307
|
async function ensureQueue(boss, queueName) {
|
|
1286
1308
|
await boss.createQueue(queueName);
|
|
1287
1309
|
}
|
|
1310
|
+
async function executeJobHandler(job2, pgBossJob) {
|
|
1311
|
+
jobLogger2.debug(`[Job:${job2.name}] Executing...`, { jobId: pgBossJob.id });
|
|
1312
|
+
const startTime = Date.now();
|
|
1313
|
+
try {
|
|
1314
|
+
if (job2.inputSchema) {
|
|
1315
|
+
await job2.handler(pgBossJob.data);
|
|
1316
|
+
} else {
|
|
1317
|
+
await job2.handler();
|
|
1318
|
+
}
|
|
1319
|
+
const duration = Date.now() - startTime;
|
|
1320
|
+
jobLogger2.info(`[Job:${job2.name}] Completed in ${duration}ms`, {
|
|
1321
|
+
jobId: pgBossJob.id,
|
|
1322
|
+
duration
|
|
1323
|
+
});
|
|
1324
|
+
} catch (error) {
|
|
1325
|
+
const duration = Date.now() - startTime;
|
|
1326
|
+
jobLogger2.error(`[Job:${job2.name}] Failed after ${duration}ms`, {
|
|
1327
|
+
jobId: pgBossJob.id,
|
|
1328
|
+
duration,
|
|
1329
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1330
|
+
});
|
|
1331
|
+
throw error;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1288
1334
|
async function registerWorker(boss, job2, queueName) {
|
|
1289
1335
|
await ensureQueue(boss, queueName);
|
|
1336
|
+
const batchSize = job2.options?.batchSize ?? 1;
|
|
1290
1337
|
await boss.work(
|
|
1291
1338
|
queueName,
|
|
1292
|
-
{ batchSize
|
|
1293
|
-
async (
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
jobId: pgBossJob.id,
|
|
1306
|
-
duration
|
|
1307
|
-
});
|
|
1308
|
-
} catch (error) {
|
|
1309
|
-
const duration = Date.now() - startTime;
|
|
1310
|
-
jobLogger2.error(`[Job:${job2.name}] Failed after ${duration}ms`, {
|
|
1311
|
-
jobId: pgBossJob.id,
|
|
1312
|
-
duration,
|
|
1313
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1314
|
-
});
|
|
1315
|
-
throw error;
|
|
1339
|
+
{ batchSize },
|
|
1340
|
+
async (pgBossJobs) => {
|
|
1341
|
+
if (batchSize <= 1) {
|
|
1342
|
+
await executeJobHandler(job2, pgBossJobs[0]);
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
const results = await Promise.allSettled(
|
|
1346
|
+
pgBossJobs.map((pgBossJob) => executeJobHandler(job2, pgBossJob))
|
|
1347
|
+
);
|
|
1348
|
+
const failedIds = [];
|
|
1349
|
+
for (let i = 0; i < results.length; i++) {
|
|
1350
|
+
if (results[i].status === "rejected") {
|
|
1351
|
+
failedIds.push(pgBossJobs[i].id);
|
|
1316
1352
|
}
|
|
1317
1353
|
}
|
|
1354
|
+
if (failedIds.length > 0) {
|
|
1355
|
+
await boss.fail(queueName, failedIds);
|
|
1356
|
+
}
|
|
1318
1357
|
}
|
|
1319
1358
|
);
|
|
1320
1359
|
}
|