@tstdl/base 0.92.99 → 0.92.100

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.
@@ -2,7 +2,6 @@ import { resolveApiEndpointDataProvider } from '../../../api/types.js';
2
2
  import { toArray } from '../../../utils/array/array.js';
3
3
  import { isDefined } from '../../../utils/type-guards.js';
4
4
  export function corsMiddleware(options = {}) {
5
- // eslint-disable-next-line max-statements, @typescript-eslint/no-shadow
6
5
  async function corsMiddleware(context, next) {
7
6
  try {
8
7
  await next();
@@ -15,18 +14,18 @@ export function corsMiddleware(options = {}) {
15
14
  const cors = { ...options.default, ...endpointDefinition?.cors };
16
15
  if (isOptions) {
17
16
  const allowMethods = (await resolveApiEndpointDataProvider(request, context, cors.accessControlAllowMethods)) ?? [...context.api.endpoints.keys()].join(', ');
18
- response.headers.setIfMissing('Access-Control-Allow-Methods', allowMethods);
17
+ response.headers.set('Access-Control-Allow-Methods', allowMethods);
19
18
  if (isDefined(cors.accessControlAllowHeaders) && !request.headers.has('Access-Control-Allow-Headers')) {
20
19
  const value = await resolveApiEndpointDataProvider(request, context, cors.accessControlAllowHeaders);
21
- response.headers.setIfMissing('Access-Control-Allow-Headers', value);
20
+ response.headers.set('Access-Control-Allow-Headers', value);
22
21
  }
23
22
  if (isDefined(cors.accessControlExposeHeaders) && !request.headers.has('Access-Control-Expose-Headers')) {
24
23
  const value = await resolveApiEndpointDataProvider(request, context, cors.accessControlExposeHeaders);
25
- response.headers.setIfMissing('Access-Control-Expose-Headers', value);
24
+ response.headers.set('Access-Control-Expose-Headers', value);
26
25
  }
27
26
  if (isDefined(cors.accessControlMaxAge) && !request.headers.has('Access-Control-Max-Age')) {
28
27
  const value = await resolveApiEndpointDataProvider(request, context, cors.accessControlMaxAge);
29
- response.headers.setIfMissing('Access-Control-Max-Age', value);
28
+ response.headers.set('Access-Control-Max-Age', value);
30
29
  }
31
30
  }
32
31
  if (!request.headers.has('Access-Control-Allow-Credentials')) {
@@ -34,19 +33,19 @@ export function corsMiddleware(options = {}) {
34
33
  ? await resolveApiEndpointDataProvider(request, context, cors.accessControlAllowCredentials)
35
34
  : endpointDefinition?.credentials;
36
35
  if (allowCredentials == true) {
37
- response.headers.setIfMissing('Access-Control-Allow-Credentials', 'true');
36
+ response.headers.set('Access-Control-Allow-Credentials', 'true');
38
37
  }
39
38
  }
40
39
  if (isDefined(cors.accessControlAllowOrigin) && !response.headers.has('Access-Control-Allow-Origin')) {
41
40
  const value = await resolveApiEndpointDataProvider(request, context, cors.accessControlAllowOrigin);
42
- response.headers.setIfMissing('Access-Control-Allow-Origin', value);
41
+ response.headers.set('Access-Control-Allow-Origin', value);
43
42
  }
44
43
  if (isDefined(cors.autoAccessControlAllowOrigin) && !response.headers.has('Access-Control-Allow-Origin')) {
45
44
  const value = await resolveApiEndpointDataProvider(request, context, cors.autoAccessControlAllowOrigin);
46
45
  const origin = request.headers.tryGetSingle('Origin');
47
46
  const allowed = isDefined(value) && toArray(value).includes(origin);
48
47
  if (allowed) {
49
- response.headers.setIfMissing('Access-Control-Allow-Origin', origin);
48
+ response.headers.set('Access-Control-Allow-Origin', origin);
50
49
  }
51
50
  }
52
51
  }
@@ -98,6 +98,7 @@ export declare const documentManagementApiDefinition: {
98
98
  typeId: import("../../orm/schemas/uuid.js").Uuid | null;
99
99
  subtitle: string | null;
100
100
  pages: number | null;
101
+ validated: boolean;
101
102
  originalFileName: string | null;
102
103
  collectionIds: string | string[];
103
104
  properties?: {
@@ -374,6 +375,7 @@ export declare const documentManagementApiDefinition: {
374
375
  typeId?: import("../../orm/types.js").Uuid | null | undefined;
375
376
  subtitle?: string | null | undefined;
376
377
  pages?: number | null | undefined;
378
+ validated?: boolean | undefined;
377
379
  properties?: {
378
380
  propertyId: import("../../orm/schemas/uuid.js").Uuid;
379
381
  value: unknown;
@@ -488,6 +490,7 @@ declare const _DocumentManagementApi: import("../../api/index.js").ApiClient<{
488
490
  typeId: import("../../orm/schemas/uuid.js").Uuid | null;
489
491
  subtitle: string | null;
490
492
  pages: number | null;
493
+ validated: boolean;
491
494
  originalFileName: string | null;
492
495
  collectionIds: string | string[];
493
496
  properties?: {
@@ -764,6 +767,7 @@ declare const _DocumentManagementApi: import("../../api/index.js").ApiClient<{
764
767
  typeId?: import("../../orm/types.js").Uuid | null | undefined;
765
768
  subtitle?: string | null | undefined;
766
769
  pages?: number | null | undefined;
770
+ validated?: boolean | undefined;
767
771
  properties?: {
768
772
  propertyId: import("../../orm/schemas/uuid.js").Uuid;
769
773
  value: unknown;
@@ -9,4 +9,5 @@ export declare class Document extends Entity {
9
9
  date: NumericDate | null;
10
10
  summary: string | null;
11
11
  tags: string[] | null;
12
+ validated: boolean;
12
13
  }
@@ -10,7 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  import { References } from '../../orm/decorators.js';
11
11
  import { Entity } from '../../orm/entity.js';
12
12
  import { NumericDate, Uuid } from '../../orm/types.js';
13
- import { Array, Integer, string, StringProperty } from '../../schema/index.js';
13
+ import { Array, BooleanProperty, Integer, string, StringProperty } from '../../schema/index.js';
14
14
  import { DocumentFile } from './document-file.model.js';
15
15
  import { DocumentManagementTable } from './document-management-table.js';
16
16
  import { DocumentType } from './document-type.model.js';
@@ -23,6 +23,7 @@ let Document = class Document extends Entity {
23
23
  date;
24
24
  summary;
25
25
  tags;
26
+ validated;
26
27
  };
27
28
  __decorate([
28
29
  Uuid(),
@@ -58,6 +59,10 @@ __decorate([
58
59
  Array(string(), { nullable: true }),
59
60
  __metadata("design:type", Object)
60
61
  ], Document.prototype, "tags", void 0);
62
+ __decorate([
63
+ BooleanProperty(),
64
+ __metadata("design:type", Boolean)
65
+ ], Document.prototype, "validated", void 0);
61
66
  Document = __decorate([
62
67
  DocumentManagementTable()
63
68
  ], Document);
@@ -22,6 +22,7 @@ export declare const createDocumentParametersSchema: import("../../../schema/ind
22
22
  typeId: import("../../../orm/schemas/uuid.js").Uuid | null;
23
23
  subtitle: string | null;
24
24
  pages: number | null;
25
+ validated: boolean;
25
26
  originalFileName: string | null;
26
27
  collectionIds: string | string[];
27
28
  properties?: {
@@ -45,6 +46,7 @@ export declare const updateDocumentParametersSchema: import("../../../schema/ind
45
46
  typeId?: import("../../../orm/types.js").Uuid | null | undefined;
46
47
  subtitle?: string | null | undefined;
47
48
  pages?: number | null | undefined;
49
+ validated?: boolean | undefined;
48
50
  properties?: {
49
51
  propertyId: import("../../../orm/schemas/uuid.js").Uuid;
50
52
  value: unknown;
@@ -15,7 +15,7 @@ import { Document } from '../document.model.js';
15
15
  export const metadataParameterSchema = optional(partial(pick(EntityMetadata, 'attributes')));
16
16
  export const metadataParameterObjectSchema = object({ metadata: metadataParameterSchema });
17
17
  export const setDocumentPropertyParametersSchema = assign(pick(DocumentPropertyValueBase, ['propertyId']), object({ value: unknown() }), metadataParameterObjectSchema);
18
- export const createDocumentParametersSchema = assign(pick(Document, ['typeId', 'title', 'subtitle', 'pages', 'date', 'summary', 'tags']), pick(DocumentFile, ['originalFileName']), object({
18
+ export const createDocumentParametersSchema = assign(pick(Document, ['typeId', 'title', 'subtitle', 'pages', 'date', 'summary', 'tags', 'validated']), pick(DocumentFile, ['originalFileName']), object({
19
19
  collectionIds: oneOrMany(string()),
20
20
  properties: optional(array(setDocumentPropertyParametersSchema))
21
21
  }), metadataParameterObjectSchema);
@@ -1,3 +1,4 @@
1
+ import { type EnumType } from '../../../enumeration/enumeration.js';
1
2
  import { Timestamp } from '../../../orm/types.js';
2
3
  import { DocumentCategory } from '../document-category.model.js';
3
4
  import { DocumentCollection } from '../document-collection.model.js';
@@ -7,6 +8,12 @@ import { DocumentRequestFile } from '../document-request-file.model.js';
7
8
  import { DocumentRequest } from '../document-request.model.js';
8
9
  import { DocumentType } from '../document-type.model.js';
9
10
  import { Document } from '../document.model.js';
11
+ export declare const ExtractionStatus: {
12
+ readonly Pending: "pending";
13
+ readonly Extracting: "extracting";
14
+ readonly Error: "error";
15
+ };
16
+ export type ExtractionStatus = EnumType<typeof ExtractionStatus>;
10
17
  export declare class DocumentCollectionView extends DocumentCollection {
11
18
  name: string | null;
12
19
  group: string | null;
@@ -18,6 +25,7 @@ export declare class DocumentViewCollectionAssignment {
18
25
  export declare class DocumentView extends Document {
19
26
  collectionAssignments: DocumentViewCollectionAssignment[];
20
27
  properties: DocumentPropertyValue[];
28
+ extractionStatus: ExtractionStatus | null;
21
29
  }
22
30
  export declare class DocumentRequestView extends DocumentRequest {
23
31
  collectionIds: string[];
@@ -7,8 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  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
+ import { defineEnum } from '../../../enumeration/enumeration.js';
10
11
  import { Timestamp } from '../../../orm/types.js';
11
- import { Array, StringProperty } from '../../../schema/index.js';
12
+ import { Array, Enumeration, StringProperty } from '../../../schema/index.js';
12
13
  import { DocumentCategory } from '../document-category.model.js';
13
14
  import { DocumentCollection } from '../document-collection.model.js';
14
15
  import { DocumentFile } from '../document-file.model.js';
@@ -17,6 +18,11 @@ import { DocumentRequestFile } from '../document-request-file.model.js';
17
18
  import { DocumentRequest } from '../document-request.model.js';
18
19
  import { DocumentType } from '../document-type.model.js';
19
20
  import { Document } from '../document.model.js';
21
+ export const ExtractionStatus = defineEnum('ExtractionStatus', {
22
+ Pending: 'pending',
23
+ Extracting: 'extracting',
24
+ Error: 'error'
25
+ });
20
26
  export class DocumentCollectionView extends DocumentCollection {
21
27
  name;
22
28
  group;
@@ -44,6 +50,7 @@ __decorate([
44
50
  export class DocumentView extends Document {
45
51
  collectionAssignments;
46
52
  properties;
53
+ extractionStatus;
47
54
  }
48
55
  __decorate([
49
56
  Array(DocumentViewCollectionAssignment),
@@ -53,6 +60,10 @@ __decorate([
53
60
  Array(DocumentPropertyValue),
54
61
  __metadata("design:type", Array)
55
62
  ], DocumentView.prototype, "properties", void 0);
63
+ __decorate([
64
+ Enumeration(ExtractionStatus, { nullable: true }),
65
+ __metadata("design:type", Object)
66
+ ], DocumentView.prototype, "extractionStatus", void 0);
56
67
  export class DocumentRequestView extends DocumentRequest {
57
68
  collectionIds;
58
69
  requestFiles;
@@ -2,11 +2,13 @@ import type { RequireExactlyOne, Stringified } from 'type-fest';
2
2
  import type { CancellationSignal } from '../../../cancellation/token.js';
3
3
  import { type AfterResolveContext, afterResolve } from '../../../injector/index.js';
4
4
  import { Logger } from '../../../logger/logger.js';
5
+ import { MessageBus } from '../../../message-bus/message-bus.js';
5
6
  import { ObjectStorage } from '../../../object-storage/index.js';
6
7
  import type { Query } from '../../../orm/index.js';
7
8
  import { type Transaction } from '../../../orm/server/index.js';
8
9
  import { Queue } from '../../../queue/queue.js';
9
10
  import type { OneOrMany, Record } from '../../../types.js';
11
+ import { type Observable } from 'rxjs';
10
12
  import { type AddOrArchiveDocumentToOrFromCollectionParameters, type ApplyDocumentRequestsTemplateParameters, type ApproveDocumentRequestFileParameters, type AssignPropertyToTypeParameters, type CategoryAndTypesView, type CreateCollectionParameters, type CreateDocumentCategoryParameters, type CreateDocumentParameters, type CreateDocumentPropertyParameters, type CreateDocumentRequestFileParameters, type CreateDocumentRequestParameters, type CreateDocumentRequestTemplateParameters, type CreateDocumentRequestsTemplateParameters, type CreateDocumentTypeParameters, type DeleteDocumentRequestFileParameters, type DeleteDocumentRequestParameters, type DeleteDocumentRequestTemplateParameters, type DeleteDocumentRequestsTemplateParameters, Document, DocumentCategory, DocumentCollection, DocumentCollectionDocument, DocumentFile, type DocumentManagementData, DocumentProperty, DocumentPropertyDataType, DocumentPropertyValue, DocumentRequest, DocumentRequestAssignmentTask, DocumentRequestAssignmentTaskCollection, DocumentRequestAssignmentTaskPropertyValue, DocumentRequestCollection, DocumentRequestFile, DocumentRequestFilePropertyValue, DocumentRequestTemplate, DocumentRequestsTemplate, type DocumentRequestsTemplateData, DocumentType, DocumentTypeProperty, type LoadDataCollectionsMetadataParameters, type RejectDocumentRequestFileParameters, type RequestFilesStats, type SetDocumentPropertiesParameters, type UpdateDocumentParameters, type UpdateDocumentRequestFileParameters, type UpdateDocumentRequestParameters, type UpdateDocumentRequestTemplateParameters, type UpdateDocumentRequestsTemplateParameters } from '../../models/index.js';
11
13
  import { DocumentManagementAncillaryService } from './document-management-ancillary.service.js';
12
14
  type DocumentInformationExtractionPropertyResult = {
@@ -56,8 +58,10 @@ export declare class DocumentManagementService extends DocumentManagementService
56
58
  protected readonly fileObjectStorage: ObjectStorage;
57
59
  protected readonly extractionQueue: Queue<ExtractionJobData>;
58
60
  protected readonly assignmentQueue: Queue<AssignmentJobData>;
61
+ protected readonly collectionChangeMessageBus: MessageBus<string[] | undefined>;
59
62
  protected readonly logger: Logger;
60
63
  [afterResolve](_: unknown, { cancellationSignal }: AfterResolveContext<any>): void;
64
+ changes$(collectionIds: OneOrMany<string>): Observable<void>;
61
65
  processQueues(cancellationSignal: CancellationSignal): void;
62
66
  resolveNames<const T extends (DocumentCollection | string)[]>(...collectionsOrIds: T): Promise<Stringified<T>>;
63
67
  resolveNamesMap(...collectionsOrIds: (DocumentCollection | string)[]): Promise<Record<string, string>>;
@@ -80,7 +84,7 @@ export declare class DocumentManagementService extends DocumentManagementService
80
84
  createDocumentRequestTemplate(parameters: CreateDocumentRequestTemplateParameters): Promise<DocumentRequestTemplate>;
81
85
  updateDocumentRequestTemplate({ id, typeId, requiredFilesCount, comment, metadata }: UpdateDocumentRequestTemplateParameters): Promise<DocumentRequestTemplate>;
82
86
  deleteDocumentRequestTemplate(parameters: DeleteDocumentRequestTemplateParameters): Promise<DocumentRequestTemplate>;
83
- createDocument({ typeId, title, subtitle, pages, date, summary, tags, originalFileName, collectionIds, properties, metadata }: CreateDocumentParameters, content: Uint8Array | ReadableStream<Uint8Array>): Promise<Document>;
87
+ createDocument({ typeId, title, subtitle, pages, date, summary, tags, validated, originalFileName, collectionIds, properties, metadata }: CreateDocumentParameters, content: Uint8Array | ReadableStream<Uint8Array>): Promise<Document>;
84
88
  approveDocumentRequestFile({ id, approvalComment, documentMetadata, requestFileMetadata }: ApproveDocumentRequestFileParameters): Promise<Document>;
85
89
  rejectDocumentRequestFile({ id, approvalComment, metadata }: RejectDocumentRequestFileParameters): Promise<void>;
86
90
  updateDocumentRequestFile({ id, title, approvalComment, metadata }: UpdateDocumentRequestFileParameters): Promise<DocumentRequestFile>;
@@ -108,5 +112,12 @@ export declare class DocumentManagementService extends DocumentManagementService
108
112
  protected getDocumentFileContent(fileId: string): Promise<Uint8Array>;
109
113
  protected getDocumentFileContentStream(fileId: string): ReadableStream<Uint8Array>;
110
114
  protected getDocumentFileContentObjectUrl(title: string, file: DocumentFile, download: boolean): Promise<string>;
115
+ protected publishChange({ collectionIds, documentIds, requestIds, requestFileIds, requestAssignmentTaskIds: requestAssignmentTasksIds }: {
116
+ collectionIds?: string[];
117
+ documentIds?: string[];
118
+ requestIds?: string[];
119
+ requestFileIds?: string[];
120
+ requestAssignmentTaskIds?: string[];
121
+ }): void;
111
122
  }
112
123
  export {};
@@ -63,11 +63,12 @@ import { AiService } from '../../../ai/index.js';
63
63
  import { Enumerable } from '../../../enumerable/index.js';
64
64
  import { BadRequestError } from '../../../errors/index.js';
65
65
  import { TemporaryFile, getMimeType, getMimeTypeExtensions } from '../../../file/index.js';
66
- import { Singleton, afterResolve, inject, provide } from '../../../injector/index.js';
66
+ import { Singleton, afterResolve, inject } from '../../../injector/index.js';
67
67
  import { Logger } from '../../../logger/logger.js';
68
+ import { MessageBus } from '../../../message-bus/message-bus.js';
68
69
  import { ObjectStorage } from '../../../object-storage/index.js';
69
70
  import { TRANSACTION_TIMESTAMP, arrayAgg, coalesce, getEntityMap, jsonAgg, toJsonb } from '../../../orm/index.js';
70
- import { DatabaseConfig, EntityRepositoryConfig, getRepository, injectRepository } from '../../../orm/server/index.js';
71
+ import { DatabaseConfig, getRepository, injectRepository } from '../../../orm/server/index.js';
71
72
  import { getPdfPageCount } from '../../../pdf/index.js';
72
73
  import { Queue } from '../../../queue/queue.js';
73
74
  import { array, boolean, enumeration, integer, nullable, number, object, string } from '../../../schema/index.js';
@@ -81,9 +82,11 @@ import { readBinaryStream } from '../../../utils/stream/index.js';
81
82
  import { tryIgnoreLogAsync } from '../../../utils/try-ignore.js';
82
83
  import { assertBooleanPass, assertDefined, assertDefinedPass, assertNotNullPass, assertNumberPass, assertStringPass, isBoolean, isDefined, isNotNull, isNull, isNumber, isString, isUint8Array, isUndefined } from '../../../utils/type-guards.js';
83
84
  import { millisecondsPerMinute } from '../../../utils/units.js';
85
+ import { union } from 'drizzle-orm/pg-core';
86
+ import { debounceTime, filter, map } from 'rxjs';
84
87
  import { Document, DocumentCategory, DocumentCollection, DocumentCollectionDocument, DocumentFile, DocumentProperty, DocumentPropertyDataType, DocumentPropertyValue, DocumentRequest, DocumentRequestAssignmentTask, DocumentRequestAssignmentTaskCollection, DocumentRequestAssignmentTaskPropertyValue, DocumentRequestCollection, DocumentRequestFile, DocumentRequestFilePropertyValue, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentTypeProperty } from '../../models/index.js';
85
88
  import { DocumentManagementConfig } from '../module.js';
86
- import { documentCategory, documentProperty, documentRequest, documentRequestAssignmentTask, documentRequestAssignmentTaskCollection, documentRequestAssignmentTaskPropertyValue, documentRequestCollection, documentRequestFile, documentType } from '../schemas.js';
89
+ import { documentCategory, documentCollectionDocument, documentProperty, documentRequest, documentRequestAssignmentTask, documentRequestAssignmentTaskCollection, documentRequestAssignmentTaskPropertyValue, documentRequestCollection, documentRequestFile, documentType } from '../schemas.js';
87
90
  import { DocumentManagementAncillaryService } from './document-management-ancillary.service.js';
88
91
  const defaultGenerationOptions = {
89
92
  model: 'gemini-2.0-flash',
@@ -116,6 +119,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
116
119
  fileObjectStorage = inject(ObjectStorage, inject(DocumentManagementConfig).fileObjectStorageModule);
117
120
  extractionQueue = inject((Queue), { name: 'DocumentManagement:extraction', processTimeout: 15 * millisecondsPerMinute });
118
121
  assignmentQueue = inject((Queue), { name: 'DocumentManagement:assignment', processTimeout: 15 * millisecondsPerMinute });
122
+ collectionChangeMessageBus = inject((MessageBus), 'DocumentManagement:collectionChange');
119
123
  logger = inject(Logger, DocumentManagementService_1.name);
120
124
  [afterResolve](_, { cancellationSignal }) {
121
125
  if (this.isFork) {
@@ -123,6 +127,10 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
123
127
  }
124
128
  this.processQueues(cancellationSignal);
125
129
  }
130
+ changes$(collectionIds) {
131
+ const collectionIdsArray = toArray(collectionIds);
132
+ return this.collectionChangeMessageBus.allMessages$.pipe(filter((message) => isUndefined(message) || collectionIdsArray.some((id) => message.includes(id))), debounceTime(250), map(() => undefined));
133
+ }
126
134
  processQueues(cancellationSignal) {
127
135
  this.extractionQueue.process({ concurrency: 1, cancellationSignal }, async (job) => {
128
136
  const [entry] = objectEntries(job.data);
@@ -168,11 +176,12 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
168
176
  ]);
169
177
  const documentIds = collectionDocuments.map((document) => document.documentId);
170
178
  const requestIds = requestCollections.map((requestCollection) => requestCollection.requestId);
171
- const [documents, requests, requestFiles, propertyValues] = await Promise.all([
179
+ const [documents, requests, requestFiles, propertyValues, extractionJobs] = await Promise.all([
172
180
  this.documentService.withTransaction(transaction).loadManyByQuery({ id: { $in: documentIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
173
181
  this.documentRequestService.withTransaction(transaction).loadManyByQuery({ id: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
174
182
  this.documentRequestFileService.withTransaction(transaction).loadManyByQuery({ requestId: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
175
- this.documentPropertyValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } })
183
+ this.documentPropertyValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } }),
184
+ this.extractionQueue.getByTags(documentIds)
176
185
  ]);
177
186
  const documentFileIds = documents.map((document) => document.fileId);
178
187
  const requestFileIds = requestFiles.map((requestFile) => requestFile.fileId);
@@ -184,11 +193,21 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
184
193
  name: collectionsMetadata?.[collection.id]?.name ?? null,
185
194
  group: collectionsMetadata?.[collection.id]?.group ?? null
186
195
  }));
187
- const documentViews = documents.map((document) => ({
188
- ...document,
189
- collectionAssignments: collectionDocuments.filter((collectionDocument) => collectionDocument.documentId == document.id).map(({ collectionId, archiveTimestamp }) => ({ collectionId, archiveTimestamp })),
190
- properties: valuesMap.get(document.id) ?? []
191
- }));
196
+ const documentViews = documents.map((document) => {
197
+ const job = extractionJobs.find((job) => job.tag == document.id);
198
+ return {
199
+ ...document,
200
+ collectionAssignments: collectionDocuments.filter((collectionDocument) => collectionDocument.documentId == document.id).map(({ collectionId, archiveTimestamp }) => ({ collectionId, archiveTimestamp })),
201
+ properties: valuesMap.get(document.id) ?? [],
202
+ extractionStatus: isUndefined(job)
203
+ ? null
204
+ : isNull(job.lastDequeueTimestamp)
205
+ ? 'pending'
206
+ : (job.tries >= this.extractionQueue.maxTries) && (job.lastDequeueTimestamp >= (currentTimestamp() + this.extractionQueue.processTimeout))
207
+ ? 'error'
208
+ : 'extracting'
209
+ };
210
+ });
192
211
  const requestViews = requests.map((request) => ({
193
212
  ...request,
194
213
  collectionIds: requestCollections.filter((requestCollection) => requestCollection.requestId == request.id).map((requestCollection) => requestCollection.collectionId),
@@ -287,11 +306,12 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
287
306
  async deleteDocumentRequestTemplate(parameters) {
288
307
  return this.documentRequestTemplateService.delete(parameters.id);
289
308
  }
290
- async createDocument({ typeId, title, subtitle, pages, date, summary, tags, originalFileName, collectionIds, properties, metadata }, content) {
309
+ async createDocument({ typeId, title, subtitle, pages, date, summary, tags, validated, originalFileName, collectionIds, properties, metadata }, content) {
310
+ const collectionIdsArray = toArray(collectionIds);
291
311
  const document = await this.documentService.transaction(async (_, transaction) => {
292
312
  const documentFile = await this.createDocumentFile(content, originalFileName, transaction);
293
- const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId, title, subtitle, pages, date, summary, tags, metadata });
294
- for (const collectionId of toArray(collectionIds)) {
313
+ const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId, title, subtitle, pages, date, summary, tags, metadata, validated });
314
+ for (const collectionId of collectionIdsArray) {
295
315
  await this.documentCollectionDocumentService.withTransaction(transaction).insert({ collectionId, documentId: document.id, archiveTimestamp: null });
296
316
  }
297
317
  if (isDefined(properties)) {
@@ -299,7 +319,8 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
299
319
  }
300
320
  return document;
301
321
  });
302
- void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ documentId: document.id }), this.logger);
322
+ void tryIgnoreLogAsync(this.logger, async () => this.extractionQueue.enqueue({ documentId: document.id }, { tag: document.id }));
323
+ this.publishChange({ collectionIds: collectionIdsArray });
303
324
  return document;
304
325
  }
305
326
  async approveDocumentRequestFile({ id, approvalComment, documentMetadata, requestFileMetadata }) {
@@ -319,6 +340,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
319
340
  date: requestFile.date,
320
341
  summary: requestFile.summary,
321
342
  tags: requestFile.tags,
343
+ validated: true,
322
344
  metadata: documentMetadata
323
345
  });
324
346
  for (const { collectionId } of requestCollections) {
@@ -329,16 +351,19 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
329
351
  });
330
352
  }
331
353
  async rejectDocumentRequestFile({ id, approvalComment, metadata }) {
332
- return this.documentRequestFileService.transaction(async (_, transaction) => {
354
+ const requestFile = await this.documentRequestFileService.transaction(async (_, transaction) => {
333
355
  const requestFile = await this.documentRequestFileService.withTransaction(transaction).load(id);
334
356
  if (requestFile.approval == true) {
335
357
  throw new BadRequestError('Document request file already accepted.');
336
358
  }
337
- await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: false, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, metadata });
359
+ return this.documentRequestFileService.withTransaction(transaction).update(id, { approval: false, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, metadata });
338
360
  });
361
+ this.publishChange({ requestIds: [requestFile.requestId] });
339
362
  }
340
363
  async updateDocumentRequestFile({ id, title, approvalComment, metadata }) {
341
- return this.documentRequestFileService.update(id, { title, approvalComment, metadata });
364
+ const result = await this.documentRequestFileService.update(id, { title, approvalComment, metadata });
365
+ this.publishChange({ requestIds: [result.requestId] });
366
+ return result;
342
367
  }
343
368
  async deleteDocumentRequestFile({ id, metadata }) {
344
369
  const requestFile = await this.documentRequestFileService.load(id);
@@ -346,6 +371,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
346
371
  throw new BadRequestError('Only pending request files can be deleted.');
347
372
  }
348
373
  await this.documentRequestFileService.delete(id, metadata);
374
+ this.publishChange({ requestIds: [requestFile.requestId] });
349
375
  }
350
376
  async createDocumentFile(content, originalFileName, transaction) {
351
377
  const contentAsUint8Array = isUint8Array(content) ? content : await readBinaryStream(content);
@@ -376,7 +402,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
376
402
  throw new BadRequestError('Maximum amount of allowed files reached.');
377
403
  }
378
404
  const file = await this.createDocumentFile(content, originalFileName, transaction);
379
- return await this.documentRequestFileService
405
+ const result = await this.documentRequestFileService
380
406
  .withTransaction(transaction)
381
407
  .insert({
382
408
  requestId,
@@ -393,20 +419,24 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
393
419
  approvalTimestamp: null,
394
420
  metadata
395
421
  });
422
+ return result;
396
423
  });
397
- void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ requestFileId: documentRequestFile.id }), this.logger);
424
+ void tryIgnoreLogAsync(this.logger, async () => this.extractionQueue.enqueue({ requestFileId: documentRequestFile.id }));
425
+ this.publishChange({ requestIds: [documentRequestFile.requestId] });
398
426
  return documentRequestFile;
399
427
  }
400
428
  async createDocumentRequest(parameters, transaction) {
401
429
  if (parameters.collectionIds.length == 0) {
402
430
  throw new BadRequestError('No target collectionId specified.');
403
431
  }
404
- return this.documentRequestService.useTransaction(transaction, async (_, tx) => {
432
+ const result = await this.documentRequestService.useTransaction(transaction, async (_, tx) => {
405
433
  const request = await this.documentRequestService.withTransaction(tx).insert({ ...parameters, completed: false });
406
434
  const newDocumentRequestCollections = parameters.collectionIds.map((collectionId) => ({ requestId: request.id, collectionId }));
407
435
  await this.documentRequestCollectionService.withTransaction(tx).insertMany(newDocumentRequestCollections);
408
436
  return request;
409
437
  });
438
+ this.publishChange({ collectionIds: parameters.collectionIds });
439
+ return result;
410
440
  }
411
441
  async createDocumentRequestAssignmentTask({ originalFileName, collectionIds }, content) {
412
442
  if (collectionIds.length == 0) {
@@ -428,7 +458,8 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
428
458
  });
429
459
  const newTaksCollections = collectionIds.map((collectionId) => ({ requestAssignmentTaskId: task.id, collectionId }));
430
460
  await this.documentRequestAssignmentTaskCollectionService.withTransaction(transaction).insertMany(newTaksCollections);
431
- void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ requestAssignmentTaskId: task.id }), this.logger);
461
+ void tryIgnoreLogAsync(this.logger, async () => this.extractionQueue.enqueue({ requestAssignmentTaskId: task.id }));
462
+ this.publishChange({ collectionIds });
432
463
  return task;
433
464
  });
434
465
  }
@@ -440,6 +471,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
440
471
  await this.setPropertyValues({ documentId, properties: propertyUpdates }, tx);
441
472
  }
442
473
  });
474
+ this.publishChange({ documentIds: [documentId] });
443
475
  }
444
476
  async updateDocumentRequest(parameters, transaction) {
445
477
  const { id: documentRequestId, ...update } = parameters;
@@ -451,6 +483,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
451
483
  }
452
484
  }
453
485
  await this.documentRequestService.withOptionalTransaction(transaction).update(documentRequestId, update);
486
+ this.publishChange({ requestIds: [documentRequestId] });
454
487
  }
455
488
  async deleteDocumentRequest({ id, metadata }, transaction) {
456
489
  const requestFiles = await this.documentRequestFileService.loadManyByQuery({ requestId: id });
@@ -459,6 +492,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
459
492
  throw new BadRequestError('Cannot delete requests which has approved files.');
460
493
  }
461
494
  await this.documentRequestService.withOptionalTransaction(transaction).delete(id, metadata);
495
+ this.publishChange({ requestIds: [id] });
462
496
  }
463
497
  async setPropertyValues({ documentId, requestFileId, requestAssignmentTaskId, properties: items }, transaction) {
464
498
  if ((items.length == 0)) {
@@ -497,12 +531,19 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
497
531
  .upsertMany(['requestAssignmentTaskId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, requestAssignmentTaskId })));
498
532
  }
499
533
  });
534
+ this.publishChange({
535
+ documentIds: isDefined(documentId) ? [documentId] : undefined,
536
+ requestFileIds: isDefined(requestFileId) ? [requestFileId] : undefined,
537
+ requestAssignmentTaskIds: isDefined(requestAssignmentTaskId) ? [requestAssignmentTaskId] : undefined
538
+ });
500
539
  }
501
540
  async addDocumentToCollection(parameters, transaction) {
502
541
  await this.documentCollectionDocumentService.withOptionalTransaction(transaction).upsert(['collectionId', 'documentId'], { ...parameters, archiveTimestamp: null });
542
+ this.publishChange({ documentIds: [parameters.documentId] });
503
543
  }
504
544
  async archiveDocument({ collectionId, documentId, metadata }) {
505
545
  await this.documentCollectionDocumentService.updateByQuery({ collectionId, documentId }, { archiveTimestamp: TRANSACTION_TIMESTAMP, metadata });
546
+ this.publishChange({ documentIds: [documentId] });
506
547
  }
507
548
  async createProperty(parameters) {
508
549
  return this.documentPropertyService.insert(parameters);
@@ -514,9 +555,10 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
514
555
  const document = await this.documentService.load(documentId);
515
556
  const { properties, ...extractionResult } = await this.extractFileInformation(document.fileId, document.typeId);
516
557
  await this.documentService.transaction(async (documentService, transaction) => {
517
- await documentService.update(document.id, extractionResult);
558
+ await documentService.update(document.id, { ...extractionResult, validated: false });
518
559
  await this.setPropertyValues({ documentId, properties: properties }, transaction);
519
560
  });
561
+ this.publishChange({ documentIds: [documentId] });
520
562
  }
521
563
  async enrichDocumentRequestFile(requestFileId) {
522
564
  const requestFile = await this.documentRequestFileService.load(requestFileId);
@@ -526,6 +568,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
526
568
  await documentRequestFileService.update(requestFile.id, extractionResult);
527
569
  await this.setPropertyValues({ requestFileId, properties: properties }, transaction);
528
570
  });
571
+ this.publishChange({ requestIds: [request.id] });
529
572
  }
530
573
  async enrichDocumentRequestAssignmentTask(requestAssignmentTaskId) {
531
574
  const task = await this.documentRequestAssignmentTaskService.load(requestAssignmentTaskId);
@@ -534,7 +577,8 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
534
577
  await documentRequestAssignmentTaskService.update(task.id, extractionResult);
535
578
  await this.setPropertyValues({ requestAssignmentTaskId, properties: properties }, transaction);
536
579
  });
537
- void tryIgnoreLogAsync(async () => this.assignmentQueue.enqueue({ requestAssignmentTaskId }), this.logger);
580
+ void tryIgnoreLogAsync(this.logger, async () => this.assignmentQueue.enqueue({ requestAssignmentTaskId }));
581
+ this.publishChange({ requestAssignmentTaskIds: [requestAssignmentTaskId] });
538
582
  }
539
583
  async assignDocumentRequest(requestAssignmentTaskId) {
540
584
  const session = this.documentRequestAssignmentTaskCollectionService.session;
@@ -656,6 +700,7 @@ Ordne die Datei unter "file" der passenden Anforderungen unter "requests" zu. Gi
656
700
  });
657
701
  await this.setPropertyValues({ requestFileId: requestFile.id, properties: task.properties }, transaction);
658
702
  });
703
+ this.publishChange({ collectionIds: requestsCollectionIds });
659
704
  }
660
705
  async extractFileInformation(fileId, assumeTypeId) {
661
706
  const env_1 = { stack: [], error: void 0, hasError: false };
@@ -665,7 +710,7 @@ Ordne die Datei unter "file" der passenden Anforderungen unter "requests" zu. Gi
665
710
  const tmpFile = __addDisposableResource(env_1, await TemporaryFile.from(fileContentStream), true);
666
711
  const filePart = await this.#aiService.processFile({ path: tmpFile.path, mimeType: file.mimeType });
667
712
  const pages = file.mimeType.includes('pdf')
668
- ? await tryIgnoreLogAsync(async () => getPdfPageCount(tmpFile.path), this.logger, null)
713
+ ? await tryIgnoreLogAsync(this.logger, async () => getPdfPageCount(tmpFile.path), null)
669
714
  : null;
670
715
  const types = await this.documentTypeService.session
671
716
  .select({
@@ -797,6 +842,51 @@ Antworte auf deutsch.`
797
842
  'Response-Content-Disposition': `${disposition}; filename = "${encodeURIComponent(filename)}"`
798
843
  });
799
844
  }
845
+ publishChange({ collectionIds, documentIds, requestIds, requestFileIds, requestAssignmentTaskIds: requestAssignmentTasksIds }) {
846
+ void tryIgnoreLogAsync(this.logger, async () => {
847
+ const queries = [
848
+ isUndefined(documentIds)
849
+ ? undefined
850
+ : this.session
851
+ .selectDistinct({ collectionId: documentCollectionDocument.collectionId })
852
+ .from(documentCollectionDocument)
853
+ .where(inArray(documentCollectionDocument.documentId, documentIds)),
854
+ isUndefined(requestIds)
855
+ ? undefined
856
+ : this.session
857
+ .selectDistinct({ collectionId: documentRequestCollection.collectionId })
858
+ .from(documentRequestCollection)
859
+ .where(inArray(documentRequestCollection.requestId, requestIds)),
860
+ isUndefined(requestFileIds)
861
+ ? undefined
862
+ : this.session
863
+ .selectDistinct({ collectionId: documentRequestCollection.collectionId })
864
+ .from(documentRequestFile)
865
+ .innerJoin(documentRequestCollection, eq(documentRequestCollection.requestId, documentRequestFile.requestId))
866
+ .where(inArray(documentRequestFile.requestId, requestFileIds)),
867
+ isUndefined(requestAssignmentTasksIds)
868
+ ? undefined
869
+ : this.session
870
+ .selectDistinct({ collectionId: documentRequestAssignmentTaskCollection.collectionId })
871
+ .from(documentRequestAssignmentTaskCollection)
872
+ .where(inArray(documentRequestAssignmentTaskCollection.requestAssignmentTaskId, requestAssignmentTasksIds))
873
+ ];
874
+ const filteredQueries = queries.filter(isDefined);
875
+ const mappings = (filteredQueries.length == 1)
876
+ ? await filteredQueries[0]
877
+ : (filteredQueries.length > 1)
878
+ ? await union(filteredQueries[0], filteredQueries[1], ...filteredQueries.slice(2))
879
+ : [];
880
+ const allCollectionIds = distinct([
881
+ ...(collectionIds ?? []),
882
+ ...mappings.map((mapping) => mapping.collectionId)
883
+ ]);
884
+ if (allCollectionIds.length == 0) {
885
+ return;
886
+ }
887
+ this.collectionChangeMessageBus.publishAndForget(allCollectionIds);
888
+ });
889
+ }
800
890
  };
801
891
  DocumentManagementService = DocumentManagementService_1 = __decorate([
802
892
  Singleton({
@@ -17,7 +17,6 @@ export declare abstract class MessageBusBase<T> extends MessageBus<T> {
17
17
  publishAndForget(message: T): void;
18
18
  publish(message: T): Promise<void>;
19
19
  [disposeAsync](): Promise<void>;
20
- private _publishAndForget;
21
20
  /**
22
21
  * publish messages to other instances
23
22
  * @param message message to send to other instances
@@ -1,4 +1,5 @@
1
1
  import { defer, merge, share, Subject, takeUntil } from 'rxjs';
2
+ import { tryIgnoreLogAsync } from '../utils/try-ignore.js';
2
3
  import { CancellationToken } from '../cancellation/token.js';
3
4
  import { disposeAsync } from '../disposable/disposable.js';
4
5
  import { MessageBus } from './message-bus.js';
@@ -17,7 +18,7 @@ export class MessageBusBase extends MessageBus {
17
18
  this.allMessages$ = merge(this.messages$, this.publishSubject);
18
19
  }
19
20
  publishAndForget(message) {
20
- void this._publishAndForget(message);
21
+ void tryIgnoreLogAsync(this.logger, async () => this.publish(message));
21
22
  }
22
23
  async publish(message) {
23
24
  if (this.disposeToken.isSet) {
@@ -34,12 +35,4 @@ export class MessageBusBase extends MessageBus {
34
35
  this.publishSubject.complete();
35
36
  return this._dispose();
36
37
  }
37
- async _publishAndForget(message) {
38
- try {
39
- await this.publish(message);
40
- }
41
- catch (error) {
42
- this.logger.error(error);
43
- }
44
- }
45
38
  }
@@ -5,6 +5,7 @@ import type { Entity, EntityType } from './entity.js';
5
5
  import type { PgTableFromType } from './server/types.js';
6
6
  type IndexMethod = LiteralUnion<'hash' | 'btree' | 'gist' | 'spgist' | 'gin' | 'brin' | 'hnsw' | 'ivfflat', string>;
7
7
  type NamingStrategy = 'abbreviated-table';
8
+ type Columns<T> = [Extract<keyof T, string>, ...Extract<keyof T, string>[]];
8
9
  export type CheckBuilder<T extends Entity = any> = (table: PgTableFromType<EntityType<T>>) => SQL;
9
10
  export type OrmTableReflectionData = {
10
11
  name?: string;
@@ -69,9 +70,9 @@ type TableOptions = Partial<Pick<OrmTableReflectionData, 'name' | 'schema'>>;
69
70
  export declare function Table(name?: string, options?: TypedOmit<TableOptions, 'schema'>): ClassDecorator;
70
71
  export declare function Table(options?: TableOptions): ClassDecorator;
71
72
  export declare function Unique(name?: string, options?: UniqueReflectionData['options']): PropertyDecorator;
72
- export declare function Unique<T>(name: string | undefined, columns: [Extract<keyof T, string>, ...Extract<keyof T, string>[]], options?: UniqueReflectionData['options']): ClassDecorator;
73
- export declare function Unique<T>(columns: [Extract<keyof T, string>, ...Extract<keyof T, string>[]], options?: UniqueReflectionData['options']): ClassDecorator;
73
+ export declare function Unique<T>(name: string | undefined, columns: Columns<T>, options?: UniqueReflectionData['options']): ClassDecorator;
74
+ export declare function Unique<T>(columns: Columns<T>, options?: UniqueReflectionData['options']): ClassDecorator;
74
75
  export declare function Index(name?: string, options?: IndexReflectionData['options']): PropertyDecorator;
75
- export declare function Index(name: string, columns: [string, ...string[]], options?: IndexReflectionData['options']): ClassDecorator;
76
- export declare function Index(columns: [string, ...string[]], options?: IndexReflectionData['options']): ClassDecorator;
76
+ export declare function Index<T>(name: string, columns: Columns<T>, options?: IndexReflectionData['options']): ClassDecorator;
77
+ export declare function Index<T>(columns: Columns<T>, options?: IndexReflectionData['options']): ClassDecorator;
77
78
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.92.99",
3
+ "version": "0.92.100",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -8,9 +8,9 @@ export declare class MongoQueue<T = unknown> extends Queue<T> {
8
8
  private readonly repository;
9
9
  private readonly lock;
10
10
  private readonly queueKey;
11
- private readonly processTimeout;
12
- private readonly maxTries;
13
11
  private readonly messageBus;
12
+ readonly processTimeout: number;
13
+ readonly maxTries: number;
14
14
  constructor(repository: MongoJobRepository<T>, lock: Lock, messageBusProvider: MessageBusProvider, key: string, config?: QueueConfig);
15
15
  enqueue(data: T, options?: EnqueueOneOptions): Promise<Job<T>>;
16
16
  enqueueMany(items: EnqueueManyItem<T>[], options?: EnqueueManyOptions & {
@@ -24,6 +24,7 @@ export declare class MongoQueue<T = unknown> extends Queue<T> {
24
24
  countByTag(tag: JobTag): Promise<number>;
25
25
  get(id: string): Promise<Job<T> | undefined>;
26
26
  getByTag(tag: JobTag): Promise<Job<T>[]>;
27
+ getByTags(tags: JobTag[]): Promise<Job<T>[]>;
27
28
  cancel(id: string): Promise<void>;
28
29
  cancelMany(ids: string[]): Promise<void>;
29
30
  cancelByTag(tag: JobTag): Promise<void>;
@@ -36,9 +36,9 @@ let MongoQueue = class MongoQueue extends Queue {
36
36
  repository;
37
37
  lock;
38
38
  queueKey;
39
+ messageBus;
39
40
  processTimeout;
40
41
  maxTries;
41
- messageBus;
42
42
  constructor(repository, lock, messageBusProvider, key, config) {
43
43
  super();
44
44
  this.repository = repository;
@@ -122,6 +122,9 @@ let MongoQueue = class MongoQueue extends Queue {
122
122
  async getByTag(tag) {
123
123
  return this.repository.loadManyByFilter({ queue: this.queueKey, tag });
124
124
  }
125
+ async getByTags(tags) {
126
+ return this.repository.loadManyByFilter({ queue: this.queueKey, tag: { $in: tags } });
127
+ }
125
128
  async cancel(id) {
126
129
  await this.repository.deleteByFilter({ queue: this.queueKey, jobId: id });
127
130
  }
@@ -237,7 +240,10 @@ function toModelJob(mongoJob) {
237
240
  id: mongoJob.jobId,
238
241
  priority: mongoJob.priority,
239
242
  tag: mongoJob.tag,
240
- data: mongoJob.data
243
+ data: mongoJob.data,
244
+ enqueueTimestamp: mongoJob.enqueueTimestamp,
245
+ lastDequeueTimestamp: mongoJob.lastDequeueTimestamp,
246
+ tries: mongoJob.tries
241
247
  };
242
248
  return job;
243
249
  }
@@ -7,9 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  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
- import { Table } from '../../orm/decorators.js';
10
+ import { Index, Table } from '../../orm/decorators.js';
11
11
  import { EntityWithoutMetadata } from '../../orm/entity.js';
12
- import { Integer, Json, Timestamp, Unique } from '../../orm/index.js';
12
+ import { Integer, Json, Timestamp } from '../../orm/index.js';
13
13
  import { StringProperty } from '../../schema/index.js';
14
14
  let PostgresJob = class PostgresJob extends EntityWithoutMetadata {
15
15
  queue;
@@ -50,6 +50,6 @@ __decorate([
50
50
  ], PostgresJob.prototype, "data", void 0);
51
51
  PostgresJob = __decorate([
52
52
  Table('job'),
53
- Unique(['queue', 'tag'])
53
+ Index(['queue', 'tag'])
54
54
  ], PostgresJob);
55
55
  export { PostgresJob };
@@ -3,6 +3,8 @@ import type { ObjectLiteral } from '../../types.js';
3
3
  import { Queue, type EnqueueManyItem, type EnqueueManyOptions, type EnqueueOneOptions, type Job, type JobTag } from '../queue.js';
4
4
  export declare class PostgresQueue<T extends ObjectLiteral> extends Queue<T> {
5
5
  #private;
6
+ readonly processTimeout: number;
7
+ readonly maxTries: number;
6
8
  enqueue(data: T, options?: EnqueueOneOptions): Promise<Job<T>>;
7
9
  enqueueMany(items: EnqueueManyItem<T>[], options?: EnqueueManyOptions & {
8
10
  returnJobs?: false;
@@ -15,6 +17,7 @@ export declare class PostgresQueue<T extends ObjectLiteral> extends Queue<T> {
15
17
  countByTag(tag: JobTag): Promise<number>;
16
18
  get(id: string): Promise<Job<T> | undefined>;
17
19
  getByTag(tag: JobTag): Promise<Job<T>[]>;
20
+ getByTags(tags: JobTag[]): Promise<Job<T>[]>;
18
21
  cancel(id: string): Promise<void>;
19
22
  cancelMany(ids: string[]): Promise<void>;
20
23
  cancelByTag(tag: JobTag): Promise<void>;
@@ -12,7 +12,7 @@ import { MessageBus } from '../../message-bus/index.js';
12
12
  import { interval, RANDOM_UUID, TRANSACTION_TIMESTAMP } from '../../orm/index.js';
13
13
  import { DatabaseConfig, EntityRepositoryConfig, injectRepository } from '../../orm/server/index.js';
14
14
  import { cancelableTimeout } from '../../utils/timing.js';
15
- import { isDefined, isString } from '../../utils/type-guards.js';
15
+ import { isDefined, isString, isUndefined } from '../../utils/type-guards.js';
16
16
  import { millisecondsPerSecond } from '../../utils/units.js';
17
17
  import { defaultQueueConfig, Queue, UniqueTagStrategy } from '../queue.js';
18
18
  import { PostgresJob } from './job.model.js';
@@ -21,11 +21,11 @@ import { job } from './schemas.js';
21
21
  let PostgresQueue = class PostgresQueue extends Queue {
22
22
  #repository = injectRepository(PostgresJob);
23
23
  #config = injectArgument(this);
24
- #processTimeout = (isString(this.#config) ? undefined : this.#config.processTimeout) ?? defaultQueueConfig.processTimeout;
25
- #maxTries = (isString(this.#config) ? undefined : this.#config.maxTries) ?? defaultQueueConfig.maxTries;
26
24
  #queueName = isString(this.#config) ? this.#config : this.#config.name;
27
25
  #messageBus = inject((MessageBus), `PostgresQueue:${this.#queueName}`);
28
26
  #keepOldUpdate = { id: sql `${job.id}` };
27
+ processTimeout = (isString(this.#config) ? undefined : this.#config.processTimeout) ?? defaultQueueConfig.processTimeout;
28
+ maxTries = (isString(this.#config) ? undefined : this.#config.maxTries) ?? defaultQueueConfig.maxTries;
29
29
  #takeNewUpdate = {
30
30
  id: RANDOM_UUID,
31
31
  queue: this.#queueName,
@@ -36,7 +36,7 @@ let PostgresQueue = class PostgresQueue extends Queue {
36
36
  lastDequeueTimestamp: null,
37
37
  data: sql `excluded.data`
38
38
  };
39
- #dequeueQuery = and(eq(job.queue, this.#queueName), lt(job.tries, this.#maxTries), or(isSqlNull(job.lastDequeueTimestamp), lte(sql `${job.lastDequeueTimestamp} + ${interval(this.#processTimeout, 'milliseconds')}`, TRANSACTION_TIMESTAMP)));
39
+ #dequeueQuery = and(eq(job.queue, this.#queueName), lt(job.tries, this.maxTries), or(isSqlNull(job.lastDequeueTimestamp), lte(sql `${job.lastDequeueTimestamp} + ${interval(this.processTimeout, 'milliseconds')}`, TRANSACTION_TIMESTAMP)));
40
40
  #dequeueUpdate = {
41
41
  tries: sql `${job.tries} + 1`,
42
42
  lastDequeueTimestamp: TRANSACTION_TIMESTAMP
@@ -57,8 +57,12 @@ let PostgresQueue = class PostgresQueue extends Queue {
57
57
  }));
58
58
  const update = (options?.uniqueTag == UniqueTagStrategy.TakeNew)
59
59
  ? this.#takeNewUpdate
60
- : this.#keepOldUpdate;
61
- const jobs = await this.#repository.upsertMany(['queue', 'tag'], newEntities, update);
60
+ : (options?.uniqueTag == UniqueTagStrategy.KeepOld)
61
+ ? this.#keepOldUpdate
62
+ : undefined;
63
+ const jobs = isUndefined(update)
64
+ ? await this.#repository.insertMany(newEntities)
65
+ : await this.#repository.upsertMany(['queue', 'tag'], newEntities, update);
62
66
  this.#messageBus.publishAndForget();
63
67
  return jobs;
64
68
  }
@@ -74,6 +78,9 @@ let PostgresQueue = class PostgresQueue extends Queue {
74
78
  async getByTag(tag) {
75
79
  return this.#repository.loadManyByQuery({ queue: this.#queueName, tag });
76
80
  }
81
+ async getByTags(tags) {
82
+ return this.#repository.loadManyByQuery({ queue: this.#queueName, tag: { $in: tags } });
83
+ }
77
84
  async cancel(id) {
78
85
  await this.#repository.hardDeleteByQuery({ queue: this.#queueName, id });
79
86
  }
package/queue/queue.d.ts CHANGED
@@ -14,6 +14,9 @@ export type Job<T> = {
14
14
  priority: number;
15
15
  tag: JobTag;
16
16
  data: T;
17
+ enqueueTimestamp: number;
18
+ lastDequeueTimestamp: number | null;
19
+ tries: number;
17
20
  };
18
21
  export declare const defaultJobPriority = 1000;
19
22
  export declare enum UniqueTagStrategy {
@@ -46,6 +49,8 @@ export type QueueArgument = string | (QueueConfig & {
46
49
  export declare const defaultQueueConfig: Required<QueueConfig>;
47
50
  export declare abstract class Queue<T> implements Resolvable<QueueArgument> {
48
51
  readonly [resolveArgumentType]: QueueArgument;
52
+ abstract readonly processTimeout: number;
53
+ abstract readonly maxTries: number;
49
54
  batch(): QueueEnqueueBatch<T>;
50
55
  abstract enqueue(data: T, options?: EnqueueOneOptions): Promise<Job<T>>;
51
56
  abstract enqueueMany(items: EnqueueManyItem<T>[], options?: EnqueueManyOptions & {
@@ -59,6 +64,7 @@ export declare abstract class Queue<T> implements Resolvable<QueueArgument> {
59
64
  abstract countByTag(tag: JobTag): Promise<number>;
60
65
  abstract get(id: string): Promise<Job<T> | undefined>;
61
66
  abstract getByTag(tag: JobTag): Promise<Job<T>[]>;
67
+ abstract getByTags(tags: JobTag[]): Promise<Job<T>[]>;
62
68
  abstract cancel(id: string): Promise<void>;
63
69
  abstract cancelMany(ids: string[]): Promise<void>;
64
70
  abstract cancelByTag(tag: JobTag): Promise<void>;
@@ -3,7 +3,7 @@ export declare function tryIgnore<R>(fn: () => R): R;
3
3
  export declare function tryIgnore<R, F>(fn: () => R, fallback: F): R | F;
4
4
  export declare function tryIgnoreAsync<R>(fn: () => Promise<R>): Promise<R>;
5
5
  export declare function tryIgnoreAsync<R, F>(fn: () => Promise<R>, fallback: F): Promise<F>;
6
- export declare function tryIgnoreLog<R>(fn: () => R, logger: Logger): R;
7
- export declare function tryIgnoreLog<R, F>(fn: () => R, logger: Logger, fallback: F): R | F;
8
- export declare function tryIgnoreLogAsync<R>(fn: () => Promise<R>, logger: Logger): Promise<R>;
9
- export declare function tryIgnoreLogAsync<R, F>(fn: () => Promise<R>, logger: Logger, fallback: F): Promise<F>;
6
+ export declare function tryIgnoreLog<R>(logger: Logger, fn: () => R): R;
7
+ export declare function tryIgnoreLog<R, F>(logger: Logger, fn: () => R, fallback: F): R | F;
8
+ export declare function tryIgnoreLogAsync<R>(logger: Logger, fn: () => Promise<R>): Promise<R>;
9
+ export declare function tryIgnoreLogAsync<R, F>(logger: Logger, fn: () => Promise<R>, fallback: F): Promise<F>;
@@ -15,7 +15,7 @@ export async function tryIgnoreAsync(fn, fallback) {
15
15
  return fallback;
16
16
  }
17
17
  }
18
- export function tryIgnoreLog(fn, logger, fallback) {
18
+ export function tryIgnoreLog(logger, fn, fallback) {
19
19
  try {
20
20
  return fn();
21
21
  }
@@ -24,7 +24,7 @@ export function tryIgnoreLog(fn, logger, fallback) {
24
24
  return fallback;
25
25
  }
26
26
  }
27
- export async function tryIgnoreLogAsync(fn, logger, fallback) {
27
+ export async function tryIgnoreLogAsync(logger, fn, fallback) {
28
28
  try {
29
29
  const value = await fn();
30
30
  return value;