velocious 1.0.381 → 1.0.382

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +18 -0
  2. package/build/src/background-jobs/store.d.ts +35 -0
  3. package/build/src/background-jobs/store.d.ts.map +1 -1
  4. package/build/src/background-jobs/store.js +88 -1
  5. package/build/src/background-jobs/web/authorization.d.ts +20 -0
  6. package/build/src/background-jobs/web/authorization.d.ts.map +1 -0
  7. package/build/src/background-jobs/web/authorization.js +75 -0
  8. package/build/src/background-jobs/web/controller.d.ts +69 -0
  9. package/build/src/background-jobs/web/controller.d.ts.map +1 -0
  10. package/build/src/background-jobs/web/controller.js +212 -0
  11. package/build/src/background-jobs/web/index.d.ts +41 -0
  12. package/build/src/background-jobs/web/index.d.ts.map +1 -0
  13. package/build/src/background-jobs/web/index.js +53 -0
  14. package/build/src/background-jobs/web/path-matcher.d.ts +38 -0
  15. package/build/src/background-jobs/web/path-matcher.d.ts.map +1 -0
  16. package/build/src/background-jobs/web/path-matcher.js +71 -0
  17. package/build/src/background-jobs/web/registry.d.ts +41 -0
  18. package/build/src/background-jobs/web/registry.d.ts.map +1 -0
  19. package/build/src/background-jobs/web/registry.js +41 -0
  20. package/build/src/configuration.d.ts +11 -0
  21. package/build/src/configuration.d.ts.map +1 -1
  22. package/build/src/configuration.js +25 -2
  23. package/build/src/routes/base-route.d.ts +14 -0
  24. package/build/src/routes/base-route.d.ts.map +1 -1
  25. package/build/src/routes/base-route.js +5 -1
  26. package/build/src/routes/basic-route.d.ts +12 -0
  27. package/build/src/routes/basic-route.d.ts.map +1 -1
  28. package/build/src/routes/basic-route.js +20 -1
  29. package/build/src/routes/index.d.ts +11 -0
  30. package/build/src/routes/index.d.ts.map +1 -1
  31. package/build/src/routes/index.js +21 -1
  32. package/build/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +1 -1
@@ -0,0 +1,212 @@
1
+ // @ts-check
2
+ import Controller from "../../controller.js";
3
+ import BackgroundJobsStore from "../store.js";
4
+ import { authorizeJobsRequest } from "./authorization.js";
5
+ import { getJobsMount } from "./registry.js";
6
+ const DASHBOARD_STATUSES = ["queued", "handed_off", "completed", "failed", "orphaned"];
7
+ const SORTABLE_KEYS = ["attempts", "completedAtMs", "createdAtMs", "failedAtMs", "handedOffAtMs", "scheduledAtMs"];
8
+ const DEFAULT_PER_PAGE = 25;
9
+ const MAX_PER_PAGE = 100;
10
+ /**
11
+ * Read-only HTTP API backing the background-jobs dashboard. Mounted by
12
+ * {@link import("./index.js").default} as a route-resolver hook so it can ship
13
+ * inside the velocious package. Every action is gated by {@link authorizeJobsRequest}.
14
+ */
15
+ export default class VelociousBackgroundJobsWebController extends Controller {
16
+ /** @returns {import("./registry.js").JobsMountOptions} - Options for the mount that matched this request. */
17
+ _mountOptions() {
18
+ const at = this.params().velociousJobsMountAt;
19
+ return getJobsMount(this.getConfiguration(), at) || {};
20
+ }
21
+ /** @returns {BackgroundJobsStore} - Jobs store scoped to the mount's database. */
22
+ _store() {
23
+ if (!this._jobsStore) {
24
+ this._jobsStore = new BackgroundJobsStore({
25
+ configuration: this.getConfiguration(),
26
+ databaseIdentifier: this._mountOptions().databaseIdentifier
27
+ });
28
+ }
29
+ return this._jobsStore;
30
+ }
31
+ /**
32
+ * Adds CORS headers when the request origin is allowed, so the standalone
33
+ * browser dashboard can read the API cross-origin.
34
+ * @param {import("./registry.js").JobsMountOptions} options - Mount options.
35
+ * @returns {void} - No return value.
36
+ */
37
+ _applyCorsHeaders(options) {
38
+ const allowedOrigins = Array.isArray(options.allowedOrigins) ? options.allowedOrigins : [];
39
+ if (allowedOrigins.length === 0)
40
+ return;
41
+ const origin = this.request().origin();
42
+ const allowAll = allowedOrigins.includes("*");
43
+ if (!origin)
44
+ return;
45
+ if (!allowAll && !allowedOrigins.includes(origin))
46
+ return;
47
+ const response = this.response();
48
+ response.setHeader("Access-Control-Allow-Origin", allowAll ? "*" : origin);
49
+ response.setHeader("Vary", "Origin");
50
+ response.setHeader("Access-Control-Allow-Headers", "authorization, content-type");
51
+ response.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
52
+ }
53
+ /**
54
+ * Applies CORS headers, authorizes the request, and runs the action body only
55
+ * when authorized. Renders a 401 otherwise. The base controller has no
56
+ * before-action halting, so authorization is enforced here per action.
57
+ * @param {() => Promise<void>} actionFn - Action body.
58
+ * @returns {Promise<void>} - Resolves when complete.
59
+ */
60
+ async _respond(actionFn) {
61
+ const options = this._mountOptions();
62
+ this._applyCorsHeaders(options);
63
+ const authorized = await authorizeJobsRequest({
64
+ ability: this.currentAbility(),
65
+ configuration: this.getConfiguration(),
66
+ options,
67
+ request: this.request()
68
+ });
69
+ if (!authorized) {
70
+ await this.render({ json: { error: "unauthorized" }, status: 401 });
71
+ return;
72
+ }
73
+ await actionFn();
74
+ }
75
+ /** @returns {Promise<void>} - Resolves when complete. */
76
+ async health() {
77
+ await this._respond(async () => {
78
+ await this.render({ json: { ok: true, service: "velocious-background-jobs" } });
79
+ });
80
+ }
81
+ /** @returns {Promise<void>} - Resolves when complete. */
82
+ async stats() {
83
+ await this._respond(async () => {
84
+ const counts = await this._store().countsByStatus();
85
+ /** @type {Record<string, number>} */
86
+ const byStatus = {};
87
+ let total = 0;
88
+ for (const status of DASHBOARD_STATUSES) {
89
+ byStatus[status] = counts[status] || 0;
90
+ }
91
+ for (const value of Object.values(counts)) {
92
+ total += value;
93
+ }
94
+ await this.render({ json: { counts: byStatus, generatedAtMs: Date.now(), total } });
95
+ });
96
+ }
97
+ /** @returns {Promise<void>} - Resolves when complete. */
98
+ async index() {
99
+ await this._respond(async () => {
100
+ const params = this.params();
101
+ const status = this._sanitizeStatus(params.status);
102
+ const jobName = typeof params.jobName === "string" && params.jobName.length > 0 ? params.jobName : undefined;
103
+ const page = this._positiveInt(params.page, 1);
104
+ const perPage = Math.min(this._positiveInt(params.perPage, DEFAULT_PER_PAGE), MAX_PER_PAGE);
105
+ const { sortColumn, sortDirection } = this._sanitizeSort(params.sort);
106
+ const store = this._store();
107
+ const jobs = await store.listJobs({ jobName, limit: perPage, offset: (page - 1) * perPage, sortColumn, sortDirection, status });
108
+ const total = await store.countJobs({ jobName, status });
109
+ await this.render({ json: {
110
+ jobs: jobs.map((job) => this._serializeJob(job)),
111
+ pagination: { page, perPage, total, totalPages: perPage > 0 ? Math.ceil(total / perPage) : 0 }
112
+ } });
113
+ });
114
+ }
115
+ /** @returns {Promise<void>} - Resolves when complete. */
116
+ async show() {
117
+ await this._respond(async () => {
118
+ const job = await this._store().getJob(this.params().id);
119
+ if (!job) {
120
+ await this.render({ json: { error: "not_found" }, status: 404 });
121
+ return;
122
+ }
123
+ await this.render({ json: { job: this._serializeJob(job) } });
124
+ });
125
+ }
126
+ /** @returns {Promise<void>} - Resolves when complete. */
127
+ async schedule() {
128
+ await this._respond(async () => {
129
+ const scheduled = await this.getConfiguration().getScheduledBackgroundJobsConfig();
130
+ await this.render({ json: { schedule: this._serializeSchedule(scheduled) } });
131
+ });
132
+ }
133
+ /**
134
+ * @param {import("../types.js").BackgroundJobRow} job - Job row.
135
+ * @returns {Record<string, any>} - Serialized job for the API.
136
+ */
137
+ _serializeJob(job) {
138
+ const redactArgs = Boolean(this._mountOptions().redactArgs);
139
+ return {
140
+ args: redactArgs ? undefined : job.args,
141
+ argsRedacted: redactArgs,
142
+ attempts: job.attempts,
143
+ completedAtMs: job.completedAtMs,
144
+ createdAtMs: job.createdAtMs,
145
+ failedAtMs: job.failedAtMs,
146
+ forked: job.forked,
147
+ handedOffAtMs: job.handedOffAtMs,
148
+ id: job.id,
149
+ jobName: job.jobName,
150
+ lastError: job.lastError,
151
+ maxRetries: job.maxRetries,
152
+ orphanedAtMs: job.orphanedAtMs,
153
+ scheduledAtMs: job.scheduledAtMs,
154
+ status: job.status,
155
+ workerId: job.workerId
156
+ };
157
+ }
158
+ /**
159
+ * @param {import("../../configuration-types.js").ScheduledBackgroundJobsConfiguration | undefined} scheduled - Scheduled jobs config.
160
+ * @returns {Array<Record<string, any>>} - Serialized recurring jobs.
161
+ */
162
+ _serializeSchedule(scheduled) {
163
+ const jobs = scheduled?.jobs;
164
+ if (!jobs || typeof jobs !== "object")
165
+ return [];
166
+ const redactArgs = Boolean(this._mountOptions().redactArgs);
167
+ return Object.keys(jobs).map((name) => {
168
+ const entry = jobs[name] || /** @type {any} */ ({});
169
+ return {
170
+ args: redactArgs ? undefined : (entry.args || []),
171
+ cron: entry.cron,
172
+ enabled: entry.enabled !== false,
173
+ every: entry.every,
174
+ jobName: typeof entry.class === "function" ? entry.class.name : undefined,
175
+ name,
176
+ options: entry.options || {}
177
+ };
178
+ });
179
+ }
180
+ /**
181
+ * @param {unknown} value - Raw status param.
182
+ * @returns {string | undefined} - Valid status or undefined.
183
+ */
184
+ _sanitizeStatus(value) {
185
+ return typeof value === "string" && DASHBOARD_STATUSES.includes(value) ? value : undefined;
186
+ }
187
+ /**
188
+ * @param {unknown} value - Raw sort param (e.g. "createdAtMs" or "-failedAtMs").
189
+ * @returns {{sortColumn: string, sortDirection: "ASC" | "DESC"}} - Normalized sort.
190
+ */
191
+ _sanitizeSort(value) {
192
+ if (typeof value !== "string" || value.length === 0) {
193
+ return { sortColumn: "createdAtMs", sortDirection: "DESC" };
194
+ }
195
+ const descending = value.startsWith("-");
196
+ const key = descending ? value.slice(1) : value;
197
+ const sortColumn = SORTABLE_KEYS.includes(key) ? key : "createdAtMs";
198
+ return { sortColumn, sortDirection: descending ? "DESC" : "ASC" };
199
+ }
200
+ /**
201
+ * @param {unknown} value - Raw numeric param.
202
+ * @param {number} fallback - Fallback when invalid.
203
+ * @returns {number} - Positive integer.
204
+ */
205
+ _positiveInt(value, fallback) {
206
+ const numeric = Number(Array.isArray(value) ? value[0] : value);
207
+ if (!Number.isFinite(numeric) || numeric < 1)
208
+ return fallback;
209
+ return Math.floor(numeric);
210
+ }
211
+ }
212
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udHJvbGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9iYWNrZ3JvdW5kLWpvYnMvd2ViL2NvbnRyb2xsZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sVUFBVSxNQUFNLHFCQUFxQixDQUFBO0FBQzVDLE9BQU8sbUJBQW1CLE1BQU0sYUFBYSxDQUFBO0FBQzdDLE9BQU8sRUFBQyxvQkFBb0IsRUFBQyxNQUFNLG9CQUFvQixDQUFBO0FBQ3ZELE9BQU8sRUFBQyxZQUFZLEVBQUMsTUFBTSxlQUFlLENBQUE7QUFFMUMsTUFBTSxrQkFBa0IsR0FBRyxDQUFDLFFBQVEsRUFBRSxZQUFZLEVBQUUsV0FBVyxFQUFFLFFBQVEsRUFBRSxVQUFVLENBQUMsQ0FBQTtBQUN0RixNQUFNLGFBQWEsR0FBRyxDQUFDLFVBQVUsRUFBRSxlQUFlLEVBQUUsYUFBYSxFQUFFLFlBQVksRUFBRSxlQUFlLEVBQUUsZUFBZSxDQUFDLENBQUE7QUFDbEgsTUFBTSxnQkFBZ0IsR0FBRyxFQUFFLENBQUE7QUFDM0IsTUFBTSxZQUFZLEdBQUcsR0FBRyxDQUFBO0FBRXhCOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsT0FBTyxPQUFPLG9DQUFxQyxTQUFRLFVBQVU7SUFDMUUsNkdBQTZHO0lBQzdHLGFBQWE7UUFDWCxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsb0JBQW9CLENBQUE7UUFFN0MsT0FBTyxZQUFZLENBQUMsSUFBSSxDQUFDLGdCQUFnQixFQUFFLEVBQUUsRUFBRSxDQUFDLElBQUksRUFBRSxDQUFBO0lBQ3hELENBQUM7SUFFRCxrRkFBa0Y7SUFDbEYsTUFBTTtRQUNKLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDckIsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLG1CQUFtQixDQUFDO2dCQUN4QyxhQUFhLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixF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Q0FBQTtJQUNqRSxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILFlBQVksQ0FBQyxLQUFLLEVBQUUsUUFBUTtRQUMxQixNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQTtRQUUvRCxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsSUFBSSxPQUFPLEdBQUcsQ0FBQztZQUFFLE9BQU8sUUFBUSxDQUFBO1FBRTdELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUM1QixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IENvbnRyb2xsZXIgZnJvbSBcIi4uLy4uL2NvbnRyb2xsZXIuanNcIlxuaW1wb3J0IEJhY2tncm91bmRKb2JzU3RvcmUgZnJvbSBcIi4uL3N0b3JlLmpzXCJcbmltcG9ydCB7YXV0aG9yaXplSm9ic1JlcXVlc3R9IGZyb20gXCIuL2F1dGhvcml6YXRpb24uanNcIlxuaW1wb3J0IHtnZXRKb2JzTW91bnR9IGZyb20gXCIuL3JlZ2lzdHJ5LmpzXCJcblxuY29uc3QgREFTSEJPQVJEX1NUQVRVU0VTID0gW1wicXVldWVkXCIsIFwiaGFuZGVkX29mZlwiLCBcImNvbXBsZXRlZFwiLCBcImZhaWxlZFwiLCBcIm9ycGhhbmVkXCJdXG5jb25zdCBTT1JUQUJMRV9LRVlTID0gW1wiYXR0ZW1wdHNcIiwgXCJjb21wbGV0ZWRBdE1zXCIsIFwiY3JlYXRlZEF0TXNcIiwgXCJmYWlsZWRBdE1zXCIsIFwiaGFuZGVkT2ZmQXRNc1wiLCBcInNjaGVkdWxlZEF0TXNcIl1cbmNvbnN0IERFRkFVTFRfUEVSX1BBR0UgPSAyNVxuY29uc3QgTUFYX1BFUl9QQUdFID0gMTAwXG5cbi8qKlxuICogUmVhZC1vbmx5IEhUVFAgQVBJIGJhY2tpbmcgdGhlIGJhY2tncm91bmQtam9icyBkYXNoYm9hcmQuIE1vdW50ZWQgYnlcbiAqIHtAbGluayBpbXBvcnQoXCIuL2luZGV4LmpzXCIpLmRlZmF1bHR9IGFzIGEgcm91dGUtcmVzb2x2ZXIgaG9vayBzbyBpdCBjYW4gc2hpcFxuICogaW5zaWRlIHRoZSB2ZWxvY2lvdXMgcGFja2FnZS4gRXZlcnkgYWN0aW9uIGlzIGdhdGVkIGJ5IHtAbGluayBhdXRob3JpemVKb2JzUmVxdWVzdH0uXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFZlbG9jaW91c0JhY2tncm91bmRKb2JzV2ViQ29udHJvbGxlciBleHRlbmRzIENvbnRyb2xsZXIge1xuICAvKiogQHJldHVybnMge2ltcG9ydChcIi4vcmVnaXN0cnkuanNcIikuSm9ic01vdW50T3B0aW9uc30gLSBPcHRpb25zIGZvciB0aGUgbW91bnQgdGhhdCBtYXRjaGVkIHRoaXMgcmVxdWVzdC4gKi9cbiAgX21vdW50T3B0aW9ucygpIHtcbiAgICBjb25zdCBhdCA9IHRoaXMucGFyYW1zKCkudmVsb2Npb3VzSm9ic01vdW50QXRcblxuICAgIHJldHVybiBnZXRKb2JzTW91bnQodGhpcy5nZXRDb25maWd1cmF0aW9uKCksIGF0KSB8fCB7fVxuICB9XG5cbiAgLyoqIEByZXR1cm5zIHtCYWNrZ3JvdW5kSm9ic1N0b3JlfSAtIEpvYnMgc3RvcmUgc2NvcGVkIHRvIHRoZSBtb3VudCdzIGRhdGFiYXNlLiAqL1xuICBfc3RvcmUoKSB7XG4gICAgaWYgKCF0aGlzLl9qb2JzU3RvcmUpIHtcbiAgICAgIHRoaXMuX2pvYnNTdG9yZSA9IG5ldyBCYWNrZ3JvdW5kSm9ic1N0b3JlKHtcbiAgICAgICAgY29uZmlndXJhdGlvbjogdGhpcy5nZXRDb25maWd1cmF0aW9uKCksXG4gICAgICAgIGRhdGFiYXNlSWRlbnRpZmllcjogdGhpcy5fbW91bnRPcHRpb25zKCkuZGF0YWJhc2VJZGVudGlmaWVyXG4gICAgICB9KVxuICAgIH1cblxuICAgIHJldHVybiB0aGlzLl9qb2JzU3RvcmVcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIENPUlMgaGVhZGVycyB3aGVuIHRoZSByZXF1ZXN0IG9yaWdpbiBpcyBhbGxvd2VkLCBzbyB0aGUgc3RhbmRhbG9uZVxuICAgKiBicm93c2VyIGRhc2hib2FyZCBjYW4gcmVhZCB0aGUgQVBJIGNyb3NzLW9yaWdpbi5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3JlZ2lzdHJ5LmpzXCIpLkpvYnNNb3VudE9wdGlvbnN9IG9wdGlvbnMgLSBNb3VudCBvcHRpb25zLlxuICAgKiBAcmV0dXJucyB7dm9pZH0gLSBObyByZXR1cm4gdmFsdWUuXG4gICAqL1xuICBfYXBwbHlDb3JzSGVhZGVycyhvcHRpb25zKSB7XG4gICAgY29uc3QgYWxsb3dlZE9yaWdpbnMgPSBBcnJheS5pc0FycmF5KG9wdGlvbnMuYWxsb3dlZE9yaWdpbnMpID8gb3B0aW9ucy5hbGxvd2VkT3JpZ2lucyA6IFtdXG5cbiAgICBpZiAoYWxsb3dlZE9yaWdpbnMubGVuZ3RoID09PSAwKSByZXR1cm5cblxuICAgIGNvbnN0IG9yaWdpbiA9IHRoaXMucmVxdWVzdCgpLm9yaWdpbigpXG4gICAgY29uc3QgYWxsb3dBbGwgPSBhbGxvd2VkT3JpZ2lucy5pbmNsdWRlcyhcIipcIilcblxuICAgIGlmICghb3JpZ2luKSByZXR1cm5cbiAgICBpZiAoIWFsbG93QWxsICYmICFhbGxvd2VkT3JpZ2lucy5pbmNsdWRlcyhvcmlnaW4pKSByZXR1cm5cblxuICAgIGNvbnN0IHJlc3BvbnNlID0gdGhpcy5yZXNwb25zZSgpXG5cbiAgICByZXNwb25zZS5zZXRIZWFkZXIoXCJBY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW5cIiwgYWxsb3dBbGwgPyBcIipcIiA6IG9yaWdpbilcbiAgICByZXNwb25zZS5zZXRIZWFkZXIoXCJWYXJ5XCIsIFwiT3JpZ2luXCIpXG4gICAgcmVzcG9uc2Uuc2V0SGVhZGVyKFwiQWNjZXNzLUNvbnRyb2wtQWxsb3ctSGVhZGVyc1wiLCBcImF1dGhvcml6YXRpb24sIGNvbnRlbnQtdHlwZVwiKVxuICAgIHJlc3BvbnNlLnNldEhlYWRlcihcIkFjY2Vzcy1Db250cm9sLUFsbG93LU1ldGhvZHNcIiwgXCJHRVQsIFBPU1QsIERFTEVURSwgT1BUSU9OU1wiKVxuICB9XG5cbiAgLyoqXG4gICAqIEFwcGxpZXMgQ09SUyBoZWFkZXJzLCBhdXRob3JpemVzIHRoZSByZXF1ZXN0LCBhbmQgcnVucyB0aGUgYWN0aW9uIGJvZHkgb25seVxuICAgKiB3aGVuIGF1dGhvcml6ZWQuIFJlbmRlcnMgYSA0MDEgb3RoZXJ3aXNlLiBUaGUgYmFzZSBjb250cm9sbGVyIGhhcyBub1xuICAgKiBiZWZvcmUtYWN0aW9uIGhhbHRpbmcsIHNvIGF1dGhvcml6YXRpb24gaXMgZW5mb3JjZWQgaGVyZSBwZXIgYWN0aW9uLlxuICAgKiBAcGFyYW0geygpID0+IFByb21pc2U8dm9pZD59IGFjdGlvbkZuIC0gQWN0aW9uIGJvZHkuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gY29tcGxldGUuXG4gICAqL1xuICBhc3luYyBfcmVzcG9uZChhY3Rpb25Gbikge1xuICAgIGNvbnN0IG9wdGlvbnMgPSB0aGlzLl9tb3VudE9wdGlvbnMoKVxuXG4gICAgdGhpcy5fYXBwbHlDb3JzSGVhZGVycyhvcHRpb25zKVxuXG4gICAgY29uc3QgYXV0aG9yaXplZCA9IGF3YWl0IGF1dGhvcml6ZUpvYnNSZXF1ZXN0KHtcbiAgICAgIGFiaWxpdHk6IHRoaXMuY3VycmVudEFiaWxpdHkoKSxcbiAgICAgIGNvbmZpZ3VyYXRpb246IHRoaXMuZ2V0Q29uZmlndXJhdGlvbigpLFxuICAgICAgb3B0aW9ucyxcbiAgICAgIHJlcXVlc3Q6IHRoaXMucmVxdWVzdCgpXG4gICAgfSlcblxuICAgIGlmICghYXV0aG9yaXplZCkge1xuICAgICAgYXdhaXQgdGhpcy5yZW5kZXIoe2pzb246IHtlcnJvcjogXCJ1bmF1dGhvcml6ZWRcIn0sIHN0YXR1czogNDAxfSlcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGF3YWl0IGFjdGlvbkZuKClcbiAgfVxuXG4gIC8qKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGNvbXBsZXRlLiAqL1xuICBhc3luYyBoZWFsdGgoKSB7XG4gICAgYXdhaXQgdGhpcy5fcmVzcG9uZChhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCB0aGlzLnJlbmRlcih7anNvbjoge29rOiB0cnVlLCBzZXJ2aWNlOiBcInZlbG9jaW91cy1iYWNrZ3JvdW5kLWpvYnNcIn19KVxuICAgIH0pXG4gIH1cblxuICAvKiogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiBjb21wbGV0ZS4gKi9cbiAgYXN5bmMgc3RhdHMoKSB7XG4gICAgYXdhaXQgdGhpcy5fcmVzcG9uZChhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBjb3VudHMgPSBhd2FpdCB0aGlzLl9zdG9yZSgpLmNvdW50c0J5U3RhdHVzKClcbiAgICAgIC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywgbnVtYmVyPn0gKi9cbiAgICAgIGNvbnN0IGJ5U3RhdHVzID0ge31cbiAgICAgIGxldCB0b3RhbCA9IDBcblxuICAgICAgZm9yIChjb25zdCBzdGF0dXMgb2YgREFTSEJPQVJEX1NUQVRVU0VTKSB7XG4gICAgICAgIGJ5U3RhdHVzW3N0YXR1c10gPSBjb3VudHNbc3RhdHVzXSB8fCAwXG4gICAgICB9XG5cbiAgICAgIGZvciAoY29uc3QgdmFsdWUgb2YgT2JqZWN0LnZhbHVlcyhjb3VudHMpKSB7XG4gICAgICAgIHRvdGFsICs9IHZhbHVlXG4gICAgICB9XG5cbiAgICAgIGF3YWl0IHRoaXMucmVuZGVyKHtqc29uOiB7Y291bnRzOiBieVN0YXR1cywgZ2VuZXJhdGVkQXRNczogRGF0ZS5ub3coKSwgdG90YWx9fSlcbiAgICB9KVxuICB9XG5cbiAgLyoqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gY29tcGxldGUuICovXG4gIGFzeW5jIGluZGV4KCkge1xuICAgIGF3YWl0IHRoaXMuX3Jlc3BvbmQoYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgcGFyYW1zID0gdGhpcy5wYXJhbXMoKVxuICAgICAgY29uc3Qgc3RhdHVzID0gdGhpcy5fc2FuaXRpemVTdGF0dXMocGFyYW1zLnN0YXR1cylcbiAgICAgIGNvbnN0IGpvYk5hbWUgPSB0eXBlb2YgcGFyYW1zLmpvYk5hbWUgPT09IFwic3RyaW5nXCIgJiYgcGFyYW1zLmpvYk5hbWUubGVuZ3RoID4gMCA/IHBhcmFtcy5qb2JOYW1lIDogdW5kZWZpbmVkXG4gICAgICBjb25zdCBwYWdlID0gdGhpcy5fcG9zaXRpdmVJbnQocGFyYW1zLnBhZ2UsIDEpXG4gICAgICBjb25zdCBwZXJQYWdlID0gTWF0aC5taW4odGhpcy5fcG9zaXRpdmVJbnQocGFyYW1zLnBlclBhZ2UsIERFRkFVTFRfUEVSX1BBR0UpLCBNQVhfUEVSX1BBR0UpXG4gICAgICBjb25zdCB7c29ydENvbHVtbiwgc29ydERpcmVjdGlvbn0gPSB0aGlzLl9zYW5pdGl6ZVNvcnQocGFyYW1zLnNvcnQpXG4gICAgICBjb25zdCBzdG9yZSA9IHRoaXMuX3N0b3JlKClcbiAgICAgIGNvbnN0IGpvYnMgPSBhd2FpdCBzdG9yZS5saXN0Sm9icyh7am9iTmFtZSwgbGltaXQ6IHBlclBhZ2UsIG9mZnNldDogKHBhZ2UgLSAxKSAqIHBlclBhZ2UsIHNvcnRDb2x1bW4sIHNvcnREaXJlY3Rpb24sIHN0YXR1c30pXG4gICAgICBjb25zdCB0b3RhbCA9IGF3YWl0IHN0b3JlLmNvdW50Sm9icyh7am9iTmFtZSwgc3RhdHVzfSlcblxuICAgICAgYXdhaXQgdGhpcy5yZW5kZXIoe2pzb246IHtcbiAgICAgICAgam9iczogam9icy5tYXAoKGpvYikgPT4gdGhpcy5fc2VyaWFsaXplSm9iKGpvYikpLFxuICAgICAgICBwYWdpbmF0aW9uOiB7cGFnZSwgcGVyUGFnZSwgdG90YWwsIHRvdGFsUGFnZXM6IHBlclBhZ2UgPiAwID8gTWF0aC5jZWlsKHRvdGFsIC8gcGVyUGFnZSkgOiAwfVxuICAgICAgfX0pXG4gICAgfSlcbiAgfVxuXG4gIC8qKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGNvbXBsZXRlLiAqL1xuICBhc3luYyBzaG93KCkge1xuICAgIGF3YWl0IHRoaXMuX3Jlc3BvbmQoYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3Qgam9iID0gYXdhaXQgdGhpcy5fc3RvcmUoKS5nZXRKb2IodGhpcy5wYXJhbXMoKS5pZClcblxuICAgICAgaWYgKCFqb2IpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5yZW5kZXIoe2pzb246IHtlcnJvcjogXCJub3RfZm91bmRcIn0sIHN0YXR1czogNDA0fSlcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGF3YWl0IHRoaXMucmVuZGVyKHtqc29uOiB7am9iOiB0aGlzLl9zZXJpYWxpemVKb2Ioam9iKX19KVxuICAgIH0pXG4gIH1cblxuICAvKiogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgd2hlbiBjb21wbGV0ZS4gKi9cbiAgYXN5bmMgc2NoZWR1bGUoKSB7XG4gICAgYXdhaXQgdGhpcy5fcmVzcG9uZChhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBzY2hlZHVsZWQgPSBhd2FpdCB0aGlzLmdldENvbmZpZ3VyYXRpb24oKS5nZXRTY2hlZHVsZWRCYWNrZ3JvdW5kSm9ic0NvbmZpZygpXG5cbiAgICAgIGF3YWl0IHRoaXMucmVuZGVyKHtqc29uOiB7c2NoZWR1bGU6IHRoaXMuX3NlcmlhbGl6ZVNjaGVkdWxlKHNjaGVkdWxlZCl9fSlcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvd30gam9iIC0gSm9iIHJvdy5cbiAgICogQHJldHVybnMge1JlY29yZDxzdHJpbmcsIGFueT59IC0gU2VyaWFsaXplZCBqb2IgZm9yIHRoZSBBUEkuXG4gICAqL1xuICBfc2VyaWFsaXplSm9iKGpvYikge1xuICAgIGNvbnN0IHJlZGFjdEFyZ3MgPSBCb29sZWFuKHRoaXMuX21vdW50T3B0aW9ucygpLnJlZGFjdEFyZ3MpXG5cbiAgICByZXR1cm4ge1xuICAgICAgYXJnczogcmVkYWN0QXJncyA/IHVuZGVmaW5lZCA6IGpvYi5hcmdzLFxuICAgICAgYXJnc1JlZGFjdGVkOiByZWRhY3RBcmdzLFxuICAgICAgYXR0ZW1wdHM6IGpvYi5hdHRlbXB0cyxcbiAgICAgIGNvbXBsZXRlZEF0TXM6IGpvYi5jb21wbGV0ZWRBdE1zLFxuICAgICAgY3JlYXRlZEF0TXM6IGpvYi5jcmVhdGVkQXRNcyxcbiAgICAgIGZhaWxlZEF0TXM6IGpvYi5mYWlsZWRBdE1zLFxuICAgICAgZm9ya2VkOiBqb2IuZm9ya2VkLFxuICAgICAgaGFuZGVkT2ZmQXRNczogam9iLmhhbmRlZE9mZkF0TXMsXG4gICAgICBpZDogam9iLmlkLFxuICAgICAgam9iTmFtZTogam9iLmpvYk5hbWUsXG4gICAgICBsYXN0RXJyb3I6IGpvYi5sYXN0RXJyb3IsXG4gICAgICBtYXhSZXRyaWVzOiBqb2IubWF4UmV0cmllcyxcbiAgICAgIG9ycGhhbmVkQXRNczogam9iLm9ycGhhbmVkQXRNcyxcbiAgICAgIHNjaGVkdWxlZEF0TXM6IGpvYi5zY2hlZHVsZWRBdE1zLFxuICAgICAgc3RhdHVzOiBqb2Iuc3RhdHVzLFxuICAgICAgd29ya2VySWQ6IGpvYi53b3JrZXJJZFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uLy4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuU2NoZWR1bGVkQmFja2dyb3VuZEpvYnNDb25maWd1cmF0aW9uIHwgdW5kZWZpbmVkfSBzY2hlZHVsZWQgLSBTY2hlZHVsZWQgam9icyBjb25maWcuXG4gICAqIEByZXR1cm5zIHtBcnJheTxSZWNvcmQ8c3RyaW5nLCBhbnk+Pn0gLSBTZXJpYWxpemVkIHJlY3VycmluZyBqb2JzLlxuICAgKi9cbiAgX3NlcmlhbGl6ZVNjaGVkdWxlKHNjaGVkdWxlZCkge1xuICAgIGNvbnN0IGpvYnMgPSBzY2hlZHVsZWQ/LmpvYnNcblxuICAgIGlmICgham9icyB8fCB0eXBlb2Ygam9icyAhPT0gXCJvYmplY3RcIikgcmV0dXJuIFtdXG5cbiAgICBjb25zdCByZWRhY3RBcmdzID0gQm9vbGVhbih0aGlzLl9tb3VudE9wdGlvbnMoKS5yZWRhY3RBcmdzKVxuXG4gICAgcmV0dXJuIE9iamVjdC5rZXlzKGpvYnMpLm1hcCgobmFtZSkgPT4ge1xuICAgICAgY29uc3QgZW50cnkgPSBqb2JzW25hbWVdIHx8IC8qKiBAdHlwZSB7YW55fSAqLyAoe30pXG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGFyZ3M6IHJlZGFjdEFyZ3MgPyB1bmRlZmluZWQgOiAoZW50cnkuYXJncyB8fCBbXSksXG4gICAgICAgIGNyb246IGVudHJ5LmNyb24sXG4gICAgICAgIGVuYWJsZWQ6IGVudHJ5LmVuYWJsZWQgIT09IGZhbHNlLFxuICAgICAgICBldmVyeTogZW50cnkuZXZlcnksXG4gICAgICAgIGpvYk5hbWU6IHR5cGVvZiBlbnRyeS5jbGFzcyA9PT0gXCJmdW5jdGlvblwiID8gZW50cnkuY2xhc3MubmFtZSA6IHVuZGVmaW5lZCxcbiAgICAgICAgbmFtZSxcbiAgICAgICAgb3B0aW9uczogZW50cnkub3B0aW9ucyB8fCB7fVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHt1bmtub3dufSB2YWx1ZSAtIFJhdyBzdGF0dXMgcGFyYW0uXG4gICAqIEByZXR1cm5zIHtzdHJpbmcgfCB1bmRlZmluZWR9IC0gVmFsaWQgc3RhdHVzIG9yIHVuZGVmaW5lZC5cbiAgICovXG4gIF9zYW5pdGl6ZVN0YXR1cyh2YWx1ZSkge1xuICAgIHJldHVybiB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgJiYgREFTSEJPQVJEX1NUQVRVU0VTLmluY2x1ZGVzKHZhbHVlKSA/IHZhbHVlIDogdW5kZWZpbmVkXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHt1bmtub3dufSB2YWx1ZSAtIFJhdyBzb3J0IHBhcmFtIChlLmcuIFwiY3JlYXRlZEF0TXNcIiBvciBcIi1mYWlsZWRBdE1zXCIpLlxuICAgKiBAcmV0dXJucyB7e3NvcnRDb2x1bW46IHN0cmluZywgc29ydERpcmVjdGlvbjogXCJBU0NcIiB8IFwiREVTQ1wifX0gLSBOb3JtYWxpemVkIHNvcnQuXG4gICAqL1xuICBfc2FuaXRpemVTb3J0KHZhbHVlKSB7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gXCJzdHJpbmdcIiB8fCB2YWx1ZS5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiB7c29ydENvbHVtbjogXCJjcmVhdGVkQXRNc1wiLCBzb3J0RGlyZWN0aW9uOiBcIkRFU0NcIn1cbiAgICB9XG5cbiAgICBjb25zdCBkZXNjZW5kaW5nID0gdmFsdWUuc3RhcnRzV2l0aChcIi1cIilcbiAgICBjb25zdCBrZXkgPSBkZXNjZW5kaW5nID8gdmFsdWUuc2xpY2UoMSkgOiB2YWx1ZVxuICAgIGNvbnN0IHNvcnRDb2x1bW4gPSBTT1JUQUJMRV9LRVlTLmluY2x1ZGVzKGtleSkgPyBrZXkgOiBcImNyZWF0ZWRBdE1zXCJcblxuICAgIHJldHVybiB7c29ydENvbHVtbiwgc29ydERpcmVjdGlvbjogZGVzY2VuZGluZyA/IFwiREVTQ1wiIDogXCJBU0NcIn1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge3Vua25vd259IHZhbHVlIC0gUmF3IG51bWVyaWMgcGFyYW0uXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBmYWxsYmFjayAtIEZhbGxiYWNrIHdoZW4gaW52YWxpZC5cbiAgICogQHJldHVybnMge251bWJlcn0gLSBQb3NpdGl2ZSBpbnRlZ2VyLlxuICAgKi9cbiAgX3Bvc2l0aXZlSW50KHZhbHVlLCBmYWxsYmFjaykge1xuICAgIGNvbnN0IG51bWVyaWMgPSBOdW1iZXIoQXJyYXkuaXNBcnJheSh2YWx1ZSkgPyB2YWx1ZVswXSA6IHZhbHVlKVxuXG4gICAgaWYgKCFOdW1iZXIuaXNGaW5pdGUobnVtZXJpYykgfHwgbnVtZXJpYyA8IDEpIHJldHVybiBmYWxsYmFja1xuXG4gICAgcmV0dXJuIE1hdGguZmxvb3IobnVtZXJpYylcbiAgfVxufVxuIl19
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Mountable read-only background-jobs dashboard API. Include it in a routes file
3
+ * the way Sidekiq::Web is mounted in Rails:
4
+ *
5
+ * ```js
6
+ * routes.draw((route) => {
7
+ * route.mount(VelociousBackgroundJobsApi, {
8
+ * at: "/velocious/jobs",
9
+ * authorize: async ({request, ability}) => { ... },
10
+ * accessTokens: [process.env.VELOCIOUS_JOBS_TOKEN]
11
+ * })
12
+ * })
13
+ * ```
14
+ */
15
+ export default class VelociousBackgroundJobsApi {
16
+ /**
17
+ * Registers the jobs API under `at`. Implemented as a route-resolver hook so
18
+ * the controller can live inside the velocious package rather than the host
19
+ * app's `src/routes` directory. Invoked by the routing layer for each
20
+ * `route.mount(...)` registration.
21
+ * @param {object} args - Options.
22
+ * @param {import("../../configuration.js").default} args.configuration - Configuration instance.
23
+ * @param {string} args.at - Mount path prefix (e.g. "/velocious/jobs").
24
+ * @param {import("./registry.js").JobsMountOptions["authorize"]} [args.authorize] - Authorization callback.
25
+ * @param {string[]} [args.accessTokens] - Accepted bearer tokens for cross-origin/native access.
26
+ * @param {string[]} [args.allowedOrigins] - Allowed CORS origins for browser access.
27
+ * @param {boolean} [args.redactArgs] - When true, job arguments are omitted from responses.
28
+ * @param {string} [args.databaseIdentifier] - Database identifier the jobs store reads from.
29
+ * @returns {void} - No return value.
30
+ */
31
+ static mountInto({ accessTokens, allowedOrigins, at, authorize, configuration, databaseIdentifier, redactArgs }: {
32
+ configuration: import("../../configuration.js").default;
33
+ at: string;
34
+ authorize?: import("./registry.js").JobsMountOptions["authorize"];
35
+ accessTokens?: string[] | undefined;
36
+ allowedOrigins?: string[] | undefined;
37
+ redactArgs?: boolean | undefined;
38
+ databaseIdentifier?: string | undefined;
39
+ }): void;
40
+ }
41
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/background-jobs/web/index.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;GAaG;AACH;IACE;;;;;;;;;;;;;;OAcG;IACH,iHATG;QAAuD,aAAa,EAA5D,OAAO,wBAAwB,EAAE,OAAO;QAC3B,EAAE,EAAf,MAAM;QACuD,SAAS,GAAtE,OAAO,eAAe,EAAE,gBAAgB,CAAC,WAAW,CAAC;QACrC,YAAY;QACZ,cAAc;QACf,UAAU;QACX,kBAAkB;KACxC,GAAU,IAAI,CAqBhB;CACF"}
@@ -0,0 +1,53 @@
1
+ // @ts-check
2
+ import VelociousBackgroundJobsWebController from "./controller.js";
3
+ import { matchJobsApiPath, normalizeMountPrefix } from "./path-matcher.js";
4
+ import { registerJobsMount } from "./registry.js";
5
+ /**
6
+ * Mountable read-only background-jobs dashboard API. Include it in a routes file
7
+ * the way Sidekiq::Web is mounted in Rails:
8
+ *
9
+ * ```js
10
+ * routes.draw((route) => {
11
+ * route.mount(VelociousBackgroundJobsApi, {
12
+ * at: "/velocious/jobs",
13
+ * authorize: async ({request, ability}) => { ... },
14
+ * accessTokens: [process.env.VELOCIOUS_JOBS_TOKEN]
15
+ * })
16
+ * })
17
+ * ```
18
+ */
19
+ export default class VelociousBackgroundJobsApi {
20
+ /**
21
+ * Registers the jobs API under `at`. Implemented as a route-resolver hook so
22
+ * the controller can live inside the velocious package rather than the host
23
+ * app's `src/routes` directory. Invoked by the routing layer for each
24
+ * `route.mount(...)` registration.
25
+ * @param {object} args - Options.
26
+ * @param {import("../../configuration.js").default} args.configuration - Configuration instance.
27
+ * @param {string} args.at - Mount path prefix (e.g. "/velocious/jobs").
28
+ * @param {import("./registry.js").JobsMountOptions["authorize"]} [args.authorize] - Authorization callback.
29
+ * @param {string[]} [args.accessTokens] - Accepted bearer tokens for cross-origin/native access.
30
+ * @param {string[]} [args.allowedOrigins] - Allowed CORS origins for browser access.
31
+ * @param {boolean} [args.redactArgs] - When true, job arguments are omitted from responses.
32
+ * @param {string} [args.databaseIdentifier] - Database identifier the jobs store reads from.
33
+ * @returns {void} - No return value.
34
+ */
35
+ static mountInto({ accessTokens, allowedOrigins, at, authorize, configuration, databaseIdentifier, redactArgs }) {
36
+ if (!configuration)
37
+ throw new Error("No configuration given");
38
+ const prefix = normalizeMountPrefix(at);
39
+ registerJobsMount(configuration, prefix, { accessTokens, allowedOrigins, authorize, databaseIdentifier, redactArgs });
40
+ configuration.addRouteResolverHook(({ currentPath, request }) => {
41
+ const match = matchJobsApiPath({ method: request.httpMethod(), path: currentPath, prefix });
42
+ if (!match)
43
+ return null;
44
+ return {
45
+ action: match.action,
46
+ controller: "velociousBackgroundJobsWeb",
47
+ controllerClass: VelociousBackgroundJobsWebController,
48
+ params: { ...match.params, velociousJobsMountAt: prefix }
49
+ };
50
+ });
51
+ }
52
+ }
53
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYmFja2dyb3VuZC1qb2JzL3dlYi9pbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosT0FBTyxvQ0FBb0MsTUFBTSxpQkFBaUIsQ0FBQTtBQUNsRSxPQUFPLEVBQUMsZ0JBQWdCLEVBQUUsb0JBQW9CLEVBQUMsTUFBTSxtQkFBbUIsQ0FBQTtBQUN4RSxPQUFPLEVBQUMsaUJBQWlCLEVBQUMsTUFBTSxlQUFlLENBQUE7QUFFL0M7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQU0sQ0FBQyxPQUFPLE9BQU8sMEJBQTBCO0lBQzdDOzs7Ozs7Ozs7Ozs7OztPQWNHO0lBQ0gsTUFBTSxDQUFDLFNBQVMsQ0FBQyxFQUFDLFlBQVksRUFBRSxjQUFjLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsa0JBQWtCLEVBQUUsVUFBVSxFQUFDO1FBQzNHLElBQUksQ0FBQyxhQUFhO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyx3QkFBd0IsQ0FBQyxDQUFBO1FBRTdELE1BQU0sTUFBTSxHQUFHLG9CQUFvQixDQUFDLEVBQUUsQ0FBQyxDQUFBO1FBRXZDLGlCQUFpQixDQUFDLGFBQWEsRUFBRSxNQUFNLEVBQUUsRUFBQyxZQUFZLEVBQUUsY0FBYyxFQUFFLFNBQVMsRUFBRSxrQkFBa0IsRUFBRSxVQUFVLEVBQUMsQ0FBQyxDQUFBO1FBRW5ILGFBQWEsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLEVBQUMsV0FBVyxFQUFFLE9BQU8sRUFBQyxFQUFFLEVBQUU7WUFDNUQsTUFBTSxLQUFLLEdBQUcsZ0JBQWdCLENBQUMsRUFBQyxNQUFNLEVBQUUsT0FBTyxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsTUFBTSxFQUFDLENBQUMsQ0FBQTtZQUV6RixJQUFJLENBQUMsS0FBSztnQkFBRSxPQUFPLElBQUksQ0FBQTtZQUV2QixPQUFPO2dCQUNMLE1BQU0sRUFBRSxLQUFLLENBQUMsTUFBTTtnQkFDcEIsVUFBVSxFQUFFLDRCQUE0QjtnQkFDeEMsZUFBZSxFQUFFLG9DQUFvQztnQkFDckQsTUFBTSxFQUFFLEVBQUMsR0FBRyxLQUFLLENBQUMsTUFBTSxFQUFFLG9CQUFvQixFQUFFLE1BQU0sRUFBQzthQUN4RCxDQUFBO1FBQ0gsQ0FBQyxDQUFDLENBQUE7SUFDSixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IFZlbG9jaW91c0JhY2tncm91bmRKb2JzV2ViQ29udHJvbGxlciBmcm9tIFwiLi9jb250cm9sbGVyLmpzXCJcbmltcG9ydCB7bWF0Y2hKb2JzQXBpUGF0aCwgbm9ybWFsaXplTW91bnRQcmVmaXh9IGZyb20gXCIuL3BhdGgtbWF0Y2hlci5qc1wiXG5pbXBvcnQge3JlZ2lzdGVySm9ic01vdW50fSBmcm9tIFwiLi9yZWdpc3RyeS5qc1wiXG5cbi8qKlxuICogTW91bnRhYmxlIHJlYWQtb25seSBiYWNrZ3JvdW5kLWpvYnMgZGFzaGJvYXJkIEFQSS4gSW5jbHVkZSBpdCBpbiBhIHJvdXRlcyBmaWxlXG4gKiB0aGUgd2F5IFNpZGVraXE6OldlYiBpcyBtb3VudGVkIGluIFJhaWxzOlxuICpcbiAqIGBgYGpzXG4gKiByb3V0ZXMuZHJhdygocm91dGUpID0+IHtcbiAqICAgcm91dGUubW91bnQoVmVsb2Npb3VzQmFja2dyb3VuZEpvYnNBcGksIHtcbiAqICAgICBhdDogXCIvdmVsb2Npb3VzL2pvYnNcIixcbiAqICAgICBhdXRob3JpemU6IGFzeW5jICh7cmVxdWVzdCwgYWJpbGl0eX0pID0+IHsgLi4uIH0sXG4gKiAgICAgYWNjZXNzVG9rZW5zOiBbcHJvY2Vzcy5lbnYuVkVMT0NJT1VTX0pPQlNfVE9LRU5dXG4gKiAgIH0pXG4gKiB9KVxuICogYGBgXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFZlbG9jaW91c0JhY2tncm91bmRKb2JzQXBpIHtcbiAgLyoqXG4gICAqIFJlZ2lzdGVycyB0aGUgam9icyBBUEkgdW5kZXIgYGF0YC4gSW1wbGVtZW50ZWQgYXMgYSByb3V0ZS1yZXNvbHZlciBob29rIHNvXG4gICAqIHRoZSBjb250cm9sbGVyIGNhbiBsaXZlIGluc2lkZSB0aGUgdmVsb2Npb3VzIHBhY2thZ2UgcmF0aGVyIHRoYW4gdGhlIGhvc3RcbiAgICogYXBwJ3MgYHNyYy9yb3V0ZXNgIGRpcmVjdG9yeS4gSW52b2tlZCBieSB0aGUgcm91dGluZyBsYXllciBmb3IgZWFjaFxuICAgKiBgcm91dGUubW91bnQoLi4uKWAgcmVnaXN0cmF0aW9uLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vLi4vY29uZmlndXJhdGlvbi5qc1wiKS5kZWZhdWx0fSBhcmdzLmNvbmZpZ3VyYXRpb24gLSBDb25maWd1cmF0aW9uIGluc3RhbmNlLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5hdCAtIE1vdW50IHBhdGggcHJlZml4IChlLmcuIFwiL3ZlbG9jaW91cy9qb2JzXCIpLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vcmVnaXN0cnkuanNcIikuSm9ic01vdW50T3B0aW9uc1tcImF1dGhvcml6ZVwiXX0gW2FyZ3MuYXV0aG9yaXplXSAtIEF1dGhvcml6YXRpb24gY2FsbGJhY2suXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFthcmdzLmFjY2Vzc1Rva2Vuc10gLSBBY2NlcHRlZCBiZWFyZXIgdG9rZW5zIGZvciBjcm9zcy1vcmlnaW4vbmF0aXZlIGFjY2Vzcy5cbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gW2FyZ3MuYWxsb3dlZE9yaWdpbnNdIC0gQWxsb3dlZCBDT1JTIG9yaWdpbnMgZm9yIGJyb3dzZXIgYWNjZXNzLlxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFthcmdzLnJlZGFjdEFyZ3NdIC0gV2hlbiB0cnVlLCBqb2IgYXJndW1lbnRzIGFyZSBvbWl0dGVkIGZyb20gcmVzcG9uc2VzLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FyZ3MuZGF0YWJhc2VJZGVudGlmaWVyXSAtIERhdGFiYXNlIGlkZW50aWZpZXIgdGhlIGpvYnMgc3RvcmUgcmVhZHMgZnJvbS5cbiAgICogQHJldHVybnMge3ZvaWR9IC0gTm8gcmV0dXJuIHZhbHVlLlxuICAgKi9cbiAgc3RhdGljIG1vdW50SW50byh7YWNjZXNzVG9rZW5zLCBhbGxvd2VkT3JpZ2lucywgYXQsIGF1dGhvcml6ZSwgY29uZmlndXJhdGlvbiwgZGF0YWJhc2VJZGVudGlmaWVyLCByZWRhY3RBcmdzfSkge1xuICAgIGlmICghY29uZmlndXJhdGlvbikgdGhyb3cgbmV3IEVycm9yKFwiTm8gY29uZmlndXJhdGlvbiBnaXZlblwiKVxuXG4gICAgY29uc3QgcHJlZml4ID0gbm9ybWFsaXplTW91bnRQcmVmaXgoYXQpXG5cbiAgICByZWdpc3RlckpvYnNNb3VudChjb25maWd1cmF0aW9uLCBwcmVmaXgsIHthY2Nlc3NUb2tlbnMsIGFsbG93ZWRPcmlnaW5zLCBhdXRob3JpemUsIGRhdGFiYXNlSWRlbnRpZmllciwgcmVkYWN0QXJnc30pXG5cbiAgICBjb25maWd1cmF0aW9uLmFkZFJvdXRlUmVzb2x2ZXJIb29rKCh7Y3VycmVudFBhdGgsIHJlcXVlc3R9KSA9PiB7XG4gICAgICBjb25zdCBtYXRjaCA9IG1hdGNoSm9ic0FwaVBhdGgoe21ldGhvZDogcmVxdWVzdC5odHRwTWV0aG9kKCksIHBhdGg6IGN1cnJlbnRQYXRoLCBwcmVmaXh9KVxuXG4gICAgICBpZiAoIW1hdGNoKSByZXR1cm4gbnVsbFxuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBhY3Rpb246IG1hdGNoLmFjdGlvbixcbiAgICAgICAgY29udHJvbGxlcjogXCJ2ZWxvY2lvdXNCYWNrZ3JvdW5kSm9ic1dlYlwiLFxuICAgICAgICBjb250cm9sbGVyQ2xhc3M6IFZlbG9jaW91c0JhY2tncm91bmRKb2JzV2ViQ29udHJvbGxlcixcbiAgICAgICAgcGFyYW1zOiB7Li4ubWF0Y2gucGFyYW1zLCB2ZWxvY2lvdXNKb2JzTW91bnRBdDogcHJlZml4fVxuICAgICAgfVxuICAgIH0pXG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @typedef {object} JobsApiMatch
3
+ * @property {string} action - Controller action to run.
4
+ * @property {Record<string, string>} params - Extra params extracted from the path.
5
+ */
6
+ /**
7
+ * Normalizes a mount prefix: ensures a leading slash and strips any trailing
8
+ * slash so `/velocious/jobs/` and `/velocious/jobs` behave identically.
9
+ * @param {string} at - Raw mount prefix.
10
+ * @returns {string} - Normalized prefix.
11
+ */
12
+ export function normalizeMountPrefix(at: string): string;
13
+ /**
14
+ * Matches an incoming request against the read-only jobs API routes that live
15
+ * under the mount prefix. Returns the controller action plus any extracted
16
+ * params, or null when the path/method isn't part of the jobs API.
17
+ * @param {object} args - Options.
18
+ * @param {string} args.prefix - Normalized mount prefix.
19
+ * @param {string} args.path - Request path without query string.
20
+ * @param {string} args.method - HTTP method.
21
+ * @returns {JobsApiMatch | null} - Matched action or null.
22
+ */
23
+ export function matchJobsApiPath({ prefix, path, method }: {
24
+ prefix: string;
25
+ path: string;
26
+ method: string;
27
+ }): JobsApiMatch | null;
28
+ export type JobsApiMatch = {
29
+ /**
30
+ * - Controller action to run.
31
+ */
32
+ action: string;
33
+ /**
34
+ * - Extra params extracted from the path.
35
+ */
36
+ params: Record<string, string>;
37
+ };
38
+ //# sourceMappingURL=path-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-matcher.d.ts","sourceRoot":"","sources":["../../../../src/background-jobs/web/path-matcher.js"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH;;;;;GAKG;AACH,yCAHW,MAAM,GACJ,MAAM,CAYlB;AAED;;;;;;;;;GASG;AACH,2DALG;IAAqB,MAAM,EAAnB,MAAM;IACO,IAAI,EAAjB,MAAM;IACO,MAAM,EAAnB,MAAM;CACd,GAAU,YAAY,GAAG,IAAI,CAuC/B;;;;;YArEa,MAAM;;;;YACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC"}
@@ -0,0 +1,71 @@
1
+ // @ts-check
2
+ /**
3
+ * @typedef {object} JobsApiMatch
4
+ * @property {string} action - Controller action to run.
5
+ * @property {Record<string, string>} params - Extra params extracted from the path.
6
+ */
7
+ /**
8
+ * Normalizes a mount prefix: ensures a leading slash and strips any trailing
9
+ * slash so `/velocious/jobs/` and `/velocious/jobs` behave identically.
10
+ * @param {string} at - Raw mount prefix.
11
+ * @returns {string} - Normalized prefix.
12
+ */
13
+ export function normalizeMountPrefix(at) {
14
+ if (typeof at !== "string" || !at.startsWith("/")) {
15
+ throw new Error(`mount requires an 'at' path starting with '/', got: ${String(at)}`);
16
+ }
17
+ if (at.length > 1 && at.endsWith("/")) {
18
+ return at.slice(0, -1);
19
+ }
20
+ return at;
21
+ }
22
+ /**
23
+ * Matches an incoming request against the read-only jobs API routes that live
24
+ * under the mount prefix. Returns the controller action plus any extracted
25
+ * params, or null when the path/method isn't part of the jobs API.
26
+ * @param {object} args - Options.
27
+ * @param {string} args.prefix - Normalized mount prefix.
28
+ * @param {string} args.path - Request path without query string.
29
+ * @param {string} args.method - HTTP method.
30
+ * @returns {JobsApiMatch | null} - Matched action or null.
31
+ */
32
+ export function matchJobsApiPath({ prefix, path, method }) {
33
+ /** @type {string} */
34
+ let subPath;
35
+ if (prefix === "/") {
36
+ // Root mount: the whole path is the sub-path (avoid building a "//" guard).
37
+ subPath = path;
38
+ }
39
+ else if (path === prefix) {
40
+ subPath = "/";
41
+ }
42
+ else if (path.startsWith(`${prefix}/`)) {
43
+ subPath = path.slice(prefix.length);
44
+ }
45
+ else {
46
+ return null;
47
+ }
48
+ if (method === "GET" && subPath === "/api/health")
49
+ return { action: "health", params: {} };
50
+ if (method === "GET" && subPath === "/api/stats")
51
+ return { action: "stats", params: {} };
52
+ if (method === "GET" && subPath === "/api/schedule")
53
+ return { action: "schedule", params: {} };
54
+ if (method === "GET" && subPath === "/api/jobs")
55
+ return { action: "index", params: {} };
56
+ if (method === "GET") {
57
+ const jobMatch = subPath.match(/^\/api\/jobs\/([^/]+)$/);
58
+ if (jobMatch) {
59
+ let id;
60
+ try {
61
+ id = decodeURIComponent(jobMatch[1]);
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ return { action: "show", params: { id } };
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGF0aC1tYXRjaGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2JhY2tncm91bmQtam9icy93ZWIvcGF0aC1tYXRjaGVyLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWjs7OztHQUlHO0FBRUg7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsb0JBQW9CLENBQUMsRUFBRTtJQUNyQyxJQUFJLE9BQU8sRUFBRSxLQUFLLFFBQVEsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNsRCxNQUFNLElBQUksS0FBSyxDQUFDLHVEQUF1RCxNQUFNLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFBO0lBQ3RGLENBQUM7SUFFRCxJQUFJLEVBQUUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN0QyxPQUFPLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDeEIsQ0FBQztJQUVELE9BQU8sRUFBRSxDQUFBO0FBQ1gsQ0FBQztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxFQUFDLE1BQU0sRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFDO0lBQ3JELHFCQUFxQjtJQUNyQixJQUFJLE9BQU8sQ0FBQTtJQUVYLElBQUksTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDO1FBQ25CLDRFQUE0RTtRQUM1RSxPQUFPLEdBQUcsSUFBSSxDQUFBO0lBQ2hCLENBQUM7U0FBTSxJQUFJLElBQUksS0FBSyxNQUFNLEVBQUUsQ0FBQztRQUMzQixPQUFPLEdBQUcsR0FBRyxDQUFBO0lBQ2YsQ0FBQztTQUFNLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN6QyxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUE7SUFDckMsQ0FBQztTQUFNLENBQUM7UUFDTixPQUFPLElBQUksQ0FBQTtJQUNiLENBQUM7SUFFRCxJQUFJLE1BQU0sS0FBSyxLQUFLLElBQUksT0FBTyxLQUFLLGFBQWE7UUFBRSxPQUFPLEVBQUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFDLENBQUE7SUFDeEYsSUFBSSxNQUFNLEtBQUssS0FBSyxJQUFJLE9BQU8sS0FBSyxZQUFZO1FBQUUsT0FBTyxFQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBQyxDQUFBO0lBQ3RGLElBQUksTUFBTSxLQUFLLEtBQUssSUFBSSxPQUFPLEtBQUssZUFBZTtRQUFFLE9BQU8sRUFBQyxNQUFNLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUMsQ0FBQTtJQUM1RixJQUFJLE1BQU0sS0FBSyxLQUFLLElBQUksT0FBTyxLQUFLLFdBQVc7UUFBRSxPQUFPLEVBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFDLENBQUE7SUFFckYsSUFBSSxNQUFNLEtBQUssS0FBSyxFQUFFLENBQUM7UUFDckIsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyx3QkFBd0IsQ0FBQyxDQUFBO1FBRXhELElBQUksUUFBUSxFQUFFLENBQUM7WUFDYixJQUFJLEVBQUUsQ0FBQTtZQUVOLElBQUksQ0FBQztnQkFDSCxFQUFFLEdBQUcsa0JBQWtCLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUE7WUFDdEMsQ0FBQztZQUFDLE1BQU0sQ0FBQztnQkFDUCxPQUFPLElBQUksQ0FBQTtZQUNiLENBQUM7WUFFRCxPQUFPLEVBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsRUFBQyxFQUFFLEVBQUMsRUFBQyxDQUFBO1FBQ3ZDLENBQUM7SUFDSCxDQUFDO0lBRUQsT0FBTyxJQUFJLENBQUE7QUFDYixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbi8qKlxuICogQHR5cGVkZWYge29iamVjdH0gSm9ic0FwaU1hdGNoXG4gKiBAcHJvcGVydHkge3N0cmluZ30gYWN0aW9uIC0gQ29udHJvbGxlciBhY3Rpb24gdG8gcnVuLlxuICogQHByb3BlcnR5IHtSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+fSBwYXJhbXMgLSBFeHRyYSBwYXJhbXMgZXh0cmFjdGVkIGZyb20gdGhlIHBhdGguXG4gKi9cblxuLyoqXG4gKiBOb3JtYWxpemVzIGEgbW91bnQgcHJlZml4OiBlbnN1cmVzIGEgbGVhZGluZyBzbGFzaCBhbmQgc3RyaXBzIGFueSB0cmFpbGluZ1xuICogc2xhc2ggc28gYC92ZWxvY2lvdXMvam9icy9gIGFuZCBgL3ZlbG9jaW91cy9qb2JzYCBiZWhhdmUgaWRlbnRpY2FsbHkuXG4gKiBAcGFyYW0ge3N0cmluZ30gYXQgLSBSYXcgbW91bnQgcHJlZml4LlxuICogQHJldHVybnMge3N0cmluZ30gLSBOb3JtYWxpemVkIHByZWZpeC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG5vcm1hbGl6ZU1vdW50UHJlZml4KGF0KSB7XG4gIGlmICh0eXBlb2YgYXQgIT09IFwic3RyaW5nXCIgfHwgIWF0LnN0YXJ0c1dpdGgoXCIvXCIpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBtb3VudCByZXF1aXJlcyBhbiAnYXQnIHBhdGggc3RhcnRpbmcgd2l0aCAnLycsIGdvdDogJHtTdHJpbmcoYXQpfWApXG4gIH1cblxuICBpZiAoYXQubGVuZ3RoID4gMSAmJiBhdC5lbmRzV2l0aChcIi9cIikpIHtcbiAgICByZXR1cm4gYXQuc2xpY2UoMCwgLTEpXG4gIH1cblxuICByZXR1cm4gYXRcbn1cblxuLyoqXG4gKiBNYXRjaGVzIGFuIGluY29taW5nIHJlcXVlc3QgYWdhaW5zdCB0aGUgcmVhZC1vbmx5IGpvYnMgQVBJIHJvdXRlcyB0aGF0IGxpdmVcbiAqIHVuZGVyIHRoZSBtb3VudCBwcmVmaXguIFJldHVybnMgdGhlIGNvbnRyb2xsZXIgYWN0aW9uIHBsdXMgYW55IGV4dHJhY3RlZFxuICogcGFyYW1zLCBvciBudWxsIHdoZW4gdGhlIHBhdGgvbWV0aG9kIGlzbid0IHBhcnQgb2YgdGhlIGpvYnMgQVBJLlxuICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MucHJlZml4IC0gTm9ybWFsaXplZCBtb3VudCBwcmVmaXguXG4gKiBAcGFyYW0ge3N0cmluZ30gYXJncy5wYXRoIC0gUmVxdWVzdCBwYXRoIHdpdGhvdXQgcXVlcnkgc3RyaW5nLlxuICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MubWV0aG9kIC0gSFRUUCBtZXRob2QuXG4gKiBAcmV0dXJucyB7Sm9ic0FwaU1hdGNoIHwgbnVsbH0gLSBNYXRjaGVkIGFjdGlvbiBvciBudWxsLlxuICovXG5leHBvcnQgZnVuY3Rpb24gbWF0Y2hKb2JzQXBpUGF0aCh7cHJlZml4LCBwYXRoLCBtZXRob2R9KSB7XG4gIC8qKiBAdHlwZSB7c3RyaW5nfSAqL1xuICBsZXQgc3ViUGF0aFxuXG4gIGlmIChwcmVmaXggPT09IFwiL1wiKSB7XG4gICAgLy8gUm9vdCBtb3VudDogdGhlIHdob2xlIHBhdGggaXMgdGhlIHN1Yi1wYXRoIChhdm9pZCBidWlsZGluZyBhIFwiLy9cIiBndWFyZCkuXG4gICAgc3ViUGF0aCA9IHBhdGhcbiAgfSBlbHNlIGlmIChwYXRoID09PSBwcmVmaXgpIHtcbiAgICBzdWJQYXRoID0gXCIvXCJcbiAgfSBlbHNlIGlmIChwYXRoLnN0YXJ0c1dpdGgoYCR7cHJlZml4fS9gKSkge1xuICAgIHN1YlBhdGggPSBwYXRoLnNsaWNlKHByZWZpeC5sZW5ndGgpXG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGlmIChtZXRob2QgPT09IFwiR0VUXCIgJiYgc3ViUGF0aCA9PT0gXCIvYXBpL2hlYWx0aFwiKSByZXR1cm4ge2FjdGlvbjogXCJoZWFsdGhcIiwgcGFyYW1zOiB7fX1cbiAgaWYgKG1ldGhvZCA9PT0gXCJHRVRcIiAmJiBzdWJQYXRoID09PSBcIi9hcGkvc3RhdHNcIikgcmV0dXJuIHthY3Rpb246IFwic3RhdHNcIiwgcGFyYW1zOiB7fX1cbiAgaWYgKG1ldGhvZCA9PT0gXCJHRVRcIiAmJiBzdWJQYXRoID09PSBcIi9hcGkvc2NoZWR1bGVcIikgcmV0dXJuIHthY3Rpb246IFwic2NoZWR1bGVcIiwgcGFyYW1zOiB7fX1cbiAgaWYgKG1ldGhvZCA9PT0gXCJHRVRcIiAmJiBzdWJQYXRoID09PSBcIi9hcGkvam9ic1wiKSByZXR1cm4ge2FjdGlvbjogXCJpbmRleFwiLCBwYXJhbXM6IHt9fVxuXG4gIGlmIChtZXRob2QgPT09IFwiR0VUXCIpIHtcbiAgICBjb25zdCBqb2JNYXRjaCA9IHN1YlBhdGgubWF0Y2goL15cXC9hcGlcXC9qb2JzXFwvKFteL10rKSQvKVxuXG4gICAgaWYgKGpvYk1hdGNoKSB7XG4gICAgICBsZXQgaWRcblxuICAgICAgdHJ5IHtcbiAgICAgICAgaWQgPSBkZWNvZGVVUklDb21wb25lbnQoam9iTWF0Y2hbMV0pXG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHthY3Rpb246IFwic2hvd1wiLCBwYXJhbXM6IHtpZH19XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIG51bGxcbn1cbiJdfQ==
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @param {import("../../configuration.js").default} configuration - Configuration instance.
3
+ * @param {string} at - Normalized mount path.
4
+ * @param {JobsMountOptions} options - Mount options.
5
+ * @returns {void} - No return value.
6
+ */
7
+ export function registerJobsMount(configuration: import("../../configuration.js").default, at: string, options: JobsMountOptions): void;
8
+ /**
9
+ * @param {import("../../configuration.js").default} configuration - Configuration instance.
10
+ * @param {string} at - Normalized mount path.
11
+ * @returns {JobsMountOptions | undefined} - Mount options if registered.
12
+ */
13
+ export function getJobsMount(configuration: import("../../configuration.js").default, at: string): JobsMountOptions | undefined;
14
+ export type JobsMountOptions = {
15
+ /**
16
+ * - Authorization callback. Return true to allow the request.
17
+ */
18
+ authorize?: ((args: {
19
+ request: import("../../http-server/client/request.js").default;
20
+ ability: (import("../../authorization/ability.js").default | undefined);
21
+ token: (string | null);
22
+ configuration: import("../../configuration.js").default;
23
+ }) => (boolean | void | Promise<boolean | void>)) | undefined;
24
+ /**
25
+ * - Bearer tokens accepted for cross-origin/native access.
26
+ */
27
+ accessTokens?: string[] | undefined;
28
+ /**
29
+ * - Origins allowed for cross-origin browser access.
30
+ */
31
+ allowedOrigins?: string[] | undefined;
32
+ /**
33
+ * - When true, job arguments are omitted from API responses.
34
+ */
35
+ redactArgs?: boolean | undefined;
36
+ /**
37
+ * - Database identifier the jobs store reads from.
38
+ */
39
+ databaseIdentifier?: string | undefined;
40
+ };
41
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../../src/background-jobs/web/registry.js"],"names":[],"mappings":"AAqBA;;;;;GAKG;AACH,iDALW,OAAO,wBAAwB,EAAE,OAAO,MACxC,MAAM,WACN,gBAAgB,GACd,IAAI,CAWhB;AAED;;;;GAIG;AACH,4CAJW,OAAO,wBAAwB,EAAE,OAAO,MACxC,MAAM,GACJ,gBAAgB,GAAG,SAAS,CAIxC;;;;;wBAzCoB;QAAC,OAAO,EAAE,OAAO,qCAAqC,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,CAAC,OAAO,gCAAgC,EAAE,OAAO,GAAG,SAAS,CAAC,CAAC;QAAC,KAAK,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QAAC,aAAa,EAAE,OAAO,wBAAwB,EAAE,OAAO,CAAA;KAAC,KAAK,CAAC,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC"}
@@ -0,0 +1,41 @@
1
+ // @ts-check
2
+ /**
3
+ * @typedef {object} JobsMountOptions
4
+ * @property {(args: {request: import("../../http-server/client/request.js").default, ability: (import("../../authorization/ability.js").default | undefined), token: (string | null), configuration: import("../../configuration.js").default}) => (boolean | void | Promise<boolean | void>)} [authorize] - Authorization callback. Return true to allow the request.
5
+ * @property {string[]} [accessTokens] - Bearer tokens accepted for cross-origin/native access.
6
+ * @property {string[]} [allowedOrigins] - Origins allowed for cross-origin browser access.
7
+ * @property {boolean} [redactArgs] - When true, job arguments are omitted from API responses.
8
+ * @property {string} [databaseIdentifier] - Database identifier the jobs store reads from.
9
+ */
10
+ /**
11
+ * Mount options are keyed by configuration so multiple configurations (e.g.
12
+ * across tests) never share state, and by mount path so a single configuration
13
+ * can mount the dashboard at more than one prefix. Functions in the options
14
+ * (the `authorize` callback) can't travel through route params, so the
15
+ * controller looks them up here using the plain `at` string it receives.
16
+ * @type {WeakMap<import("../../configuration.js").default, Map<string, JobsMountOptions>>}
17
+ */
18
+ const registry = new WeakMap();
19
+ /**
20
+ * @param {import("../../configuration.js").default} configuration - Configuration instance.
21
+ * @param {string} at - Normalized mount path.
22
+ * @param {JobsMountOptions} options - Mount options.
23
+ * @returns {void} - No return value.
24
+ */
25
+ export function registerJobsMount(configuration, at, options) {
26
+ let byPath = registry.get(configuration);
27
+ if (!byPath) {
28
+ byPath = new Map();
29
+ registry.set(configuration, byPath);
30
+ }
31
+ byPath.set(at, options);
32
+ }
33
+ /**
34
+ * @param {import("../../configuration.js").default} configuration - Configuration instance.
35
+ * @param {string} at - Normalized mount path.
36
+ * @returns {JobsMountOptions | undefined} - Mount options if registered.
37
+ */
38
+ export function getJobsMount(configuration, at) {
39
+ return registry.get(configuration)?.get(at);
40
+ }
41
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVnaXN0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYmFja2dyb3VuZC1qb2JzL3dlYi9yZWdpc3RyeS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVo7Ozs7Ozs7R0FPRztBQUVIOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFFBQVEsR0FBRyxJQUFJLE9BQU8sRUFBRSxDQUFBO0FBRTlCOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUFDLGFBQWEsRUFBRSxFQUFFLEVBQUUsT0FBTztJQUMxRCxJQUFJLE1BQU0sR0FBRyxRQUFRLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxDQUFBO0lBRXhDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE1BQU0sR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFBO1FBQ2xCLFFBQVEsQ0FBQyxHQUFHLENBQUMsYUFBYSxFQUFFLE1BQU0sQ0FBQyxDQUFBO0lBQ3JDLENBQUM7SUFFRCxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQTtBQUN6QixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxZQUFZLENBQUMsYUFBYSxFQUFFLEVBQUU7SUFDNUMsT0FBTyxRQUFRLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQTtBQUM3QyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbi8qKlxuICogQHR5cGVkZWYge29iamVjdH0gSm9ic01vdW50T3B0aW9uc1xuICogQHByb3BlcnR5IHsoYXJnczoge3JlcXVlc3Q6IGltcG9ydChcIi4uLy4uL2h0dHAtc2VydmVyL2NsaWVudC9yZXF1ZXN0LmpzXCIpLmRlZmF1bHQsIGFiaWxpdHk6IChpbXBvcnQoXCIuLi8uLi9hdXRob3JpemF0aW9uL2FiaWxpdHkuanNcIikuZGVmYXVsdCB8IHVuZGVmaW5lZCksIHRva2VuOiAoc3RyaW5nIHwgbnVsbCksIGNvbmZpZ3VyYXRpb246IGltcG9ydChcIi4uLy4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0pID0+IChib29sZWFuIHwgdm9pZCB8IFByb21pc2U8Ym9vbGVhbiB8IHZvaWQ+KX0gW2F1dGhvcml6ZV0gLSBBdXRob3JpemF0aW9uIGNhbGxiYWNrLiBSZXR1cm4gdHJ1ZSB0byBhbGxvdyB0aGUgcmVxdWVzdC5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nW119IFthY2Nlc3NUb2tlbnNdIC0gQmVhcmVyIHRva2VucyBhY2NlcHRlZCBmb3IgY3Jvc3Mtb3JpZ2luL25hdGl2ZSBhY2Nlc3MuXG4gKiBAcHJvcGVydHkge3N0cmluZ1tdfSBbYWxsb3dlZE9yaWdpbnNdIC0gT3JpZ2lucyBhbGxvd2VkIGZvciBjcm9zcy1vcmlnaW4gYnJvd3NlciBhY2Nlc3MuXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IFtyZWRhY3RBcmdzXSAtIFdoZW4gdHJ1ZSwgam9iIGFyZ3VtZW50cyBhcmUgb21pdHRlZCBmcm9tIEFQSSByZXNwb25zZXMuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gW2RhdGFiYXNlSWRlbnRpZmllcl0gLSBEYXRhYmFzZSBpZGVudGlmaWVyIHRoZSBqb2JzIHN0b3JlIHJlYWRzIGZyb20uXG4gKi9cblxuLyoqXG4gKiBNb3VudCBvcHRpb25zIGFyZSBrZXllZCBieSBjb25maWd1cmF0aW9uIHNvIG11bHRpcGxlIGNvbmZpZ3VyYXRpb25zIChlLmcuXG4gKiBhY3Jvc3MgdGVzdHMpIG5ldmVyIHNoYXJlIHN0YXRlLCBhbmQgYnkgbW91bnQgcGF0aCBzbyBhIHNpbmdsZSBjb25maWd1cmF0aW9uXG4gKiBjYW4gbW91bnQgdGhlIGRhc2hib2FyZCBhdCBtb3JlIHRoYW4gb25lIHByZWZpeC4gRnVuY3Rpb25zIGluIHRoZSBvcHRpb25zXG4gKiAodGhlIGBhdXRob3JpemVgIGNhbGxiYWNrKSBjYW4ndCB0cmF2ZWwgdGhyb3VnaCByb3V0ZSBwYXJhbXMsIHNvIHRoZVxuICogY29udHJvbGxlciBsb29rcyB0aGVtIHVwIGhlcmUgdXNpbmcgdGhlIHBsYWluIGBhdGAgc3RyaW5nIGl0IHJlY2VpdmVzLlxuICogQHR5cGUge1dlYWtNYXA8aW1wb3J0KFwiLi4vLi4vY29uZmlndXJhdGlvbi5qc1wiKS5kZWZhdWx0LCBNYXA8c3RyaW5nLCBKb2JzTW91bnRPcHRpb25zPj59XG4gKi9cbmNvbnN0IHJlZ2lzdHJ5ID0gbmV3IFdlYWtNYXAoKVxuXG4vKipcbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vLi4vY29uZmlndXJhdGlvbi5qc1wiKS5kZWZhdWx0fSBjb25maWd1cmF0aW9uIC0gQ29uZmlndXJhdGlvbiBpbnN0YW5jZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBhdCAtIE5vcm1hbGl6ZWQgbW91bnQgcGF0aC5cbiAqIEBwYXJhbSB7Sm9ic01vdW50T3B0aW9uc30gb3B0aW9ucyAtIE1vdW50IG9wdGlvbnMuXG4gKiBAcmV0dXJucyB7dm9pZH0gLSBObyByZXR1cm4gdmFsdWUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWdpc3RlckpvYnNNb3VudChjb25maWd1cmF0aW9uLCBhdCwgb3B0aW9ucykge1xuICBsZXQgYnlQYXRoID0gcmVnaXN0cnkuZ2V0KGNvbmZpZ3VyYXRpb24pXG5cbiAgaWYgKCFieVBhdGgpIHtcbiAgICBieVBhdGggPSBuZXcgTWFwKClcbiAgICByZWdpc3RyeS5zZXQoY29uZmlndXJhdGlvbiwgYnlQYXRoKVxuICB9XG5cbiAgYnlQYXRoLnNldChhdCwgb3B0aW9ucylcbn1cblxuLyoqXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4uLy4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0gY29uZmlndXJhdGlvbiAtIENvbmZpZ3VyYXRpb24gaW5zdGFuY2UuXG4gKiBAcGFyYW0ge3N0cmluZ30gYXQgLSBOb3JtYWxpemVkIG1vdW50IHBhdGguXG4gKiBAcmV0dXJucyB7Sm9ic01vdW50T3B0aW9ucyB8IHVuZGVmaW5lZH0gLSBNb3VudCBvcHRpb25zIGlmIHJlZ2lzdGVyZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRKb2JzTW91bnQoY29uZmlndXJhdGlvbiwgYXQpIHtcbiAgcmV0dXJuIHJlZ2lzdHJ5LmdldChjb25maWd1cmF0aW9uKT8uZ2V0KGF0KVxufVxuIl19
@@ -84,6 +84,8 @@ export default class VelociousConfiguration {
84
84
  _logging: import("./configuration-types.js").LoggingConfiguration | undefined;
85
85
  _mailerBackend: import("./configuration-types.js").MailerBackend | undefined;
86
86
  _routeResolverHooks: import("./configuration-types.js").RouteResolverHookType[];
87
+ /** @type {WeakSet<object>} */
88
+ _appliedRouteMounts: WeakSet<object>;
87
89
  _errorEvents: import("eventemitter3").EventEmitter<string | symbol, any>;
88
90
  /** @type {{[key: string]: import("./database/pool/base.js").default}} */
89
91
  databasePools: {
@@ -481,6 +483,15 @@ export default class VelociousConfiguration {
481
483
  */
482
484
  setRoutes(newRoutes: import("./routes/index.js").default): void;
483
485
  _routes: import("./routes/index.js").default | undefined;
486
+ /**
487
+ * Applies any `route.mount(...)` registrations from the routes file by letting
488
+ * each mountable register its routes (typically route-resolver hooks) against
489
+ * this configuration. Guarded so repeated setRoutes calls with the same routes
490
+ * don't register a mount more than once.
491
+ * @param {import("./routes/index.js").default} newRoutes - Routes instance.
492
+ * @returns {void} - No return value.
493
+ */
494
+ _applyRouteMounts(newRoutes: import("./routes/index.js").default): void;
484
495
  /**
485
496
  * Adds plugin/library routes using a lightweight route DSL backed by route resolver hooks.
486
497
  * @param {(routes: import("./routes/plugin-routes.js").default) => void} callback - Routes callback.