scrapme-flow 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +126 -2
- package/dist/src/commands/commands.models.d.ts +41 -0
- package/dist/src/commands/commands.models.js +2 -0
- package/dist/src/commands/commands.models.js.map +1 -0
- package/dist/src/commands/save-document.command.d.ts +9 -0
- package/dist/src/commands/save-document.command.js +33 -0
- package/dist/src/commands/save-document.command.js.map +1 -0
- package/dist/src/commands/scrape-page.command.d.ts +20 -0
- package/dist/src/commands/scrape-page.command.js +93 -0
- package/dist/src/commands/scrape-page.command.js.map +1 -0
- package/dist/src/commands/send-mail.command.d.ts +11 -0
- package/dist/src/commands/send-mail.command.js +39 -0
- package/dist/src/commands/send-mail.command.js.map +1 -0
- package/dist/src/config/config-builder.d.ts +24 -0
- package/dist/src/config/config-builder.js +129 -0
- package/dist/src/config/config-builder.js.map +1 -0
- package/dist/src/config/config-loader.d.ts +10 -0
- package/dist/src/config/config-loader.js +79 -0
- package/dist/src/config/config-loader.js.map +1 -0
- package/dist/src/config/config.models.d.ts +144 -0
- package/dist/src/config/config.models.js +2 -0
- package/dist/src/config/config.models.js.map +1 -0
- package/dist/src/config/config.schema.d.ts +75 -0
- package/dist/src/config/config.schema.js +87 -0
- package/dist/src/config/config.schema.js.map +1 -0
- package/dist/src/config/dsn-check.d.ts +5 -0
- package/dist/src/config/dsn-check.js +12 -0
- package/dist/src/config/dsn-check.js.map +1 -0
- package/dist/src/di/container.d.ts +13 -0
- package/dist/src/di/container.js +67 -0
- package/dist/src/di/container.js.map +1 -0
- package/dist/src/di/types.d.ts +27 -0
- package/dist/src/di/types.js +28 -0
- package/dist/src/di/types.js.map +1 -0
- package/dist/src/flow/command-executor.d.ts +18 -0
- package/dist/src/flow/command-executor.js +87 -0
- package/dist/src/flow/command-executor.js.map +1 -0
- package/dist/src/flow/command-resolver.d.ts +11 -0
- package/dist/src/flow/command-resolver.js +35 -0
- package/dist/src/flow/command-resolver.js.map +1 -0
- package/dist/src/flow/flow-catalog.d.ts +12 -0
- package/dist/src/flow/flow-catalog.js +31 -0
- package/dist/src/flow/flow-catalog.js.map +1 -0
- package/dist/src/flow/flow-errors.d.ts +66 -0
- package/dist/src/flow/flow-errors.js +119 -0
- package/dist/src/flow/flow-errors.js.map +1 -0
- package/dist/src/flow/flow-job-processor.d.ts +33 -0
- package/dist/src/flow/flow-job-processor.js +208 -0
- package/dist/src/flow/flow-job-processor.js.map +1 -0
- package/dist/src/flow/flow-params.d.ts +9 -0
- package/dist/src/flow/flow-params.js +52 -0
- package/dist/src/flow/flow-params.js.map +1 -0
- package/dist/src/flow/flow-runner.d.ts +36 -0
- package/dist/src/flow/flow-runner.js +209 -0
- package/dist/src/flow/flow-runner.js.map +1 -0
- package/dist/src/flow/flow-scheduler.d.ts +24 -0
- package/dist/src/flow/flow-scheduler.js +96 -0
- package/dist/src/flow/flow-scheduler.js.map +1 -0
- package/dist/src/flow/flow-validator.d.ts +29 -0
- package/dist/src/flow/flow-validator.js +141 -0
- package/dist/src/flow/flow-validator.js.map +1 -0
- package/dist/src/flow/flow.d.ts +32 -0
- package/dist/src/flow/flow.js +50 -0
- package/dist/src/flow/flow.js.map +1 -0
- package/dist/src/flow/flow.models.d.ts +288 -0
- package/dist/src/flow/flow.models.js +38 -0
- package/dist/src/flow/flow.models.js.map +1 -0
- package/dist/src/flow/retry-policy.d.ts +9 -0
- package/dist/src/flow/retry-policy.js +15 -0
- package/dist/src/flow/retry-policy.js.map +1 -0
- package/dist/src/flow/state-path.d.ts +21 -0
- package/dist/src/flow/state-path.js +37 -0
- package/dist/src/flow/state-path.js.map +1 -0
- package/dist/src/flow/step-input-builder.d.ts +15 -0
- package/dist/src/flow/step-input-builder.js +63 -0
- package/dist/src/flow/step-input-builder.js.map +1 -0
- package/dist/src/flows/scrape.flow.d.ts +6 -0
- package/dist/src/flows/scrape.flow.js +31 -0
- package/dist/src/flows/scrape.flow.js.map +1 -0
- package/dist/src/index.d.ts +57 -0
- package/dist/src/index.js +50 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/logging/logger.d.ts +17 -0
- package/dist/src/logging/logger.js +55 -0
- package/dist/src/logging/logger.js.map +1 -0
- package/dist/src/logging/logger.models.d.ts +25 -0
- package/dist/src/logging/logger.models.js +7 -0
- package/dist/src/logging/logger.models.js.map +1 -0
- package/dist/src/mail/mail.models.d.ts +15 -0
- package/dist/src/mail/mail.models.js +2 -0
- package/dist/src/mail/mail.models.js.map +1 -0
- package/dist/src/mail/smtp-mailer.d.ts +13 -0
- package/dist/src/mail/smtp-mailer.js +70 -0
- package/dist/src/mail/smtp-mailer.js.map +1 -0
- package/dist/src/mail/unconfigured-mailer.d.ts +6 -0
- package/dist/src/mail/unconfigured-mailer.js +20 -0
- package/dist/src/mail/unconfigured-mailer.js.map +1 -0
- package/dist/src/models.d.ts +11 -0
- package/dist/src/models.js +10 -0
- package/dist/src/models.js.map +1 -0
- package/dist/src/monitor/monitor-api.d.ts +18 -0
- package/dist/src/monitor/monitor-api.js +62 -0
- package/dist/src/monitor/monitor-api.js.map +1 -0
- package/dist/src/monitor/monitor-server.d.ts +33 -0
- package/dist/src/monitor/monitor-server.js +142 -0
- package/dist/src/monitor/monitor-server.js.map +1 -0
- package/dist/src/monitor/monitor.models.d.ts +27 -0
- package/dist/src/monitor/monitor.models.js +2 -0
- package/dist/src/monitor/monitor.models.js.map +1 -0
- package/dist/src/queue/pg-boss-flow-queue.d.ts +22 -0
- package/dist/src/queue/pg-boss-flow-queue.js +102 -0
- package/dist/src/queue/pg-boss-flow-queue.js.map +1 -0
- package/dist/src/queue/queue.models.d.ts +38 -0
- package/dist/src/queue/queue.models.js +2 -0
- package/dist/src/queue/queue.models.js.map +1 -0
- package/dist/src/reporting/lighty-error-reporter.d.ts +24 -0
- package/dist/src/reporting/lighty-error-reporter.js +96 -0
- package/dist/src/reporting/lighty-error-reporter.js.map +1 -0
- package/dist/src/reporting/log-error-reporter.d.ts +12 -0
- package/dist/src/reporting/log-error-reporter.js +35 -0
- package/dist/src/reporting/log-error-reporter.js.map +1 -0
- package/dist/src/reporting/reporting.models.d.ts +27 -0
- package/dist/src/reporting/reporting.models.js +2 -0
- package/dist/src/reporting/reporting.models.js.map +1 -0
- package/dist/src/scrapme.d.ts +61 -0
- package/dist/src/scrapme.js +133 -0
- package/dist/src/scrapme.js.map +1 -0
- package/dist/src/scrapme.models.d.ts +18 -0
- package/dist/src/scrapme.models.js +2 -0
- package/dist/src/scrapme.models.js.map +1 -0
- package/dist/src/storage/couch-connection.d.ts +19 -0
- package/dist/src/storage/couch-connection.js +67 -0
- package/dist/src/storage/couch-connection.js.map +1 -0
- package/dist/src/storage/couch-database.d.ts +44 -0
- package/dist/src/storage/couch-database.js +118 -0
- package/dist/src/storage/couch-database.js.map +1 -0
- package/dist/src/storage/couch-document-store.d.ts +14 -0
- package/dist/src/storage/couch-document-store.js +36 -0
- package/dist/src/storage/couch-document-store.js.map +1 -0
- package/dist/src/storage/couch-error.d.ts +12 -0
- package/dist/src/storage/couch-error.js +19 -0
- package/dist/src/storage/couch-error.js.map +1 -0
- package/dist/src/storage/couch-flow-run-store.d.ts +24 -0
- package/dist/src/storage/couch-flow-run-store.js +66 -0
- package/dist/src/storage/couch-flow-run-store.js.map +1 -0
- package/dist/src/storage/couch-run-store.d.ts +15 -0
- package/dist/src/storage/couch-run-store.js +52 -0
- package/dist/src/storage/couch-run-store.js.map +1 -0
- package/dist/src/storage/run-doc-ids.d.ts +9 -0
- package/dist/src/storage/run-doc-ids.js +15 -0
- package/dist/src/storage/run-doc-ids.js.map +1 -0
- package/dist/src/storage/storage.models.d.ts +95 -0
- package/dist/src/storage/storage.models.js +5 -0
- package/dist/src/storage/storage.models.js.map +1 -0
- package/dist/src/storage/unconfigured-document-store.d.ts +8 -0
- package/dist/src/storage/unconfigured-document-store.js +26 -0
- package/dist/src/storage/unconfigured-document-store.js.map +1 -0
- package/dist/src/util/preview.d.ts +10 -0
- package/dist/src/util/preview.js +44 -0
- package/dist/src/util/preview.js.map +1 -0
- package/dist/src/util/text.d.ts +8 -0
- package/dist/src/util/text.js +16 -0
- package/dist/src/util/text.js.map +1 -0
- package/dist/src/util/timing.d.ts +10 -0
- package/dist/src/util/timing.js +59 -0
- package/dist/src/util/timing.js.map +1 -0
- package/dist/src/util/url.d.ts +5 -0
- package/dist/src/util/url.js +11 -0
- package/dist/src/util/url.js.map +1 -0
- package/package.json +75 -4
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
var MonitorApi_1;
|
|
11
|
+
import { inject, injectable } from 'inversify';
|
|
12
|
+
import { TYPES } from '../di/types.js';
|
|
13
|
+
import { RUN_STATUS } from '../flow/flow.models.js';
|
|
14
|
+
/** The data behind the dashboard: the run documents in CouchDB (the monitor only reads them). */
|
|
15
|
+
let MonitorApi = class MonitorApi {
|
|
16
|
+
static { MonitorApi_1 = this; }
|
|
17
|
+
store;
|
|
18
|
+
couch;
|
|
19
|
+
static MAX_RUNS = 500;
|
|
20
|
+
constructor(store, couch) {
|
|
21
|
+
this.store = store;
|
|
22
|
+
this.couch = couch;
|
|
23
|
+
}
|
|
24
|
+
async runs(limit, filter = {}) {
|
|
25
|
+
const docs = await this.store.list(Math.min(Math.max(1, limit), MonitorApi_1.MAX_RUNS));
|
|
26
|
+
return docs.filter((d) => (!filter.flow || d.flow === filter.flow) && (!filter.status || d.status === filter.status)).map((d) => MonitorApi_1.summary(d));
|
|
27
|
+
}
|
|
28
|
+
async run(runId) {
|
|
29
|
+
return this.store.get(runId);
|
|
30
|
+
}
|
|
31
|
+
/** Counts per status and the flow names, over the newest MAX_RUNS runs. */
|
|
32
|
+
async overview() {
|
|
33
|
+
const docs = await this.store.list(MonitorApi_1.MAX_RUNS);
|
|
34
|
+
const counts = Object.fromEntries(Object.values(RUN_STATUS).map((s) => [s, 0]));
|
|
35
|
+
for (const d of docs)
|
|
36
|
+
counts[d.status] = (counts[d.status] ?? 0) + 1;
|
|
37
|
+
const flows = [...new Set(docs.map((d) => d.flow))].sort();
|
|
38
|
+
return { flows, counts, couchUrl: this.couch.publicUrl, runsDb: this.couch.runsDb };
|
|
39
|
+
}
|
|
40
|
+
static summary(d) {
|
|
41
|
+
return {
|
|
42
|
+
runId: d.runId,
|
|
43
|
+
flow: d.flow,
|
|
44
|
+
status: d.status,
|
|
45
|
+
currentStep: d.currentStep,
|
|
46
|
+
flowAttempt: d.flowAttempts.at(-1)?.attempt ?? 0,
|
|
47
|
+
maxFlowAttempts: d.maxFlowAttempts,
|
|
48
|
+
createdAt: d.createdAt,
|
|
49
|
+
finishedAt: d.finishedAt ?? null,
|
|
50
|
+
durationMs: d.durationMs ?? null,
|
|
51
|
+
error: d.error ? d.error.message : null,
|
|
52
|
+
steps: d.steps.map((s) => ({ key: s.key, status: s.status, attempts: s.attempts.length })),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
MonitorApi = MonitorApi_1 = __decorate([
|
|
57
|
+
injectable(),
|
|
58
|
+
__param(0, inject(TYPES.RunStore)),
|
|
59
|
+
__param(1, inject(TYPES.CouchConfig))
|
|
60
|
+
], MonitorApi);
|
|
61
|
+
export { MonitorApi };
|
|
62
|
+
//# sourceMappingURL=monitor-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor-api.js","sourceRoot":"","sources":["../../../src/monitor/monitor-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { MonitorConfig } from '../config/config.models.js';
|
|
2
|
+
import type { Logger } from '../logging/logger.js';
|
|
3
|
+
import type { MonitorApi } from './monitor-api.js';
|
|
4
|
+
/**
|
|
5
|
+
* The monitoring dashboard: the built Svelte app (dashboard/dist) and its JSON API, on node:http.
|
|
6
|
+
* GET /api/overview · /api/runs?limit=&flow=&status= · /api/runs/:runId
|
|
7
|
+
* Read-only. Binds to 127.0.0.1 unless monitor.host says otherwise.
|
|
8
|
+
*/
|
|
9
|
+
export declare class MonitorServer {
|
|
10
|
+
private readonly config;
|
|
11
|
+
private readonly api;
|
|
12
|
+
private static readonly API;
|
|
13
|
+
private static readonly DEFAULT_LIMIT;
|
|
14
|
+
private static readonly INDEX;
|
|
15
|
+
private static readonly TYPES;
|
|
16
|
+
/** Where the built dashboard is: next to the sources (tsx) or next to dist/ (the published package). */
|
|
17
|
+
private static readonly ASSET_DIRS;
|
|
18
|
+
private server;
|
|
19
|
+
private assets;
|
|
20
|
+
private readonly logger;
|
|
21
|
+
constructor(config: MonitorConfig, api: MonitorApi, logger: Logger);
|
|
22
|
+
/** Returns the dashboard URL. */
|
|
23
|
+
start(): Promise<string>;
|
|
24
|
+
get url(): string;
|
|
25
|
+
stop(): Promise<void>;
|
|
26
|
+
private handle;
|
|
27
|
+
private handleApi;
|
|
28
|
+
/** Files of the built dashboard; anything else → index.html (the app routes itself). */
|
|
29
|
+
private serveAsset;
|
|
30
|
+
private static findAssets;
|
|
31
|
+
private static isFile;
|
|
32
|
+
private static json;
|
|
33
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
var MonitorServer_1;
|
|
11
|
+
import { createReadStream } from 'node:fs';
|
|
12
|
+
import { stat } from 'node:fs/promises';
|
|
13
|
+
import { createServer } from 'node:http';
|
|
14
|
+
import { extname, join, normalize, sep } from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { inject, injectable } from 'inversify';
|
|
17
|
+
import { TYPES } from '../di/types.js';
|
|
18
|
+
/**
|
|
19
|
+
* The monitoring dashboard: the built Svelte app (dashboard/dist) and its JSON API, on node:http.
|
|
20
|
+
* GET /api/overview · /api/runs?limit=&flow=&status= · /api/runs/:runId
|
|
21
|
+
* Read-only. Binds to 127.0.0.1 unless monitor.host says otherwise.
|
|
22
|
+
*/
|
|
23
|
+
let MonitorServer = class MonitorServer {
|
|
24
|
+
static { MonitorServer_1 = this; }
|
|
25
|
+
config;
|
|
26
|
+
api;
|
|
27
|
+
static API = 'api';
|
|
28
|
+
static DEFAULT_LIMIT = 100;
|
|
29
|
+
static INDEX = 'index.html';
|
|
30
|
+
static TYPES = {
|
|
31
|
+
'.html': 'text/html; charset=utf-8',
|
|
32
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
33
|
+
'.css': 'text/css; charset=utf-8',
|
|
34
|
+
'.svg': 'image/svg+xml',
|
|
35
|
+
'.json': 'application/json',
|
|
36
|
+
'.ico': 'image/x-icon',
|
|
37
|
+
};
|
|
38
|
+
/** Where the built dashboard is: next to the sources (tsx) or next to dist/ (the published package). */
|
|
39
|
+
static ASSET_DIRS = [new URL('../../dashboard/dist/', import.meta.url), new URL('../../../dashboard/dist/', import.meta.url)];
|
|
40
|
+
server = null;
|
|
41
|
+
assets = null;
|
|
42
|
+
logger;
|
|
43
|
+
constructor(config, api, logger) {
|
|
44
|
+
this.config = config;
|
|
45
|
+
this.api = api;
|
|
46
|
+
this.logger = logger.child({ component: 'monitor' });
|
|
47
|
+
}
|
|
48
|
+
/** Returns the dashboard URL. */
|
|
49
|
+
async start() {
|
|
50
|
+
this.assets = await MonitorServer_1.findAssets();
|
|
51
|
+
const server = createServer((req, res) => void this.handle(req, res));
|
|
52
|
+
await new Promise((resolve, reject) => {
|
|
53
|
+
server.once('error', reject);
|
|
54
|
+
server.listen(this.config.port, this.config.host, () => resolve());
|
|
55
|
+
});
|
|
56
|
+
this.server = server;
|
|
57
|
+
const url = this.url;
|
|
58
|
+
this.logger.info('monitor ready', { url, dashboard: this.assets ? 'built' : 'not built - run npm run build:dashboard' });
|
|
59
|
+
return url;
|
|
60
|
+
}
|
|
61
|
+
get url() {
|
|
62
|
+
const address = this.server?.address();
|
|
63
|
+
return address ? `http://${this.config.host}:${address.port}` : '';
|
|
64
|
+
}
|
|
65
|
+
async stop() {
|
|
66
|
+
const server = this.server;
|
|
67
|
+
this.server = null;
|
|
68
|
+
if (server)
|
|
69
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
70
|
+
}
|
|
71
|
+
async handle(req, res) {
|
|
72
|
+
const url = new URL(req.url ?? '/', 'http://monitor');
|
|
73
|
+
const [first, resource, id] = url.pathname.split('/').filter(Boolean);
|
|
74
|
+
try {
|
|
75
|
+
if (req.method !== 'GET')
|
|
76
|
+
return MonitorServer_1.json(res, 405, { error: 'read-only' });
|
|
77
|
+
if (first === MonitorServer_1.API)
|
|
78
|
+
return await this.handleApi(res, resource, id, url.searchParams);
|
|
79
|
+
return await this.serveAsset(res, url.pathname);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
this.logger.error('monitor request failed', { path: url.pathname, error });
|
|
83
|
+
MonitorServer_1.json(res, 500, { error: error.message });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async handleApi(res, resource, id, query) {
|
|
87
|
+
switch (resource) {
|
|
88
|
+
case 'overview':
|
|
89
|
+
return MonitorServer_1.json(res, 200, await this.api.overview());
|
|
90
|
+
case 'runs': {
|
|
91
|
+
if (id) {
|
|
92
|
+
const run = await this.api.run(decodeURIComponent(id));
|
|
93
|
+
return run ? MonitorServer_1.json(res, 200, run) : MonitorServer_1.json(res, 404, { error: 'run not found' });
|
|
94
|
+
}
|
|
95
|
+
const limit = Number(query.get('limit') ?? MonitorServer_1.DEFAULT_LIMIT);
|
|
96
|
+
const flow = query.get('flow') ?? undefined;
|
|
97
|
+
const status = query.get('status') ?? undefined;
|
|
98
|
+
return MonitorServer_1.json(res, 200, await this.api.runs(Number.isFinite(limit) ? limit : MonitorServer_1.DEFAULT_LIMIT, { ...(flow ? { flow } : {}), ...(status ? { status } : {}) }));
|
|
99
|
+
}
|
|
100
|
+
default:
|
|
101
|
+
return MonitorServer_1.json(res, 404, { error: 'unknown endpoint' });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Files of the built dashboard; anything else → index.html (the app routes itself). */
|
|
105
|
+
async serveAsset(res, path) {
|
|
106
|
+
if (!this.assets) {
|
|
107
|
+
res.writeHead(200, { 'content-type': MonitorServer_1.TYPES['.html'] }).end('<h1>scrapme monitor</h1><p>The dashboard is not built: run <code>npm run build:dashboard</code> in scrapme. The API works: <a href="/api/runs">/api/runs</a>.</p>');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const wanted = normalize(join(this.assets, decodeURIComponent(path)));
|
|
111
|
+
const inside = wanted.startsWith(this.assets + sep) || wanted === this.assets;
|
|
112
|
+
const file = inside && (await MonitorServer_1.isFile(wanted)) ? wanted : join(this.assets, MonitorServer_1.INDEX);
|
|
113
|
+
res.writeHead(200, { 'content-type': MonitorServer_1.TYPES[extname(file)] ?? 'application/octet-stream' });
|
|
114
|
+
createReadStream(file).pipe(res);
|
|
115
|
+
}
|
|
116
|
+
static async findAssets() {
|
|
117
|
+
for (const candidate of MonitorServer_1.ASSET_DIRS) {
|
|
118
|
+
const path = fileURLToPath(candidate);
|
|
119
|
+
if (!(await MonitorServer_1.isFile(join(path, MonitorServer_1.INDEX))))
|
|
120
|
+
continue;
|
|
121
|
+
let dir = normalize(path);
|
|
122
|
+
while (dir.endsWith(sep))
|
|
123
|
+
dir = dir.slice(0, -1);
|
|
124
|
+
return dir;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
static async isFile(path) {
|
|
129
|
+
return stat(path).then((s) => s.isFile()).catch(() => false);
|
|
130
|
+
}
|
|
131
|
+
static json(res, status, body) {
|
|
132
|
+
res.writeHead(status, { 'content-type': MonitorServer_1.TYPES['.json'], 'cache-control': 'no-store' }).end(JSON.stringify(body));
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
MonitorServer = MonitorServer_1 = __decorate([
|
|
136
|
+
injectable(),
|
|
137
|
+
__param(0, inject(TYPES.MonitorConfig)),
|
|
138
|
+
__param(1, inject(TYPES.MonitorApi)),
|
|
139
|
+
__param(2, inject(TYPES.Logger))
|
|
140
|
+
], MonitorServer);
|
|
141
|
+
export { MonitorServer };
|
|
142
|
+
//# sourceMappingURL=monitor-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor-server.js","sourceRoot":"","sources":["../../../src/monitor/monitor-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RunStatus, StepStatus } from '../flow/flow.models.js';
|
|
2
|
+
/** One row of the runs list (GET /api/runs). */
|
|
3
|
+
export interface RunSummary {
|
|
4
|
+
runId: string;
|
|
5
|
+
flow: string;
|
|
6
|
+
status: RunStatus;
|
|
7
|
+
currentStep: string | null;
|
|
8
|
+
flowAttempt: number;
|
|
9
|
+
maxFlowAttempts: number;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
finishedAt: string | null;
|
|
12
|
+
durationMs: number | null;
|
|
13
|
+
error: string | null;
|
|
14
|
+
steps: {
|
|
15
|
+
key: string;
|
|
16
|
+
status: StepStatus;
|
|
17
|
+
attempts: number;
|
|
18
|
+
}[];
|
|
19
|
+
}
|
|
20
|
+
/** GET /api/overview */
|
|
21
|
+
export interface Overview {
|
|
22
|
+
/** Names of the flows that have runs. */
|
|
23
|
+
flows: string[];
|
|
24
|
+
counts: Record<RunStatus, number>;
|
|
25
|
+
couchUrl: string;
|
|
26
|
+
runsDb: string;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor.models.js","sourceRoot":"","sources":["../../../src/monitor/monitor.models.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PostgresConfig, QueueConfig } from '../config/config.models.js';
|
|
2
|
+
import type { Logger } from '../logging/logger.js';
|
|
3
|
+
import type { FlowJobData, FlowJobHandler, FlowJobOptions, FlowQueue } from './queue.models.js';
|
|
4
|
+
/**
|
|
5
|
+
* Flow jobs on pg-boss. pg-boss retries a failed attempt after retryDelay (with backoff) and expires an attempt
|
|
6
|
+
* after expireInSeconds; scrapme aborts the job's signal at the same moment, so the running command stops.
|
|
7
|
+
*/
|
|
8
|
+
export declare class PgBossFlowQueue implements FlowQueue {
|
|
9
|
+
private readonly postgres;
|
|
10
|
+
private readonly queue;
|
|
11
|
+
private static readonly MS;
|
|
12
|
+
private boss;
|
|
13
|
+
private readonly workers;
|
|
14
|
+
private readonly running;
|
|
15
|
+
private readonly logger;
|
|
16
|
+
constructor(postgres: PostgresConfig, queue: QueueConfig, logger: Logger);
|
|
17
|
+
start(): Promise<void>;
|
|
18
|
+
work(queue: string, concurrency: number, handler: FlowJobHandler): Promise<void>;
|
|
19
|
+
schedule(queue: string, data: FlowJobData, o: FlowJobOptions): Promise<string>;
|
|
20
|
+
stop(): Promise<void>;
|
|
21
|
+
private requireBoss;
|
|
22
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
var PgBossFlowQueue_1;
|
|
11
|
+
import { inject, injectable } from 'inversify';
|
|
12
|
+
import PgBoss from 'pg-boss';
|
|
13
|
+
import { TYPES } from '../di/types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Flow jobs on pg-boss. pg-boss retries a failed attempt after retryDelay (with backoff) and expires an attempt
|
|
16
|
+
* after expireInSeconds; scrapme aborts the job's signal at the same moment, so the running command stops.
|
|
17
|
+
*/
|
|
18
|
+
let PgBossFlowQueue = class PgBossFlowQueue {
|
|
19
|
+
static { PgBossFlowQueue_1 = this; }
|
|
20
|
+
postgres;
|
|
21
|
+
queue;
|
|
22
|
+
static MS = 1_000;
|
|
23
|
+
boss = null;
|
|
24
|
+
workers = [];
|
|
25
|
+
running = new Set();
|
|
26
|
+
logger;
|
|
27
|
+
constructor(postgres, queue, logger) {
|
|
28
|
+
this.postgres = postgres;
|
|
29
|
+
this.queue = queue;
|
|
30
|
+
this.logger = logger.child({ component: 'pg-boss' });
|
|
31
|
+
}
|
|
32
|
+
async start() {
|
|
33
|
+
if (this.boss)
|
|
34
|
+
return;
|
|
35
|
+
const boss = new PgBoss({ connectionString: this.postgres.connectionString, schema: this.postgres.schema, deleteAfterDays: this.queue.retentionDays });
|
|
36
|
+
boss.on('error', (error) => this.logger.error('pg-boss error', { error }));
|
|
37
|
+
await boss.start();
|
|
38
|
+
this.boss = boss;
|
|
39
|
+
this.logger.debug('connected', { schema: this.postgres.schema });
|
|
40
|
+
}
|
|
41
|
+
async work(queue, concurrency, handler) {
|
|
42
|
+
const boss = this.requireBoss();
|
|
43
|
+
if (!(await boss.getQueue(queue)))
|
|
44
|
+
await boss.createQueue(queue);
|
|
45
|
+
for (let i = 0; i < concurrency; i++) {
|
|
46
|
+
const id = await boss.work(queue, { batchSize: 1, pollingIntervalSeconds: this.queue.pollingIntervalSeconds, includeMetadata: true }, async ([job]) => {
|
|
47
|
+
if (!job)
|
|
48
|
+
return;
|
|
49
|
+
// pg-boss expires the attempt after expireInSeconds; stop the commands at the same moment
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const timer = setTimeout(() => controller.abort(new Error('flow attempt expired after ' + job.expireInSeconds + ' s')), job.expireInSeconds * PgBossFlowQueue_1.MS);
|
|
52
|
+
this.running.add(controller);
|
|
53
|
+
try {
|
|
54
|
+
await handler({ id: job.id, data: job.data, retryCount: job.retryCount, retryLimit: job.retryLimit, signal: controller.signal });
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
this.running.delete(controller);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
this.workers.push(id);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async schedule(queue, data, o) {
|
|
65
|
+
const id = await this.requireBoss().send(queue, data, {
|
|
66
|
+
id: o.id,
|
|
67
|
+
retryLimit: o.retryLimit,
|
|
68
|
+
retryDelay: o.retryDelaySeconds,
|
|
69
|
+
retryBackoff: o.retryBackoff,
|
|
70
|
+
expireInSeconds: o.expireInSeconds,
|
|
71
|
+
...(o.startAfter ? { startAfter: o.startAfter } : {}),
|
|
72
|
+
...(o.priority !== undefined ? { priority: o.priority } : {}),
|
|
73
|
+
});
|
|
74
|
+
if (!id)
|
|
75
|
+
throw new Error('run ' + o.id + ' is already queued');
|
|
76
|
+
return id;
|
|
77
|
+
}
|
|
78
|
+
async stop() {
|
|
79
|
+
const boss = this.boss;
|
|
80
|
+
if (!boss)
|
|
81
|
+
return;
|
|
82
|
+
this.boss = null;
|
|
83
|
+
await Promise.all(this.workers.map((id) => boss.offWork({ id }).catch(() => undefined)));
|
|
84
|
+
this.workers.length = 0;
|
|
85
|
+
for (const controller of this.running)
|
|
86
|
+
controller.abort(new Error('scrapme is stopping'));
|
|
87
|
+
await boss.stop({ graceful: true, timeout: this.queue.shutdownTimeoutMs });
|
|
88
|
+
}
|
|
89
|
+
requireBoss() {
|
|
90
|
+
if (!this.boss)
|
|
91
|
+
throw new Error('scrapme is not started - call await scrapme.start() first');
|
|
92
|
+
return this.boss;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
PgBossFlowQueue = PgBossFlowQueue_1 = __decorate([
|
|
96
|
+
injectable(),
|
|
97
|
+
__param(0, inject(TYPES.PostgresConfig)),
|
|
98
|
+
__param(1, inject(TYPES.QueueConfig)),
|
|
99
|
+
__param(2, inject(TYPES.Logger))
|
|
100
|
+
], PgBossFlowQueue);
|
|
101
|
+
export { PgBossFlowQueue };
|
|
102
|
+
//# sourceMappingURL=pg-boss-flow-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pg-boss-flow-queue.js","sourceRoot":"","sources":["../../../src/queue/pg-boss-flow-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** What a flow job carries (pg-boss job data). The run itself lives in CouchDB, not in the job. */
|
|
2
|
+
export interface FlowJobData {
|
|
3
|
+
runId: string;
|
|
4
|
+
}
|
|
5
|
+
/** One execution of a flow job, as the worker sees it. */
|
|
6
|
+
export interface FlowJob {
|
|
7
|
+
id: string;
|
|
8
|
+
data: FlowJobData;
|
|
9
|
+
/** 0 on the first attempt (pg-boss retryCount). */
|
|
10
|
+
retryCount: number;
|
|
11
|
+
/** pg-boss retryLimit: attempts = retryLimit + 1. */
|
|
12
|
+
retryLimit: number;
|
|
13
|
+
/** Aborted when the attempt has run for its timeout (pg-boss expires it then) or the worker stops. */
|
|
14
|
+
signal: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
/** How one run is sent to its flow's queue. */
|
|
17
|
+
export interface FlowJobOptions {
|
|
18
|
+
/** pg-boss job id = the run id, so one run is one job. */
|
|
19
|
+
id: string;
|
|
20
|
+
retryLimit: number;
|
|
21
|
+
retryDelaySeconds: number;
|
|
22
|
+
retryBackoff: boolean;
|
|
23
|
+
expireInSeconds: number;
|
|
24
|
+
startAfter?: Date;
|
|
25
|
+
priority?: number;
|
|
26
|
+
}
|
|
27
|
+
export type FlowJobHandler = (job: FlowJob) => Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* The flow queues: pg-boss (PgBossFlowQueue), an in-memory fake in tests. One queue per flow; retries, delays and
|
|
30
|
+
* expiry of the jobs are pg-boss's — scrapme only says how many and how long.
|
|
31
|
+
*/
|
|
32
|
+
export interface FlowQueue {
|
|
33
|
+
start(): Promise<void>;
|
|
34
|
+
/** Create the queue (no-op when it exists) and start `concurrency` workers on it in this process. */
|
|
35
|
+
work(queue: string, concurrency: number, handler: FlowJobHandler): Promise<void>;
|
|
36
|
+
schedule(queue: string, data: FlowJobData, options: FlowJobOptions): Promise<string>;
|
|
37
|
+
stop(): Promise<void>;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.models.js","sourceRoot":"","sources":["../../../src/queue/queue.models.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { LightyConfig } from '../config/config.models.js';
|
|
2
|
+
import type { Logger } from '../logging/logger.js';
|
|
3
|
+
import { type LineLevel } from '../logging/logger.models.js';
|
|
4
|
+
import type { ErrorReportContext, ErrorReporter } from './reporting.models.js';
|
|
5
|
+
/**
|
|
6
|
+
* Reports to LightyErrors (sentry-clone) with lighty-errors-client. The client is created lazily and NOT
|
|
7
|
+
* installed: a library must not patch the host's console or process hooks — FlowRunner flushes after a failed run.
|
|
8
|
+
*/
|
|
9
|
+
export declare class LightyErrorReporter implements ErrorReporter {
|
|
10
|
+
private readonly config;
|
|
11
|
+
/** Our line levels → lighty-errors-client's (it says "warning"). */
|
|
12
|
+
private static readonly LEVELS;
|
|
13
|
+
private static readonly LIBRARY;
|
|
14
|
+
/** Level of the event for a step that finally failed. */
|
|
15
|
+
private static readonly REPORT_LEVEL;
|
|
16
|
+
private client;
|
|
17
|
+
private readonly logger;
|
|
18
|
+
constructor(config: LightyConfig, logger: Logger);
|
|
19
|
+
report(error: unknown, context: ErrorReportContext): string;
|
|
20
|
+
log(message: string, data?: Record<string, unknown>, level?: LineLevel): void;
|
|
21
|
+
flush(timeoutMs?: number): Promise<boolean>;
|
|
22
|
+
close(timeoutMs?: number): Promise<void>;
|
|
23
|
+
private lighty;
|
|
24
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
var LightyErrorReporter_1;
|
|
11
|
+
import { inject, injectable } from 'inversify';
|
|
12
|
+
import { LightyClient } from 'lighty-errors-client';
|
|
13
|
+
import { TYPES } from '../di/types.js';
|
|
14
|
+
import { LINE_LEVEL } from '../logging/logger.models.js';
|
|
15
|
+
/**
|
|
16
|
+
* Reports to LightyErrors (sentry-clone) with lighty-errors-client. The client is created lazily and NOT
|
|
17
|
+
* installed: a library must not patch the host's console or process hooks — FlowRunner flushes after a failed run.
|
|
18
|
+
*/
|
|
19
|
+
let LightyErrorReporter = class LightyErrorReporter {
|
|
20
|
+
static { LightyErrorReporter_1 = this; }
|
|
21
|
+
config;
|
|
22
|
+
/** Our line levels → lighty-errors-client's (it says "warning"). */
|
|
23
|
+
static LEVELS = {
|
|
24
|
+
[LINE_LEVEL.DEBUG]: 'debug',
|
|
25
|
+
[LINE_LEVEL.INFO]: 'info',
|
|
26
|
+
[LINE_LEVEL.WARN]: 'warning',
|
|
27
|
+
[LINE_LEVEL.ERROR]: 'error',
|
|
28
|
+
};
|
|
29
|
+
static LIBRARY = 'scrapme';
|
|
30
|
+
/** Level of the event for a step that finally failed. */
|
|
31
|
+
static REPORT_LEVEL = 'error';
|
|
32
|
+
client = null;
|
|
33
|
+
logger;
|
|
34
|
+
constructor(config, logger) {
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.logger = logger.child({ component: 'lighty' });
|
|
37
|
+
}
|
|
38
|
+
report(error, context) {
|
|
39
|
+
try {
|
|
40
|
+
return this.lighty().captureException(error, {
|
|
41
|
+
level: LightyErrorReporter_1.REPORT_LEVEL,
|
|
42
|
+
tags: { flow: context.flow, step: context.step, command: context.command, ...context.tags },
|
|
43
|
+
extra: {
|
|
44
|
+
runId: context.runId,
|
|
45
|
+
attempt: context.attempt,
|
|
46
|
+
maxAttempts: context.maxAttempts,
|
|
47
|
+
...(context.input !== undefined ? { input: context.input } : {}),
|
|
48
|
+
...context.extra,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
this.logger.warn('could not report the error to LightyErrors', { error: err });
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
log(message, data, level = LINE_LEVEL.INFO) {
|
|
58
|
+
this.lighty().addLog(message, data, LightyErrorReporter_1.LEVELS[level]);
|
|
59
|
+
}
|
|
60
|
+
async flush(timeoutMs = this.config.flushTimeoutMs) {
|
|
61
|
+
if (!this.client)
|
|
62
|
+
return true;
|
|
63
|
+
const ok = await this.client.flush(timeoutMs);
|
|
64
|
+
if (!ok)
|
|
65
|
+
this.logger.warn('LightyErrors did not accept all events in time (they stay queued and are retried)', { timeoutMs });
|
|
66
|
+
return ok;
|
|
67
|
+
}
|
|
68
|
+
async close(timeoutMs = this.config.flushTimeoutMs) {
|
|
69
|
+
const client = this.client;
|
|
70
|
+
this.client = null;
|
|
71
|
+
if (client)
|
|
72
|
+
await client.close(timeoutMs);
|
|
73
|
+
}
|
|
74
|
+
lighty() {
|
|
75
|
+
if (!this.client) {
|
|
76
|
+
const l = this.config;
|
|
77
|
+
this.client = new LightyClient({
|
|
78
|
+
dsn: l.dsn,
|
|
79
|
+
environment: l.environment,
|
|
80
|
+
release: l.release,
|
|
81
|
+
captureConsole: false,
|
|
82
|
+
captureUncaught: false,
|
|
83
|
+
unhandledRejections: 'none',
|
|
84
|
+
extra: { library: LightyErrorReporter_1.LIBRARY },
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return this.client;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
LightyErrorReporter = LightyErrorReporter_1 = __decorate([
|
|
91
|
+
injectable(),
|
|
92
|
+
__param(0, inject(TYPES.LightyConfig)),
|
|
93
|
+
__param(1, inject(TYPES.Logger))
|
|
94
|
+
], LightyErrorReporter);
|
|
95
|
+
export { LightyErrorReporter };
|
|
96
|
+
//# sourceMappingURL=lighty-error-reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lighty-error-reporter.js","sourceRoot":"","sources":["../../../src/reporting/lighty-error-reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Logger } from '../logging/logger.js';
|
|
2
|
+
import type { LineLevel } from '../logging/logger.models.js';
|
|
3
|
+
import type { ErrorReportContext, ErrorReporter } from './reporting.models.js';
|
|
4
|
+
/** Used when LIGHTY_DSN is empty: the final error of a run goes to the log (and CouchDB, via FlowRunStore). */
|
|
5
|
+
export declare class LogErrorReporter implements ErrorReporter {
|
|
6
|
+
private readonly logger;
|
|
7
|
+
constructor(logger: Logger);
|
|
8
|
+
report(error: unknown, context: ErrorReportContext): string;
|
|
9
|
+
log(_message: string, _data?: Record<string, unknown>, _level?: LineLevel): void;
|
|
10
|
+
flush(): Promise<boolean>;
|
|
11
|
+
close(): Promise<void>;
|
|
12
|
+
}
|