@tstdl/base 0.92.123 → 0.92.125

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 (221) hide show
  1. package/ai/ai-file.service.js +23 -18
  2. package/ai/ai.service.d.ts +4 -12
  3. package/ai/ai.service.js +79 -147
  4. package/ai/functions.d.ts +1 -1
  5. package/ai/types.d.ts +3 -1
  6. package/api/server/gateway.js +6 -6
  7. package/authentication/authentication.api.js +24 -24
  8. package/authentication/server/authentication.service.js +20 -20
  9. package/browser/page-controller.js +1 -1
  10. package/context/context.js +5 -5
  11. package/document-management/api/document-management.api.d.ts +60 -215
  12. package/document-management/api/document-management.api.js +32 -68
  13. package/document-management/models/document-assignment-scope.model.d.ts +11 -0
  14. package/document-management/models/{document-request-assignment-task-collection.model.js → document-assignment-scope.model.js} +14 -10
  15. package/document-management/models/document-assignment-task.model.d.ts +13 -0
  16. package/document-management/models/document-assignment-task.model.js +38 -0
  17. package/document-management/models/document-category.model.d.ts +2 -0
  18. package/document-management/models/document-category.model.js +7 -1
  19. package/document-management/models/{document-collection-document.model.d.ts → document-collection-assignment.model.d.ts} +1 -1
  20. package/document-management/models/{document-collection-document.model.js → document-collection-assignment.model.js} +7 -7
  21. package/document-management/models/document-collection.model.d.ts +2 -0
  22. package/document-management/models/document-collection.model.js +10 -0
  23. package/document-management/models/document-property-value.model.d.ts +3 -11
  24. package/document-management/models/document-property-value.model.js +15 -46
  25. package/document-management/models/document-property.model.d.ts +2 -1
  26. package/document-management/models/document-property.model.js +2 -2
  27. package/document-management/models/document-request-submission.model.d.ts +7 -0
  28. package/document-management/models/document-request-submission.model.js +34 -0
  29. package/document-management/models/document-request-template.d.ts +2 -4
  30. package/document-management/models/document-request-template.js +3 -8
  31. package/document-management/models/document-request.model.d.ts +14 -6
  32. package/document-management/models/document-request.model.js +22 -12
  33. package/document-management/models/document-type-validation.model.d.ts +6 -0
  34. package/document-management/models/document-type-validation.model.js +34 -0
  35. package/document-management/models/document-type.model.d.ts +0 -1
  36. package/document-management/models/document-type.model.js +0 -5
  37. package/document-management/models/document-validation-definition.model.d.ts +10 -0
  38. package/document-management/models/document-validation-definition.model.js +39 -0
  39. package/document-management/models/document-validation-execution-related-document.model.d.ts +7 -0
  40. package/document-management/models/document-validation-execution-related-document.model.js +34 -0
  41. package/document-management/models/document-validation-execution.model.d.ts +26 -0
  42. package/document-management/models/document-validation-execution.model.js +72 -0
  43. package/document-management/models/document-workflow.model.d.ts +35 -0
  44. package/document-management/models/document-workflow.model.js +70 -0
  45. package/document-management/models/document.model.d.ts +12 -1
  46. package/document-management/models/document.model.js +21 -5
  47. package/document-management/models/index.d.ts +10 -5
  48. package/document-management/models/index.js +10 -5
  49. package/document-management/models/service-models/categories-and-types.view-model.d.ts +3 -3
  50. package/document-management/models/service-models/categories-and-types.view-model.js +2 -21
  51. package/document-management/models/service-models/document-folders.view-model.d.ts +7 -14
  52. package/document-management/models/service-models/document-folders.view-model.js +20 -44
  53. package/document-management/models/service-models/document-management.view-model.d.ts +41 -0
  54. package/document-management/models/service-models/{document.view-model.js → document-management.view-model.js} +43 -26
  55. package/document-management/models/service-models/document.service-model.d.ts +51 -81
  56. package/document-management/models/service-models/document.service-model.js +30 -28
  57. package/document-management/models/service-models/enriched/enriched-document-assignment.view.d.ts +11 -0
  58. package/document-management/models/service-models/enriched/enriched-document-assignment.view.js +12 -0
  59. package/document-management/models/service-models/enriched/enriched-document-category.view.d.ts +13 -0
  60. package/document-management/models/service-models/enriched/enriched-document-category.view.js +47 -0
  61. package/document-management/models/service-models/enriched/enriched-document-collection.view.d.ts +22 -0
  62. package/document-management/models/service-models/enriched/enriched-document-collection.view.js +89 -0
  63. package/document-management/models/service-models/enriched/enriched-document-file.view.d.ts +12 -0
  64. package/document-management/models/service-models/enriched/enriched-document-file.view.js +16 -0
  65. package/document-management/models/service-models/enriched/enriched-document-management-data.view.d.ts +41 -0
  66. package/document-management/models/service-models/enriched/enriched-document-management-data.view.js +130 -0
  67. package/document-management/models/service-models/enriched/enriched-document-request.view.d.ts +17 -0
  68. package/document-management/models/service-models/enriched/enriched-document-request.view.js +52 -0
  69. package/document-management/models/service-models/enriched/enriched-document-type.view.d.ts +9 -0
  70. package/document-management/models/service-models/enriched/enriched-document-type.view.js +10 -0
  71. package/document-management/models/service-models/enriched/enriched-document.view.d.ts +28 -0
  72. package/document-management/models/service-models/enriched/enriched-document.view.js +77 -0
  73. package/document-management/models/service-models/enriched/enriched.d.ts +18 -0
  74. package/document-management/models/service-models/enriched/enriched.js +39 -0
  75. package/document-management/models/service-models/enriched/index.d.ts +9 -0
  76. package/document-management/models/service-models/enriched/index.js +9 -0
  77. package/document-management/models/service-models/index.d.ts +2 -2
  78. package/document-management/models/service-models/index.js +2 -2
  79. package/document-management/models/service-models/normalized-requests-template-data.model.d.ts +12 -10
  80. package/document-management/models/service-models/normalized-requests-template-data.model.js +5 -4
  81. package/document-management/models/service-models/stats.view-model.d.ts +7 -5
  82. package/document-management/models/service-models/stats.view-model.js +19 -9
  83. package/document-management/server/module.d.ts +2 -2
  84. package/document-management/server/module.js +3 -7
  85. package/document-management/server/schemas.d.ts +18 -23
  86. package/document-management/server/schemas.js +18 -23
  87. package/document-management/server/services/document-category-type.service.d.ts +25 -0
  88. package/document-management/server/services/document-category-type.service.js +66 -0
  89. package/document-management/server/services/document-collection.service.d.ts +13 -0
  90. package/document-management/server/services/document-collection.service.js +41 -0
  91. package/document-management/server/services/document-file.service.d.ts +17 -0
  92. package/document-management/server/services/document-file.service.js +204 -0
  93. package/document-management/server/services/document-management-ai.service.d.ts +22 -0
  94. package/document-management/server/services/document-management-ai.service.js +340 -0
  95. package/document-management/server/services/document-management-ancillary.service.d.ts +127 -3
  96. package/document-management/server/services/document-management-ancillary.service.js +24 -0
  97. package/document-management/server/services/document-management.service.d.ts +10 -122
  98. package/document-management/server/services/document-management.service.js +106 -888
  99. package/document-management/server/services/document-property.service.d.ts +84 -0
  100. package/document-management/server/services/document-property.service.js +87 -0
  101. package/document-management/server/services/document-request.service.d.ts +30 -0
  102. package/document-management/server/services/document-request.service.js +138 -0
  103. package/document-management/server/services/document-validation.service.d.ts +20 -0
  104. package/document-management/server/services/document-validation.service.js +145 -0
  105. package/document-management/server/services/document-workflow.service.d.ts +20 -0
  106. package/document-management/server/services/document-workflow.service.js +132 -0
  107. package/document-management/server/services/document.service.d.ts +16 -0
  108. package/document-management/server/services/document.service.js +81 -0
  109. package/document-management/server/services/index.d.ts +9 -0
  110. package/document-management/server/services/index.js +9 -0
  111. package/document-management/server/validators/ai-validation-executor.d.ts +19 -0
  112. package/document-management/server/validators/ai-validation-executor.js +51 -0
  113. package/document-management/server/validators/index.d.ts +2 -0
  114. package/document-management/server/validators/index.js +2 -0
  115. package/document-management/server/validators/single-document-validation-executor.d.ts +16 -0
  116. package/document-management/server/validators/single-document-validation-executor.js +20 -0
  117. package/document-management/server/validators/validator.d.ts +21 -0
  118. package/document-management/server/validators/validator.js +2 -0
  119. package/eslint.config.js +31 -17
  120. package/examples/document-management/main.d.ts +18 -3
  121. package/examples/document-management/main.js +28 -12
  122. package/file/mime-type.js +2 -9
  123. package/formats.d.ts +5 -2
  124. package/formats.js +32 -23
  125. package/http/client/http-client.js +1 -1
  126. package/injector/injector.js +2 -2
  127. package/object-storage/s3/s3.object-storage.js +1 -1
  128. package/orm/data-types/bytea.d.ts +8 -0
  129. package/orm/data-types/bytea.js +8 -0
  130. package/orm/data-types/index.d.ts +4 -0
  131. package/orm/data-types/index.js +4 -0
  132. package/orm/data-types/numeric-date.d.ts +9 -0
  133. package/orm/data-types/numeric-date.js +9 -0
  134. package/orm/data-types/timestamp.d.ts +9 -0
  135. package/orm/data-types/timestamp.js +9 -0
  136. package/orm/decorators.d.ts +139 -5
  137. package/orm/decorators.js +50 -0
  138. package/orm/entity.d.ts +19 -0
  139. package/orm/entity.js +19 -0
  140. package/orm/index.d.ts +5 -0
  141. package/orm/index.js +5 -0
  142. package/orm/query.d.ts +51 -0
  143. package/orm/query.js +6 -0
  144. package/orm/repository.types.d.ts +54 -2
  145. package/orm/server/database-schema.d.ts +34 -0
  146. package/orm/server/database-schema.js +29 -0
  147. package/orm/server/database.d.ts +19 -1
  148. package/orm/server/database.js +17 -3
  149. package/orm/server/drizzle/schema-converter.d.ts +2 -1
  150. package/orm/server/drizzle/schema-converter.js +12 -1
  151. package/orm/server/encryption.d.ts +16 -0
  152. package/orm/server/encryption.js +29 -4
  153. package/orm/server/index.d.ts +7 -0
  154. package/orm/server/index.js +7 -0
  155. package/orm/server/module.d.ts +20 -0
  156. package/orm/server/module.js +9 -0
  157. package/orm/server/query-converter.d.ts +17 -0
  158. package/orm/server/query-converter.js +66 -11
  159. package/orm/server/repository.d.ts +324 -18
  160. package/orm/server/repository.js +344 -73
  161. package/orm/server/transaction.d.ts +5 -5
  162. package/orm/server/transaction.js +5 -5
  163. package/orm/server/transactional.d.ts +75 -0
  164. package/orm/server/transactional.js +134 -0
  165. package/orm/server/types.d.ts +1 -0
  166. package/orm/sqls.d.ts +55 -0
  167. package/orm/sqls.js +60 -0
  168. package/orm/types.d.ts +67 -4
  169. package/orm/utils.d.ts +19 -3
  170. package/orm/utils.js +12 -0
  171. package/package.json +32 -31
  172. package/password/password-check-result.model.d.ts +9 -7
  173. package/password/password-check-result.model.js +8 -8
  174. package/password/password-check.js +5 -7
  175. package/password/password-check.localization.js +12 -12
  176. package/pdf/pdf.service.js +1 -1
  177. package/pdf/utils.d.ts +9 -0
  178. package/pdf/utils.js +19 -2
  179. package/process/spawn.d.ts +11 -4
  180. package/process/spawn.js +42 -5
  181. package/queue/postgres/queue.js +5 -5
  182. package/queue/queue.d.ts +6 -4
  183. package/queue/queue.js +6 -6
  184. package/schema/schemas/one-or-many.d.ts +2 -1
  185. package/schema/schemas/one-or-many.js +1 -1
  186. package/search-index/elastic/model/index-mapping.d.ts +1 -1
  187. package/search-index/elastic/model/index-mapping.js +0 -1
  188. package/search-index/elastic/search-index.d.ts +1 -2
  189. package/search-index/elastic/search-index.js +3 -3
  190. package/types.d.ts +1 -0
  191. package/utils/async-hook/async-hook.d.ts +9 -0
  192. package/utils/async-hook/async-hook.js +21 -0
  193. package/utils/async-hook/index.d.ts +1 -0
  194. package/utils/async-hook/index.js +1 -0
  195. package/utils/compression.js +1 -1
  196. package/utils/function/class.d.ts +6 -0
  197. package/utils/function/class.js +9 -0
  198. package/utils/function/index.d.ts +1 -0
  199. package/utils/function/index.js +1 -0
  200. package/utils/function/memoize.d.ts +18 -0
  201. package/utils/function/memoize.js +41 -2
  202. package/utils/jwt.d.ts +1 -1
  203. package/utils/jwt.js +5 -5
  204. package/utils/object/forward-ref.d.ts +3 -2
  205. package/utils/object/forward-ref.js +12 -12
  206. package/utils/object/lazy-property.js +2 -2
  207. package/utils/proxy.js +1 -1
  208. package/utils/stream/{readable-stream-from-promise.d.ts → from-promise.d.ts} +1 -0
  209. package/utils/stream/from-promise.js +27 -0
  210. package/utils/stream/index.d.ts +1 -1
  211. package/utils/stream/index.js +1 -1
  212. package/utils/stream/stream-reader.js +71 -31
  213. package/document-management/models/document-request-assignment-task-collection.model.d.ts +0 -7
  214. package/document-management/models/document-request-assignment-task.model.d.ts +0 -14
  215. package/document-management/models/document-request-assignment-task.model.js +0 -77
  216. package/document-management/models/document-request-file.model.d.ts +0 -16
  217. package/document-management/models/document-request-file.model.js +0 -86
  218. package/document-management/models/service-models/document.view-model.d.ts +0 -41
  219. package/document-management/models/service-models/normalized-document-collection-view.model.d.ts +0 -73
  220. package/document-management/models/service-models/normalized-document-collection-view.model.js +0 -110
  221. package/utils/stream/readable-stream-from-promise.js +0 -8
@@ -0,0 +1,17 @@
1
+ import type { FileContentPart } from '../../../ai/types.js';
2
+ import { Transactional } from '../../../orm/server/index.js';
3
+ import { DocumentFile } from '../../models/index.js';
4
+ export declare class DocumentFileService extends Transactional {
5
+ #private;
6
+ load(id: string): Promise<DocumentFile>;
7
+ create(content: Uint8Array | ReadableStream<Uint8Array>, originalFileName: string | null): Promise<DocumentFile>;
8
+ getContent(fileId: string): Promise<Uint8Array>;
9
+ getContentStream(fileId: string): ReadableStream<Uint8Array>;
10
+ getContentUrl(fileId: string, title: string | null, download?: boolean): Promise<string>;
11
+ getPreview(fileId: string, page?: number): Promise<Uint8Array>;
12
+ getPreviewStream(fileId: string, page?: number): ReadableStream<Uint8Array>;
13
+ getPreviewUrl(fileId: string, page?: number): Promise<string>;
14
+ getAiFileContentPart(documentId: string): Promise<FileContentPart>;
15
+ private createPreviewIfNotExists;
16
+ private getDocumentFileContentObjectUrl;
17
+ }
@@ -0,0 +1,204 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ var _a;
54
+ import sharp, {} from 'sharp';
55
+ import { match } from 'ts-pattern';
56
+ import { AiService } from '../../../ai/ai.service.js';
57
+ import { NotImplementedError } from '../../../errors/not-implemented.error.js';
58
+ import { getMimeType, getMimeTypeExtensions } from '../../../file/index.js';
59
+ import { TemporaryFile } from '../../../file/server/index.js';
60
+ import { inject } from '../../../injector/inject.js';
61
+ import { Logger } from '../../../logger/logger.js';
62
+ import { ObjectStorage } from '../../../object-storage/index.js';
63
+ import { Transactional } from '../../../orm/server/index.js';
64
+ import { injectRepository } from '../../../orm/server/repository.js';
65
+ import { pdfToImage } from '../../../pdf/index.js';
66
+ import { digest } from '../../../utils/cryptography.js';
67
+ import { currentTimestamp } from '../../../utils/date-time.js';
68
+ import { readableStreamFromPromise, readBinaryStream } from '../../../utils/stream/index.js';
69
+ import { isDefined, isUint8Array } from '../../../utils/type-guards.js';
70
+ import { millisecondsPerMinute } from '../../../utils/units.js';
71
+ import { Document, DocumentFile } from '../../models/index.js';
72
+ import { DocumentManagementConfig } from '../module.js';
73
+ export class DocumentFileService extends Transactional {
74
+ #documentFileRepository = injectRepository(DocumentFile);
75
+ #documentRepository = injectRepository(Document);
76
+ #aiService = inject(AiService);
77
+ #fileObjectStorage = inject(ObjectStorage, inject(DocumentManagementConfig).fileObjectStorageModule);
78
+ #filePreviewObjectStorage = inject(ObjectStorage, inject(DocumentManagementConfig).filePreviewObjectStorageModule);
79
+ #logger = inject(Logger, _a.name);
80
+ #aiFilePartCache = new Map();
81
+ async load(id) {
82
+ return this.#documentFileRepository.load(id);
83
+ }
84
+ async create(content, originalFileName) {
85
+ const contentAsUint8Array = isUint8Array(content) ? content : await readBinaryStream(content);
86
+ const hash = await digest('SHA-256', contentAsUint8Array).toHex();
87
+ const mimeType = await getMimeType(contentAsUint8Array);
88
+ return this.transaction(async (tx) => {
89
+ const documentFile = await this.#documentFileRepository
90
+ .withTransaction(tx)
91
+ .insert({
92
+ originalFileName,
93
+ mimeType,
94
+ hash,
95
+ size: contentAsUint8Array.length,
96
+ });
97
+ const key = getDocumentFileKey(documentFile.id);
98
+ await this.#fileObjectStorage.uploadObject(key, contentAsUint8Array);
99
+ return documentFile;
100
+ });
101
+ }
102
+ async getContent(fileId) {
103
+ const key = getDocumentFileKey(fileId);
104
+ return this.#fileObjectStorage.getContent(key);
105
+ }
106
+ getContentStream(fileId) {
107
+ const key = getDocumentFileKey(fileId);
108
+ return this.#fileObjectStorage.getContentStream(key);
109
+ }
110
+ async getContentUrl(fileId, title, download = false) {
111
+ const file = await this.#documentFileRepository.load(fileId);
112
+ return this.getDocumentFileContentObjectUrl(title ?? fileId, file, download);
113
+ }
114
+ async getPreview(fileId, page = 1) {
115
+ const key = getDocumentFileKey(fileId);
116
+ await this.createPreviewIfNotExists(fileId, page);
117
+ return this.#filePreviewObjectStorage.getContent(key);
118
+ }
119
+ getPreviewStream(fileId, page = 1) {
120
+ return readableStreamFromPromise(async () => {
121
+ const key = getDocumentFileKey(fileId);
122
+ await this.createPreviewIfNotExists(fileId, page);
123
+ return this.#filePreviewObjectStorage.getContentStream(key);
124
+ });
125
+ }
126
+ async getPreviewUrl(fileId, page = 1) {
127
+ const key = getDocumentFileKey(fileId);
128
+ await this.createPreviewIfNotExists(fileId, page);
129
+ return this.#filePreviewObjectStorage.getDownloadUrl(key, currentTimestamp() + (5 * millisecondsPerMinute), {
130
+ 'Response-Content-Type': 'image/jpeg',
131
+ });
132
+ }
133
+ async getAiFileContentPart(documentId) {
134
+ const env_1 = { stack: [], error: void 0, hasError: false };
135
+ try {
136
+ const document = await this.#documentRepository.load(documentId);
137
+ const cachedAiFilePart = this.#aiFilePartCache.get(document.fileId);
138
+ if (isDefined(cachedAiFilePart)) {
139
+ if (cachedAiFilePart.timestamp > (currentTimestamp() - (5 * millisecondsPerMinute))) {
140
+ return cachedAiFilePart.part;
141
+ }
142
+ this.#aiFilePartCache.delete(documentId);
143
+ }
144
+ const file = await this.load(document.fileId);
145
+ const fileContentStream = this.getContentStream(document.id);
146
+ const tmpFile = __addDisposableResource(env_1, await TemporaryFile.from(fileContentStream), true);
147
+ const filePart = await this.#aiService.processFile({ path: tmpFile.path, mimeType: file.mimeType });
148
+ this.#aiFilePartCache.set(document.fileId, { part: filePart, timestamp: currentTimestamp() });
149
+ return filePart;
150
+ }
151
+ catch (e_1) {
152
+ env_1.error = e_1;
153
+ env_1.hasError = true;
154
+ }
155
+ finally {
156
+ const result_1 = __disposeResources(env_1);
157
+ if (result_1)
158
+ await result_1;
159
+ }
160
+ }
161
+ async createPreviewIfNotExists(fileId, page = 1) {
162
+ const key = getDocumentFileKey(fileId);
163
+ const hasPreview = await this.#filePreviewObjectStorage.exists(key);
164
+ if (!hasPreview) {
165
+ const file = await this.#documentFileRepository.load(fileId);
166
+ const content = await this.#fileObjectStorage.getContent(key);
167
+ const image = await match(file.mimeType)
168
+ .with('application/pdf', async () => {
169
+ const imageBytes = await pdfToImage(content, page, 768, 'jpeg');
170
+ return imageToPreview(imageBytes);
171
+ })
172
+ .with('image/*', async () => imageToPreview(content))
173
+ .otherwise(() => { throw new NotImplementedError('Preview generation is not implemented for this file type.'); });
174
+ await this.#filePreviewObjectStorage.uploadObject(key, image, { contentLength: image.length, metadata: { 'Content-Type': 'image/jpeg' } });
175
+ }
176
+ }
177
+ async getDocumentFileContentObjectUrl(title, file, download) {
178
+ const key = getDocumentFileKey(file.id);
179
+ const fileExtension = getMimeTypeExtensions(file.mimeType)[0] ?? 'bin';
180
+ const disposition = download ? 'attachment' : 'inline';
181
+ const filename = `${title}.${fileExtension}`;
182
+ return this.#fileObjectStorage.getDownloadUrl(key, currentTimestamp() + (5 * millisecondsPerMinute), {
183
+ 'Response-Content-Type': file.mimeType,
184
+ 'Response-Content-Disposition': `${disposition}; filename = "${encodeURIComponent(filename)}"`,
185
+ });
186
+ }
187
+ }
188
+ _a = DocumentFileService;
189
+ function getDocumentFileKey(id) {
190
+ return `${id.slice(0, 2)}/${id.slice(0, 4)}/${id}`;
191
+ }
192
+ async function imageToPreview(input) {
193
+ return sharp(input)
194
+ .resize({
195
+ width: 768,
196
+ height: 768,
197
+ fit: 'cover',
198
+ position: 'top',
199
+ withoutEnlargement: true,
200
+ fastShrinkOnLoad: false,
201
+ })
202
+ .toFormat('jpeg', { quality: 75 })
203
+ .toBuffer();
204
+ }
@@ -0,0 +1,22 @@
1
+ import type { DocumentPropertyDataType } from '../../../document-management/models/index.js';
2
+ type DocumentInformationExtractionPropertyResult = {
3
+ propertyId: string;
4
+ dataType: DocumentPropertyDataType;
5
+ value: string | number | boolean;
6
+ };
7
+ export type DocumentInformationExtractionResult = {
8
+ title: string;
9
+ subtitle: string | null;
10
+ date: number | null;
11
+ summary: string;
12
+ tags: string[];
13
+ properties: DocumentInformationExtractionPropertyResult[];
14
+ };
15
+ export declare class DocumentManagementAiService {
16
+ #private;
17
+ classifyDocumentType(documentId: string): Promise<string>;
18
+ extractDocumentInformation(documentId: string): Promise<DocumentInformationExtractionResult>;
19
+ findSuitableCollectionsForDocument(documentId: string, collectionIds: string[]): Promise<string[]>;
20
+ findSuitableRequestForDocument(documentId: string, collectionIds: string[]): Promise<string | null>;
21
+ }
22
+ export {};
@@ -0,0 +1,340 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ var _a;
54
+ import { and, isNull as drizzleIsNull, eq, inArray } from 'drizzle-orm';
55
+ import { P, match } from 'ts-pattern';
56
+ import { AiService } from '../../../ai/index.js';
57
+ import { TemporaryFile } from '../../../file/server/index.js';
58
+ import { inject } from '../../../injector/inject.js';
59
+ import { Logger } from '../../../logger/logger.js';
60
+ import { arrayAgg } from '../../../orm/index.js';
61
+ import { injectRepository } from '../../../orm/server/index.js';
62
+ import { array, boolean, enumeration, integer, nullable, number, object, string } from '../../../schema/index.js';
63
+ import { distinct } from '../../../utils/array/index.js';
64
+ import { dateObjectToNumericDate, numericDateToDateObject } from '../../../utils/date-time.js';
65
+ import { fromEntries, objectEntries } from '../../../utils/object/object.js';
66
+ import { assertDefined, assertDefinedPass, assertNotNull, isNotNull, isNull, isUndefined } from '../../../utils/type-guards.js';
67
+ import { Document, DocumentProperty, DocumentRequestState, DocumentTypeProperty } from '../../models/index.js';
68
+ import { documentCategory, documentRequest, documentRequestCollection, documentType } from '../schemas.js';
69
+ import { DocumentCategoryTypeService } from './document-category-type.service.js';
70
+ import { DocumentFileService } from './document-file.service.js';
71
+ import { DocumentManagementAncillaryService } from './document-management-ancillary.service.js';
72
+ import { DocumentPropertyService } from './document-property.service.js';
73
+ export class DocumentManagementAiService {
74
+ #documentManagementAncillaryService = inject(DocumentManagementAncillaryService);
75
+ #documentCategoryTypeService = inject(DocumentCategoryTypeService);
76
+ #documentFileService = inject(DocumentFileService);
77
+ #documentPropertyService = inject(DocumentPropertyService);
78
+ #aiService = inject(AiService);
79
+ #documentPropertyRepository = injectRepository(DocumentProperty);
80
+ #documentRepository = injectRepository(Document);
81
+ #documentTypePropertyRepository = injectRepository(DocumentTypeProperty);
82
+ #logger = inject(Logger, _a.name);
83
+ async classifyDocumentType(documentId) {
84
+ const env_1 = { stack: [], error: void 0, hasError: false };
85
+ try {
86
+ const document = await this.#documentRepository.load(documentId);
87
+ const file = await this.#documentFileService.load(document.fileId);
88
+ const fileContentStream = this.#documentFileService.getContentStream(document.id);
89
+ const tmpFile = __addDisposableResource(env_1, await TemporaryFile.from(fileContentStream), true);
90
+ const filePart = await this.#aiService.processFile({ path: tmpFile.path, mimeType: file.mimeType });
91
+ const categories = await this.#documentCategoryTypeService.loadCategoryViews();
92
+ const typeLabelEntries = getDescriptiveTypeLabels(categories);
93
+ const typeLabels = typeLabelEntries.map(({ label }) => label);
94
+ this.#logger.trace(`Classifying document ${document.id}`);
95
+ const documentTypeGeneration = await this.#aiService.generate({
96
+ model: 'gemini-2.5-flash-preview-04-17',
97
+ generationOptions: {
98
+ maxOutputTokens: 128,
99
+ temperature: 0.1,
100
+ topP: 0.75,
101
+ topK: 4,
102
+ thinkingBudget: 0,
103
+ },
104
+ generationSchema: object({
105
+ documentType: enumeration(typeLabels),
106
+ }),
107
+ contents: [
108
+ {
109
+ role: 'user',
110
+ parts: [
111
+ { file: filePart.file },
112
+ { text: `Klassifiziere den Inhalt des Dokuments in das angegebenen JSON Schema.` },
113
+ ],
114
+ },
115
+ ],
116
+ });
117
+ const typeId = typeLabelEntries.find((entry) => entry.label == documentTypeGeneration.json.documentType)?.id;
118
+ assertDefined(typeId, `Could not classify document ${document.id}`);
119
+ return typeId;
120
+ }
121
+ catch (e_1) {
122
+ env_1.error = e_1;
123
+ env_1.hasError = true;
124
+ }
125
+ finally {
126
+ const result_1 = __disposeResources(env_1);
127
+ if (result_1)
128
+ await result_1;
129
+ }
130
+ }
131
+ async extractDocumentInformation(documentId) {
132
+ const env_2 = { stack: [], error: void 0, hasError: false };
133
+ try {
134
+ const document = await this.#documentRepository.load(documentId);
135
+ const file = await this.#documentFileService.load(document.fileId);
136
+ const fileContentStream = this.#documentFileService.getContentStream(document.fileId);
137
+ const tmpFile = __addDisposableResource(env_2, await TemporaryFile.from(fileContentStream), true);
138
+ const filePart = await this.#aiService.processFile({ path: tmpFile.path, mimeType: file.mimeType });
139
+ if (isNull(document.typeId)) {
140
+ throw new Error(`Document ${document.id} has no type`);
141
+ }
142
+ const typeProperties = await this.#documentTypePropertyRepository.loadManyByQuery({ typeId: document.typeId });
143
+ const propertyIds = typeProperties.map((property) => property.propertyId);
144
+ const properties = (propertyIds.length > 0) ? await this.#documentPropertyRepository.loadManyByQuery({ id: { $in: propertyIds } }) : undefined;
145
+ const propertiesSchemaEntries = properties?.map((property) => {
146
+ const schema = match(property.dataType)
147
+ .with('text', () => nullable(string()))
148
+ .with('integer', () => nullable(integer()))
149
+ .with('decimal', () => nullable(number()))
150
+ .with('boolean', () => nullable(boolean()))
151
+ .with('date', () => nullable(object({ year: integer(), month: integer(), day: integer() })))
152
+ .exhaustive();
153
+ return [property.label, schema];
154
+ });
155
+ const generationSchema = object({
156
+ documentTitle: string(),
157
+ documentSubtitle: nullable(string()),
158
+ documentSummary: string(),
159
+ documentTags: array(string()),
160
+ documentDate: nullable(object({ year: integer(), month: integer(), day: integer() })),
161
+ ...((isUndefined(propertiesSchemaEntries) || (propertiesSchemaEntries.length == 0))
162
+ ? {}
163
+ : { documentProperties: object(fromEntries(propertiesSchemaEntries)) }),
164
+ });
165
+ this.#logger.trace(`Extracting document ${document.id}`);
166
+ const { json: extraction } = await this.#aiService.generate({
167
+ model: 'gemini-2.5-flash-preview-04-17',
168
+ generationOptions: {
169
+ maxOutputTokens: 2048,
170
+ temperature: 0.2,
171
+ topP: 0.5,
172
+ topK: 16,
173
+ thinkingBudget: 0,
174
+ },
175
+ generationSchema,
176
+ contents: [
177
+ {
178
+ role: 'user',
179
+ parts: [
180
+ { file: filePart.file },
181
+ {
182
+ text: `Extrahiere den Inhalt des Dokuments in das angegebenen JSON Schema.
183
+
184
+ Gib in der summary ausführlich an, welche Informationen in dem Dokument vorkommen (ohne konkrete Werte).
185
+ Erstelle bis zu 7 möglichst spezifische Tags.
186
+ Antworte auf deutsch.`,
187
+ },
188
+ ],
189
+ },
190
+ ],
191
+ });
192
+ const filteredDocumentTags = extraction.documentTags.filter((tag) => (tag != extraction.documentTitle) && (tag != extraction.documentSubtitle));
193
+ const date = isNotNull(extraction.documentDate) ? dateObjectToNumericDate(extraction.documentDate) : null;
194
+ const parsedProperties = isUndefined(extraction.documentProperties)
195
+ ? []
196
+ : objectEntries(extraction.documentProperties)
197
+ .map(([propertyLabel, rawValue]) => {
198
+ if (isNull(rawValue)) {
199
+ return null;
200
+ }
201
+ const property = assertDefinedPass(properties?.find((property) => property.label == propertyLabel));
202
+ const value = match(rawValue)
203
+ .with({ year: P.number }, (val) => dateObjectToNumericDate(val))
204
+ .otherwise((val) => val);
205
+ return { propertyId: property.id, dataType: property.dataType, value };
206
+ })
207
+ .filter(isNotNull);
208
+ return {
209
+ title: extraction.documentTitle,
210
+ subtitle: extraction.documentSubtitle,
211
+ date,
212
+ summary: extraction.documentSummary,
213
+ tags: filteredDocumentTags,
214
+ properties: parsedProperties,
215
+ };
216
+ }
217
+ catch (e_2) {
218
+ env_2.error = e_2;
219
+ env_2.hasError = true;
220
+ }
221
+ finally {
222
+ const result_2 = __disposeResources(env_2);
223
+ if (result_2)
224
+ await result_2;
225
+ }
226
+ }
227
+ async findSuitableCollectionsForDocument(documentId, collectionIds) {
228
+ const document = await this.#documentRepository.load(documentId);
229
+ assertNotNull(document.typeId, 'Document has no type');
230
+ const documentProperties = await this.#documentPropertyService.loadDocumentProperties(documentId);
231
+ const collectionNamesMap = await this.#documentManagementAncillaryService.resolveMetadata(...collectionIds);
232
+ const collections = collectionIds.map((collectionId, index) => ({
233
+ id: collectionId,
234
+ ...assertDefinedPass(collectionNamesMap[index]),
235
+ }));
236
+ const propertyEntries = documentProperties.map((property) => [property.label, property.value]);
237
+ const context = {
238
+ document: {
239
+ title: document.title ?? undefined,
240
+ subtitle: document.subtitle ?? undefined,
241
+ date: isNotNull(document.date) ? numericDateToDateObject(document.date) : undefined,
242
+ summary: document.summary ?? undefined,
243
+ tags: ((document.tags?.length ?? 0) > 0) ? document.tags : undefined,
244
+ properties: fromEntries(propertyEntries),
245
+ },
246
+ collections,
247
+ };
248
+ const result = await this.#aiService.generate({
249
+ model: 'gemini-2.5-flash-preview-04-17',
250
+ generationOptions: {
251
+ maxOutputTokens: 100,
252
+ temperature: 0,
253
+ topP: 0.2,
254
+ topK: 16,
255
+ thinkingBudget: 0,
256
+ },
257
+ generationSchema: object({ collectionIds: array(string()) }),
258
+ contents: [{
259
+ role: 'user',
260
+ parts: [
261
+ {
262
+ text: `<context>
263
+ ${JSON.stringify(context, null, 2)}
264
+ </context>
265
+
266
+ Ordne das Dokument unter "document" einer oder mehreren passenden Collection unter "collections" zu. Gib es als JSON im angegebenen Schema aus. Wenn keine Collection passt, gib collectionIds als leeres Array zurück.`,
267
+ },
268
+ ],
269
+ }],
270
+ });
271
+ return result.json.collectionIds;
272
+ }
273
+ async findSuitableRequestForDocument(documentId, collectionIds) {
274
+ const session = this.#documentPropertyRepository.session;
275
+ const document = await this.#documentRepository.load(documentId);
276
+ assertNotNull(document.typeId, 'Document has no type');
277
+ const documentProperties = await this.#documentPropertyService.loadDocumentProperties(documentId);
278
+ const openRequestsWithoutDocument = await session
279
+ .select({
280
+ id: documentRequest.id,
281
+ collectionIds: arrayAgg(documentRequestCollection.collectionId),
282
+ documentCategory: documentCategory.label,
283
+ documentType: documentType.label,
284
+ comment: documentRequest.comment,
285
+ })
286
+ .from(documentRequest)
287
+ .innerJoin(documentRequestCollection, eq(documentRequestCollection.requestId, documentRequest.id))
288
+ .innerJoin(documentType, eq(documentType.id, documentRequest.typeId))
289
+ .innerJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
290
+ .where(and(inArray(documentRequestCollection.collectionId, collectionIds), eq(documentRequest.typeId, document.typeId), eq(documentRequest.state, DocumentRequestState.Open), drizzleIsNull(documentRequest.documentId)))
291
+ .groupBy(documentRequest.id, documentCategory.label, documentType.label, documentRequest.comment);
292
+ const requestsCollectionIds = distinct(openRequestsWithoutDocument.flatMap((request) => request.collectionIds));
293
+ const collectionNamesMap = await this.#documentManagementAncillaryService.resolveMetadataMap(...requestsCollectionIds);
294
+ const requests = openRequestsWithoutDocument.map((request) => ({
295
+ id: request.id,
296
+ collections: request.collectionIds.map((collectionId) => assertDefinedPass(collectionNamesMap[collectionId]).name),
297
+ comment: request.comment ?? undefined,
298
+ }));
299
+ const propertyEntries = documentProperties.map((property) => [property.label, property.value]);
300
+ const context = {
301
+ document: {
302
+ title: document.title ?? undefined,
303
+ subtitle: document.subtitle ?? undefined,
304
+ date: isNotNull(document.date) ? numericDateToDateObject(document.date) : undefined,
305
+ summary: document.summary ?? undefined,
306
+ tags: ((document.tags?.length ?? 0) > 0) ? document.tags : undefined,
307
+ properties: fromEntries(propertyEntries),
308
+ },
309
+ requests,
310
+ };
311
+ const result = await this.#aiService.generate({
312
+ model: 'gemini-2.5-flash-preview-04-17',
313
+ generationOptions: {
314
+ maxOutputTokens: 100,
315
+ temperature: 0,
316
+ topP: 0.2,
317
+ topK: 16,
318
+ thinkingBudget: 0,
319
+ },
320
+ generationSchema: object({ requestId: nullable(string()) }),
321
+ contents: [{
322
+ role: 'user',
323
+ parts: [
324
+ {
325
+ text: `<context>
326
+ ${JSON.stringify(context, null, 2)}
327
+ </context>
328
+
329
+ Ordne das Dokument unter "document" der passenden Anforderungen unter "requests" zu. Gib es als JSON im angegebenen Schema aus. Wenn keine Anforderung passt, setze requestId auf null.`,
330
+ },
331
+ ],
332
+ }],
333
+ });
334
+ return result.json.requestId;
335
+ }
336
+ }
337
+ _a = DocumentManagementAiService;
338
+ function getDescriptiveTypeLabels(categories, prefix = 'Category: ') {
339
+ return categories.flatMap((category) => [...category.types.map((type) => ({ id: type.id, label: `${prefix}${category.label} | Type: ${type.label}` }), ...getDescriptiveTypeLabels(category.children, `${prefix}${category.label} -> `))]);
340
+ }