workmatic 1.0.4 β†’ 1.0.7

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/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A persistent job queue for Node.js using SQLite. Simple, reliable, and zero external dependencies beyond SQLite.
4
4
 
5
+ **Full single-page reference (same material, browsable offline): [`docs/index.html`](docs/index.html)**.
6
+
5
7
  ## Why?
6
8
 
7
9
  I love [fastq](https://github.com/mcollina/fastq) - it's fast, simple, and has a great API. But it's in-memory only, which can be frustrating when you need jobs to survive process restarts or crashes.
@@ -63,11 +65,15 @@ worker.start();
63
65
 
64
66
  ## API Reference
65
67
 
68
+ Browsable copy with anchored sections: **[`docs/index.html`](docs/index.html)** (API from [`#api-database`](docs/index.html#api-database)).
69
+
66
70
  ### `createDatabase(options)`
67
71
 
68
72
  Initialize the database connection and schema.
69
73
 
70
74
  ```typescript
75
+ import { createDatabase, getUnderlyingDb } from 'workmatic';
76
+
71
77
  const db = createDatabase({
72
78
  // Option 1: File path (creates or opens existing)
73
79
  filename: './jobs.db',
@@ -78,6 +84,9 @@ const db = createDatabase({
78
84
  // Option 3: Existing better-sqlite3 instance
79
85
  db: existingSqliteInstance,
80
86
  });
87
+
88
+ // Underlying driver (only reliable for instances from createDatabase above)
89
+ const sqlite = getUnderlyingDb(db);
81
90
  ```
82
91
 
83
92
  ### `createClient(options)`
@@ -112,7 +121,32 @@ Get job statistics for the queue.
112
121
 
113
122
  ```typescript
114
123
  const stats = await client.stats();
115
- // { ready: 5, running: 2, done: 100, failed: 0, dead: 1, total: 108 }
124
+ // { ready: 5, running: 2, done: 100, dead: 1, total: 108 }
125
+ ```
126
+
127
+ #### `client.addMany(payloads, options?)`
128
+
129
+ Insert many jobs in a **single transaction**, sharing the same `priority`, `delayMs`, and `maxAttempts`.
130
+
131
+ ```typescript
132
+ const { ok, ids } = await client.addMany(
133
+ [{ email: 'a@x.com' }, { email: 'b@x.com' }],
134
+ { priority: 1, delayMs: 0, maxAttempts: 3 }
135
+ );
136
+ ```
137
+
138
+ #### `client.clear(options?)`
139
+
140
+ Clear all jobs from the queue.
141
+
142
+ ```typescript
143
+ // Clear all jobs
144
+ const deleted = await client.clear();
145
+ console.log(`Deleted ${deleted} jobs`);
146
+
147
+ // Clear only jobs with specific status
148
+ const deleted = await client.clear({ status: 'done' });
149
+ console.log(`Deleted ${deleted} done jobs`);
116
150
  ```
117
151
 
118
152
  ### `createWorker(options)`
@@ -126,10 +160,13 @@ const worker = createWorker({
126
160
  concurrency: 1, // Optional: Parallel job count (default: 1)
127
161
  leaseMs: 30000, // Optional: Job lease duration in ms (default: 30000)
128
162
  pollMs: 1000, // Optional: Poll interval when idle (default: 1000)
129
- timeoutMs: 60000, // Optional: Job execution timeout in ms (default: none)
163
+ timeoutMs: 60000, // Optional: Job execution timeout in ms (default: 60000). Use 0 for no limit
130
164
  backoff: (n) => 1000 * Math.pow(2, n), // Optional: Retry backoff function
131
165
  persistState: false, // Optional: Persist worker state to database (default: false)
132
166
  autoRestore: true, // Optional: Auto-restore state on creation (default: true)
167
+ pauseCheckIntervalMs: 300, // Optional: Min ms between DB pause checks (CLI pause). Default: 300
168
+ requeueExpiredIntervalMs: 0, // Optional: Min ms between lease requeue scans; 0 = every pump
169
+ onPumpError: (err) => { /* optional hook after default log */ },
133
170
  });
134
171
  ```
135
172
 
@@ -181,6 +218,20 @@ Get job statistics for the queue.
181
218
  const stats = await worker.stats();
182
219
  ```
183
220
 
221
+ #### `worker.clear(options?)`
222
+
223
+ Clear all jobs from the queue.
224
+
225
+ ```typescript
226
+ // Clear all jobs in the queue
227
+ const deleted = await worker.clear();
228
+ console.log(`Deleted ${deleted} jobs`);
229
+
230
+ // Clear only jobs with specific status
231
+ const deleted = await worker.clear({ status: 'dead' });
232
+ console.log(`Deleted ${deleted} dead jobs`);
233
+ ```
234
+
184
235
  #### Worker properties
185
236
 
186
237
  ```typescript
@@ -302,7 +353,7 @@ interface Job<TPayload> {
302
353
  id: string; // Unique public ID (nanoid)
303
354
  queue: string; // Queue name
304
355
  payload: TPayload; // Your job data
305
- status: JobStatus; // 'ready' | 'running' | 'done' | 'failed' | 'dead'
356
+ status: JobStatus; // 'ready' | 'running' | 'done' | 'dead' while running
306
357
  priority: number; // Priority value
307
358
  attempts: number; // Current attempt count (starts at 0)
308
359
  maxAttempts: number; // Maximum attempts allowed
@@ -378,11 +429,14 @@ worker.process(async (job) => {
378
429
  | `concurrency` | `1` | Number of jobs to process in parallel |
379
430
  | `leaseMs` | `30000` | How long a job is "locked" during processing |
380
431
  | `pollMs` | `1000` | How often to check for new jobs when idle |
381
- | `timeoutMs` | `undefined` | Job execution timeout (fails job if exceeded) |
432
+ | `timeoutMs` | `60000` | Job timeout in ms (`0` = no limit) |
382
433
  | `priority` | `0` | Job priority (lower = processed first) |
383
434
  | `delayMs` | `0` | Delay before job becomes available |
384
435
  | `maxAttempts` | `3` | Maximum processing attempts |
385
436
  | `backoff` | `2^n * 1000` | Function returning retry delay in ms |
437
+ | `pauseCheckIntervalMs` | `300` | Throttle CLI/live pause checks from the pump loop |
438
+ | `requeueExpiredIntervalMs` | `0` | Throttle expired-lease requeue (0 = run every pump tick) |
439
+ | `onPumpError` | `undefined` | Callback after the default pump error log |
386
440
 
387
441
  ## Dashboard
388
442
 
@@ -422,6 +476,25 @@ npm run bench
422
476
  npm run bench -- --file
423
477
  ```
424
478
 
479
+ ### Micro benchmarks
480
+
481
+ Short suite: **2,000 sequential `add()` calls** (same code path as the full insert benchmark, smaller batch) plus **1,000 `client.stats()` calls** on a queue that already holds jobs. Useful for quick regression checks without running the full workload.
482
+
483
+ ```bash
484
+ npm run bench:micro
485
+ # or: npm run bench -- --micro
486
+
487
+ npm run bench:micro -- --file
488
+ # or: npm run bench -- --micro --file
489
+ ```
490
+
491
+ | Benchmark | In-Memory | File-based |
492
+ |-----------|-----------|------------|
493
+ | Micro Sequential Insert (2,000) | ~27,000/s | ~9,300/s |
494
+ | Stats Query (Γ—1,000) | ~9,200/s | ~5,500/s |
495
+
496
+ Figures are rounded from a representative run; throughput changes with hardware, SQLite settings, and how β€œwarm” the database is.
497
+
425
498
  ### Results Comparison
426
499
 
427
500
  | Benchmark | In-Memory | File-based |
@@ -433,6 +506,8 @@ npm run bench -- --file
433
506
  | Process (concurrency=8) | 10,000/s | 8,300/s |
434
507
  | Process (concurrency=16) | 18,000/s | 5,700/s |
435
508
  | Mixed Insert+Process | 7,500/s | 3,500/s |
509
+ | Micro Sequential Insert (2,000) | ~27,000/s | ~9,300/s |
510
+ | Stats Query (Γ—1,000) | ~9,200/s | ~5,500/s |
436
511
  | Claim + Process Batch | 23,600/s | 11,800/s |
437
512
 
438
513
  **Note**: File-based performance degrades at high concurrency due to disk I/O. For file-based databases, `concurrency=8` is often optimal. Performance varies by hardware.
@@ -457,15 +532,9 @@ npx workmatic list ./jobs.db --status=dead --limit=10
457
532
 
458
533
  # Export jobs to CSV
459
534
  npx workmatic export ./jobs.db backup.csv
460
- npx workmatic export ./jobs.db --status=failed > failed.csv
461
-
462
- # Import jobs from CSV
463
- npx workmatic import ./jobs.db backup.csv
464
-
465
- # Delete jobs by status
466
- npx workmatic purge ./jobs.db --status=done
535
+ npx workmatic export ./jobs.db --status=dead > dead-export.csv
467
536
 
468
- # Retry dead/failed jobs (reset to ready)
537
+ # Retry dead jobs (reset to ready)
469
538
  npx workmatic retry ./jobs.db --status=dead
470
539
  ```
471
540
 
@@ -487,7 +556,7 @@ npx workmatic retry ./jobs.db --status=dead
487
556
 
488
557
  | Option | Description |
489
558
  |--------|-------------|
490
- | `--status=<status>` | Filter by status (ready/running/done/failed/dead) |
559
+ | `--status=<status>` | Filter by status (ready/running/done/dead) |
491
560
  | `--queue=<queue>` | Filter by queue name |
492
561
  | `--limit=<n>` | Limit results (default: 100) |
493
562
 
package/dashboard/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Dashboard state
2
2
  const state = {
3
- stats: { ready: 0, running: 0, done: 0, failed: 0, dead: 0, total: 0 },
3
+ stats: { ready: 0, running: 0, done: 0, dead: 0, total: 0 },
4
4
  jobs: [],
5
5
  queues: [],
6
6
  workers: [],
@@ -18,7 +18,6 @@ const elements = {
18
18
  statReady: document.getElementById('stat-ready'),
19
19
  statRunning: document.getElementById('stat-running'),
20
20
  statDone: document.getElementById('stat-done'),
21
- statFailed: document.getElementById('stat-failed'),
22
21
  statDead: document.getElementById('stat-dead'),
23
22
  workersSection: document.getElementById('workers-section'),
24
23
  workersGrid: document.getElementById('workers-grid'),
@@ -95,7 +94,6 @@ function updateStatsUI() {
95
94
  elements.statReady.textContent = state.stats.ready.toLocaleString();
96
95
  elements.statRunning.textContent = state.stats.running.toLocaleString();
97
96
  elements.statDone.textContent = state.stats.done.toLocaleString();
98
- elements.statFailed.textContent = state.stats.failed.toLocaleString();
99
97
  elements.statDead.textContent = state.stats.dead.toLocaleString();
100
98
  }
101
99
 
@@ -60,19 +60,6 @@
60
60
  <span class="stat-label">Done</span>
61
61
  </div>
62
62
  </div>
63
- <div class="stat-card failed">
64
- <div class="stat-icon">
65
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
66
- <circle cx="12" cy="12" r="10"/>
67
- <line x1="12" y1="8" x2="12" y2="12"/>
68
- <line x1="12" y1="16" x2="12.01" y2="16"/>
69
- </svg>
70
- </div>
71
- <div class="stat-content">
72
- <span class="stat-value" id="stat-failed">0</span>
73
- <span class="stat-label">Failed</span>
74
- </div>
75
- </div>
76
63
  <div class="stat-card dead">
77
64
  <div class="stat-icon">
78
65
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -105,7 +92,6 @@
105
92
  <option value="ready">Ready</option>
106
93
  <option value="running">Running</option>
107
94
  <option value="done">Done</option>
108
- <option value="failed">Failed</option>
109
95
  <option value="dead">Dead</option>
110
96
  </select>
111
97
  </div>
package/dist/cli.cjs CHANGED
@@ -44,12 +44,12 @@ Commands:
44
44
  export <db> [output.csv] Export jobs to CSV (default: stdout)
45
45
  import <db> <input.csv> Import jobs from CSV
46
46
  purge <db> --status=<status> Delete jobs by status
47
- retry <db> [--status=dead] Retry dead/failed jobs (reset to ready)
47
+ retry <db> [--status=dead] Retry dead jobs (reset to ready)
48
48
  pause <db> <queue> Pause a queue (running workers stop claiming)
49
49
  resume <db> <queue> Resume a paused queue
50
50
 
51
51
  Options:
52
- --status=<status> Filter by status (ready|running|done|failed|dead)
52
+ --status=<status> Filter by status (ready|running|done|dead)
53
53
  --queue=<queue> Filter by queue name
54
54
  --limit=<n> Limit number of results (default: 100)
55
55
 
@@ -57,7 +57,7 @@ Examples:
57
57
  workmatic stats ./jobs.db
58
58
  workmatic list ./jobs.db --status=dead --limit=10
59
59
  workmatic export ./jobs.db backup.csv
60
- workmatic export ./jobs.db --status=failed > failed-jobs.csv
60
+ workmatic export ./jobs.db --status=dead > dead-jobs.csv
61
61
  workmatic import ./jobs.db backup.csv
62
62
  workmatic purge ./jobs.db --status=done
63
63
  workmatic retry ./jobs.db --status=dead
@@ -132,12 +132,11 @@ async function cmdStats(dbPath) {
132
132
  `).all();
133
133
  const total = stats.reduce((sum, s) => sum + s.count, 0);
134
134
  console.log("\n\u{1F4CA} Job Statistics\n");
135
- const statusOrder = ["ready", "running", "done", "failed", "dead"];
135
+ const statusOrder = ["ready", "running", "done", "dead"];
136
136
  const statusEmoji = {
137
137
  ready: "\u23F3",
138
138
  running: "\u25B6\uFE0F",
139
139
  done: "\u2705",
140
- failed: "\u26A0\uFE0F",
141
140
  dead: "\u{1F480}"
142
141
  };
143
142
  const statusTable = new import_cli_table3.default({
@@ -194,7 +193,6 @@ async function cmdList(dbPath, options) {
194
193
  ready: "\u23F3",
195
194
  running: "\u25B6\uFE0F",
196
195
  done: "\u2705",
197
- failed: "\u26A0\uFE0F",
198
196
  dead: "\u{1F480}"
199
197
  };
200
198
  const table = new import_cli_table3.default({
package/dist/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Workmatic CLI\n * \n * Commands:\n * stats <db> Show job statistics\n * export <db> [output] Export jobs to CSV\n * import <db> <input> Import jobs from CSV\n * purge <db> [--status=done] Delete jobs by status\n * retry <db> [--status=dead] Retry failed/dead jobs\n * pause <db> <queue> Pause a queue (workers stop claiming)\n * resume <db> <queue> Resume a paused queue\n */\n\nimport Database from 'better-sqlite3';\nimport { createReadStream, createWriteStream } from 'fs';\nimport { createInterface } from 'readline';\nimport Table from 'cli-table3';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printUsage(): void {\n console.log(`\nWorkmatic CLI - Job Queue Management\n\nUsage:\n workmatic <command> [options]\n\nCommands:\n stats <db> Show job statistics\n queues <db> List all queues with status\n list <db> [--status=<status>] List jobs (optionally filter by status)\n export <db> [output.csv] Export jobs to CSV (default: stdout)\n import <db> <input.csv> Import jobs from CSV\n purge <db> --status=<status> Delete jobs by status\n retry <db> [--status=dead] Retry dead/failed jobs (reset to ready)\n pause <db> <queue> Pause a queue (running workers stop claiming)\n resume <db> <queue> Resume a paused queue\n\nOptions:\n --status=<status> Filter by status (ready|running|done|failed|dead)\n --queue=<queue> Filter by queue name\n --limit=<n> Limit number of results (default: 100)\n\nExamples:\n workmatic stats ./jobs.db\n workmatic list ./jobs.db --status=dead --limit=10\n workmatic export ./jobs.db backup.csv\n workmatic export ./jobs.db --status=failed > failed-jobs.csv\n workmatic import ./jobs.db backup.csv\n workmatic purge ./jobs.db --status=done\n workmatic retry ./jobs.db --status=dead\n workmatic pause ./jobs.db emails\n workmatic resume ./jobs.db emails\n`);\n}\n\nfunction parseOptions(args: string[]): Record<string, string> {\n const options: Record<string, string> = {};\n for (const arg of args) {\n if (arg.startsWith('--')) {\n const [key, value] = arg.slice(2).split('=');\n options[key] = value ?? 'true';\n }\n }\n return options;\n}\n\nfunction getPositionalArgs(args: string[]): string[] {\n return args.filter(arg => !arg.startsWith('--'));\n}\n\n// CSV helpers\nfunction escapeCSV(value: string | null): string {\n if (value === null) return '';\n const str = String(value);\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\nfunction parseCSVLine(line: string): string[] {\n const result: string[] = [];\n let current = '';\n let inQuotes = false;\n \n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n \n if (inQuotes) {\n if (char === '\"') {\n if (line[i + 1] === '\"') {\n current += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n current += char;\n }\n } else {\n if (char === '\"') {\n inQuotes = true;\n } else if (char === ',') {\n result.push(current);\n current = '';\n } else {\n current += char;\n }\n }\n }\n \n result.push(current);\n return result;\n}\n\n// Commands\nasync function cmdStats(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n const stats = db.prepare(`\n SELECT status, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY status\n `).all() as { status: string; count: number }[];\n \n const queues = db.prepare(`\n SELECT queue, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY queue\n `).all() as { queue: string; count: number }[];\n \n const total = stats.reduce((sum, s) => sum + s.count, 0);\n \n console.log('\\nπŸ“Š Job Statistics\\n');\n \n const statusOrder = ['ready', 'running', 'done', 'failed', 'dead'];\n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▢️',\n done: 'βœ…',\n failed: '⚠️',\n dead: 'πŸ’€',\n };\n \n const statusTable = new Table({\n head: ['', 'Status', 'Count'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right'],\n });\n \n for (const status of statusOrder) {\n const stat = stats.find(s => s.status === status);\n const count = stat?.count ?? 0;\n const emoji = statusEmoji[status] || '';\n statusTable.push([emoji, status, count.toLocaleString()]);\n }\n statusTable.push([{ colSpan: 2, content: 'Total', hAlign: 'right' }, total.toLocaleString()]);\n \n console.log(statusTable.toString());\n \n if (queues.length > 0) {\n console.log('\\nπŸ“‹ By Queue\\n');\n \n const queueTable = new Table({\n head: ['Queue', 'Jobs'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'right'],\n });\n \n for (const q of queues) {\n queueTable.push([q.queue, q.count.toLocaleString()]);\n }\n \n console.log(queueTable.toString());\n }\n \n console.log();\n } finally {\n db.close();\n }\n}\n\nasync function cmdList(dbPath: string, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT public_id, queue, status, priority, attempts, max_attempts, created_at, last_error FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY created_at DESC LIMIT ?';\n params.push(parseInt(options.limit || '100', 10));\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n if (jobs.length === 0) {\n console.log('No jobs found.');\n return;\n }\n \n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▢️',\n done: 'βœ…',\n failed: '⚠️',\n dead: 'πŸ’€',\n };\n \n const table = new Table({\n head: ['ID', 'Queue', 'Status', 'Pri', 'Attempts', 'Created'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'left', 'right', 'center', 'left'],\n colWidths: [24, 16, 12, 5, 10, 21],\n wordWrap: true,\n });\n \n for (const job of jobs) {\n const emoji = statusEmoji[job.status] || '';\n table.push([\n job.public_id.slice(0, 21),\n job.queue.slice(0, 14),\n `${emoji} ${job.status}`,\n job.priority,\n `${job.attempts}/${job.max_attempts}`,\n new Date(job.created_at).toISOString().slice(0, 19),\n ]);\n }\n \n console.log();\n console.log(table.toString());\n console.log(`\\n Showing ${jobs.length} jobs\\n`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdExport(dbPath: string, outputPath: string | undefined, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT * FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY id';\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n const output = outputPath ? createWriteStream(outputPath) : process.stdout;\n \n // Header\n const columns = [\n 'public_id', 'queue', 'payload', 'status', 'priority',\n 'run_at', 'attempts', 'max_attempts', 'lease_until',\n 'created_at', 'updated_at', 'last_error'\n ];\n \n if (outputPath) {\n output.write(columns.join(',') + '\\n');\n } else {\n console.log(columns.join(','));\n }\n \n // Rows\n for (const job of jobs) {\n const row = columns.map(col => escapeCSV(job[col]));\n if (outputPath) {\n output.write(row.join(',') + '\\n');\n } else {\n console.log(row.join(','));\n }\n }\n \n if (outputPath) {\n (output as any).end();\n console.error(`βœ… Exported ${jobs.length} jobs to ${outputPath}`);\n }\n } finally {\n db.close();\n }\n}\n\nasync function cmdImport(dbPath: string, inputPath: string): Promise<void> {\n const db = new Database(dbPath);\n \n // Ensure schema exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n public_id TEXT UNIQUE NOT NULL,\n queue TEXT NOT NULL,\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'ready',\n priority INTEGER NOT NULL DEFAULT 0,\n run_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n lease_until INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n last_error TEXT\n )\n `);\n \n const insert = db.prepare(`\n INSERT OR REPLACE INTO workmatic_jobs \n (public_id, queue, payload, status, priority, run_at, attempts, max_attempts, lease_until, created_at, updated_at, last_error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const rl = createInterface({\n input: createReadStream(inputPath),\n crlfDelay: Infinity,\n });\n \n let lineNum = 0;\n let imported = 0;\n let columns: string[] = [];\n \n try {\n const insertMany = db.transaction((rows: any[]) => {\n for (const row of rows) {\n insert.run(\n row.public_id,\n row.queue,\n row.payload,\n row.status,\n parseInt(row.priority, 10) || 0,\n parseInt(row.run_at, 10) || Date.now(),\n parseInt(row.attempts, 10) || 0,\n parseInt(row.max_attempts, 10) || 3,\n parseInt(row.lease_until, 10) || 0,\n parseInt(row.created_at, 10) || Date.now(),\n parseInt(row.updated_at, 10) || Date.now(),\n row.last_error || null\n );\n imported++;\n }\n });\n \n const batch: any[] = [];\n \n for await (const line of rl) {\n lineNum++;\n \n if (lineNum === 1) {\n // Header row\n columns = parseCSVLine(line);\n continue;\n }\n \n if (!line.trim()) continue;\n \n const values = parseCSVLine(line);\n const row: Record<string, string> = {};\n \n for (let i = 0; i < columns.length; i++) {\n row[columns[i]] = values[i] ?? '';\n }\n \n batch.push(row);\n \n // Batch insert every 1000 rows\n if (batch.length >= 1000) {\n insertMany(batch);\n batch.length = 0;\n process.stderr.write(`\\rImported ${imported} jobs...`);\n }\n }\n \n // Insert remaining\n if (batch.length > 0) {\n insertMany(batch);\n }\n \n console.log(`\\nβœ… Imported ${imported} jobs from ${inputPath}`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPurge(dbPath: string, options: Record<string, string>): Promise<void> {\n if (!options.status) {\n console.error('Error: --status is required for purge command');\n console.error('Example: workmatic purge ./jobs.db --status=done');\n process.exit(1);\n }\n \n const db = new Database(dbPath);\n \n try {\n const result = db.prepare('DELETE FROM workmatic_jobs WHERE status = ?').run(options.status);\n console.log(`πŸ—‘οΈ Deleted ${result.changes} jobs with status '${options.status}'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdRetry(dbPath: string, options: Record<string, string>): Promise<void> {\n const status = options.status || 'dead';\n const db = new Database(dbPath);\n \n try {\n const now = Date.now();\n const result = db.prepare(`\n UPDATE workmatic_jobs \n SET status = 'ready', \n attempts = 0, \n run_at = ?, \n lease_until = 0,\n updated_at = ?\n WHERE status = ?\n `).run(now, now, status);\n \n console.log(`πŸ”„ Reset ${result.changes} jobs from '${status}' to 'ready'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPause(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 1, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 1, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`⏸️ Paused queue '${queueName}'`);\n console.log(` Running workers will stop claiming new jobs.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdResume(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 0, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 0, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`▢️ Resumed queue '${queueName}'`);\n console.log(` Workers will start claiming jobs again.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdQueues(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n // Check if settings table exists\n const settingsExists = db.prepare(`\n SELECT name FROM sqlite_master WHERE type='table' AND name='workmatic_settings'\n `).get();\n \n // Get all queues with their job counts and pause state\n let queues: { queue: string; total: number; ready: number; running: number; paused: number }[];\n \n if (settingsExists) {\n queues = db.prepare(`\n SELECT \n j.queue,\n COUNT(*) as total,\n SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,\n COALESCE(s.paused, 0) as paused\n FROM workmatic_jobs j\n LEFT JOIN workmatic_settings s ON j.queue = s.queue\n GROUP BY j.queue\n `).all() as typeof queues;\n } else {\n queues = db.prepare(`\n SELECT \n queue,\n COUNT(*) as total,\n SUM(CASE WHEN status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running,\n 0 as paused\n FROM workmatic_jobs\n GROUP BY queue\n `).all() as typeof queues;\n }\n \n if (queues.length === 0) {\n console.log('No queues found.');\n return;\n }\n \n console.log('\\nπŸ“‹ Queues\\n');\n \n const table = new Table({\n head: ['Queue', 'Status', 'Ready', 'Running', 'Total'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right', 'right', 'right'],\n });\n \n for (const q of queues) {\n const status = q.paused ? '⏸️ PAUSED' : '▢️ ACTIVE';\n table.push([\n q.queue,\n status,\n q.ready.toLocaleString(),\n q.running.toLocaleString(),\n q.total.toLocaleString(),\n ]);\n }\n \n console.log(table.toString());\n console.log();\n } finally {\n db.close();\n }\n}\n\n// Main\nasync function main(): Promise<void> {\n if (!command || command === '--help' || command === '-h') {\n printUsage();\n process.exit(0);\n }\n \n const positionalArgs = getPositionalArgs(args.slice(1));\n const options = parseOptions(args);\n \n const dbPath = positionalArgs[0];\n \n if (!dbPath) {\n console.error('Error: Database path is required');\n printUsage();\n process.exit(1);\n }\n \n try {\n switch (command) {\n case 'stats':\n await cmdStats(dbPath);\n break;\n \n case 'list':\n await cmdList(dbPath, options);\n break;\n \n case 'export':\n await cmdExport(dbPath, positionalArgs[1], options);\n break;\n \n case 'import':\n if (!positionalArgs[1]) {\n console.error('Error: Input CSV file is required');\n process.exit(1);\n }\n await cmdImport(dbPath, positionalArgs[1]);\n break;\n \n case 'purge':\n await cmdPurge(dbPath, options);\n break;\n \n case 'retry':\n await cmdRetry(dbPath, options);\n break;\n \n case 'pause':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic pause ./jobs.db emails');\n process.exit(1);\n }\n await cmdPause(dbPath, positionalArgs[1]);\n break;\n \n case 'resume':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic resume ./jobs.db emails');\n process.exit(1);\n }\n await cmdResume(dbPath, positionalArgs[1]);\n break;\n \n case 'queues':\n await cmdQueues(dbPath);\n break;\n \n default:\n console.error(`Unknown command: ${command}`);\n printUsage();\n process.exit(1);\n }\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":["args","Database","Table"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Workmatic CLI\n * \n * Commands:\n * stats <db> Show job statistics\n * export <db> [output] Export jobs to CSV\n * import <db> <input> Import jobs from CSV\n * purge <db> [--status=done] Delete jobs by status\n * retry <db> [--status=dead] Retry dead jobs (reset to ready)\n * pause <db> <queue> Pause a queue (workers stop claiming)\n * resume <db> <queue> Resume a paused queue\n */\n\nimport Database from 'better-sqlite3';\nimport { createReadStream, createWriteStream } from 'fs';\nimport { createInterface } from 'readline';\nimport Table from 'cli-table3';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printUsage(): void {\n console.log(`\nWorkmatic CLI - Job Queue Management\n\nUsage:\n workmatic <command> [options]\n\nCommands:\n stats <db> Show job statistics\n queues <db> List all queues with status\n list <db> [--status=<status>] List jobs (optionally filter by status)\n export <db> [output.csv] Export jobs to CSV (default: stdout)\n import <db> <input.csv> Import jobs from CSV\n purge <db> --status=<status> Delete jobs by status\n retry <db> [--status=dead] Retry dead jobs (reset to ready)\n pause <db> <queue> Pause a queue (running workers stop claiming)\n resume <db> <queue> Resume a paused queue\n\nOptions:\n --status=<status> Filter by status (ready|running|done|dead)\n --queue=<queue> Filter by queue name\n --limit=<n> Limit number of results (default: 100)\n\nExamples:\n workmatic stats ./jobs.db\n workmatic list ./jobs.db --status=dead --limit=10\n workmatic export ./jobs.db backup.csv\n workmatic export ./jobs.db --status=dead > dead-jobs.csv\n workmatic import ./jobs.db backup.csv\n workmatic purge ./jobs.db --status=done\n workmatic retry ./jobs.db --status=dead\n workmatic pause ./jobs.db emails\n workmatic resume ./jobs.db emails\n`);\n}\n\nfunction parseOptions(args: string[]): Record<string, string> {\n const options: Record<string, string> = {};\n for (const arg of args) {\n if (arg.startsWith('--')) {\n const [key, value] = arg.slice(2).split('=');\n options[key] = value ?? 'true';\n }\n }\n return options;\n}\n\nfunction getPositionalArgs(args: string[]): string[] {\n return args.filter(arg => !arg.startsWith('--'));\n}\n\n// CSV helpers\nfunction escapeCSV(value: string | null): string {\n if (value === null) return '';\n const str = String(value);\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\nfunction parseCSVLine(line: string): string[] {\n const result: string[] = [];\n let current = '';\n let inQuotes = false;\n \n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n \n if (inQuotes) {\n if (char === '\"') {\n if (line[i + 1] === '\"') {\n current += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n current += char;\n }\n } else {\n if (char === '\"') {\n inQuotes = true;\n } else if (char === ',') {\n result.push(current);\n current = '';\n } else {\n current += char;\n }\n }\n }\n \n result.push(current);\n return result;\n}\n\n// Commands\nasync function cmdStats(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n const stats = db.prepare(`\n SELECT status, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY status\n `).all() as { status: string; count: number }[];\n \n const queues = db.prepare(`\n SELECT queue, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY queue\n `).all() as { queue: string; count: number }[];\n \n const total = stats.reduce((sum, s) => sum + s.count, 0);\n \n console.log('\\nπŸ“Š Job Statistics\\n');\n \n const statusOrder = ['ready', 'running', 'done', 'dead'];\n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▢️',\n done: 'βœ…',\n dead: 'πŸ’€',\n };\n \n const statusTable = new Table({\n head: ['', 'Status', 'Count'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right'],\n });\n \n for (const status of statusOrder) {\n const stat = stats.find(s => s.status === status);\n const count = stat?.count ?? 0;\n const emoji = statusEmoji[status] || '';\n statusTable.push([emoji, status, count.toLocaleString()]);\n }\n statusTable.push([{ colSpan: 2, content: 'Total', hAlign: 'right' }, total.toLocaleString()]);\n \n console.log(statusTable.toString());\n \n if (queues.length > 0) {\n console.log('\\nπŸ“‹ By Queue\\n');\n \n const queueTable = new Table({\n head: ['Queue', 'Jobs'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'right'],\n });\n \n for (const q of queues) {\n queueTable.push([q.queue, q.count.toLocaleString()]);\n }\n \n console.log(queueTable.toString());\n }\n \n console.log();\n } finally {\n db.close();\n }\n}\n\nasync function cmdList(dbPath: string, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT public_id, queue, status, priority, attempts, max_attempts, created_at, last_error FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY created_at DESC LIMIT ?';\n params.push(parseInt(options.limit || '100', 10));\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n if (jobs.length === 0) {\n console.log('No jobs found.');\n return;\n }\n \n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▢️',\n done: 'βœ…',\n dead: 'πŸ’€',\n };\n \n const table = new Table({\n head: ['ID', 'Queue', 'Status', 'Pri', 'Attempts', 'Created'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'left', 'right', 'center', 'left'],\n colWidths: [24, 16, 12, 5, 10, 21],\n wordWrap: true,\n });\n \n for (const job of jobs) {\n const emoji = statusEmoji[job.status] || '';\n table.push([\n job.public_id.slice(0, 21),\n job.queue.slice(0, 14),\n `${emoji} ${job.status}`,\n job.priority,\n `${job.attempts}/${job.max_attempts}`,\n new Date(job.created_at).toISOString().slice(0, 19),\n ]);\n }\n \n console.log();\n console.log(table.toString());\n console.log(`\\n Showing ${jobs.length} jobs\\n`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdExport(dbPath: string, outputPath: string | undefined, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT * FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY id';\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n const output = outputPath ? createWriteStream(outputPath) : process.stdout;\n \n // Header\n const columns = [\n 'public_id', 'queue', 'payload', 'status', 'priority',\n 'run_at', 'attempts', 'max_attempts', 'lease_until',\n 'created_at', 'updated_at', 'last_error'\n ];\n \n if (outputPath) {\n output.write(columns.join(',') + '\\n');\n } else {\n console.log(columns.join(','));\n }\n \n // Rows\n for (const job of jobs) {\n const row = columns.map(col => escapeCSV(job[col]));\n if (outputPath) {\n output.write(row.join(',') + '\\n');\n } else {\n console.log(row.join(','));\n }\n }\n \n if (outputPath) {\n (output as any).end();\n console.error(`βœ… Exported ${jobs.length} jobs to ${outputPath}`);\n }\n } finally {\n db.close();\n }\n}\n\nasync function cmdImport(dbPath: string, inputPath: string): Promise<void> {\n const db = new Database(dbPath);\n \n // Ensure schema exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n public_id TEXT UNIQUE NOT NULL,\n queue TEXT NOT NULL,\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'ready',\n priority INTEGER NOT NULL DEFAULT 0,\n run_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n lease_until INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n last_error TEXT\n )\n `);\n \n const insert = db.prepare(`\n INSERT OR REPLACE INTO workmatic_jobs \n (public_id, queue, payload, status, priority, run_at, attempts, max_attempts, lease_until, created_at, updated_at, last_error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const rl = createInterface({\n input: createReadStream(inputPath),\n crlfDelay: Infinity,\n });\n \n let lineNum = 0;\n let imported = 0;\n let columns: string[] = [];\n \n try {\n const insertMany = db.transaction((rows: any[]) => {\n for (const row of rows) {\n insert.run(\n row.public_id,\n row.queue,\n row.payload,\n row.status,\n parseInt(row.priority, 10) || 0,\n parseInt(row.run_at, 10) || Date.now(),\n parseInt(row.attempts, 10) || 0,\n parseInt(row.max_attempts, 10) || 3,\n parseInt(row.lease_until, 10) || 0,\n parseInt(row.created_at, 10) || Date.now(),\n parseInt(row.updated_at, 10) || Date.now(),\n row.last_error || null\n );\n imported++;\n }\n });\n \n const batch: any[] = [];\n \n for await (const line of rl) {\n lineNum++;\n \n if (lineNum === 1) {\n // Header row\n columns = parseCSVLine(line);\n continue;\n }\n \n if (!line.trim()) continue;\n \n const values = parseCSVLine(line);\n const row: Record<string, string> = {};\n \n for (let i = 0; i < columns.length; i++) {\n row[columns[i]] = values[i] ?? '';\n }\n \n batch.push(row);\n \n // Batch insert every 1000 rows\n if (batch.length >= 1000) {\n insertMany(batch);\n batch.length = 0;\n process.stderr.write(`\\rImported ${imported} jobs...`);\n }\n }\n \n // Insert remaining\n if (batch.length > 0) {\n insertMany(batch);\n }\n \n console.log(`\\nβœ… Imported ${imported} jobs from ${inputPath}`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPurge(dbPath: string, options: Record<string, string>): Promise<void> {\n if (!options.status) {\n console.error('Error: --status is required for purge command');\n console.error('Example: workmatic purge ./jobs.db --status=done');\n process.exit(1);\n }\n \n const db = new Database(dbPath);\n \n try {\n const result = db.prepare('DELETE FROM workmatic_jobs WHERE status = ?').run(options.status);\n console.log(`πŸ—‘οΈ Deleted ${result.changes} jobs with status '${options.status}'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdRetry(dbPath: string, options: Record<string, string>): Promise<void> {\n const status = options.status || 'dead';\n const db = new Database(dbPath);\n \n try {\n const now = Date.now();\n const result = db.prepare(`\n UPDATE workmatic_jobs \n SET status = 'ready', \n attempts = 0, \n run_at = ?, \n lease_until = 0,\n updated_at = ?\n WHERE status = ?\n `).run(now, now, status);\n \n console.log(`πŸ”„ Reset ${result.changes} jobs from '${status}' to 'ready'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPause(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 1, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 1, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`⏸️ Paused queue '${queueName}'`);\n console.log(` Running workers will stop claiming new jobs.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdResume(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 0, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 0, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`▢️ Resumed queue '${queueName}'`);\n console.log(` Workers will start claiming jobs again.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdQueues(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n // Check if settings table exists\n const settingsExists = db.prepare(`\n SELECT name FROM sqlite_master WHERE type='table' AND name='workmatic_settings'\n `).get();\n \n // Get all queues with their job counts and pause state\n let queues: { queue: string; total: number; ready: number; running: number; paused: number }[];\n \n if (settingsExists) {\n queues = db.prepare(`\n SELECT \n j.queue,\n COUNT(*) as total,\n SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,\n COALESCE(s.paused, 0) as paused\n FROM workmatic_jobs j\n LEFT JOIN workmatic_settings s ON j.queue = s.queue\n GROUP BY j.queue\n `).all() as typeof queues;\n } else {\n queues = db.prepare(`\n SELECT \n queue,\n COUNT(*) as total,\n SUM(CASE WHEN status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running,\n 0 as paused\n FROM workmatic_jobs\n GROUP BY queue\n `).all() as typeof queues;\n }\n \n if (queues.length === 0) {\n console.log('No queues found.');\n return;\n }\n \n console.log('\\nπŸ“‹ Queues\\n');\n \n const table = new Table({\n head: ['Queue', 'Status', 'Ready', 'Running', 'Total'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right', 'right', 'right'],\n });\n \n for (const q of queues) {\n const status = q.paused ? '⏸️ PAUSED' : '▢️ ACTIVE';\n table.push([\n q.queue,\n status,\n q.ready.toLocaleString(),\n q.running.toLocaleString(),\n q.total.toLocaleString(),\n ]);\n }\n \n console.log(table.toString());\n console.log();\n } finally {\n db.close();\n }\n}\n\n// Main\nasync function main(): Promise<void> {\n if (!command || command === '--help' || command === '-h') {\n printUsage();\n process.exit(0);\n }\n \n const positionalArgs = getPositionalArgs(args.slice(1));\n const options = parseOptions(args);\n \n const dbPath = positionalArgs[0];\n \n if (!dbPath) {\n console.error('Error: Database path is required');\n printUsage();\n process.exit(1);\n }\n \n try {\n switch (command) {\n case 'stats':\n await cmdStats(dbPath);\n break;\n \n case 'list':\n await cmdList(dbPath, options);\n break;\n \n case 'export':\n await cmdExport(dbPath, positionalArgs[1], options);\n break;\n \n case 'import':\n if (!positionalArgs[1]) {\n console.error('Error: Input CSV file is required');\n process.exit(1);\n }\n await cmdImport(dbPath, positionalArgs[1]);\n break;\n \n case 'purge':\n await cmdPurge(dbPath, options);\n break;\n \n case 'retry':\n await cmdRetry(dbPath, options);\n break;\n \n case 'pause':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic pause ./jobs.db emails');\n process.exit(1);\n }\n await cmdPause(dbPath, positionalArgs[1]);\n break;\n \n case 'resume':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic resume ./jobs.db emails');\n process.exit(1);\n }\n await cmdResume(dbPath, positionalArgs[1]);\n break;\n \n case 'queues':\n await cmdQueues(dbPath);\n break;\n \n default:\n console.error(`Unknown command: ${command}`);\n printUsage();\n process.exit(1);\n }\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":["args","Database","Table"]}
package/dist/cli.js CHANGED
@@ -21,12 +21,12 @@ Commands:
21
21
  export <db> [output.csv] Export jobs to CSV (default: stdout)
22
22
  import <db> <input.csv> Import jobs from CSV
23
23
  purge <db> --status=<status> Delete jobs by status
24
- retry <db> [--status=dead] Retry dead/failed jobs (reset to ready)
24
+ retry <db> [--status=dead] Retry dead jobs (reset to ready)
25
25
  pause <db> <queue> Pause a queue (running workers stop claiming)
26
26
  resume <db> <queue> Resume a paused queue
27
27
 
28
28
  Options:
29
- --status=<status> Filter by status (ready|running|done|failed|dead)
29
+ --status=<status> Filter by status (ready|running|done|dead)
30
30
  --queue=<queue> Filter by queue name
31
31
  --limit=<n> Limit number of results (default: 100)
32
32
 
@@ -34,7 +34,7 @@ Examples:
34
34
  workmatic stats ./jobs.db
35
35
  workmatic list ./jobs.db --status=dead --limit=10
36
36
  workmatic export ./jobs.db backup.csv
37
- workmatic export ./jobs.db --status=failed > failed-jobs.csv
37
+ workmatic export ./jobs.db --status=dead > dead-jobs.csv
38
38
  workmatic import ./jobs.db backup.csv
39
39
  workmatic purge ./jobs.db --status=done
40
40
  workmatic retry ./jobs.db --status=dead
@@ -109,12 +109,11 @@ async function cmdStats(dbPath) {
109
109
  `).all();
110
110
  const total = stats.reduce((sum, s) => sum + s.count, 0);
111
111
  console.log("\n\u{1F4CA} Job Statistics\n");
112
- const statusOrder = ["ready", "running", "done", "failed", "dead"];
112
+ const statusOrder = ["ready", "running", "done", "dead"];
113
113
  const statusEmoji = {
114
114
  ready: "\u23F3",
115
115
  running: "\u25B6\uFE0F",
116
116
  done: "\u2705",
117
- failed: "\u26A0\uFE0F",
118
117
  dead: "\u{1F480}"
119
118
  };
120
119
  const statusTable = new Table({
@@ -171,7 +170,6 @@ async function cmdList(dbPath, options) {
171
170
  ready: "\u23F3",
172
171
  running: "\u25B6\uFE0F",
173
172
  done: "\u2705",
174
- failed: "\u26A0\uFE0F",
175
173
  dead: "\u{1F480}"
176
174
  };
177
175
  const table = new Table({
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Workmatic CLI\n * \n * Commands:\n * stats <db> Show job statistics\n * export <db> [output] Export jobs to CSV\n * import <db> <input> Import jobs from CSV\n * purge <db> [--status=done] Delete jobs by status\n * retry <db> [--status=dead] Retry failed/dead jobs\n * pause <db> <queue> Pause a queue (workers stop claiming)\n * resume <db> <queue> Resume a paused queue\n */\n\nimport Database from 'better-sqlite3';\nimport { createReadStream, createWriteStream } from 'fs';\nimport { createInterface } from 'readline';\nimport Table from 'cli-table3';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printUsage(): void {\n console.log(`\nWorkmatic CLI - Job Queue Management\n\nUsage:\n workmatic <command> [options]\n\nCommands:\n stats <db> Show job statistics\n queues <db> List all queues with status\n list <db> [--status=<status>] List jobs (optionally filter by status)\n export <db> [output.csv] Export jobs to CSV (default: stdout)\n import <db> <input.csv> Import jobs from CSV\n purge <db> --status=<status> Delete jobs by status\n retry <db> [--status=dead] Retry dead/failed jobs (reset to ready)\n pause <db> <queue> Pause a queue (running workers stop claiming)\n resume <db> <queue> Resume a paused queue\n\nOptions:\n --status=<status> Filter by status (ready|running|done|failed|dead)\n --queue=<queue> Filter by queue name\n --limit=<n> Limit number of results (default: 100)\n\nExamples:\n workmatic stats ./jobs.db\n workmatic list ./jobs.db --status=dead --limit=10\n workmatic export ./jobs.db backup.csv\n workmatic export ./jobs.db --status=failed > failed-jobs.csv\n workmatic import ./jobs.db backup.csv\n workmatic purge ./jobs.db --status=done\n workmatic retry ./jobs.db --status=dead\n workmatic pause ./jobs.db emails\n workmatic resume ./jobs.db emails\n`);\n}\n\nfunction parseOptions(args: string[]): Record<string, string> {\n const options: Record<string, string> = {};\n for (const arg of args) {\n if (arg.startsWith('--')) {\n const [key, value] = arg.slice(2).split('=');\n options[key] = value ?? 'true';\n }\n }\n return options;\n}\n\nfunction getPositionalArgs(args: string[]): string[] {\n return args.filter(arg => !arg.startsWith('--'));\n}\n\n// CSV helpers\nfunction escapeCSV(value: string | null): string {\n if (value === null) return '';\n const str = String(value);\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\nfunction parseCSVLine(line: string): string[] {\n const result: string[] = [];\n let current = '';\n let inQuotes = false;\n \n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n \n if (inQuotes) {\n if (char === '\"') {\n if (line[i + 1] === '\"') {\n current += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n current += char;\n }\n } else {\n if (char === '\"') {\n inQuotes = true;\n } else if (char === ',') {\n result.push(current);\n current = '';\n } else {\n current += char;\n }\n }\n }\n \n result.push(current);\n return result;\n}\n\n// Commands\nasync function cmdStats(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n const stats = db.prepare(`\n SELECT status, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY status\n `).all() as { status: string; count: number }[];\n \n const queues = db.prepare(`\n SELECT queue, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY queue\n `).all() as { queue: string; count: number }[];\n \n const total = stats.reduce((sum, s) => sum + s.count, 0);\n \n console.log('\\nπŸ“Š Job Statistics\\n');\n \n const statusOrder = ['ready', 'running', 'done', 'failed', 'dead'];\n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▢️',\n done: 'βœ…',\n failed: '⚠️',\n dead: 'πŸ’€',\n };\n \n const statusTable = new Table({\n head: ['', 'Status', 'Count'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right'],\n });\n \n for (const status of statusOrder) {\n const stat = stats.find(s => s.status === status);\n const count = stat?.count ?? 0;\n const emoji = statusEmoji[status] || '';\n statusTable.push([emoji, status, count.toLocaleString()]);\n }\n statusTable.push([{ colSpan: 2, content: 'Total', hAlign: 'right' }, total.toLocaleString()]);\n \n console.log(statusTable.toString());\n \n if (queues.length > 0) {\n console.log('\\nπŸ“‹ By Queue\\n');\n \n const queueTable = new Table({\n head: ['Queue', 'Jobs'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'right'],\n });\n \n for (const q of queues) {\n queueTable.push([q.queue, q.count.toLocaleString()]);\n }\n \n console.log(queueTable.toString());\n }\n \n console.log();\n } finally {\n db.close();\n }\n}\n\nasync function cmdList(dbPath: string, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT public_id, queue, status, priority, attempts, max_attempts, created_at, last_error FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY created_at DESC LIMIT ?';\n params.push(parseInt(options.limit || '100', 10));\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n if (jobs.length === 0) {\n console.log('No jobs found.');\n return;\n }\n \n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▢️',\n done: 'βœ…',\n failed: '⚠️',\n dead: 'πŸ’€',\n };\n \n const table = new Table({\n head: ['ID', 'Queue', 'Status', 'Pri', 'Attempts', 'Created'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'left', 'right', 'center', 'left'],\n colWidths: [24, 16, 12, 5, 10, 21],\n wordWrap: true,\n });\n \n for (const job of jobs) {\n const emoji = statusEmoji[job.status] || '';\n table.push([\n job.public_id.slice(0, 21),\n job.queue.slice(0, 14),\n `${emoji} ${job.status}`,\n job.priority,\n `${job.attempts}/${job.max_attempts}`,\n new Date(job.created_at).toISOString().slice(0, 19),\n ]);\n }\n \n console.log();\n console.log(table.toString());\n console.log(`\\n Showing ${jobs.length} jobs\\n`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdExport(dbPath: string, outputPath: string | undefined, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT * FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY id';\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n const output = outputPath ? createWriteStream(outputPath) : process.stdout;\n \n // Header\n const columns = [\n 'public_id', 'queue', 'payload', 'status', 'priority',\n 'run_at', 'attempts', 'max_attempts', 'lease_until',\n 'created_at', 'updated_at', 'last_error'\n ];\n \n if (outputPath) {\n output.write(columns.join(',') + '\\n');\n } else {\n console.log(columns.join(','));\n }\n \n // Rows\n for (const job of jobs) {\n const row = columns.map(col => escapeCSV(job[col]));\n if (outputPath) {\n output.write(row.join(',') + '\\n');\n } else {\n console.log(row.join(','));\n }\n }\n \n if (outputPath) {\n (output as any).end();\n console.error(`βœ… Exported ${jobs.length} jobs to ${outputPath}`);\n }\n } finally {\n db.close();\n }\n}\n\nasync function cmdImport(dbPath: string, inputPath: string): Promise<void> {\n const db = new Database(dbPath);\n \n // Ensure schema exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n public_id TEXT UNIQUE NOT NULL,\n queue TEXT NOT NULL,\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'ready',\n priority INTEGER NOT NULL DEFAULT 0,\n run_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n lease_until INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n last_error TEXT\n )\n `);\n \n const insert = db.prepare(`\n INSERT OR REPLACE INTO workmatic_jobs \n (public_id, queue, payload, status, priority, run_at, attempts, max_attempts, lease_until, created_at, updated_at, last_error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const rl = createInterface({\n input: createReadStream(inputPath),\n crlfDelay: Infinity,\n });\n \n let lineNum = 0;\n let imported = 0;\n let columns: string[] = [];\n \n try {\n const insertMany = db.transaction((rows: any[]) => {\n for (const row of rows) {\n insert.run(\n row.public_id,\n row.queue,\n row.payload,\n row.status,\n parseInt(row.priority, 10) || 0,\n parseInt(row.run_at, 10) || Date.now(),\n parseInt(row.attempts, 10) || 0,\n parseInt(row.max_attempts, 10) || 3,\n parseInt(row.lease_until, 10) || 0,\n parseInt(row.created_at, 10) || Date.now(),\n parseInt(row.updated_at, 10) || Date.now(),\n row.last_error || null\n );\n imported++;\n }\n });\n \n const batch: any[] = [];\n \n for await (const line of rl) {\n lineNum++;\n \n if (lineNum === 1) {\n // Header row\n columns = parseCSVLine(line);\n continue;\n }\n \n if (!line.trim()) continue;\n \n const values = parseCSVLine(line);\n const row: Record<string, string> = {};\n \n for (let i = 0; i < columns.length; i++) {\n row[columns[i]] = values[i] ?? '';\n }\n \n batch.push(row);\n \n // Batch insert every 1000 rows\n if (batch.length >= 1000) {\n insertMany(batch);\n batch.length = 0;\n process.stderr.write(`\\rImported ${imported} jobs...`);\n }\n }\n \n // Insert remaining\n if (batch.length > 0) {\n insertMany(batch);\n }\n \n console.log(`\\nβœ… Imported ${imported} jobs from ${inputPath}`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPurge(dbPath: string, options: Record<string, string>): Promise<void> {\n if (!options.status) {\n console.error('Error: --status is required for purge command');\n console.error('Example: workmatic purge ./jobs.db --status=done');\n process.exit(1);\n }\n \n const db = new Database(dbPath);\n \n try {\n const result = db.prepare('DELETE FROM workmatic_jobs WHERE status = ?').run(options.status);\n console.log(`πŸ—‘οΈ Deleted ${result.changes} jobs with status '${options.status}'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdRetry(dbPath: string, options: Record<string, string>): Promise<void> {\n const status = options.status || 'dead';\n const db = new Database(dbPath);\n \n try {\n const now = Date.now();\n const result = db.prepare(`\n UPDATE workmatic_jobs \n SET status = 'ready', \n attempts = 0, \n run_at = ?, \n lease_until = 0,\n updated_at = ?\n WHERE status = ?\n `).run(now, now, status);\n \n console.log(`πŸ”„ Reset ${result.changes} jobs from '${status}' to 'ready'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPause(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 1, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 1, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`⏸️ Paused queue '${queueName}'`);\n console.log(` Running workers will stop claiming new jobs.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdResume(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 0, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 0, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`▢️ Resumed queue '${queueName}'`);\n console.log(` Workers will start claiming jobs again.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdQueues(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n // Check if settings table exists\n const settingsExists = db.prepare(`\n SELECT name FROM sqlite_master WHERE type='table' AND name='workmatic_settings'\n `).get();\n \n // Get all queues with their job counts and pause state\n let queues: { queue: string; total: number; ready: number; running: number; paused: number }[];\n \n if (settingsExists) {\n queues = db.prepare(`\n SELECT \n j.queue,\n COUNT(*) as total,\n SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,\n COALESCE(s.paused, 0) as paused\n FROM workmatic_jobs j\n LEFT JOIN workmatic_settings s ON j.queue = s.queue\n GROUP BY j.queue\n `).all() as typeof queues;\n } else {\n queues = db.prepare(`\n SELECT \n queue,\n COUNT(*) as total,\n SUM(CASE WHEN status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running,\n 0 as paused\n FROM workmatic_jobs\n GROUP BY queue\n `).all() as typeof queues;\n }\n \n if (queues.length === 0) {\n console.log('No queues found.');\n return;\n }\n \n console.log('\\nπŸ“‹ Queues\\n');\n \n const table = new Table({\n head: ['Queue', 'Status', 'Ready', 'Running', 'Total'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right', 'right', 'right'],\n });\n \n for (const q of queues) {\n const status = q.paused ? '⏸️ PAUSED' : '▢️ ACTIVE';\n table.push([\n q.queue,\n status,\n q.ready.toLocaleString(),\n q.running.toLocaleString(),\n q.total.toLocaleString(),\n ]);\n }\n \n console.log(table.toString());\n console.log();\n } finally {\n db.close();\n }\n}\n\n// Main\nasync function main(): Promise<void> {\n if (!command || command === '--help' || command === '-h') {\n printUsage();\n process.exit(0);\n }\n \n const positionalArgs = getPositionalArgs(args.slice(1));\n const options = parseOptions(args);\n \n const dbPath = positionalArgs[0];\n \n if (!dbPath) {\n console.error('Error: Database path is required');\n printUsage();\n process.exit(1);\n }\n \n try {\n switch (command) {\n case 'stats':\n await cmdStats(dbPath);\n break;\n \n case 'list':\n await cmdList(dbPath, options);\n break;\n \n case 'export':\n await cmdExport(dbPath, positionalArgs[1], options);\n break;\n \n case 'import':\n if (!positionalArgs[1]) {\n console.error('Error: Input CSV file is required');\n process.exit(1);\n }\n await cmdImport(dbPath, positionalArgs[1]);\n break;\n \n case 'purge':\n await cmdPurge(dbPath, options);\n break;\n \n case 'retry':\n await cmdRetry(dbPath, options);\n break;\n \n case 'pause':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic pause ./jobs.db emails');\n process.exit(1);\n }\n await cmdPause(dbPath, positionalArgs[1]);\n break;\n \n case 'resume':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic resume ./jobs.db emails');\n process.exit(1);\n }\n await cmdResume(dbPath, positionalArgs[1]);\n break;\n \n case 'queues':\n await cmdQueues(dbPath);\n break;\n \n default:\n console.error(`Unknown command: ${command}`);\n printUsage();\n process.exit(1);\n }\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":["args"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Workmatic CLI\n * \n * Commands:\n * stats <db> Show job statistics\n * export <db> [output] Export jobs to CSV\n * import <db> <input> Import jobs from CSV\n * purge <db> [--status=done] Delete jobs by status\n * retry <db> [--status=dead] Retry dead jobs (reset to ready)\n * pause <db> <queue> Pause a queue (workers stop claiming)\n * resume <db> <queue> Resume a paused queue\n */\n\nimport Database from 'better-sqlite3';\nimport { createReadStream, createWriteStream } from 'fs';\nimport { createInterface } from 'readline';\nimport Table from 'cli-table3';\n\n// Parse command line arguments\nconst args = process.argv.slice(2);\nconst command = args[0];\n\nfunction printUsage(): void {\n console.log(`\nWorkmatic CLI - Job Queue Management\n\nUsage:\n workmatic <command> [options]\n\nCommands:\n stats <db> Show job statistics\n queues <db> List all queues with status\n list <db> [--status=<status>] List jobs (optionally filter by status)\n export <db> [output.csv] Export jobs to CSV (default: stdout)\n import <db> <input.csv> Import jobs from CSV\n purge <db> --status=<status> Delete jobs by status\n retry <db> [--status=dead] Retry dead jobs (reset to ready)\n pause <db> <queue> Pause a queue (running workers stop claiming)\n resume <db> <queue> Resume a paused queue\n\nOptions:\n --status=<status> Filter by status (ready|running|done|dead)\n --queue=<queue> Filter by queue name\n --limit=<n> Limit number of results (default: 100)\n\nExamples:\n workmatic stats ./jobs.db\n workmatic list ./jobs.db --status=dead --limit=10\n workmatic export ./jobs.db backup.csv\n workmatic export ./jobs.db --status=dead > dead-jobs.csv\n workmatic import ./jobs.db backup.csv\n workmatic purge ./jobs.db --status=done\n workmatic retry ./jobs.db --status=dead\n workmatic pause ./jobs.db emails\n workmatic resume ./jobs.db emails\n`);\n}\n\nfunction parseOptions(args: string[]): Record<string, string> {\n const options: Record<string, string> = {};\n for (const arg of args) {\n if (arg.startsWith('--')) {\n const [key, value] = arg.slice(2).split('=');\n options[key] = value ?? 'true';\n }\n }\n return options;\n}\n\nfunction getPositionalArgs(args: string[]): string[] {\n return args.filter(arg => !arg.startsWith('--'));\n}\n\n// CSV helpers\nfunction escapeCSV(value: string | null): string {\n if (value === null) return '';\n const str = String(value);\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\nfunction parseCSVLine(line: string): string[] {\n const result: string[] = [];\n let current = '';\n let inQuotes = false;\n \n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n \n if (inQuotes) {\n if (char === '\"') {\n if (line[i + 1] === '\"') {\n current += '\"';\n i++;\n } else {\n inQuotes = false;\n }\n } else {\n current += char;\n }\n } else {\n if (char === '\"') {\n inQuotes = true;\n } else if (char === ',') {\n result.push(current);\n current = '';\n } else {\n current += char;\n }\n }\n }\n \n result.push(current);\n return result;\n}\n\n// Commands\nasync function cmdStats(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n const stats = db.prepare(`\n SELECT status, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY status\n `).all() as { status: string; count: number }[];\n \n const queues = db.prepare(`\n SELECT queue, COUNT(*) as count \n FROM workmatic_jobs \n GROUP BY queue\n `).all() as { queue: string; count: number }[];\n \n const total = stats.reduce((sum, s) => sum + s.count, 0);\n \n console.log('\\nπŸ“Š Job Statistics\\n');\n \n const statusOrder = ['ready', 'running', 'done', 'dead'];\n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▢️',\n done: 'βœ…',\n dead: 'πŸ’€',\n };\n \n const statusTable = new Table({\n head: ['', 'Status', 'Count'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right'],\n });\n \n for (const status of statusOrder) {\n const stat = stats.find(s => s.status === status);\n const count = stat?.count ?? 0;\n const emoji = statusEmoji[status] || '';\n statusTable.push([emoji, status, count.toLocaleString()]);\n }\n statusTable.push([{ colSpan: 2, content: 'Total', hAlign: 'right' }, total.toLocaleString()]);\n \n console.log(statusTable.toString());\n \n if (queues.length > 0) {\n console.log('\\nπŸ“‹ By Queue\\n');\n \n const queueTable = new Table({\n head: ['Queue', 'Jobs'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'right'],\n });\n \n for (const q of queues) {\n queueTable.push([q.queue, q.count.toLocaleString()]);\n }\n \n console.log(queueTable.toString());\n }\n \n console.log();\n } finally {\n db.close();\n }\n}\n\nasync function cmdList(dbPath: string, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT public_id, queue, status, priority, attempts, max_attempts, created_at, last_error FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY created_at DESC LIMIT ?';\n params.push(parseInt(options.limit || '100', 10));\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n if (jobs.length === 0) {\n console.log('No jobs found.');\n return;\n }\n \n const statusEmoji: Record<string, string> = {\n ready: '⏳',\n running: '▢️',\n done: 'βœ…',\n dead: 'πŸ’€',\n };\n \n const table = new Table({\n head: ['ID', 'Queue', 'Status', 'Pri', 'Attempts', 'Created'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'left', 'right', 'center', 'left'],\n colWidths: [24, 16, 12, 5, 10, 21],\n wordWrap: true,\n });\n \n for (const job of jobs) {\n const emoji = statusEmoji[job.status] || '';\n table.push([\n job.public_id.slice(0, 21),\n job.queue.slice(0, 14),\n `${emoji} ${job.status}`,\n job.priority,\n `${job.attempts}/${job.max_attempts}`,\n new Date(job.created_at).toISOString().slice(0, 19),\n ]);\n }\n \n console.log();\n console.log(table.toString());\n console.log(`\\n Showing ${jobs.length} jobs\\n`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdExport(dbPath: string, outputPath: string | undefined, options: Record<string, string>): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n let query = 'SELECT * FROM workmatic_jobs WHERE 1=1';\n const params: any[] = [];\n \n if (options.status) {\n query += ' AND status = ?';\n params.push(options.status);\n }\n if (options.queue) {\n query += ' AND queue = ?';\n params.push(options.queue);\n }\n \n query += ' ORDER BY id';\n \n const jobs = db.prepare(query).all(...params) as any[];\n \n const output = outputPath ? createWriteStream(outputPath) : process.stdout;\n \n // Header\n const columns = [\n 'public_id', 'queue', 'payload', 'status', 'priority',\n 'run_at', 'attempts', 'max_attempts', 'lease_until',\n 'created_at', 'updated_at', 'last_error'\n ];\n \n if (outputPath) {\n output.write(columns.join(',') + '\\n');\n } else {\n console.log(columns.join(','));\n }\n \n // Rows\n for (const job of jobs) {\n const row = columns.map(col => escapeCSV(job[col]));\n if (outputPath) {\n output.write(row.join(',') + '\\n');\n } else {\n console.log(row.join(','));\n }\n }\n \n if (outputPath) {\n (output as any).end();\n console.error(`βœ… Exported ${jobs.length} jobs to ${outputPath}`);\n }\n } finally {\n db.close();\n }\n}\n\nasync function cmdImport(dbPath: string, inputPath: string): Promise<void> {\n const db = new Database(dbPath);\n \n // Ensure schema exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n public_id TEXT UNIQUE NOT NULL,\n queue TEXT NOT NULL,\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'ready',\n priority INTEGER NOT NULL DEFAULT 0,\n run_at INTEGER NOT NULL,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n lease_until INTEGER NOT NULL DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n last_error TEXT\n )\n `);\n \n const insert = db.prepare(`\n INSERT OR REPLACE INTO workmatic_jobs \n (public_id, queue, payload, status, priority, run_at, attempts, max_attempts, lease_until, created_at, updated_at, last_error)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n \n const rl = createInterface({\n input: createReadStream(inputPath),\n crlfDelay: Infinity,\n });\n \n let lineNum = 0;\n let imported = 0;\n let columns: string[] = [];\n \n try {\n const insertMany = db.transaction((rows: any[]) => {\n for (const row of rows) {\n insert.run(\n row.public_id,\n row.queue,\n row.payload,\n row.status,\n parseInt(row.priority, 10) || 0,\n parseInt(row.run_at, 10) || Date.now(),\n parseInt(row.attempts, 10) || 0,\n parseInt(row.max_attempts, 10) || 3,\n parseInt(row.lease_until, 10) || 0,\n parseInt(row.created_at, 10) || Date.now(),\n parseInt(row.updated_at, 10) || Date.now(),\n row.last_error || null\n );\n imported++;\n }\n });\n \n const batch: any[] = [];\n \n for await (const line of rl) {\n lineNum++;\n \n if (lineNum === 1) {\n // Header row\n columns = parseCSVLine(line);\n continue;\n }\n \n if (!line.trim()) continue;\n \n const values = parseCSVLine(line);\n const row: Record<string, string> = {};\n \n for (let i = 0; i < columns.length; i++) {\n row[columns[i]] = values[i] ?? '';\n }\n \n batch.push(row);\n \n // Batch insert every 1000 rows\n if (batch.length >= 1000) {\n insertMany(batch);\n batch.length = 0;\n process.stderr.write(`\\rImported ${imported} jobs...`);\n }\n }\n \n // Insert remaining\n if (batch.length > 0) {\n insertMany(batch);\n }\n \n console.log(`\\nβœ… Imported ${imported} jobs from ${inputPath}`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPurge(dbPath: string, options: Record<string, string>): Promise<void> {\n if (!options.status) {\n console.error('Error: --status is required for purge command');\n console.error('Example: workmatic purge ./jobs.db --status=done');\n process.exit(1);\n }\n \n const db = new Database(dbPath);\n \n try {\n const result = db.prepare('DELETE FROM workmatic_jobs WHERE status = ?').run(options.status);\n console.log(`πŸ—‘οΈ Deleted ${result.changes} jobs with status '${options.status}'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdRetry(dbPath: string, options: Record<string, string>): Promise<void> {\n const status = options.status || 'dead';\n const db = new Database(dbPath);\n \n try {\n const now = Date.now();\n const result = db.prepare(`\n UPDATE workmatic_jobs \n SET status = 'ready', \n attempts = 0, \n run_at = ?, \n lease_until = 0,\n updated_at = ?\n WHERE status = ?\n `).run(now, now, status);\n \n console.log(`πŸ”„ Reset ${result.changes} jobs from '${status}' to 'ready'`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdPause(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 1, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 1, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`⏸️ Paused queue '${queueName}'`);\n console.log(` Running workers will stop claiming new jobs.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdResume(dbPath: string, queueName: string): Promise<void> {\n const db = new Database(dbPath);\n \n try {\n // Ensure settings table exists\n db.exec(`\n CREATE TABLE IF NOT EXISTS workmatic_settings (\n queue TEXT PRIMARY KEY,\n paused INTEGER NOT NULL DEFAULT 0,\n updated_at INTEGER NOT NULL\n )\n `);\n \n const now = Date.now();\n db.prepare(`\n INSERT INTO workmatic_settings (queue, paused, updated_at)\n VALUES (?, 0, ?)\n ON CONFLICT(queue) DO UPDATE SET paused = 0, updated_at = ?\n `).run(queueName, now, now);\n \n console.log(`▢️ Resumed queue '${queueName}'`);\n console.log(` Workers will start claiming jobs again.`);\n } finally {\n db.close();\n }\n}\n\nasync function cmdQueues(dbPath: string): Promise<void> {\n const db = new Database(dbPath, { readonly: true });\n \n try {\n // Check if settings table exists\n const settingsExists = db.prepare(`\n SELECT name FROM sqlite_master WHERE type='table' AND name='workmatic_settings'\n `).get();\n \n // Get all queues with their job counts and pause state\n let queues: { queue: string; total: number; ready: number; running: number; paused: number }[];\n \n if (settingsExists) {\n queues = db.prepare(`\n SELECT \n j.queue,\n COUNT(*) as total,\n SUM(CASE WHEN j.status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN j.status = 'running' THEN 1 ELSE 0 END) as running,\n COALESCE(s.paused, 0) as paused\n FROM workmatic_jobs j\n LEFT JOIN workmatic_settings s ON j.queue = s.queue\n GROUP BY j.queue\n `).all() as typeof queues;\n } else {\n queues = db.prepare(`\n SELECT \n queue,\n COUNT(*) as total,\n SUM(CASE WHEN status = 'ready' THEN 1 ELSE 0 END) as ready,\n SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running,\n 0 as paused\n FROM workmatic_jobs\n GROUP BY queue\n `).all() as typeof queues;\n }\n \n if (queues.length === 0) {\n console.log('No queues found.');\n return;\n }\n \n console.log('\\nπŸ“‹ Queues\\n');\n \n const table = new Table({\n head: ['Queue', 'Status', 'Ready', 'Running', 'Total'],\n style: { head: ['cyan'], border: ['gray'] },\n colAligns: ['left', 'left', 'right', 'right', 'right'],\n });\n \n for (const q of queues) {\n const status = q.paused ? '⏸️ PAUSED' : '▢️ ACTIVE';\n table.push([\n q.queue,\n status,\n q.ready.toLocaleString(),\n q.running.toLocaleString(),\n q.total.toLocaleString(),\n ]);\n }\n \n console.log(table.toString());\n console.log();\n } finally {\n db.close();\n }\n}\n\n// Main\nasync function main(): Promise<void> {\n if (!command || command === '--help' || command === '-h') {\n printUsage();\n process.exit(0);\n }\n \n const positionalArgs = getPositionalArgs(args.slice(1));\n const options = parseOptions(args);\n \n const dbPath = positionalArgs[0];\n \n if (!dbPath) {\n console.error('Error: Database path is required');\n printUsage();\n process.exit(1);\n }\n \n try {\n switch (command) {\n case 'stats':\n await cmdStats(dbPath);\n break;\n \n case 'list':\n await cmdList(dbPath, options);\n break;\n \n case 'export':\n await cmdExport(dbPath, positionalArgs[1], options);\n break;\n \n case 'import':\n if (!positionalArgs[1]) {\n console.error('Error: Input CSV file is required');\n process.exit(1);\n }\n await cmdImport(dbPath, positionalArgs[1]);\n break;\n \n case 'purge':\n await cmdPurge(dbPath, options);\n break;\n \n case 'retry':\n await cmdRetry(dbPath, options);\n break;\n \n case 'pause':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic pause ./jobs.db emails');\n process.exit(1);\n }\n await cmdPause(dbPath, positionalArgs[1]);\n break;\n \n case 'resume':\n if (!positionalArgs[1]) {\n console.error('Error: Queue name is required');\n console.error('Example: workmatic resume ./jobs.db emails');\n process.exit(1);\n }\n await cmdResume(dbPath, positionalArgs[1]);\n break;\n \n case 'queues':\n await cmdQueues(dbPath);\n break;\n \n default:\n console.error(`Unknown command: ${command}`);\n printUsage();\n process.exit(1);\n }\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":";;;AAeA,OAAO,cAAc;AACrB,SAAS,kBAAkB,yBAAyB;AACpD,SAAS,uBAAuB;AAChC,OAAO,WAAW;AAGlB,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UAAU,KAAK,CAAC;AAEtB,SAAS,aAAmB;AAC1B,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAgCb;AACD;AAEA,SAAS,aAAaA,OAAwC;AAC5D,QAAM,UAAkC,CAAC;AACzC,aAAW,OAAOA,OAAM;AACtB,QAAI,IAAI,WAAW,IAAI,GAAG;AACxB,YAAM,CAAC,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,EAAE,MAAM,GAAG;AAC3C,cAAQ,GAAG,IAAI,SAAS;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,kBAAkBA,OAA0B;AACnD,SAAOA,MAAK,OAAO,SAAO,CAAC,IAAI,WAAW,IAAI,CAAC;AACjD;AAGA,SAAS,UAAU,OAA8B;AAC/C,MAAI,UAAU,KAAM,QAAO;AAC3B,QAAM,MAAM,OAAO,KAAK;AACxB,MAAI,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,IAAI,GAAG;AAChE,WAAO,IAAI,IAAI,QAAQ,MAAM,IAAI,CAAC;AAAA,EACpC;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAAwB;AAC5C,QAAM,SAAmB,CAAC;AAC1B,MAAI,UAAU;AACd,MAAI,WAAW;AAEf,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,OAAO,KAAK,CAAC;AAEnB,QAAI,UAAU;AACZ,UAAI,SAAS,KAAK;AAChB,YAAI,KAAK,IAAI,CAAC,MAAM,KAAK;AACvB,qBAAW;AACX;AAAA,QACF,OAAO;AACL,qBAAW;AAAA,QACb;AAAA,MACF,OAAO;AACL,mBAAW;AAAA,MACb;AAAA,IACF,OAAO;AACL,UAAI,SAAS,KAAK;AAChB,mBAAW;AAAA,MACb,WAAW,SAAS,KAAK;AACvB,eAAO,KAAK,OAAO;AACnB,kBAAU;AAAA,MACZ,OAAO;AACL,mBAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAEA,SAAO,KAAK,OAAO;AACnB,SAAO;AACT;AAGA,eAAe,SAAS,QAA+B;AACrD,QAAM,KAAK,IAAI,SAAS,QAAQ,EAAE,UAAU,KAAK,CAAC;AAElD,MAAI;AACF,UAAM,QAAQ,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAIxB,EAAE,IAAI;AAEP,UAAM,SAAS,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAIzB,EAAE,IAAI;AAEP,UAAM,QAAQ,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAEvD,YAAQ,IAAI,8BAAuB;AAEnC,UAAM,cAAc,CAAC,SAAS,WAAW,QAAQ,MAAM;AACvD,UAAM,cAAsC;AAAA,MAC1C,OAAO;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAEA,UAAM,cAAc,IAAI,MAAM;AAAA,MAC5B,MAAM,CAAC,IAAI,UAAU,OAAO;AAAA,MAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE;AAAA,MAC1C,WAAW,CAAC,QAAQ,QAAQ,OAAO;AAAA,IACrC,CAAC;AAED,eAAW,UAAU,aAAa;AAChC,YAAM,OAAO,MAAM,KAAK,OAAK,EAAE,WAAW,MAAM;AAChD,YAAM,QAAQ,MAAM,SAAS;AAC7B,YAAM,QAAQ,YAAY,MAAM,KAAK;AACrC,kBAAY,KAAK,CAAC,OAAO,QAAQ,MAAM,eAAe,CAAC,CAAC;AAAA,IAC1D;AACA,gBAAY,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,SAAS,QAAQ,QAAQ,GAAG,MAAM,eAAe,CAAC,CAAC;AAE5F,YAAQ,IAAI,YAAY,SAAS,CAAC;AAElC,QAAI,OAAO,SAAS,GAAG;AACrB,cAAQ,IAAI,wBAAiB;AAE7B,YAAM,aAAa,IAAI,MAAM;AAAA,QAC3B,MAAM,CAAC,SAAS,MAAM;AAAA,QACtB,OAAO,EAAE,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE;AAAA,QAC1C,WAAW,CAAC,QAAQ,OAAO;AAAA,MAC7B,CAAC;AAED,iBAAW,KAAK,QAAQ;AACtB,mBAAW,KAAK,CAAC,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC,CAAC;AAAA,MACrD;AAEA,cAAQ,IAAI,WAAW,SAAS,CAAC;AAAA,IACnC;AAEA,YAAQ,IAAI;AAAA,EACd,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,QAAQ,QAAgB,SAAgD;AACrF,QAAM,KAAK,IAAI,SAAS,QAAQ,EAAE,UAAU,KAAK,CAAC;AAElD,MAAI;AACF,QAAI,QAAQ;AACZ,UAAM,SAAgB,CAAC;AAEvB,QAAI,QAAQ,QAAQ;AAClB,eAAS;AACT,aAAO,KAAK,QAAQ,MAAM;AAAA,IAC5B;AACA,QAAI,QAAQ,OAAO;AACjB,eAAS;AACT,aAAO,KAAK,QAAQ,KAAK;AAAA,IAC3B;AAEA,aAAS;AACT,WAAO,KAAK,SAAS,QAAQ,SAAS,OAAO,EAAE,CAAC;AAEhD,UAAM,OAAO,GAAG,QAAQ,KAAK,EAAE,IAAI,GAAG,MAAM;AAE5C,QAAI,KAAK,WAAW,GAAG;AACrB,cAAQ,IAAI,gBAAgB;AAC5B;AAAA,IACF;AAEA,UAAM,cAAsC;AAAA,MAC1C,OAAO;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAEA,UAAM,QAAQ,IAAI,MAAM;AAAA,MACtB,MAAM,CAAC,MAAM,SAAS,UAAU,OAAO,YAAY,SAAS;AAAA,MAC5D,OAAO,EAAE,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE;AAAA,MAC1C,WAAW,CAAC,QAAQ,QAAQ,QAAQ,SAAS,UAAU,MAAM;AAAA,MAC7D,WAAW,CAAC,IAAI,IAAI,IAAI,GAAG,IAAI,EAAE;AAAA,MACjC,UAAU;AAAA,IACZ,CAAC;AAED,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,YAAY,IAAI,MAAM,KAAK;AACzC,YAAM,KAAK;AAAA,QACT,IAAI,UAAU,MAAM,GAAG,EAAE;AAAA,QACzB,IAAI,MAAM,MAAM,GAAG,EAAE;AAAA,QACrB,GAAG,KAAK,IAAI,IAAI,MAAM;AAAA,QACtB,IAAI;AAAA,QACJ,GAAG,IAAI,QAAQ,IAAI,IAAI,YAAY;AAAA,QACnC,IAAI,KAAK,IAAI,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAAA,MACpD,CAAC;AAAA,IACH;AAEA,YAAQ,IAAI;AACZ,YAAQ,IAAI,MAAM,SAAS,CAAC;AAC5B,YAAQ,IAAI;AAAA,YAAe,KAAK,MAAM;AAAA,CAAS;AAAA,EACjD,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,UAAU,QAAgB,YAAgC,SAAgD;AACvH,QAAM,KAAK,IAAI,SAAS,QAAQ,EAAE,UAAU,KAAK,CAAC;AAElD,MAAI;AACF,QAAI,QAAQ;AACZ,UAAM,SAAgB,CAAC;AAEvB,QAAI,QAAQ,QAAQ;AAClB,eAAS;AACT,aAAO,KAAK,QAAQ,MAAM;AAAA,IAC5B;AACA,QAAI,QAAQ,OAAO;AACjB,eAAS;AACT,aAAO,KAAK,QAAQ,KAAK;AAAA,IAC3B;AAEA,aAAS;AAET,UAAM,OAAO,GAAG,QAAQ,KAAK,EAAE,IAAI,GAAG,MAAM;AAE5C,UAAM,SAAS,aAAa,kBAAkB,UAAU,IAAI,QAAQ;AAGpE,UAAM,UAAU;AAAA,MACd;AAAA,MAAa;AAAA,MAAS;AAAA,MAAW;AAAA,MAAU;AAAA,MAC3C;AAAA,MAAU;AAAA,MAAY;AAAA,MAAgB;AAAA,MACtC;AAAA,MAAc;AAAA,MAAc;AAAA,IAC9B;AAEA,QAAI,YAAY;AACd,aAAO,MAAM,QAAQ,KAAK,GAAG,IAAI,IAAI;AAAA,IACvC,OAAO;AACL,cAAQ,IAAI,QAAQ,KAAK,GAAG,CAAC;AAAA,IAC/B;AAGA,eAAW,OAAO,MAAM;AACtB,YAAM,MAAM,QAAQ,IAAI,SAAO,UAAU,IAAI,GAAG,CAAC,CAAC;AAClD,UAAI,YAAY;AACd,eAAO,MAAM,IAAI,KAAK,GAAG,IAAI,IAAI;AAAA,MACnC,OAAO;AACL,gBAAQ,IAAI,IAAI,KAAK,GAAG,CAAC;AAAA,MAC3B;AAAA,IACF;AAEA,QAAI,YAAY;AACd,MAAC,OAAe,IAAI;AACpB,cAAQ,MAAM,mBAAc,KAAK,MAAM,YAAY,UAAU,EAAE;AAAA,IACjE;AAAA,EACF,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,UAAU,QAAgB,WAAkC;AACzE,QAAM,KAAK,IAAI,SAAS,MAAM;AAG9B,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAgBP;AAED,QAAM,SAAS,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,GAIzB;AAED,QAAM,KAAK,gBAAgB;AAAA,IACzB,OAAO,iBAAiB,SAAS;AAAA,IACjC,WAAW;AAAA,EACb,CAAC;AAED,MAAI,UAAU;AACd,MAAI,WAAW;AACf,MAAI,UAAoB,CAAC;AAEzB,MAAI;AACF,UAAM,aAAa,GAAG,YAAY,CAAC,SAAgB;AACjD,iBAAW,OAAO,MAAM;AACtB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,SAAS,IAAI,UAAU,EAAE,KAAK;AAAA,UAC9B,SAAS,IAAI,QAAQ,EAAE,KAAK,KAAK,IAAI;AAAA,UACrC,SAAS,IAAI,UAAU,EAAE,KAAK;AAAA,UAC9B,SAAS,IAAI,cAAc,EAAE,KAAK;AAAA,UAClC,SAAS,IAAI,aAAa,EAAE,KAAK;AAAA,UACjC,SAAS,IAAI,YAAY,EAAE,KAAK,KAAK,IAAI;AAAA,UACzC,SAAS,IAAI,YAAY,EAAE,KAAK,KAAK,IAAI;AAAA,UACzC,IAAI,cAAc;AAAA,QACpB;AACA;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,QAAe,CAAC;AAEtB,qBAAiB,QAAQ,IAAI;AAC3B;AAEA,UAAI,YAAY,GAAG;AAEjB,kBAAU,aAAa,IAAI;AAC3B;AAAA,MACF;AAEA,UAAI,CAAC,KAAK,KAAK,EAAG;AAElB,YAAM,SAAS,aAAa,IAAI;AAChC,YAAM,MAA8B,CAAC;AAErC,eAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAI,QAAQ,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK;AAAA,MACjC;AAEA,YAAM,KAAK,GAAG;AAGd,UAAI,MAAM,UAAU,KAAM;AACxB,mBAAW,KAAK;AAChB,cAAM,SAAS;AACf,gBAAQ,OAAO,MAAM,cAAc,QAAQ,UAAU;AAAA,MACvD;AAAA,IACF;AAGA,QAAI,MAAM,SAAS,GAAG;AACpB,iBAAW,KAAK;AAAA,IAClB;AAEA,YAAQ,IAAI;AAAA,kBAAgB,QAAQ,cAAc,SAAS,EAAE;AAAA,EAC/D,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,SAAS,QAAgB,SAAgD;AACtF,MAAI,CAAC,QAAQ,QAAQ;AACnB,YAAQ,MAAM,+CAA+C;AAC7D,YAAQ,MAAM,kDAAkD;AAChE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,KAAK,IAAI,SAAS,MAAM;AAE9B,MAAI;AACF,UAAM,SAAS,GAAG,QAAQ,6CAA6C,EAAE,IAAI,QAAQ,MAAM;AAC3F,YAAQ,IAAI,4BAAgB,OAAO,OAAO,sBAAsB,QAAQ,MAAM,GAAG;AAAA,EACnF,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,SAAS,QAAgB,SAAgD;AACtF,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,KAAK,IAAI,SAAS,MAAM;AAE9B,MAAI;AACF,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAQzB,EAAE,IAAI,KAAK,KAAK,MAAM;AAEvB,YAAQ,IAAI,mBAAY,OAAO,OAAO,eAAe,MAAM,cAAc;AAAA,EAC3E,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,SAAS,QAAgB,WAAkC;AACxE,QAAM,KAAK,IAAI,SAAS,MAAM;AAE9B,MAAI;AAEF,OAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMP;AAED,UAAM,MAAM,KAAK,IAAI;AACrB,OAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAIV,EAAE,IAAI,WAAW,KAAK,GAAG;AAE1B,YAAQ,IAAI,+BAAqB,SAAS,GAAG;AAC7C,YAAQ,IAAI,iDAAiD;AAAA,EAC/D,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,UAAU,QAAgB,WAAkC;AACzE,QAAM,KAAK,IAAI,SAAS,MAAM;AAE9B,MAAI;AAEF,OAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMP;AAED,UAAM,MAAM,KAAK,IAAI;AACrB,OAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,KAIV,EAAE,IAAI,WAAW,KAAK,GAAG;AAE1B,YAAQ,IAAI,gCAAsB,SAAS,GAAG;AAC9C,YAAQ,IAAI,4CAA4C;AAAA,EAC1D,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEA,eAAe,UAAU,QAA+B;AACtD,QAAM,KAAK,IAAI,SAAS,QAAQ,EAAE,UAAU,KAAK,CAAC;AAElD,MAAI;AAEF,UAAM,iBAAiB,GAAG,QAAQ;AAAA;AAAA,KAEjC,EAAE,IAAI;AAGP,QAAI;AAEJ,QAAI,gBAAgB;AAClB,eAAS,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAUnB,EAAE,IAAI;AAAA,IACT,OAAO;AACL,eAAS,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OASnB,EAAE,IAAI;AAAA,IACT;AAEA,QAAI,OAAO,WAAW,GAAG;AACvB,cAAQ,IAAI,kBAAkB;AAC9B;AAAA,IACF;AAEA,YAAQ,IAAI,sBAAe;AAE3B,UAAM,QAAQ,IAAI,MAAM;AAAA,MACtB,MAAM,CAAC,SAAS,UAAU,SAAS,WAAW,OAAO;AAAA,MACrD,OAAO,EAAE,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE;AAAA,MAC1C,WAAW,CAAC,QAAQ,QAAQ,SAAS,SAAS,OAAO;AAAA,IACvD,CAAC;AAED,eAAW,KAAK,QAAQ;AACtB,YAAM,SAAS,EAAE,SAAS,yBAAe;AACzC,YAAM,KAAK;AAAA,QACT,EAAE;AAAA,QACF;AAAA,QACA,EAAE,MAAM,eAAe;AAAA,QACvB,EAAE,QAAQ,eAAe;AAAA,QACzB,EAAE,MAAM,eAAe;AAAA,MACzB,CAAC;AAAA,IACH;AAEA,YAAQ,IAAI,MAAM,SAAS,CAAC;AAC5B,YAAQ,IAAI;AAAA,EACd,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAGA,eAAe,OAAsB;AACnC,MAAI,CAAC,WAAW,YAAY,YAAY,YAAY,MAAM;AACxD,eAAW;AACX,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,iBAAiB,kBAAkB,KAAK,MAAM,CAAC,CAAC;AACtD,QAAM,UAAU,aAAa,IAAI;AAEjC,QAAM,SAAS,eAAe,CAAC;AAE/B,MAAI,CAAC,QAAQ;AACX,YAAQ,MAAM,kCAAkC;AAChD,eAAW;AACX,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEE,MAAI;AACJ,YAAQ,SAAS;AAAA,MACf,KAAK;AACH,cAAM,SAAS,MAAM;AACrB;AAAA,MAEF,KAAK;AACH,cAAM,QAAQ,QAAQ,OAAO;AAC7B;AAAA,MAEF,KAAK;AACH,cAAM,UAAU,QAAQ,eAAe,CAAC,GAAG,OAAO;AAClD;AAAA,MAEF,KAAK;AACH,YAAI,CAAC,eAAe,CAAC,GAAG;AACtB,kBAAQ,MAAM,mCAAmC;AACjD,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,UAAU,QAAQ,eAAe,CAAC,CAAC;AACzC;AAAA,MAEF,KAAK;AACH,cAAM,SAAS,QAAQ,OAAO;AAC9B;AAAA,MAEF,KAAK;AACH,cAAM,SAAS,QAAQ,OAAO;AAC9B;AAAA,MAEF,KAAK;AACH,YAAI,CAAC,eAAe,CAAC,GAAG;AACtB,kBAAQ,MAAM,+BAA+B;AAC7C,kBAAQ,MAAM,2CAA2C;AACzD,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,SAAS,QAAQ,eAAe,CAAC,CAAC;AACxC;AAAA,MAEF,KAAK;AACH,YAAI,CAAC,eAAe,CAAC,GAAG;AACtB,kBAAQ,MAAM,+BAA+B;AAC7C,kBAAQ,MAAM,4CAA4C;AAC1D,kBAAQ,KAAK,CAAC;AAAA,QAChB;AACA,cAAM,UAAU,QAAQ,eAAe,CAAC,CAAC;AACzC;AAAA,MAEF,KAAK;AACH,cAAM,UAAU,MAAM;AACtB;AAAA,MAEF;AACE,gBAAQ,MAAM,oBAAoB,OAAO,EAAE;AAC3C,mBAAW;AACX,gBAAQ,KAAK,CAAC;AAAA,IAClB;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AACtE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,KAAK;","names":["args"]}