@getworkbench/core 0.4.0 → 0.5.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/{chunk-O7DNWUZD.js → chunk-4GN4APH7.js} +13 -1
- package/dist/chunk-4GN4APH7.js.map +1 -0
- package/dist/hono.d.ts +1 -1
- package/dist/hono.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +57 -3
- package/dist/index.js.map +1 -1
- package/dist/ui/assets/index.css +1 -1
- package/dist/ui/assets/index.js +101 -101
- package/dist/{workbench-Btnvit1t.d.ts → workbench-CvJrF8Cl.d.ts} +22 -0
- package/package.json +1 -1
- package/dist/chunk-O7DNWUZD.js.map +0 -1
|
@@ -226,6 +226,18 @@ function buildRouteTable(core) {
|
|
|
226
226
|
return { status: 200, body: { success: true } };
|
|
227
227
|
}
|
|
228
228
|
},
|
|
229
|
+
{
|
|
230
|
+
method: "post",
|
|
231
|
+
path: "/schedulers/:queue/:key/run",
|
|
232
|
+
handler: async ({ params }) => {
|
|
233
|
+
if (isReadonly()) return readonlyError;
|
|
234
|
+
const result = await qm.runSchedulerNow(params.queue, params.key);
|
|
235
|
+
if (!result) {
|
|
236
|
+
return { status: 400, body: { error: "Failed to run scheduler" } };
|
|
237
|
+
}
|
|
238
|
+
return { status: 200, body: result };
|
|
239
|
+
}
|
|
240
|
+
},
|
|
229
241
|
{
|
|
230
242
|
method: "get",
|
|
231
243
|
path: "/search",
|
|
@@ -568,4 +580,4 @@ export {
|
|
|
568
580
|
renderIndexHtml,
|
|
569
581
|
buildWorkbenchApp
|
|
570
582
|
};
|
|
571
|
-
//# sourceMappingURL=chunk-
|
|
583
|
+
//# sourceMappingURL=chunk-4GN4APH7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/handlers.ts","../src/api/router.ts","../src/server/base-path.ts","../src/ui-dist.ts","../src/server/static-assets.ts","../src/server/hono-app.ts"],"sourcesContent":["import type {\n CreateFlowRequest,\n JobStatus,\n SortOptions,\n TestJobRequest,\n} from \"../core/types\";\nimport type { WorkbenchCore } from \"../core/workbench\";\n\n/**\n * Framework-agnostic HTTP method.\n */\nexport type HttpMethod = \"get\" | \"post\" | \"put\" | \"patch\" | \"delete\";\n\n/**\n * Normalized input passed to every handler. Adapters are responsible for\n * mapping their framework-specific request shape to this.\n */\nexport interface HandlerInput {\n params: Record<string, string>;\n query: Record<string, string | undefined>;\n body?: unknown;\n}\n\n/**\n * Normalized output returned by every handler. Adapters serialize this\n * onto their framework-specific response object.\n */\nexport interface HandlerResult {\n status: number;\n body: unknown;\n}\n\n/**\n * A framework-agnostic route handler. Closes over a `WorkbenchCore` and\n * takes a normalized request envelope.\n */\nexport type Handler = (input: HandlerInput) => Promise<HandlerResult>;\n\n/**\n * A framework-agnostic route definition.\n *\n * `path` uses `:param` syntax compatible with Hono, Express, and Fastify.\n * Paths are relative to `/api` — adapters mount them under that prefix.\n */\nexport interface RouteDef {\n method: HttpMethod;\n path: string;\n handler: Handler;\n}\n\n/**\n * Parse sort query param in format \"field:direction\" (e.g., \"timestamp:desc\")\n * Defaults to desc if direction not specified.\n */\nfunction parseSort(sort?: string): SortOptions | undefined {\n if (!sort) return undefined;\n const [field, dir] = sort.split(\":\");\n if (!field) return undefined;\n return {\n field,\n direction: dir === \"asc\" ? \"asc\" : \"desc\",\n };\n}\n\nconst readonlyError = {\n status: 403 as const,\n body: { error: \"Dashboard is in readonly mode\" },\n};\n\n/**\n * Build the framework-agnostic route table for the Workbench API.\n *\n * Adapters iterate this list and register each route on their host framework.\n * Paths are relative to `/api`.\n */\nexport function buildRouteTable(core: WorkbenchCore): RouteDef[] {\n const qm = core.queueManager;\n const isReadonly = () => !!core.options.readonly;\n\n return [\n {\n method: \"post\",\n path: \"/refresh\",\n handler: async () => {\n qm.clearCache();\n return { status: 200, body: { success: true } };\n },\n },\n\n {\n method: \"get\",\n path: \"/overview\",\n handler: async () => ({\n status: 200,\n body: await qm.getOverview(),\n }),\n },\n\n {\n method: \"get\",\n path: \"/counts\",\n handler: async () => ({\n status: 200,\n body: await qm.getQuickCounts(),\n }),\n },\n\n {\n method: \"get\",\n path: \"/runs\",\n handler: async ({ query }) => {\n const limit = Number(query.limit) || 50;\n const cursor = query.cursor;\n const start = cursor ? Number(cursor) : 0;\n const sort = parseSort(query.sort);\n\n const status = query.status as JobStatus | undefined;\n const q = query.q;\n const from = query.from;\n const to = query.to;\n const tagsParam = query.tags;\n\n let tags: Record<string, string> | undefined;\n if (tagsParam) {\n try {\n tags = JSON.parse(tagsParam);\n } catch {\n const tagPairs = tagsParam.split(\",\");\n tags = {};\n for (const pair of tagPairs) {\n const [key, value] = pair.split(\":\");\n if (key && value) {\n tags[key.trim()] = value.trim();\n }\n }\n }\n }\n\n let timeRange: { start: number; end: number } | undefined;\n if (from && to) {\n timeRange = {\n start: Number(from),\n end: Number(to),\n };\n }\n\n let text: string | undefined;\n if (q) {\n if (!q.includes(\":\")) {\n text = q;\n } else {\n const parts = q.split(\" \");\n const textParts = parts.filter((p) => !p.includes(\":\"));\n if (textParts.length > 0) {\n text = textParts.join(\" \");\n }\n }\n }\n\n const filters =\n status || tags || text || timeRange\n ? {\n status,\n tags,\n text,\n timeRange,\n }\n : undefined;\n\n return {\n status: 200,\n body: await qm.getAllRuns(limit, start, sort, filters),\n };\n },\n },\n\n {\n method: \"get\",\n path: \"/schedulers\",\n handler: async ({ query }) => {\n const repeatableSort = parseSort(query.repeatableSort);\n const delayedSort = parseSort(query.delayedSort);\n return {\n status: 200,\n body: await qm.getSchedulers(repeatableSort, delayedSort),\n };\n },\n },\n\n {\n method: \"post\",\n path: \"/test\",\n handler: async ({ body }) => {\n if (isReadonly()) return readonlyError;\n const req = body as TestJobRequest | undefined;\n\n if (!req?.queueName || !req.jobName) {\n return {\n status: 400,\n body: { error: \"queueName and jobName are required\" },\n };\n }\n\n try {\n const result = await qm.enqueueJob(req);\n return { status: 200, body: result };\n } catch (e) {\n return { status: 400, body: { error: (e as Error).message } };\n }\n },\n },\n\n {\n method: \"get\",\n path: \"/queue-names\",\n handler: async () => ({\n status: 200,\n body: qm.getQueueNames(),\n }),\n },\n\n {\n method: \"get\",\n path: \"/queues\",\n handler: async () => ({\n status: 200,\n body: await qm.getQueues(),\n }),\n },\n\n {\n method: \"get\",\n path: \"/metrics\",\n handler: async () => ({\n status: 200,\n body: await qm.getMetrics(),\n }),\n },\n\n {\n method: \"get\",\n path: \"/activity\",\n handler: async () => ({\n status: 200,\n body: await qm.getActivityStats(),\n }),\n },\n\n {\n method: \"get\",\n path: \"/queues/:name/jobs\",\n handler: async ({ params, query }) => {\n const name = params.name!;\n const status = query.status as JobStatus | undefined;\n const limit = Number(query.limit) || 50;\n const cursor = query.cursor;\n const start = cursor ? Number(cursor) : 0;\n const sort = parseSort(query.sort);\n\n return {\n status: 200,\n body: await qm.getJobs(name, status, limit, start, sort),\n };\n },\n },\n\n {\n method: \"get\",\n path: \"/jobs/:queue/:id\",\n handler: async ({ params }) => {\n const job = await qm.getJob(params.queue!, params.id!);\n if (!job) {\n return { status: 404, body: { error: \"Job not found\" } };\n }\n return { status: 200, body: job };\n },\n },\n\n {\n method: \"post\",\n path: \"/jobs/:queue/:id/retry\",\n handler: async ({ params }) => {\n if (isReadonly()) return readonlyError;\n const success = await qm.retryJob(params.queue!, params.id!);\n if (!success) {\n return { status: 400, body: { error: \"Failed to retry job\" } };\n }\n return { status: 200, body: { success: true } };\n },\n },\n\n {\n method: \"post\",\n path: \"/jobs/:queue/:id/remove\",\n handler: async ({ params }) => {\n if (isReadonly()) return readonlyError;\n const success = await qm.removeJob(params.queue!, params.id!);\n if (!success) {\n return { status: 400, body: { error: \"Failed to remove job\" } };\n }\n return { status: 200, body: { success: true } };\n },\n },\n\n {\n method: \"post\",\n path: \"/jobs/:queue/:id/promote\",\n handler: async ({ params }) => {\n if (isReadonly()) return readonlyError;\n const success = await qm.promoteJob(params.queue!, params.id!);\n if (!success) {\n return { status: 400, body: { error: \"Failed to promote job\" } };\n }\n return { status: 200, body: { success: true } };\n },\n },\n\n {\n method: \"post\",\n path: \"/schedulers/:queue/:key/run\",\n handler: async ({ params }) => {\n if (isReadonly()) return readonlyError;\n const result = await qm.runSchedulerNow(params.queue!, params.key!);\n if (!result) {\n return { status: 400, body: { error: \"Failed to run scheduler\" } };\n }\n return { status: 200, body: result };\n },\n },\n\n {\n method: \"get\",\n path: \"/search\",\n handler: async ({ query }) => {\n const q = query.q || \"\";\n const limit = Number(query.limit) || 20;\n if (!q) return { status: 200, body: { results: [] } };\n const results = await qm.search(q, limit);\n return { status: 200, body: { results } };\n },\n },\n\n {\n method: \"get\",\n path: \"/tags/:field/values\",\n handler: async ({ params, query }) => {\n const field = params.field!;\n const limit = Number(query.limit) || 50;\n\n const tagFields = qm.getTagFields();\n if (tagFields.length > 0 && !tagFields.includes(field)) {\n return {\n status: 400,\n body: {\n error: `Field \"${field}\" is not a configured tag field`,\n },\n };\n }\n\n const values = await qm.getTagValues(field, limit);\n return { status: 200, body: { field, values } };\n },\n },\n\n {\n method: \"post\",\n path: \"/queues/:name/clean\",\n handler: async ({ params, body }) => {\n if (isReadonly()) return readonlyError;\n const req = body as\n | { status: \"completed\" | \"failed\"; grace?: number }\n | undefined;\n if (!req) {\n return { status: 400, body: { error: \"Body required\" } };\n }\n const count = await qm.cleanJobs(\n params.name!,\n req.status,\n req.grace || 0,\n );\n return { status: 200, body: { removed: count } };\n },\n },\n\n {\n method: \"post\",\n path: \"/bulk/retry\",\n handler: async ({ body }) => {\n if (isReadonly()) return readonlyError;\n const req = body as\n | { jobs: { queueName: string; jobId: string }[] }\n | undefined;\n if (!req?.jobs) {\n return { status: 400, body: { error: \"jobs is required\" } };\n }\n return { status: 200, body: await qm.bulkRetry(req.jobs) };\n },\n },\n\n {\n method: \"post\",\n path: \"/bulk/delete\",\n handler: async ({ body }) => {\n if (isReadonly()) return readonlyError;\n const req = body as\n | { jobs: { queueName: string; jobId: string }[] }\n | undefined;\n if (!req?.jobs) {\n return { status: 400, body: { error: \"jobs is required\" } };\n }\n return { status: 200, body: await qm.bulkDelete(req.jobs) };\n },\n },\n\n {\n method: \"post\",\n path: \"/bulk/promote\",\n handler: async ({ body }) => {\n if (isReadonly()) return readonlyError;\n const req = body as\n | { jobs: { queueName: string; jobId: string }[] }\n | undefined;\n if (!req?.jobs) {\n return { status: 400, body: { error: \"jobs is required\" } };\n }\n return { status: 200, body: await qm.bulkPromote(req.jobs) };\n },\n },\n\n {\n method: \"post\",\n path: \"/queues/:name/pause\",\n handler: async ({ params }) => {\n if (isReadonly()) return readonlyError;\n try {\n await qm.pauseQueue(params.name!);\n return { status: 200, body: { success: true, paused: true } };\n } catch (error) {\n return {\n status: 404,\n body: {\n error:\n error instanceof Error\n ? error.message\n : \"Failed to pause queue\",\n },\n };\n }\n },\n },\n\n {\n method: \"post\",\n path: \"/queues/:name/resume\",\n handler: async ({ params }) => {\n if (isReadonly()) return readonlyError;\n try {\n await qm.resumeQueue(params.name!);\n return { status: 200, body: { success: true, paused: false } };\n } catch (error) {\n return {\n status: 404,\n body: {\n error:\n error instanceof Error\n ? error.message\n : \"Failed to resume queue\",\n },\n };\n }\n },\n },\n\n {\n method: \"get\",\n path: \"/flows\",\n handler: async ({ query }) => {\n const limit = Number(query.limit) || 50;\n const flows = await qm.getFlows(limit);\n return { status: 200, body: { flows } };\n },\n },\n\n {\n method: \"get\",\n path: \"/flows/:queueName/:jobId\",\n handler: async ({ params }) => {\n const flow = await qm.getFlow(params.queueName!, params.jobId!);\n if (!flow) {\n return { status: 404, body: { error: \"Flow not found\" } };\n }\n return { status: 200, body: flow };\n },\n },\n\n {\n method: \"post\",\n path: \"/flows\",\n handler: async ({ body }) => {\n if (isReadonly()) return readonlyError;\n const req = body as CreateFlowRequest | undefined;\n\n if (!req?.name || !req.queueName || !req.children?.length) {\n return {\n status: 400,\n body: { error: \"name, queueName, and children are required\" },\n };\n }\n\n try {\n const result = await qm.createFlow(req);\n return { status: 200, body: result };\n } catch (e) {\n return { status: 400, body: { error: (e as Error).message } };\n }\n },\n },\n ];\n}\n","import { Hono } from \"hono\";\nimport type { WorkbenchCore } from \"../core/workbench\";\nimport { buildRouteTable, type HandlerInput } from \"./handlers\";\n\n/**\n * Create API routes for Workbench as a Hono app.\n *\n * Iterates the framework-agnostic `buildRouteTable(core)` and registers\n * each route on a fresh Hono instance. Adapters that don't speak Hono can\n * use `buildRouteTable` directly — see `@getworkbench/express` and\n * `@getworkbench/fastify`.\n */\nexport function createApiRoutes(core: WorkbenchCore): Hono {\n const app = new Hono();\n\n for (const route of buildRouteTable(core)) {\n app[route.method](route.path, async (c) => {\n let body: unknown;\n const method = c.req.method;\n if (method !== \"GET\" && method !== \"HEAD\") {\n const contentType = c.req.header(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n try {\n body = await c.req.json();\n } catch {\n body = undefined;\n }\n }\n }\n\n const input: HandlerInput = {\n params: c.req.param() as Record<string, string>,\n query: c.req.query(),\n body,\n };\n\n const result = await route.handler(input);\n return new Response(JSON.stringify(result.body), {\n status: result.status,\n headers: { \"Content-Type\": \"application/json\" },\n });\n });\n }\n\n return app;\n}\n","/**\n * Compute the dashboard's base path from an incoming request URL.\n *\n * The dashboard is mounted at some prefix (e.g. `/jobs`) and the client\n * router renders deep links like `/jobs/queues/email`, `/jobs/metrics`,\n * `/jobs/flows/email/123`, etc. The HTML `<base href>` needs to point at\n * the mount prefix so client-side asset URLs resolve correctly — strip\n * any client-side route segment from the pathname.\n */\nconst CLIENT_ROUTES: RegExp[] = [\n /\\/queues\\/[^/]+\\/jobs\\/[^/]+\\/?$/,\n /\\/queues\\/[^/]+\\/?$/,\n /\\/flows\\/[^/]+\\/[^/]+\\/?$/,\n /\\/schedulers\\/?$/,\n /\\/flows\\/?$/,\n /\\/metrics\\/?$/,\n /\\/test\\/?$/,\n];\n\nexport function computeBasePath(pathname: string): string {\n let basePath = pathname;\n for (const route of CLIENT_ROUTES) {\n basePath = basePath.replace(route, \"\");\n }\n if (!basePath.endsWith(\"/\")) {\n basePath = `${basePath}/`;\n }\n return basePath;\n}\n\n/**\n * Resolve the dashboard's base path, preferring an explicit override.\n *\n * Adapters where the host framework preserves the mount prefix on the\n * incoming URL (Hono `.route()`, Express `req.originalUrl`, Next.js route\n * files) can rely on auto-detection. Adapters where the prefix is stripped\n * before the handler runs (Elysia `.mount()`) require the user to pass\n * `basePath` so the dashboard's HTML still references assets under the\n * correct prefix.\n */\nexport function resolveBasePath(\n override: string | undefined,\n pathname: string,\n): string {\n if (override) {\n return override.endsWith(\"/\") ? override : `${override}/`;\n }\n return computeBasePath(pathname);\n}\n","import { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Absolute filesystem path to the bundled UI assets (index.html + /assets).\n * Adapters that don't go through {@link createFetchHandler} serve static\n * files from this directory directly.\n */\nexport const UI_DIST_PATH = join(dirname(fileURLToPath(import.meta.url)), \"ui\");\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { UI_DIST_PATH } from \"../ui-dist\";\n\nexport interface StaticAssetResult {\n status: 200 | 404;\n body: Buffer | null;\n contentType: string;\n}\n\n/**\n * Read a bundled UI asset from `UI_DIST_PATH/assets/<filename>`.\n *\n * Returns a uniform `{ status, body, contentType }` shape so each adapter\n * can serialize it onto its framework-native response without re-implementing\n * the file lookup or content-type sniffing.\n */\nexport function serveStaticAsset(filename: string): StaticAssetResult {\n const filePath = join(UI_DIST_PATH, \"assets\", filename);\n\n if (!existsSync(filePath)) {\n return { status: 404, body: null, contentType: \"text/plain\" };\n }\n\n const body = readFileSync(filePath);\n const contentType = filename.endsWith(\".js\")\n ? \"application/javascript\"\n : filename.endsWith(\".css\")\n ? \"text/css\"\n : filename.endsWith(\".svg\")\n ? \"image/svg+xml\"\n : filename.endsWith(\".png\")\n ? \"image/png\"\n : filename.endsWith(\".woff2\")\n ? \"font/woff2\"\n : \"application/octet-stream\";\n\n return { status: 200, body, contentType };\n}\n\nexport interface IndexHtmlResult {\n body: string;\n contentType: \"text/html; charset=utf-8\";\n}\n\n/**\n * Read the bundled `index.html`, inject a `<base href>` matching the request's\n * mount path so client-side asset URLs resolve correctly, and return it.\n *\n * Falls back to a tiny \"UI assets not found\" stub when the core package has\n * not been built yet — useful for `bun run dev` against a fresh checkout.\n */\nexport function renderIndexHtml(\n basePath: string,\n title: string,\n): IndexHtmlResult {\n const indexPath = join(UI_DIST_PATH, \"index.html\");\n\n if (existsSync(indexPath)) {\n let html = readFileSync(indexPath, \"utf-8\");\n html = html.replace(\"<head>\", `<head>\\n <base href=\"${basePath}\">`);\n return { body: html, contentType: \"text/html; charset=utf-8\" };\n }\n\n return {\n body: fallbackHtml(title, basePath),\n contentType: \"text/html; charset=utf-8\",\n };\n}\n\nfunction fallbackHtml(title: string, basePath: string): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <base href=\"${basePath}\">\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${title}</title>\n <style>\n body {\n font-family: system-ui, sans-serif;\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 100vh;\n margin: 0;\n background: #0a0a0a;\n color: #fafafa;\n }\n .message {\n text-align: center;\n padding: 2rem;\n }\n code {\n background: #1a1a1a;\n padding: 0.5rem 1rem;\n border-radius: 0.5rem;\n display: block;\n margin-top: 1rem;\n }\n </style>\n </head>\n <body>\n <div class=\"message\">\n <h1>${title}</h1>\n <p>UI assets not found. Build @getworkbench/core first:</p>\n <code>bun run --filter=@getworkbench/core build</code>\n </div>\n </body>\n</html>`;\n}\n","import { Hono } from \"hono\";\nimport { basicAuth } from \"hono/basic-auth\";\nimport { cors } from \"hono/cors\";\nimport { createApiRoutes } from \"../api/router\";\nimport type { WorkbenchCore } from \"../core/workbench\";\nimport { resolveBasePath } from \"./base-path\";\nimport { renderIndexHtml, serveStaticAsset } from \"./static-assets\";\n\n/**\n * Build a fully-wired Hono app for Workbench:\n *\n * - `POST /api/*`, `GET /api/*` etc. — JSON API\n * - `GET /config` — UI bootstrap config\n * - `GET /assets/:file` — static asset reader\n * - `GET *` — `index.html` with `<base href>`\n * - CORS on `/api/*`\n * - Basic auth on everything when `core.requiresAuth()` is true\n *\n * Used directly by `@getworkbench/hono` (returned as-is for `.route()`\n * mounting) and indirectly by `createFetchHandler` for non-Hono adapters.\n */\nexport function buildWorkbenchApp(core: WorkbenchCore): Hono {\n const app = new Hono();\n\n app.use(\"/api/*\", cors());\n\n if (core.requiresAuth()) {\n app.use(\n \"*\",\n basicAuth({\n username: core.options.auth!.username,\n password: core.options.auth!.password,\n }),\n );\n }\n\n app.route(\"/api\", createApiRoutes(core));\n\n app.get(\"/config\", (c) => c.json(core.getConfig()));\n\n app.get(\"/assets/:file\", (c) => {\n const fileName = c.req.param(\"file\");\n const asset = serveStaticAsset(fileName);\n if (asset.status === 404 || !asset.body) {\n return c.text(\"Not found\", 404);\n }\n return new Response(new Uint8Array(asset.body), {\n status: 200,\n headers: { \"Content-Type\": asset.contentType },\n });\n });\n\n app.get(\"*\", (c) => {\n const url = new URL(c.req.url);\n const basePath = resolveBasePath(core.options.basePath, url.pathname);\n const html = renderIndexHtml(basePath, core.options.title || \"Workbench\");\n return c.html(html.body);\n });\n\n return app;\n}\n"],"mappings":"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","names":["join","join","Hono","Hono"]}
|
package/dist/hono.d.ts
CHANGED
package/dist/hono.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Queue, RedisOptions } from 'bullmq';
|
|
2
|
-
import { W as WorkbenchCore, r as WorkbenchOptions } from './workbench-
|
|
3
|
-
export { A as ActivityBucket, a as ActivityStatsResponse, C as CreateFlowChildRequest, b as CreateFlowRequest, D as DelayedJobInfo, c as DelayedSortField, d as DiscoveryMeta, F as FailingJobType, e as FlowNode, f as FlowSummary, H as HourlyBucket, J as JobInfo, g as JobStatus, h as JobTags, M as MetricsResponse, O as OverviewStats, P as PaginatedResponse, Q as QueueInfo, i as QueueManager, j as QueueMetrics, R as RepeatableSortField, k as RunInfo, l as RunInfoList, m as RunSortField, S as SchedulerInfo, n as SearchResult, o as SlowestJob, p as SortDirection, q as SortOptions, T as TestJobRequest, s as WorkerInfo } from './workbench-
|
|
2
|
+
import { W as WorkbenchCore, r as WorkbenchOptions } from './workbench-CvJrF8Cl.js';
|
|
3
|
+
export { A as ActivityBucket, a as ActivityStatsResponse, C as CreateFlowChildRequest, b as CreateFlowRequest, D as DelayedJobInfo, c as DelayedSortField, d as DiscoveryMeta, F as FailingJobType, e as FlowNode, f as FlowSummary, H as HourlyBucket, J as JobInfo, g as JobStatus, h as JobTags, M as MetricsResponse, O as OverviewStats, P as PaginatedResponse, Q as QueueInfo, i as QueueManager, j as QueueMetrics, R as RepeatableSortField, k as RunInfo, l as RunInfoList, m as RunSortField, S as SchedulerInfo, n as SearchResult, o as SlowestJob, p as SortDirection, q as SortOptions, T as TestJobRequest, s as WorkerInfo } from './workbench-CvJrF8Cl.js';
|
|
4
4
|
|
|
5
5
|
interface FetchHandlerResult {
|
|
6
6
|
/**
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
renderIndexHtml,
|
|
7
7
|
resolveBasePath,
|
|
8
8
|
serveStaticAsset
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-4GN4APH7.js";
|
|
10
10
|
|
|
11
11
|
// src/core/discover.ts
|
|
12
12
|
import { Queue } from "bullmq";
|
|
@@ -1228,7 +1228,9 @@ var QueueManager = class {
|
|
|
1228
1228
|
const results = await Promise.all(
|
|
1229
1229
|
queueEntries.map(async ([queueName, queue]) => {
|
|
1230
1230
|
const [repeatableJobs, delayedJobs] = await Promise.all([
|
|
1231
|
-
|
|
1231
|
+
// getJobSchedulers replaces the deprecated getRepeatableJobs (removed
|
|
1232
|
+
// in BullMQ v6) and exposes the scheduler id + template "Run now" needs.
|
|
1233
|
+
queue.getJobSchedulers(),
|
|
1232
1234
|
queue.getJobs("delayed", 0, 50)
|
|
1233
1235
|
]);
|
|
1234
1236
|
return { queueName, repeatableJobs, delayedJobs };
|
|
@@ -1241,13 +1243,14 @@ var QueueManager = class {
|
|
|
1241
1243
|
name: job.name || "unnamed",
|
|
1242
1244
|
queueName,
|
|
1243
1245
|
pattern: job.pattern ?? void 0,
|
|
1244
|
-
every: job.every
|
|
1246
|
+
every: job.every ?? void 0,
|
|
1245
1247
|
next: job.next ?? void 0,
|
|
1246
1248
|
endDate: job.endDate ?? void 0,
|
|
1247
1249
|
tz: job.tz ?? void 0
|
|
1248
1250
|
});
|
|
1249
1251
|
}
|
|
1250
1252
|
for (const job of delayedJobs) {
|
|
1253
|
+
if (job.repeatJobKey) continue;
|
|
1251
1254
|
const delay = job.opts.delay || 0;
|
|
1252
1255
|
delayed.push({
|
|
1253
1256
|
id: job.id || "",
|
|
@@ -1294,6 +1297,57 @@ var QueueManager = class {
|
|
|
1294
1297
|
});
|
|
1295
1298
|
return { id: job.id || "" };
|
|
1296
1299
|
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Trigger an immediate, one-off run of a repeatable job scheduler.
|
|
1302
|
+
*
|
|
1303
|
+
* Enqueues a clone of the scheduler's job (name + data + opts) so the run
|
|
1304
|
+
* behaves like a scheduled execution, but as a standalone job. The repeat
|
|
1305
|
+
* schedule is left untouched, and scheduling internals are stripped so it
|
|
1306
|
+
* runs now and never collides with the deterministic ids of scheduled
|
|
1307
|
+
* iterations.
|
|
1308
|
+
*
|
|
1309
|
+
* `schedulerKey` is the scheduler's `key` from {@link getSchedulers} (the id
|
|
1310
|
+
* passed to upsertJobScheduler). We resolve it via getJobSchedulers rather
|
|
1311
|
+
* than the singular getJobScheduler, which mis-parses some keys.
|
|
1312
|
+
*
|
|
1313
|
+
* Data/opts source: modern schedulers (upsertJobScheduler) carry a `template`.
|
|
1314
|
+
* Legacy repeatables — `queue.add(name, data, { repeat })` — store NO template,
|
|
1315
|
+
* so we fall back to the next pending iteration (a delayed job tagged with this
|
|
1316
|
+
* scheduler's key), which carries the real payload and options.
|
|
1317
|
+
*/
|
|
1318
|
+
async runSchedulerNow(queueName, schedulerKey) {
|
|
1319
|
+
const queue = this.queues.get(queueName);
|
|
1320
|
+
if (!queue) return null;
|
|
1321
|
+
const scheduler = (await queue.getJobSchedulers()).find(
|
|
1322
|
+
(s) => s.key === schedulerKey
|
|
1323
|
+
);
|
|
1324
|
+
if (!scheduler) return null;
|
|
1325
|
+
let data = scheduler.template?.data;
|
|
1326
|
+
let rawOpts = { ...scheduler.template?.opts };
|
|
1327
|
+
if (data === void 0) {
|
|
1328
|
+
const delayed = await queue.getJobs("delayed", 0, -1);
|
|
1329
|
+
const next = delayed.find((j) => j.repeatJobKey === schedulerKey);
|
|
1330
|
+
if (next) {
|
|
1331
|
+
data = next.data;
|
|
1332
|
+
rawOpts = { ...next.opts };
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
const {
|
|
1336
|
+
repeat: _repeat,
|
|
1337
|
+
jobId: _jobId,
|
|
1338
|
+
delay: _delay,
|
|
1339
|
+
repeatJobKey: _repeatJobKey,
|
|
1340
|
+
prevMillis: _prevMillis,
|
|
1341
|
+
timestamp: _timestamp,
|
|
1342
|
+
...opts
|
|
1343
|
+
} = rawOpts;
|
|
1344
|
+
const job = await queue.add(
|
|
1345
|
+
scheduler.name,
|
|
1346
|
+
data ?? {},
|
|
1347
|
+
opts
|
|
1348
|
+
);
|
|
1349
|
+
return { id: job.id ?? "" };
|
|
1350
|
+
}
|
|
1297
1351
|
/**
|
|
1298
1352
|
* Extract tag values from job data based on configured tag fields
|
|
1299
1353
|
*/
|