@bull-board/cli 0.0.0 → 9.3.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.
Files changed (57) hide show
  1. package/README.md +69 -0
  2. package/dist/auth.d.ts +5 -0
  3. package/dist/auth.js +29 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/bin.d.ts +2 -0
  6. package/dist/bin.js +69 -0
  7. package/dist/bin.js.map +1 -0
  8. package/dist/config/file.d.ts +9 -0
  9. package/dist/config/file.js +82 -0
  10. package/dist/config/file.js.map +1 -0
  11. package/dist/config/flags.d.ts +77 -0
  12. package/dist/config/flags.js +29 -0
  13. package/dist/config/flags.js.map +1 -0
  14. package/dist/config/resolve.d.ts +7 -0
  15. package/dist/config/resolve.js +73 -0
  16. package/dist/config/resolve.js.map +1 -0
  17. package/dist/config/types.d.ts +36 -0
  18. package/dist/config/types.js +3 -0
  19. package/dist/config/types.js.map +1 -0
  20. package/dist/connectionState.d.ts +21 -0
  21. package/dist/connectionState.js +28 -0
  22. package/dist/connectionState.js.map +1 -0
  23. package/dist/describeError.d.ts +1 -0
  24. package/dist/describeError.js +12 -0
  25. package/dist/describeError.js.map +1 -0
  26. package/dist/diagnosticPage.d.ts +3 -0
  27. package/dist/diagnosticPage.js +150 -0
  28. package/dist/diagnosticPage.js.map +1 -0
  29. package/dist/discovery/index.d.ts +9 -0
  30. package/dist/discovery/index.js +63 -0
  31. package/dist/discovery/index.js.map +1 -0
  32. package/dist/discovery/scan.d.ts +2 -0
  33. package/dist/discovery/scan.js +12 -0
  34. package/dist/discovery/scan.js.map +1 -0
  35. package/dist/help.d.ts +1 -0
  36. package/dist/help.js +48 -0
  37. package/dist/help.js.map +1 -0
  38. package/dist/index.d.ts +10 -0
  39. package/dist/index.js +238 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/openBrowser.d.ts +1 -0
  42. package/dist/openBrowser.js +46 -0
  43. package/dist/openBrowser.js.map +1 -0
  44. package/dist/queueFactory.d.ts +15 -0
  45. package/dist/queueFactory.js +63 -0
  46. package/dist/queueFactory.js.map +1 -0
  47. package/dist/registry.d.ts +22 -0
  48. package/dist/registry.js +61 -0
  49. package/dist/registry.js.map +1 -0
  50. package/dist/server.d.ts +14 -0
  51. package/dist/server.js +34 -0
  52. package/dist/server.js.map +1 -0
  53. package/dist/unavailableGate.d.ts +8 -0
  54. package/dist/unavailableGate.js +37 -0
  55. package/dist/unavailableGate.js.map +1 -0
  56. package/package.json +57 -4
  57. package/index.js +0 -1
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.STATUS_PATH = void 0;
4
+ exports.renderDiagnosticPage = renderDiagnosticPage;
5
+ const connectionState_1 = require("./connectionState");
6
+ exports.STATUS_PATH = '/__bull-board-cli/status';
7
+ function escapeHtml(value) {
8
+ return value
9
+ .replace(/&/g, '&')
10
+ .replace(/</g, '&lt;')
11
+ .replace(/>/g, '&gt;')
12
+ .replace(/"/g, '&quot;');
13
+ }
14
+ function describeState(state) {
15
+ const retrySeconds = connectionState_1.RETRY_INTERVAL_MS / 1000;
16
+ if (state.status === 'degraded') {
17
+ const afterRetrying = state.attempts > 0 ? ` after ${state.attempts} failed attempt(s) first` : '';
18
+ return {
19
+ headline: `Connected to Redis${afterRetrying}, but could not finish starting up.`,
20
+ error: state.lastError,
21
+ footer: 'This is not a connectivity problem, so bull-board is not retrying the failed step on ' +
22
+ 'its own. It is still watching the connection, though: if Redis itself drops and comes ' +
23
+ 'back, that can clear this on its own. Otherwise, fix the underlying issue (check the ' +
24
+ 'error above) and restart bull-board.',
25
+ };
26
+ }
27
+ if (state.status === 'unavailable') {
28
+ return {
29
+ headline: `Not connected yet (attempt ${state.attempts}).`,
30
+ error: state.lastError,
31
+ footer: `Retrying every ${retrySeconds}s. Pass --no-retry to exit immediately instead of waiting.`,
32
+ };
33
+ }
34
+ return {
35
+ headline: 'Not connected yet.',
36
+ error: 'No connection attempt has failed yet.',
37
+ footer: `Retrying every ${retrySeconds}s if the first attempt does not succeed. Pass --no-retry to exit immediately instead of waiting.`,
38
+ };
39
+ }
40
+ const LIKELY_CAUSES = `
41
+ <p>Likely causes:</p>
42
+ <ul>
43
+ <li>Redis is not running.</li>
44
+ <li>The port is wrong. The default is 6379.</li>
45
+ <li>Redis is in a container whose port is not published to the host.</li>
46
+ <li>Redis needs credentials that are missing from the URL.</li>
47
+ <li>Redis needs TLS, which means the URL should start with <code>rediss://</code> instead of <code>redis://</code>.</li>
48
+ </ul>
49
+ `;
50
+ function renderDiagnosticPage(state) {
51
+ const { headline, error, footer } = describeState(state);
52
+ const attemptsRow = state.attempts > 0 ? `<dt>Attempts</dt><dd>${state.attempts}</dd>` : '';
53
+ const causes = state.status === 'degraded' ? '' : LIKELY_CAUSES;
54
+ return `<!doctype html>
55
+ <html lang="en">
56
+ <head>
57
+ <meta charset="utf-8" />
58
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
59
+ <meta http-equiv="refresh" content="5" />
60
+ <title>bull-board - waiting for Redis</title>
61
+ <style>
62
+ :root {
63
+ color-scheme: light dark;
64
+ --bg: #f6f6f7;
65
+ --panel: #ffffff;
66
+ --text: #1b1b1d;
67
+ --muted: #63646a;
68
+ --border: #dcdde0;
69
+ --accent: #b3261e;
70
+ --code-bg: #eef0f2;
71
+ }
72
+ @media (prefers-color-scheme: dark) {
73
+ :root {
74
+ --bg: #16171a;
75
+ --panel: #202126;
76
+ --text: #e8e9eb;
77
+ --muted: #9a9ba3;
78
+ --border: #34353b;
79
+ --accent: #ff8a80;
80
+ --code-bg: #2a2c33;
81
+ }
82
+ }
83
+ * { box-sizing: border-box; }
84
+ body {
85
+ margin: 0;
86
+ padding: 2.5rem 1.25rem;
87
+ background: var(--bg);
88
+ color: var(--text);
89
+ font: 16px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
90
+ }
91
+ main {
92
+ max-width: 640px;
93
+ margin: 0 auto;
94
+ background: var(--panel);
95
+ border: 1px solid var(--border);
96
+ border-radius: 10px;
97
+ padding: 2rem;
98
+ }
99
+ h1 { margin-top: 0; font-size: 1.35rem; }
100
+ .status { color: var(--accent); font-weight: 600; margin-top: -0.25rem; }
101
+ code {
102
+ background: var(--code-bg);
103
+ border-radius: 4px;
104
+ padding: 0.15rem 0.4rem;
105
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
106
+ font-size: 0.9em;
107
+ word-break: break-all;
108
+ }
109
+ ul { padding-left: 1.25rem; }
110
+ li { margin: 0.35rem 0; }
111
+ .muted { color: var(--muted); font-size: 0.9em; }
112
+ dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.4rem 1rem; margin: 1.25rem 0; }
113
+ dt { color: var(--muted); }
114
+ dd { margin: 0; }
115
+ </style>
116
+ </head>
117
+ <body>
118
+ <main>
119
+ <h1>bull-board is waiting for Redis</h1>
120
+ <p class="status">${headline}</p>
121
+ <dl>
122
+ <dt>Redis URL</dt><dd><code>${escapeHtml(state.redisUrl)}</code></dd>
123
+ <dt>Error</dt><dd><code>${escapeHtml(error)}</code></dd>
124
+ ${attemptsRow}
125
+ </dl>
126
+ <p>This page checks in on its own and will switch to the dashboard the moment Redis answers. Nothing to do here but wait, or go fix Redis.</p>
127
+ ${causes}
128
+ <p class="muted">${footer}</p>
129
+ </main>
130
+ <script>
131
+ (function poll() {
132
+ fetch(${JSON.stringify(exports.STATUS_PATH)}, { cache: 'no-store' })
133
+ .then(function (res) { return res.json(); })
134
+ .then(function (nextState) {
135
+ if (nextState.status === 'connected') {
136
+ location.reload();
137
+ return;
138
+ }
139
+ setTimeout(poll, 2000);
140
+ })
141
+ .catch(function () {
142
+ setTimeout(poll, 2000);
143
+ });
144
+ })();
145
+ </script>
146
+ </body>
147
+ </html>
148
+ `;
149
+ }
150
+ //# sourceMappingURL=diagnosticPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnosticPage.js","sourceRoot":"","sources":["../src/diagnosticPage.ts"],"names":[],"mappings":";;;AA2DA,oDAoGC;AA/JD,uDAA4E;AAE/D,QAAA,WAAW,GAAG,0BAA0B,CAAC;AAEtD,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,aAAa,CAAC,KAAsB;IAK3C,MAAM,YAAY,GAAG,mCAAiB,GAAG,IAAI,CAAC;IAE9C,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,aAAa,GACjB,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,CAAC,QAAQ,0BAA0B,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/E,OAAO;YACL,QAAQ,EAAE,qBAAqB,aAAa,qCAAqC;YACjF,KAAK,EAAE,KAAK,CAAC,SAAS;YACtB,MAAM,EACJ,uFAAuF;gBACvF,wFAAwF;gBACxF,uFAAuF;gBACvF,sCAAsC;SACzC,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACnC,OAAO;YACL,QAAQ,EAAE,8BAA8B,KAAK,CAAC,QAAQ,IAAI;YAC1D,KAAK,EAAE,KAAK,CAAC,SAAS;YACtB,MAAM,EAAE,kBAAkB,YAAY,4DAA4D;SACnG,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,oBAAoB;QAC9B,KAAK,EAAE,uCAAuC;QAC9C,MAAM,EAAE,kBAAkB,YAAY,kGAAkG;KACzI,CAAC;AACJ,CAAC;AAED,MAAM,aAAa,GAAG;;;;;;;;;CASrB,CAAC;AAEF,SAAgB,oBAAoB,CAAC,KAAsB;IACzD,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,wBAAwB,KAAK,CAAC,QAAQ,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5F,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;IAEhE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAkEa,QAAQ;;kCAEI,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;8BAC9B,UAAU,CAAC,KAAK,CAAC;MACzC,WAAW;;;IAGb,MAAM;qBACW,MAAM;;;;YAIf,IAAI,CAAC,SAAS,CAAC,mBAAW,CAAC;;;;;;;;;;;;;;;;CAgBtC,CAAC;AACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Redis } from 'ioredis';
2
+ export interface DiscoveredQueue {
3
+ prefix: string;
4
+ name: string;
5
+ lib: 'bull' | 'bullmq';
6
+ }
7
+ export declare function discoverQueues(client: Redis, prefixes: string[]): Promise<DiscoveredQueue[]>;
8
+ /** Explicitly named queues skip the scan; only their library still has to be established. */
9
+ export declare function probeQueues(client: Redis, prefix: string, names: string[]): Promise<DiscoveredQueue[]>;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.discoverQueues = discoverQueues;
4
+ exports.probeQueues = probeQueues;
5
+ const scan_1 = require("./scan");
6
+ /**
7
+ * Bull and BullMQ are told apart by key shape rather than by asking the library.
8
+ * Bull's key list (`bull/lib/queue.js`) has `id` and `meta-paused` but no `meta`,
9
+ * BullMQ writes `meta`. So `:meta` means BullMQ and a bare `:id` means Bull.
10
+ */
11
+ const BULLMQ_MARKER = 'meta';
12
+ const BULL_MARKER = 'id';
13
+ /** Queue names may contain colons, so slice off the known prefix and suffix rather than splitting. */
14
+ function queueNameOf(key, prefix, marker) {
15
+ const head = `${prefix}:`;
16
+ const tail = `:${marker}`;
17
+ if (!key.startsWith(head) || !key.endsWith(tail))
18
+ return null;
19
+ const name = key.slice(head.length, key.length - tail.length);
20
+ return name.length > 0 ? name : null;
21
+ }
22
+ async function discoverInPrefix(client, prefix, found) {
23
+ const bullmqNames = new Set();
24
+ await (0, scan_1.scanKeys)(client, `${prefix}:*:${BULLMQ_MARKER}`, (key) => {
25
+ const name = queueNameOf(key, prefix, BULLMQ_MARKER);
26
+ if (name)
27
+ bullmqNames.add(name);
28
+ });
29
+ for (const name of bullmqNames) {
30
+ found.set(`${prefix}:${name}`, { prefix, name, lib: 'bullmq' });
31
+ }
32
+ await (0, scan_1.scanKeys)(client, `${prefix}:*:${BULL_MARKER}`, (key) => {
33
+ const name = queueNameOf(key, prefix, BULL_MARKER);
34
+ if (!name || bullmqNames.has(name))
35
+ return;
36
+ found.set(`${prefix}:${name}`, { prefix, name, lib: 'bull' });
37
+ });
38
+ }
39
+ async function discoverQueues(client, prefixes) {
40
+ const found = new Map();
41
+ for (const prefix of prefixes) {
42
+ // A wildcard prefix cannot be resolved without guessing where the prefix ends and the
43
+ // queue name begins, since both may contain colons. Deferred, see the plan. Failing
44
+ // loudly beats scanning `*:*:meta` and silently returning nothing.
45
+ if (prefix.includes('*')) {
46
+ throw new Error(`Wildcard prefixes are not supported yet: "${prefix}". List prefixes explicitly, ` +
47
+ `for example --prefix bull,tenant-a`);
48
+ }
49
+ await discoverInPrefix(client, prefix, found);
50
+ }
51
+ return [...found.entries()]
52
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
53
+ .map(([, queue]) => queue);
54
+ }
55
+ /** Explicitly named queues skip the scan; only their library still has to be established. */
56
+ async function probeQueues(client, prefix, names) {
57
+ return Promise.all(names.map(async (name) => {
58
+ const isBullMQ = await client.exists(`${prefix}:${name}:${BULLMQ_MARKER}`);
59
+ const isBull = isBullMQ ? 0 : await client.exists(`${prefix}:${name}:${BULL_MARKER}`);
60
+ return { prefix, name, lib: isBull ? 'bull' : 'bullmq' };
61
+ }));
62
+ }
63
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Redis } from 'ioredis';
2
+ export declare function scanKeys(client: Redis, pattern: string, onKey: (key: string) => void): Promise<void>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scanKeys = scanKeys;
4
+ async function scanKeys(client, pattern, onKey) {
5
+ let cursor = '0';
6
+ do {
7
+ const [next, keys] = await client.scan(cursor, 'MATCH', pattern, 'COUNT', 500);
8
+ cursor = next;
9
+ keys.forEach(onKey);
10
+ } while (cursor !== '0');
11
+ }
12
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/discovery/scan.ts"],"names":[],"mappings":";;AAEA,4BAYC;AAZM,KAAK,UAAU,QAAQ,CAC5B,MAAa,EACb,OAAe,EACf,KAA4B;IAE5B,IAAI,MAAM,GAAG,GAAG,CAAC;IAEjB,GAAG,CAAC;QACF,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QAC/E,MAAM,GAAG,IAAI,CAAC;QACd,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC,QAAQ,MAAM,KAAK,GAAG,EAAE;AAC3B,CAAC"}
package/dist/help.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare const HELP = "\nbull-board - run the bull-board dashboard against a Redis instance\n\nUsage:\n bull-board [options]\n npx @bull-board/cli [options]\n\nOptions:\n -r, --redis <url> Redis connection URL [redis://localhost:6379]\n -p, --port <port> Port to listen on [3000]\n --host <address> Interface to bind [127.0.0.1]\n --prefix <list> Comma separated key prefixes [bull]\n --queues <list> Use these queue names instead of discovering\n --scan-interval <s> Seconds between rescans, 0 to scan once [10]\n --base-path <path> Serve the dashboard under a path prefix\n --read-only Disable every destructive action\n --user <name> Basic auth user (requires --password)\n --password <pass> Basic auth password (requires --user)\n --board-title <s> Dashboard title\n --config <file> Path to a config file\n --browser <command> Command to open the browser with [$BROWSER]\n --no-open Do not open a browser\n --no-retry Exit if Redis is unreachable instead of retrying\n -h, --help Show this help\n -v, --version Show the version\n\nIf Redis is unreachable at startup, bull-board still opens: it serves a\ndiagnostic page explaining why, keeps retrying every 3 seconds, and switches\nto the real dashboard on its own once Redis answers. That only covers\nstartup: once the dashboard is live it stays live, even if Redis goes away\nlater. The diagnostic page does not come back; API requests just stop\nreturning until Redis is reachable again, and Ctrl-C still works.\nPass --no-retry to get the old behaviour back instead: print the error and\nexit 1 immediately, without opening a port.\n\nEnvironment variables mirror every flag, for example BULL_BOARD_REDIS_URL,\nBULL_BOARD_PORT, BULL_BOARD_READ_ONLY.\n\nExamples:\n bull-board\n bull-board -r redis://localhost:6379 -p 4000\n bull-board --prefix tenant-a,tenant-b --read-only\n bull-board --user admin --password secret --host 0.0.0.0\n";
package/dist/help.js ADDED
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HELP = void 0;
4
+ exports.HELP = `
5
+ bull-board - run the bull-board dashboard against a Redis instance
6
+
7
+ Usage:
8
+ bull-board [options]
9
+ npx @bull-board/cli [options]
10
+
11
+ Options:
12
+ -r, --redis <url> Redis connection URL [redis://localhost:6379]
13
+ -p, --port <port> Port to listen on [3000]
14
+ --host <address> Interface to bind [127.0.0.1]
15
+ --prefix <list> Comma separated key prefixes [bull]
16
+ --queues <list> Use these queue names instead of discovering
17
+ --scan-interval <s> Seconds between rescans, 0 to scan once [10]
18
+ --base-path <path> Serve the dashboard under a path prefix
19
+ --read-only Disable every destructive action
20
+ --user <name> Basic auth user (requires --password)
21
+ --password <pass> Basic auth password (requires --user)
22
+ --board-title <s> Dashboard title
23
+ --config <file> Path to a config file
24
+ --browser <command> Command to open the browser with [$BROWSER]
25
+ --no-open Do not open a browser
26
+ --no-retry Exit if Redis is unreachable instead of retrying
27
+ -h, --help Show this help
28
+ -v, --version Show the version
29
+
30
+ If Redis is unreachable at startup, bull-board still opens: it serves a
31
+ diagnostic page explaining why, keeps retrying every 3 seconds, and switches
32
+ to the real dashboard on its own once Redis answers. That only covers
33
+ startup: once the dashboard is live it stays live, even if Redis goes away
34
+ later. The diagnostic page does not come back; API requests just stop
35
+ returning until Redis is reachable again, and Ctrl-C still works.
36
+ Pass --no-retry to get the old behaviour back instead: print the error and
37
+ exit 1 immediately, without opening a port.
38
+
39
+ Environment variables mirror every flag, for example BULL_BOARD_REDIS_URL,
40
+ BULL_BOARD_PORT, BULL_BOARD_READ_ONLY.
41
+
42
+ Examples:
43
+ bull-board
44
+ bull-board -r redis://localhost:6379 -p 4000
45
+ bull-board --prefix tenant-a,tenant-b --read-only
46
+ bull-board --user admin --password secret --host 0.0.0.0
47
+ `;
48
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":";;;AAAa,QAAA,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CnB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { CliConfig } from './config/types';
2
+ import { describeError } from './describeError';
3
+ export { describeError };
4
+ export interface RunningBoard {
5
+ url: string;
6
+ close(): Promise<void>;
7
+ }
8
+ export declare function run(config: CliConfig, log?: Console, { beforeReady, }?: {
9
+ beforeReady?: (close: () => Promise<void>) => void;
10
+ }): Promise<RunningBoard>;
package/dist/index.js ADDED
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeError = void 0;
4
+ exports.run = run;
5
+ const api_1 = require("@bull-board/api");
6
+ const express_1 = require("@bull-board/express");
7
+ const ioredis_1 = require("ioredis");
8
+ const connectionState_1 = require("./connectionState");
9
+ const describeError_1 = require("./describeError");
10
+ Object.defineProperty(exports, "describeError", { enumerable: true, get: function () { return describeError_1.describeError; } });
11
+ const discovery_1 = require("./discovery");
12
+ const queueFactory_1 = require("./queueFactory");
13
+ const registry_1 = require("./registry");
14
+ const server_1 = require("./server");
15
+ const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);
16
+ function isLoopbackHost(host) {
17
+ return LOOPBACK_HOSTS.has(host);
18
+ }
19
+ const SHUTDOWN_GRACE_MS = 3000;
20
+ function raceTimeout(ms) {
21
+ let timer;
22
+ const promise = new Promise((resolve) => {
23
+ timer = setTimeout(() => resolve('timeout'), ms);
24
+ timer.unref();
25
+ });
26
+ return { promise, cancel: () => clearTimeout(timer) };
27
+ }
28
+ async function closeWithGrace(promise, ms, onTimeout) {
29
+ const timeout = raceTimeout(ms);
30
+ const outcome = await Promise.race([
31
+ promise.then(() => 'done', () => 'done'),
32
+ timeout.promise,
33
+ ]);
34
+ timeout.cancel();
35
+ if (outcome === 'timeout')
36
+ onTimeout();
37
+ }
38
+ async function run(config, log = console, { beforeReady, } = {}) {
39
+ const client = new ioredis_1.Redis(config.redisUrl, {
40
+ maxRetriesPerRequest: null,
41
+ lazyConnect: true,
42
+ ...(config.noRetry ? {} : { retryStrategy: () => connectionState_1.RETRY_INTERVAL_MS }),
43
+ });
44
+ let attemptError;
45
+ client.on('error', (error) => {
46
+ attemptError !== null && attemptError !== void 0 ? attemptError : (attemptError = error);
47
+ });
48
+ if (!isLoopbackHost(config.host) && !config.auth) {
49
+ log.warn(`Warning: bull-board is listening on ${config.host}, which accepts connections from ` +
50
+ 'outside this machine, with no --user/--password set. Anyone who can reach it can ' +
51
+ 'view and modify every queue. Set --user and --password, or bind to 127.0.0.1.');
52
+ }
53
+ const serverAdapter = new express_1.ExpressAdapter();
54
+ serverAdapter.setBasePath(config.basePath);
55
+ const board = (0, api_1.createBullBoard)({
56
+ queues: [],
57
+ serverAdapter,
58
+ options: { uiConfig: config.uiConfig },
59
+ });
60
+ const onWarning = (message) => log.warn(message);
61
+ const queues = (0, queueFactory_1.createQueueFactory)({
62
+ client,
63
+ readOnly: config.readOnly,
64
+ queueOptions: config.queueOptions,
65
+ onWarning,
66
+ });
67
+ const registry = new registry_1.QueueRegistry({ board, createQueue: queues.createQueue, onWarning });
68
+ let closing = false;
69
+ let rescanTimer;
70
+ const scan = async () => {
71
+ const discovered = config.queueNames
72
+ ? (await Promise.all(config.prefixes.map((prefix) => (0, discovery_1.probeQueues)(client, prefix, config.queueNames)))).flat()
73
+ : await (0, discovery_1.discoverQueues)(client, config.prefixes);
74
+ if (closing)
75
+ return discovered.length;
76
+ await registry.sync(discovered);
77
+ return discovered.length;
78
+ };
79
+ const logIfIdle = (count) => {
80
+ if (count === 0) {
81
+ log.log(`No queues found under ${config.prefixes.join(', ')} yet. ` +
82
+ (config.scanInterval > 0 ? 'Watching for new ones.' : 'Scanning was set to run once.'));
83
+ }
84
+ };
85
+ const scheduleRescan = () => {
86
+ if (closing || config.scanInterval <= 0 || rescanTimer)
87
+ return;
88
+ rescanTimer = setTimeout(() => {
89
+ rescanTimer = undefined;
90
+ scan()
91
+ .catch((error) => log.warn(`Rescan failed: ${error.message}`))
92
+ .finally(scheduleRescan);
93
+ }, config.scanInterval * 1000);
94
+ rescanTimer.unref();
95
+ };
96
+ if (config.noRetry) {
97
+ try {
98
+ await client.connect();
99
+ }
100
+ catch (error) {
101
+ throw new Error(`Could not connect to Redis at ${(0, connectionState_1.maskRedisUrl)(config.redisUrl)}: ${(0, describeError_1.describeError)(attemptError !== null && attemptError !== void 0 ? attemptError : error)}`);
102
+ }
103
+ const count = await scan();
104
+ const server = await (0, server_1.startServer)(config, { serverAdapter });
105
+ const close = async () => {
106
+ closing = true;
107
+ if (rescanTimer)
108
+ clearTimeout(rescanTimer);
109
+ await closeWithGrace(server.close(), SHUTDOWN_GRACE_MS, () => {
110
+ log.warn(`Closing the HTTP server did not finish within ${SHUTDOWN_GRACE_MS}ms; forcing remaining connections closed.`);
111
+ server.closeAllConnections();
112
+ });
113
+ await closeWithGrace(registry.close(), SHUTDOWN_GRACE_MS, () => log.warn(`Closing queues did not finish within ${SHUTDOWN_GRACE_MS}ms; continuing shutdown.`));
114
+ await closeWithGrace(queues.close(), SHUTDOWN_GRACE_MS, () => log.warn(`Closing shared Redis connections did not finish within ${SHUTDOWN_GRACE_MS}ms; continuing shutdown.`));
115
+ await closeWithGrace(client.quit(), SHUTDOWN_GRACE_MS, () => client.disconnect());
116
+ };
117
+ beforeReady === null || beforeReady === void 0 ? void 0 : beforeReady(close);
118
+ log.log(`bull-board listening on ${server.url}`);
119
+ log.log(`Redis: ${(0, connectionState_1.maskRedisUrl)(config.redisUrl)}`);
120
+ log.log(`Prefix: ${config.prefixes.join(', ')}`);
121
+ logIfIdle(count);
122
+ scheduleRescan();
123
+ return { url: server.url, close };
124
+ }
125
+ let state = {
126
+ status: 'connecting',
127
+ redisUrl: (0, connectionState_1.maskRedisUrl)(config.redisUrl),
128
+ attempts: 0,
129
+ };
130
+ const getState = () => state;
131
+ const server = await (0, server_1.startServer)(config, { serverAdapter, getConnectionState: getState });
132
+ const close = async () => {
133
+ closing = true;
134
+ if (rescanTimer)
135
+ clearTimeout(rescanTimer);
136
+ await closeWithGrace(server.close(), SHUTDOWN_GRACE_MS, () => {
137
+ log.warn(`Closing the HTTP server did not finish within ${SHUTDOWN_GRACE_MS}ms; forcing remaining connections closed.`);
138
+ server.closeAllConnections();
139
+ });
140
+ await closeWithGrace(registry.close(), SHUTDOWN_GRACE_MS, () => log.warn(`Closing queues did not finish within ${SHUTDOWN_GRACE_MS}ms; continuing shutdown.`));
141
+ await closeWithGrace(queues.close(), SHUTDOWN_GRACE_MS, () => log.warn(`Closing shared Redis connections did not finish within ${SHUTDOWN_GRACE_MS}ms; continuing shutdown.`));
142
+ await closeWithGrace(client.quit(), SHUTDOWN_GRACE_MS, () => client.disconnect());
143
+ };
144
+ beforeReady === null || beforeReady === void 0 ? void 0 : beforeReady(close);
145
+ let everFailed = false;
146
+ let lostConnection = false;
147
+ const markUnavailable = (message) => {
148
+ if (closing)
149
+ return;
150
+ if (state.status === 'connected') {
151
+ if (lostConnection)
152
+ return;
153
+ lostConnection = true;
154
+ log.warn(`Lost the Redis connection: ${message}. Retrying every ${connectionState_1.RETRY_INTERVAL_MS / 1000}s.`);
155
+ return;
156
+ }
157
+ state = {
158
+ status: 'unavailable',
159
+ redisUrl: (0, connectionState_1.maskRedisUrl)(config.redisUrl),
160
+ attempts: state.attempts + 1,
161
+ lastError: message,
162
+ };
163
+ if (!everFailed) {
164
+ everFailed = true;
165
+ log.warn(`Could not connect to Redis at ${(0, connectionState_1.maskRedisUrl)(config.redisUrl)}: ${message}`);
166
+ log.warn(`Serving a diagnostic page at ${server.url} and retrying every ` +
167
+ `${connectionState_1.RETRY_INTERVAL_MS / 1000}s. Pass --no-retry to exit instead of waiting.`);
168
+ }
169
+ };
170
+ let inFlight;
171
+ let deferredIdleCount;
172
+ const becomeConnected = (deferIdleLog = false) => {
173
+ if (closing || state.status === 'connected')
174
+ return Promise.resolve();
175
+ if (inFlight)
176
+ return inFlight;
177
+ inFlight = (async () => {
178
+ try {
179
+ const count = await scan();
180
+ if (closing)
181
+ return;
182
+ state = {
183
+ status: 'connected',
184
+ redisUrl: (0, connectionState_1.maskRedisUrl)(config.redisUrl),
185
+ attempts: state.attempts,
186
+ };
187
+ scheduleRescan();
188
+ if (everFailed)
189
+ log.log('Redis connected. The dashboard is live.');
190
+ if (deferIdleLog) {
191
+ deferredIdleCount = count;
192
+ }
193
+ else {
194
+ logIfIdle(count);
195
+ }
196
+ }
197
+ catch (error) {
198
+ if (closing)
199
+ return;
200
+ const message = error.message;
201
+ state = {
202
+ status: 'degraded',
203
+ redisUrl: (0, connectionState_1.maskRedisUrl)(config.redisUrl),
204
+ attempts: state.attempts,
205
+ lastError: message,
206
+ };
207
+ log.warn(`Connected to Redis, but could not finish starting up: ${message}`);
208
+ }
209
+ finally {
210
+ inFlight = undefined;
211
+ }
212
+ })();
213
+ return inFlight;
214
+ };
215
+ try {
216
+ await client.connect();
217
+ }
218
+ catch (error) {
219
+ markUnavailable((0, describeError_1.describeError)(attemptError !== null && attemptError !== void 0 ? attemptError : error));
220
+ }
221
+ if (client.status === 'ready') {
222
+ await becomeConnected(true);
223
+ }
224
+ log.log(`bull-board listening on ${server.url}`);
225
+ log.log(`Redis: ${(0, connectionState_1.maskRedisUrl)(config.redisUrl)}`);
226
+ log.log(`Prefix: ${config.prefixes.join(', ')}`);
227
+ if (deferredIdleCount !== undefined)
228
+ logIfIdle(deferredIdleCount);
229
+ client.on('ready', () => {
230
+ lostConnection = false;
231
+ void becomeConnected();
232
+ });
233
+ client.on('error', (error) => {
234
+ markUnavailable((0, describeError_1.describeError)(error));
235
+ });
236
+ return { url: server.url, close };
237
+ }
238
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function openBrowser(url: string, browser?: string): void;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openBrowser = openBrowser;
4
+ const node_child_process_1 = require("node:child_process");
5
+ function onSpawnError(url) {
6
+ return () => {
7
+ // oxlint-disable-next-line no-console
8
+ console.log(`Could not open a browser automatically. Open ${url} yourself.`);
9
+ };
10
+ }
11
+ // Kept separate from openUnshelled so the only shelled call is the fixed 'start', never a
12
+ // user-supplied command.
13
+ function openWindowsDefault(url) {
14
+ (0, node_child_process_1.spawn)('start', [url], {
15
+ stdio: 'ignore',
16
+ detached: true,
17
+ shell: true,
18
+ })
19
+ .on('error', onSpawnError(url))
20
+ .unref();
21
+ }
22
+ function openUnshelled(command, args, url) {
23
+ (0, node_child_process_1.spawn)(command, [...args, url], {
24
+ stdio: 'ignore',
25
+ detached: true,
26
+ shell: false,
27
+ })
28
+ .on('error', onSpawnError(url))
29
+ .unref();
30
+ }
31
+ function openBrowser(url, browser) {
32
+ var _a;
33
+ const isWindows = process.platform === 'win32';
34
+ const custom = (_a = browser === null || browser === void 0 ? void 0 : browser.split(/\s+/).filter(Boolean)) !== null && _a !== void 0 ? _a : [];
35
+ if (custom.length === 0) {
36
+ if (isWindows) {
37
+ openWindowsDefault(url);
38
+ return;
39
+ }
40
+ openUnshelled(process.platform === 'darwin' ? 'open' : 'xdg-open', [], url);
41
+ return;
42
+ }
43
+ const [command, ...args] = custom;
44
+ openUnshelled(command, args, url);
45
+ }
46
+ //# sourceMappingURL=openBrowser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openBrowser.js","sourceRoot":"","sources":["../src/openBrowser.ts"],"names":[],"mappings":";;AA+BA,kCAeC;AA9CD,2DAA2C;AAE3C,SAAS,YAAY,CAAC,GAAW;IAC/B,OAAO,GAAG,EAAE;QACV,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,gDAAgD,GAAG,YAAY,CAAC,CAAC;IAC/E,CAAC,CAAC;AACJ,CAAC;AAED,0FAA0F;AAC1F,yBAAyB;AACzB,SAAS,kBAAkB,CAAC,GAAW;IACrC,IAAA,0BAAK,EAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE;QACpB,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,IAAI;KACZ,CAAC;SACC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC;SAC9B,KAAK,EAAE,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,OAAe,EAAE,IAAc,EAAE,GAAW;IACjE,IAAA,0BAAK,EAAC,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE;QAC7B,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,KAAK;KACb,CAAC;SACC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC;SAC9B,KAAK,EAAE,CAAC;AACb,CAAC;AAED,SAAgB,WAAW,CAAC,GAAW,EAAE,OAAgB;;IACvD,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;IAC/C,MAAM,MAAM,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,mCAAI,EAAE,CAAC;IAE3D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,IAAI,SAAS,EAAE,CAAC;YACd,kBAAkB,CAAC,GAAG,CAAC,CAAC;YACxB,OAAO;QACT,CAAC;QACD,aAAa,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;QAC5E,OAAO;IACT,CAAC;IAED,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC;IAClC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { QueueAdapterOptions } from '@bull-board/api/typings/app';
2
+ import type { Redis } from 'ioredis';
3
+ import type { DiscoveredQueue } from './discovery';
4
+ import type { QueueHandle } from './registry';
5
+ export interface QueueFactoryDeps {
6
+ client: Redis;
7
+ readOnly: boolean;
8
+ queueOptions: Record<string, Partial<QueueAdapterOptions>>;
9
+ onWarning(message: string): void;
10
+ }
11
+ export interface QueueFactory {
12
+ createQueue(discovered: DiscoveredQueue): QueueHandle;
13
+ close(): Promise<void>;
14
+ }
15
+ export declare function createQueueFactory({ client, readOnly, queueOptions, onWarning, }: QueueFactoryDeps): QueueFactory;