@spinajs/queue 2.0.481 → 2.0.482

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 (83) hide show
  1. package/lib/cjs/config/queue.d.ts +10 -0
  2. package/lib/cjs/config/queue.d.ts.map +1 -1
  3. package/lib/cjs/config/queue.js +21 -0
  4. package/lib/cjs/config/queue.js.map +1 -1
  5. package/lib/cjs/decorators.d.ts.map +1 -1
  6. package/lib/cjs/decorators.js.map +1 -1
  7. package/lib/cjs/fp.d.ts.map +1 -1
  8. package/lib/cjs/fp.js.map +1 -1
  9. package/lib/cjs/index.d.ts +13 -2
  10. package/lib/cjs/index.d.ts.map +1 -1
  11. package/lib/cjs/index.js +103 -14
  12. package/lib/cjs/index.js.map +1 -1
  13. package/lib/cjs/interfaces.d.ts +178 -7
  14. package/lib/cjs/interfaces.d.ts.map +1 -1
  15. package/lib/cjs/interfaces.js +59 -11
  16. package/lib/cjs/interfaces.js.map +1 -1
  17. package/lib/cjs/migrations/Queue_2026_06_30_00_00_00.d.ts +10 -0
  18. package/lib/cjs/migrations/Queue_2026_06_30_00_00_00.d.ts.map +1 -0
  19. package/lib/cjs/migrations/Queue_2026_06_30_00_00_00.js +29 -0
  20. package/lib/cjs/migrations/Queue_2026_06_30_00_00_00.js.map +1 -0
  21. package/lib/cjs/migrations/Queue_2026_07_02_00_00_00.d.ts +14 -0
  22. package/lib/cjs/migrations/Queue_2026_07_02_00_00_00.d.ts.map +1 -0
  23. package/lib/cjs/migrations/Queue_2026_07_02_00_00_00.js +53 -0
  24. package/lib/cjs/migrations/Queue_2026_07_02_00_00_00.js.map +1 -0
  25. package/lib/cjs/migrations/Queue_2026_07_10_00_00_00.d.ts +10 -0
  26. package/lib/cjs/migrations/Queue_2026_07_10_00_00_00.d.ts.map +1 -0
  27. package/lib/cjs/migrations/Queue_2026_07_10_00_00_00.js +31 -0
  28. package/lib/cjs/migrations/Queue_2026_07_10_00_00_00.js.map +1 -0
  29. package/lib/cjs/migrations/Queue_2026_07_17_00_00_00.d.ts +16 -0
  30. package/lib/cjs/migrations/Queue_2026_07_17_00_00_00.d.ts.map +1 -0
  31. package/lib/cjs/migrations/Queue_2026_07_17_00_00_00.js +49 -0
  32. package/lib/cjs/migrations/Queue_2026_07_17_00_00_00.js.map +1 -0
  33. package/lib/cjs/models/JobModel.d.ts +66 -4
  34. package/lib/cjs/models/JobModel.d.ts.map +1 -1
  35. package/lib/cjs/models/JobModel.js +43 -2
  36. package/lib/cjs/models/JobModel.js.map +1 -1
  37. package/lib/cjs/services/JobRetentionService.d.ts +26 -0
  38. package/lib/cjs/services/JobRetentionService.d.ts.map +1 -0
  39. package/lib/cjs/services/JobRetentionService.js +87 -0
  40. package/lib/cjs/services/JobRetentionService.js.map +1 -0
  41. package/lib/mjs/config/queue.d.ts +10 -0
  42. package/lib/mjs/config/queue.d.ts.map +1 -1
  43. package/lib/mjs/config/queue.js +21 -0
  44. package/lib/mjs/config/queue.js.map +1 -1
  45. package/lib/mjs/decorators.d.ts.map +1 -1
  46. package/lib/mjs/decorators.js.map +1 -1
  47. package/lib/mjs/fp.d.ts.map +1 -1
  48. package/lib/mjs/fp.js.map +1 -1
  49. package/lib/mjs/index.d.ts +13 -2
  50. package/lib/mjs/index.d.ts.map +1 -1
  51. package/lib/mjs/index.js +105 -16
  52. package/lib/mjs/index.js.map +1 -1
  53. package/lib/mjs/interfaces.d.ts +178 -7
  54. package/lib/mjs/interfaces.d.ts.map +1 -1
  55. package/lib/mjs/interfaces.js +57 -10
  56. package/lib/mjs/interfaces.js.map +1 -1
  57. package/lib/mjs/migrations/Queue_2026_06_30_00_00_00.d.ts +10 -0
  58. package/lib/mjs/migrations/Queue_2026_06_30_00_00_00.d.ts.map +1 -0
  59. package/lib/mjs/migrations/Queue_2026_06_30_00_00_00.js +26 -0
  60. package/lib/mjs/migrations/Queue_2026_06_30_00_00_00.js.map +1 -0
  61. package/lib/mjs/migrations/Queue_2026_07_02_00_00_00.d.ts +14 -0
  62. package/lib/mjs/migrations/Queue_2026_07_02_00_00_00.d.ts.map +1 -0
  63. package/lib/mjs/migrations/Queue_2026_07_02_00_00_00.js +50 -0
  64. package/lib/mjs/migrations/Queue_2026_07_02_00_00_00.js.map +1 -0
  65. package/lib/mjs/migrations/Queue_2026_07_10_00_00_00.d.ts +10 -0
  66. package/lib/mjs/migrations/Queue_2026_07_10_00_00_00.d.ts.map +1 -0
  67. package/lib/mjs/migrations/Queue_2026_07_10_00_00_00.js +28 -0
  68. package/lib/mjs/migrations/Queue_2026_07_10_00_00_00.js.map +1 -0
  69. package/lib/mjs/migrations/Queue_2026_07_17_00_00_00.d.ts +16 -0
  70. package/lib/mjs/migrations/Queue_2026_07_17_00_00_00.d.ts.map +1 -0
  71. package/lib/mjs/migrations/Queue_2026_07_17_00_00_00.js +46 -0
  72. package/lib/mjs/migrations/Queue_2026_07_17_00_00_00.js.map +1 -0
  73. package/lib/mjs/models/JobModel.d.ts +66 -4
  74. package/lib/mjs/models/JobModel.d.ts.map +1 -1
  75. package/lib/mjs/models/JobModel.js +42 -2
  76. package/lib/mjs/models/JobModel.js.map +1 -1
  77. package/lib/mjs/services/JobRetentionService.d.ts +26 -0
  78. package/lib/mjs/services/JobRetentionService.d.ts.map +1 -0
  79. package/lib/mjs/services/JobRetentionService.js +84 -0
  80. package/lib/mjs/services/JobRetentionService.js.map +1 -0
  81. package/lib/tsconfig.cjs.tsbuildinfo +1 -1
  82. package/lib/tsconfig.mjs.tsbuildinfo +1 -1
  83. package/package.json +9 -6
@@ -0,0 +1,46 @@
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
+ /* eslint-disable @typescript-eslint/no-unused-vars */
8
+ import { OrmMigration, Migration } from '@spinajs/orm';
9
+ /**
10
+ * Widens `queue_jobs.Status` to the six states the code actually writes.
11
+ * `retrying` / `dead` arrived with retry support (see index.ts) but the enum
12
+ * was never widened, so strict-enum drivers reject or coerce them.
13
+ *
14
+ * NOT NULL and the 'created' default are restated deliberately: MySQL's MODIFY
15
+ * COLUMN replaces the whole definition and silently drops anything omitted.
16
+ * On sqlite this alteration is a logged no-op - it renders enum as
17
+ * unconstrained TEXT, so there is nothing to widen there.
18
+ */
19
+ let Queue_2026_07_17_00_00_00 = class Queue_2026_07_17_00_00_00 extends OrmMigration {
20
+ async up(connection) {
21
+ // sqlite renders enum as unconstrained TEXT and has no MODIFY COLUMN, so there is
22
+ // nothing to widen there. Emitting the MODIFY anyway produces a MySQL-flavoured
23
+ // `ALTER TABLE ... MODIFY ... ENUM(...)` that sqlite rejects, so skip it explicitly
24
+ // ( mirrors the guard in Queue_2026_07_02, and matches this migration's documented
25
+ // "no-op on sqlite" contract above ).
26
+ const isSqlite = connection.Options.Driver.toLowerCase().includes('sqlite');
27
+ if (isSqlite) {
28
+ return;
29
+ }
30
+ await connection.schema().alterTable('queue_jobs', (table) => {
31
+ // `.default().value('created')` returns the ColumnQueryBuilder, which has no
32
+ // `.modify()` - that lives on AlterColumnQueryBuilder. So `.modify()` must be
33
+ // called on the column builder itself, separately from the default.
34
+ const status = table.enum('Status', ['error', 'success', 'created', 'executing', 'retrying', 'dead']).notNull();
35
+ status.default().value('created');
36
+ status.modify();
37
+ });
38
+ }
39
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
40
+ async down(_connection) { }
41
+ };
42
+ Queue_2026_07_17_00_00_00 = __decorate([
43
+ Migration('queue')
44
+ ], Queue_2026_07_17_00_00_00);
45
+ export { Queue_2026_07_17_00_00_00 };
46
+ //# sourceMappingURL=Queue_2026_07_17_00_00_00.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Queue_2026_07_17_00_00_00.js","sourceRoot":"","sources":["../../../src/migrations/Queue_2026_07_17_00_00_00.ts"],"names":[],"mappings":";;;;;;AAAA,sDAAsD;AACtD,OAAO,EAAE,YAAY,EAAa,SAAS,EAAE,MAAM,cAAc,CAAC;AAElE;;;;;;;;;GASG;AAEI,IAAM,yBAAyB,GAA/B,MAAM,yBAA0B,SAAQ,YAAY;IAClD,KAAK,CAAC,EAAE,CAAC,UAAqB;QACnC,kFAAkF;QAClF,gFAAgF;QAChF,oFAAoF;QACpF,mFAAmF;QACnF,sCAAsC;QACtC,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC5E,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QAED,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE;YAC3D,6EAA6E;YAC7E,8EAA8E;YAC9E,oEAAoE;YACpE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;YAChH,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gEAAgE;IACzD,KAAK,CAAC,IAAI,CAAC,WAAsB,IAAkB,CAAC;CAC5D,CAAA;AAxBY,yBAAyB;IADrC,SAAS,CAAC,OAAO,CAAC;GACN,yBAAyB,CAwBrC"}
@@ -1,18 +1,80 @@
1
1
  import { DateTime } from 'luxon';
2
- import { ModelBase } from '@spinajs/orm';
2
+ import { ModelBase, QueryScope, ISelectQueryBuilder } from '@spinajs/orm';
3
3
  /**
4
- * Base modele for users used by ACL
4
+ * Possible job lifecycle statuses.
5
+ * - created: dispatched, not yet picked up
6
+ * - executing: currently running
7
+ * - retrying: failed, will be retried
8
+ * - success: finished ok ( terminal )
9
+ * - error: legacy generic failure ( terminal )
10
+ * - dead: failed and retries exhausted / dead-lettered ( terminal )
11
+ */
12
+ export type JobStatus = 'created' | 'executing' | 'retrying' | 'success' | 'error' | 'dead';
13
+ /** Statuses a job can no longer leave - safe to report as done / purge. */
14
+ export declare const JOB_TERMINAL_STATUSES: JobStatus[];
15
+ /** Statuses of jobs still in flight. */
16
+ export declare const JOB_ACTIVE_STATUSES: JobStatus[];
17
+ /**
18
+ * Reusable query scopes for {@link JobModel}. Available on any JobModel query
19
+ * ( select / update / destroy ), so callers compose readable, safe queries eg.
5
20
  *
21
+ * await JobModel.destroy().terminal().olderThan(cutoff);
22
+ * await JobModel.query().byJobId(id).first();
23
+ */
24
+ export declare class JobQueryScopes implements QueryScope {
25
+ byJobId(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes, jobId: string): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
26
+ withStatus(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes, status: JobStatus | JobStatus[]): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
27
+ /** Jobs in a terminal state ( success / error / dead ). */
28
+ terminal(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
29
+ /** Jobs still in flight ( created / executing / retrying ). */
30
+ active(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
31
+ /** Jobs created before the given moment ( row age ). */
32
+ olderThan(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes, date: DateTime): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
33
+ /** Jobs finished before the given moment. */
34
+ finishedBefore(this: ISelectQueryBuilder<JobModel[]> & JobQueryScopes, date: DateTime): ISelectQueryBuilder<JobModel<unknown>[]> & JobQueryScopes;
35
+ }
36
+ /**
37
+ * Tracks a dispatched job: its status, progress, result / error and timing.
38
+ * One row per emitted job, keyed by the unique {@link JobModel.JobId}.
6
39
  */
7
- export declare class JobModel<T> extends ModelBase {
40
+ export declare class JobModel<T = unknown> extends ModelBase {
41
+ static readonly _queryScopes: JobQueryScopes;
8
42
  Id: number;
9
43
  JobId: string;
10
44
  Name: string;
45
+ /**
46
+ * The job's successful result. Failures are recorded in {@link JobModel.LastError},
47
+ * so this holds only the actual output.
48
+ */
11
49
  Result: T;
12
- Status: 'error' | 'success' | 'created' | 'executing';
50
+ Status: JobStatus;
13
51
  Progress: number;
52
+ /**
53
+ * Optional human-facing progress phase (e.g. `loading`, `rendering`), set by
54
+ * jobs that report richer progress than a bare percentage. Nullable.
55
+ */
56
+ Phase: string;
57
+ /**
58
+ * Optional human-facing progress message accompanying {@link Phase}. Nullable.
59
+ */
60
+ Message: string;
61
+ /**
62
+ * Number of times job execution has been attempted. Incremented on every failed run.
63
+ * Once it reaches {@link JobModel.MaxAttempts} the job is marked as `dead`.
64
+ */
65
+ Attempt: number;
66
+ /**
67
+ * Total allowed executions for the job ( RetryCount + 1 ) captured at first receipt.
68
+ * Lets consumers render `Attempt / MaxAttempts` ( e.g. "attempt 2 of 2" ).
69
+ */
70
+ MaxAttempts: number;
71
+ /**
72
+ * Last error message when the job failed or was retried. Kept separate from Result.
73
+ */
74
+ LastError: string;
14
75
  Connection: string;
15
76
  CreatedAt: DateTime;
77
+ UpdatedAt: DateTime;
16
78
  ExecutedAt: DateTime;
17
79
  FinishedAt: DateTime;
18
80
  }
@@ -1 +1 @@
1
- {"version":3,"file":"JobModel.d.ts","sourceRoot":"","sources":["../../../src/models/JobModel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,SAAS,EAA+D,MAAM,cAAc,CAAC;AAEtG;;;GAGG;AACH,qBAEa,QAAQ,CAAC,CAAC,CAAE,SAAQ,SAAS;IAEjC,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,MAAM,CAAC;IAEd,IAAI,EAAE,MAAM,CAAC;IAGb,MAAM,EAAE,CAAC,CAAC;IAEV,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;IAEtD,QAAQ,EAAE,MAAM,CAAC;IAEjB,UAAU,EAAE,MAAM,CAAC;IAGnB,SAAS,EAAE,QAAQ,CAAC;IAGpB,UAAU,EAAE,QAAQ,CAAC;IAGrB,UAAU,EAAE,QAAQ,CAAC;CAC7B"}
1
+ {"version":3,"file":"JobModel.d.ts","sourceRoot":"","sources":["../../../src/models/JobModel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,SAAS,EAA0E,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAElJ;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAE5F,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,EAAE,SAAS,EAAiC,CAAC;AAE/E,wCAAwC;AACxC,eAAO,MAAM,mBAAmB,EAAE,SAAS,EAAyC,CAAC;AAErF;;;;;;GAMG;AACH,qBAAa,cAAe,YAAW,UAAU;IACxC,OAAO,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc,EAAE,KAAK,EAAE,MAAM;IAI7E,UAAU,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc,EAAE,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE;IAIzG,2DAA2D;IACpD,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc;IAItE,+DAA+D;IACxD,MAAM,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc;IAIpE,wDAAwD;IACjD,SAAS,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc,EAAE,IAAI,EAAE,QAAQ;IAIvF,6CAA6C;IACtC,cAAc,CAAC,IAAI,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,cAAc,EAAE,IAAI,EAAE,QAAQ;CAG7F;AAED;;;GAGG;AACH,qBAEa,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAE,SAAQ,SAAS;IAClD,gBAAuB,YAAY,EAAE,cAAc,CAAwB;IAGpE,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,MAAM,CAAC;IAEd,IAAI,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IAEI,MAAM,EAAE,CAAC,CAAC;IAEV,MAAM,EAAE,SAAS,CAAC;IAElB,QAAQ,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACI,KAAK,EAAE,MAAM,CAAC;IAErB;;OAEG;IACI,OAAO,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACI,OAAO,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACI,WAAW,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACI,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,EAAE,MAAM,CAAC;IAGnB,SAAS,EAAE,QAAQ,CAAC;IAGpB,SAAS,EAAE,QAAQ,CAAC;IAGpB,UAAU,EAAE,QAAQ,CAAC;IAGrB,UAAU,EAAE,QAAQ,CAAC;CAC7B"}
@@ -8,13 +8,49 @@ var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
10
  import { DateTime } from 'luxon';
11
- import { ModelBase, Connection, Model, Json, CreatedAt, DateTime as DT, Primary } from '@spinajs/orm';
11
+ import { ModelBase, Connection, Model, Json, CreatedAt, UpdatedAt, DateTime as DT, Primary } from '@spinajs/orm';
12
+ /** Statuses a job can no longer leave - safe to report as done / purge. */
13
+ export const JOB_TERMINAL_STATUSES = ['success', 'error', 'dead'];
14
+ /** Statuses of jobs still in flight. */
15
+ export const JOB_ACTIVE_STATUSES = ['created', 'executing', 'retrying'];
12
16
  /**
13
- * Base modele for users used by ACL
17
+ * Reusable query scopes for {@link JobModel}. Available on any JobModel query
18
+ * ( select / update / destroy ), so callers compose readable, safe queries eg.
14
19
  *
20
+ * await JobModel.destroy().terminal().olderThan(cutoff);
21
+ * await JobModel.query().byJobId(id).first();
22
+ */
23
+ export class JobQueryScopes {
24
+ byJobId(jobId) {
25
+ return this.where('JobId', jobId);
26
+ }
27
+ withStatus(status) {
28
+ return Array.isArray(status) ? this.whereIn('Status', status) : this.where('Status', status);
29
+ }
30
+ /** Jobs in a terminal state ( success / error / dead ). */
31
+ terminal() {
32
+ return this.whereIn('Status', JOB_TERMINAL_STATUSES);
33
+ }
34
+ /** Jobs still in flight ( created / executing / retrying ). */
35
+ active() {
36
+ return this.whereIn('Status', JOB_ACTIVE_STATUSES);
37
+ }
38
+ /** Jobs created before the given moment ( row age ). */
39
+ olderThan(date) {
40
+ return this.where('CreatedAt', '<', date);
41
+ }
42
+ /** Jobs finished before the given moment. */
43
+ finishedBefore(date) {
44
+ return this.where('FinishedAt', '<', date);
45
+ }
46
+ }
47
+ /**
48
+ * Tracks a dispatched job: its status, progress, result / error and timing.
49
+ * One row per emitted job, keyed by the unique {@link JobModel.JobId}.
15
50
  */
16
51
  let JobModel = class JobModel extends ModelBase {
17
52
  };
53
+ JobModel._queryScopes = new JobQueryScopes();
18
54
  __decorate([
19
55
  Primary(),
20
56
  __metadata("design:type", Number)
@@ -27,6 +63,10 @@ __decorate([
27
63
  CreatedAt(),
28
64
  __metadata("design:type", DateTime)
29
65
  ], JobModel.prototype, "CreatedAt", void 0);
66
+ __decorate([
67
+ UpdatedAt(),
68
+ __metadata("design:type", DateTime)
69
+ ], JobModel.prototype, "UpdatedAt", void 0);
30
70
  __decorate([
31
71
  DT(),
32
72
  __metadata("design:type", DateTime)
@@ -1 +1 @@
1
- {"version":3,"file":"JobModel.js","sourceRoot":"","sources":["../../../src/models/JobModel.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEtG;;;GAGG;AAGI,IAAM,QAAQ,GAAd,MAAM,QAAY,SAAQ,SAAS;CAyBzC,CAAA;AAvBQ;IADN,OAAO,EAAE;;oCACQ;AAOX;IADN,IAAI,EAAE;;wCACU;AASV;IADN,SAAS,EAAE;8BACM,QAAQ;2CAAC;AAGpB;IADN,EAAE,EAAE;8BACc,QAAQ;4CAAC;AAGrB;IADN,EAAE,EAAE;8BACc,QAAQ;4CAAC;AAxBjB,QAAQ;IAFpB,UAAU,CAAC,OAAO,CAAC;IACnB,KAAK,CAAC,YAAY,CAAC;GACP,QAAQ,CAyBpB"}
1
+ {"version":3,"file":"JobModel.js","sourceRoot":"","sources":["../../../src/models/JobModel.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,IAAI,EAAE,EAAE,OAAO,EAAmC,MAAM,cAAc,CAAC;AAalJ,2EAA2E;AAC3E,MAAM,CAAC,MAAM,qBAAqB,GAAgB,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAE/E,wCAAwC;AACxC,MAAM,CAAC,MAAM,mBAAmB,GAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;AAErF;;;;;;GAMG;AACH,MAAM,OAAO,cAAc;IAClB,OAAO,CAAyD,KAAa;QAClF,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IAEM,UAAU,CAAyD,MAA+B;QACvG,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/F,CAAC;IAED,2DAA2D;IACpD,QAAQ;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,qBAAqB,CAAC,CAAC;IACvD,CAAC;IAED,+DAA+D;IACxD,MAAM;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;IACrD,CAAC;IAED,wDAAwD;IACjD,SAAS,CAAyD,IAAc;QACrF,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,6CAA6C;IACtC,cAAc,CAAyD,IAAc;QAC1F,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;CACF;AAED;;;GAGG;AAGI,IAAM,QAAQ,GAAd,MAAM,QAAsB,SAAQ,SAAS;;AAC3B,qBAAY,GAAmB,IAAI,cAAc,EAAE,AAAvC,CAAwC;AAGpE;IADN,OAAO,EAAE;;oCACQ;AAWX;IADN,IAAI,EAAE;;wCACU;AAqCV;IADN,SAAS,EAAE;8BACM,QAAQ;2CAAC;AAGpB;IADN,SAAS,EAAE;8BACM,QAAQ;2CAAC;AAGpB;IADN,EAAE,EAAE;8BACc,QAAQ;4CAAC;AAGrB;IADN,EAAE,EAAE;8BACc,QAAQ;4CAAC;AA7DjB,QAAQ;IAFpB,UAAU,CAAC,OAAO,CAAC;IACnB,KAAK,CAAC,YAAY,CAAC;GACP,QAAQ,CA8DpB"}
@@ -0,0 +1,26 @@
1
+ import { Log } from '@spinajs/log';
2
+ import { DateTime } from 'luxon';
3
+ import { JobRetentionService } from '../interfaces.js';
4
+ /**
5
+ * Built-in {@link JobRetentionService}. Periodically deletes terminal jobs older than the
6
+ * configured `maxAge` so the `queue_jobs` table doesn't grow unbounded. All timings / statuses
7
+ * come from the injected `queue.retention` config block - nothing is hardcoded except the
8
+ * fallback interval used when none is configured.
9
+ */
10
+ export declare class DefaultJobRetentionService extends JobRetentionService {
11
+ protected Log: Log;
12
+ protected RetentionTimer?: ReturnType<typeof setInterval>;
13
+ resolve(): Promise<void>;
14
+ dispose(): Promise<void>;
15
+ /**
16
+ * Starts the periodic job-retention purge when configured. No-op if retention is disabled
17
+ * or no `maxAge` is set.
18
+ */
19
+ protected startRetention(): void;
20
+ /**
21
+ * Deletes terminal ( or given ) jobs created before `olderThan`. The set is selected with the
22
+ * JobModel query scopes ( status + date ), then removed by id.
23
+ */
24
+ purgeJobs(olderThan: DateTime, statuses?: string[]): Promise<number>;
25
+ }
26
+ //# sourceMappingURL=JobRetentionService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JobRetentionService.d.ts","sourceRoot":"","sources":["../../../src/services/JobRetentionService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAQvD;;;;;GAKG;AACH,qBACa,0BAA2B,SAAQ,mBAAmB;IAEjE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEnB,SAAS,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IAE7C,OAAO;IAIP,OAAO;IAOpB;;;OAGG;IACH,SAAS,CAAC,cAAc;IAyBxB;;;OAGG;IACU,SAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CAclF"}
@@ -0,0 +1,84 @@
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 __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Injectable } from '@spinajs/di';
11
+ import { Log, Logger } from '@spinajs/log';
12
+ import { DateTime } from 'luxon';
13
+ import { JobRetentionService } from '../interfaces.js';
14
+ import { JobModel, JOB_TERMINAL_STATUSES } from '../models/JobModel.js';
15
+ /**
16
+ * How often ( ms ) to run the purge when `queue.retention.interval` is not set.
17
+ */
18
+ const DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
19
+ /**
20
+ * Built-in {@link JobRetentionService}. Periodically deletes terminal jobs older than the
21
+ * configured `maxAge` so the `queue_jobs` table doesn't grow unbounded. All timings / statuses
22
+ * come from the injected `queue.retention` config block - nothing is hardcoded except the
23
+ * fallback interval used when none is configured.
24
+ */
25
+ let DefaultJobRetentionService = class DefaultJobRetentionService extends JobRetentionService {
26
+ async resolve() {
27
+ this.startRetention();
28
+ }
29
+ async dispose() {
30
+ if (this.RetentionTimer) {
31
+ clearInterval(this.RetentionTimer);
32
+ this.RetentionTimer = undefined;
33
+ }
34
+ }
35
+ /**
36
+ * Starts the periodic job-retention purge when configured. No-op if retention is disabled
37
+ * or no `maxAge` is set.
38
+ */
39
+ startRetention() {
40
+ if (!this.Options?.enabled || !this.Options.maxAge) {
41
+ return;
42
+ }
43
+ const interval = this.Options.interval ?? DEFAULT_INTERVAL_MS;
44
+ const maxAge = this.Options.maxAge;
45
+ this.RetentionTimer = setInterval(() => {
46
+ const cutoff = DateTime.now().minus({ milliseconds: maxAge });
47
+ this.purgeJobs(cutoff, this.Options.statuses)
48
+ .then((n) => {
49
+ if (n > 0) {
50
+ this.Log.info(`Retention purge removed ${n} job(s) older than ${cutoff.toISO()}`);
51
+ }
52
+ })
53
+ .catch((err) => this.Log.error(err, 'Job retention purge failed'));
54
+ }, interval);
55
+ // don't keep the process alive just for the purge timer
56
+ this.RetentionTimer.unref?.();
57
+ this.Log.info(`Job retention enabled: purging jobs older than ${maxAge}ms every ${interval}ms`);
58
+ }
59
+ /**
60
+ * Deletes terminal ( or given ) jobs created before `olderThan`. The set is selected with the
61
+ * JobModel query scopes ( status + date ), then removed by id.
62
+ */
63
+ async purgeJobs(olderThan, statuses) {
64
+ const rows = await JobModel.query()
65
+ .columns(['Id'])
66
+ .withStatus(statuses ?? JOB_TERMINAL_STATUSES)
67
+ .olderThan(olderThan);
68
+ const ids = rows.map((r) => r.Id);
69
+ if (ids.length === 0) {
70
+ return 0;
71
+ }
72
+ await JobModel.destroy(ids);
73
+ return ids.length;
74
+ }
75
+ };
76
+ __decorate([
77
+ Logger('queue'),
78
+ __metadata("design:type", Log)
79
+ ], DefaultJobRetentionService.prototype, "Log", void 0);
80
+ DefaultJobRetentionService = __decorate([
81
+ Injectable(JobRetentionService)
82
+ ], DefaultJobRetentionService);
83
+ export { DefaultJobRetentionService };
84
+ //# sourceMappingURL=JobRetentionService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JobRetentionService.js","sourceRoot":"","sources":["../../../src/services/JobRetentionService.ts"],"names":[],"mappings":"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"}