@tstdl/base 0.93.125 → 0.93.127

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 (198) hide show
  1. package/ai/genkit/tests/multi-region.test.js +6 -6
  2. package/ai/index.d.ts +2 -6
  3. package/ai/index.js +2 -6
  4. package/ai/parser/index.d.ts +1 -0
  5. package/ai/parser/index.js +1 -0
  6. package/ai/parser/parser.d.ts +12 -0
  7. package/ai/parser/parser.js +28 -0
  8. package/ai/prompts/build.d.ts +21 -0
  9. package/ai/prompts/build.js +25 -0
  10. package/ai/prompts/index.d.ts +2 -0
  11. package/ai/prompts/index.js +2 -0
  12. package/ai/prompts/instructions-formatter.d.ts +9 -22
  13. package/ai/prompts/instructions-formatter.js +20 -7
  14. package/ai/prompts/instructions.js +1 -1
  15. package/ai/prompts/steering.d.ts +27 -0
  16. package/ai/prompts/steering.js +54 -0
  17. package/ai/tests/instructions-formatter.test.js +115 -0
  18. package/ai/tests/steering.test.js +37 -0
  19. package/application/application.d.ts +2 -1
  20. package/application/application.js +3 -0
  21. package/authentication/client/module.d.ts +1 -1
  22. package/authentication/client/module.js +4 -5
  23. package/authentication/tests/authentication-ancillary.service.test.js +1 -1
  24. package/authentication/tests/authentication.api-controller.test.js +3 -1
  25. package/authentication/tests/authentication.api-request-token.provider.test.js +1 -1
  26. package/authentication/tests/authentication.client-service.test.js +1 -1
  27. package/authentication/tests/authentication.service.test.js +1 -1
  28. package/authentication/tests/subject.service.test.js +1 -1
  29. package/circuit-breaker/tests/circuit-breaker.test.js +1 -1
  30. package/document-management/api/document-management.api.d.ts +16 -16
  31. package/document-management/api/document-management.api.js +12 -12
  32. package/document-management/models/ai-configuration.d.ts +59 -0
  33. package/document-management/models/ai-configuration.js +1 -0
  34. package/document-management/models/document-assignment-scope.model.js +2 -4
  35. package/document-management/models/document-assignment-task.model.js +2 -4
  36. package/document-management/models/document-collection-assignment.model.js +2 -4
  37. package/document-management/models/document-collection.model.js +2 -3
  38. package/document-management/models/document-content.model.d.ts +6 -0
  39. package/document-management/models/document-content.model.js +32 -0
  40. package/document-management/models/document-property-value.model.js +1 -2
  41. package/document-management/models/document-request-collection-assignment.model.js +2 -4
  42. package/document-management/models/document-request.model.js +2 -4
  43. package/document-management/models/document-tag-assignment.model.js +2 -3
  44. package/document-management/models/document-validation-execution-related-document.model.js +2 -4
  45. package/document-management/models/document-validation-execution.model.js +2 -5
  46. package/document-management/models/document-workflow.model.d.ts +2 -1
  47. package/document-management/models/document-workflow.model.js +4 -5
  48. package/document-management/models/document.model.js +2 -3
  49. package/document-management/models/index.d.ts +2 -0
  50. package/document-management/models/index.js +2 -0
  51. package/document-management/server/api/document-management.api.d.ts +7 -7
  52. package/document-management/server/api/document-management.api.js +9 -9
  53. package/document-management/server/configure.d.ts +4 -1
  54. package/document-management/server/configure.js +9 -4
  55. package/document-management/server/drizzle/{0000_complex_black_bird.sql → 0000_curious_nighthawk.sql} +7 -27
  56. package/document-management/server/drizzle/meta/0000_snapshot.json +12 -284
  57. package/document-management/server/drizzle/meta/_journal.json +2 -2
  58. package/document-management/server/module.d.ts +2 -0
  59. package/document-management/server/module.js +1 -0
  60. package/document-management/server/schemas.d.ts +2 -1
  61. package/document-management/server/services/document-file.service.d.ts +6 -6
  62. package/document-management/server/services/document-file.service.js +7 -81
  63. package/document-management/server/services/document-management-ai-provider.service.d.ts +66 -0
  64. package/document-management/server/services/document-management-ai-provider.service.js +2 -0
  65. package/document-management/server/services/document-management-ai.service.d.ts +44 -7
  66. package/document-management/server/services/document-management-ai.service.js +332 -329
  67. package/document-management/server/services/document-validation.service.d.ts +1 -1
  68. package/document-management/server/services/document-workflow.service.d.ts +4 -3
  69. package/document-management/server/services/document-workflow.service.js +26 -9
  70. package/document-management/server/services/document.service.d.ts +7 -3
  71. package/document-management/server/services/document.service.js +13 -4
  72. package/document-management/server/services/index.d.ts +1 -0
  73. package/document-management/server/services/index.js +1 -0
  74. package/document-management/server/validators/ai-validation-executor.d.ts +419 -12
  75. package/document-management/server/validators/ai-validation-executor.js +51 -46
  76. package/document-management/server/validators/single-document-validation-executor.d.ts +1 -3
  77. package/document-management/server/validators/single-document-validation-executor.js +2 -4
  78. package/document-management/service-models/document.service-model.d.ts +3 -3
  79. package/document-management/service-models/document.service-model.js +1 -1
  80. package/document-management/tests/ai-config-hierarchy.test.d.ts +1 -0
  81. package/document-management/tests/ai-config-hierarchy.test.js +64 -0
  82. package/document-management/tests/ai-config-integration.test.d.ts +1 -0
  83. package/document-management/tests/ai-config-integration.test.js +125 -0
  84. package/document-management/tests/ai-config-merge.test.d.ts +1 -0
  85. package/document-management/tests/ai-config-merge.test.js +38 -0
  86. package/document-management/tests/document-management-ai-overrides.test.d.ts +1 -0
  87. package/document-management/tests/document-management-ai-overrides.test.js +64 -0
  88. package/document-management/tests/document-management-core.test.js +6 -6
  89. package/document-management/tests/document-management.api.test.js +5 -5
  90. package/document-management/tests/document-statistics.service.test.js +10 -6
  91. package/document-management/tests/document-validation-ai-overrides.test.d.ts +1 -0
  92. package/document-management/tests/document-validation-ai-overrides.test.js +85 -0
  93. package/document-management/tests/document.service.test.js +15 -11
  94. package/document-management/tests/enum-helpers.test.js +5 -5
  95. package/examples/document-management/ai-provider.d.ts +20 -0
  96. package/examples/document-management/ai-provider.js +74 -0
  97. package/examples/document-management/main.js +9 -6
  98. package/examples/injector/graph-example.d.ts +1 -0
  99. package/examples/injector/graph-example.js +340 -0
  100. package/injector/decorators.d.ts +4 -4
  101. package/injector/decorators.js +5 -6
  102. package/injector/forward-ref.d.ts +15 -0
  103. package/injector/forward-ref.js +20 -0
  104. package/injector/graph.d.ts +113 -0
  105. package/injector/graph.js +652 -0
  106. package/injector/index.d.ts +2 -0
  107. package/injector/index.js +2 -0
  108. package/injector/inject.d.ts +15 -15
  109. package/injector/injector.d.ts +101 -13
  110. package/injector/injector.js +103 -59
  111. package/injector/resolve-chain.d.ts +20 -6
  112. package/injector/resolve-chain.js +39 -14
  113. package/injector/tests/advanced.test.d.ts +1 -0
  114. package/injector/tests/advanced.test.js +116 -0
  115. package/injector/tests/async-init.test.d.ts +1 -0
  116. package/injector/tests/async-init.test.js +77 -0
  117. package/injector/tests/basic.test.d.ts +1 -0
  118. package/injector/tests/basic.test.js +114 -0
  119. package/injector/tests/hierarchical.test.d.ts +1 -0
  120. package/injector/tests/hierarchical.test.js +59 -0
  121. package/injector/tests/lifecycles.test.d.ts +1 -0
  122. package/injector/tests/lifecycles.test.js +109 -0
  123. package/injector/token.d.ts +2 -1
  124. package/injector/token.js +4 -1
  125. package/injector/type-info.d.ts +1 -5
  126. package/injector/types.d.ts +4 -10
  127. package/logger/tests/pretty-print.test.d.ts +1 -0
  128. package/logger/{formatters → tests}/pretty-print.test.js +1 -1
  129. package/logger/transports/console.d.ts +3 -2
  130. package/logger/transports/console.js +4 -3
  131. package/notification/tests/notification-api.test.js +8 -5
  132. package/notification/tests/notification-client.test.d.ts +1 -0
  133. package/notification/tests/{unit/notification-client.test.js → notification-client.test.js} +5 -5
  134. package/notification/tests/notification-flow.test.js +6 -5
  135. package/notification/tests/notification-sse.service.test.js +1 -1
  136. package/notification/tests/notification-type.service.test.js +1 -1
  137. package/object-storage/s3/s3.object-storage.js +3 -0
  138. package/object-storage/s3/tests/s3.object-storage.integration.test.js +1 -1
  139. package/orm/tests/repository-attributes.test.js +10 -17
  140. package/orm/tests/repository-cti-mapping.test.js +2 -2
  141. package/orm/tests/repository-cti-soft-delete.test.js +1 -1
  142. package/orm/tests/repository-cti.test.js +19 -33
  143. package/orm/tests/repository-extra-coverage.test.js +1 -1
  144. package/orm/tests/repository-search.test.js +5 -2
  145. package/orm/tests/transaction-safety.test.js +1 -1
  146. package/package.json +7 -9
  147. package/rate-limit/tests/postgres-rate-limiter.test.js +6 -16
  148. package/renderer/d2.d.ts +77 -0
  149. package/renderer/d2.js +68 -0
  150. package/renderer/graphviz.d.ts +47 -0
  151. package/renderer/graphviz.js +58 -0
  152. package/renderer/index.d.ts +4 -0
  153. package/renderer/index.js +4 -0
  154. package/renderer/typst.d.ts +57 -0
  155. package/renderer/typst.js +62 -0
  156. package/rpc/adapters/readable-stream.adapter.d.ts +3 -0
  157. package/rpc/adapters/readable-stream.adapter.js +5 -1
  158. package/rpc/rpc.js +28 -3
  159. package/rpc/tests/rpc.integration.test.js +3 -1
  160. package/schema/schemas/nullable.js +1 -1
  161. package/task-queue/task-queue.d.ts +2 -0
  162. package/task-queue/task-queue.js +6 -2
  163. package/task-queue/tests/complex.test.js +1 -1
  164. package/task-queue/tests/dependencies.test.js +3 -3
  165. package/task-queue/tests/extensive-dependencies.test.js +1 -1
  166. package/task-queue/tests/queue.test.js +1 -1
  167. package/task-queue/tests/worker.test.js +4 -7
  168. package/test5.js +52 -8
  169. package/{unit-test → testing}/integration-setup.d.ts +1 -0
  170. package/{unit-test → testing}/integration-setup.js +13 -0
  171. package/utils/base64.d.ts +7 -0
  172. package/utils/base64.js +10 -1
  173. package/utils/noop.d.ts +7 -1
  174. package/utils/noop.js +7 -1
  175. package/ai/ai-file.service.d.ts +0 -57
  176. package/ai/ai-file.service.js +0 -233
  177. package/ai/ai-session.d.ts +0 -38
  178. package/ai/ai-session.js +0 -50
  179. package/ai/ai.service.d.ts +0 -126
  180. package/ai/ai.service.js +0 -481
  181. package/ai/functions.d.ts +0 -9
  182. package/ai/functions.js +0 -38
  183. package/ai/module.d.ts +0 -26
  184. package/ai/module.js +0 -25
  185. package/ai/types.d.ts +0 -229
  186. package/ai/types.js +0 -33
  187. package/latex/index.d.ts +0 -1
  188. package/latex/index.js +0 -1
  189. package/typst/index.d.ts +0 -1
  190. package/typst/index.js +0 -1
  191. package/typst/render.d.ts +0 -23
  192. package/typst/render.js +0 -32
  193. /package/{logger/formatters/pretty-print.test.d.ts → ai/tests/instructions-formatter.test.d.ts} +0 -0
  194. /package/{notification/tests/unit/notification-client.test.d.ts → ai/tests/steering.test.d.ts} +0 -0
  195. /package/{latex/render.d.ts → renderer/latex.d.ts} +0 -0
  196. /package/{latex/render.js → renderer/latex.js} +0 -0
  197. /package/{unit-test → testing}/index.d.ts +0 -0
  198. /package/{unit-test → testing}/index.js +0 -0
@@ -1,5 +1,5 @@
1
1
  import { defineEnum } from '../enumeration/enumeration.js';
2
- import { inject, injectArgument } from '../injector/index.js';
2
+ import { inject, injectArgument, Injector } from '../injector/index.js';
3
3
  import { Logger } from '../logger/logger.js';
4
4
  import { Transactional } from '../orm/server/transactional.js';
5
5
  import { currentTimestamp } from '../utils/date-time.js';
@@ -78,6 +78,7 @@ export const defaultQueueConfig = {
78
78
  idempotencyWindow: millisecondsPerMinute * 60,
79
79
  };
80
80
  export class TaskQueue extends Transactional {
81
+ injector = inject(Injector);
81
82
  config = this.transactionalContextData ?? (() => { const arg = injectArgument(this); return isString(arg) ? { namespace: arg } : arg; })();
82
83
  logger = inject(Logger, TaskQueue.name).with({ namespace: this.config.namespace });
83
84
  batch() {
@@ -87,9 +88,12 @@ export class TaskQueue extends Transactional {
87
88
  * Starts processing tasks with the provided worker function in the background until the cancellation signal is triggered.
88
89
  */
89
90
  process({ concurrency = 1, cancellationSignal, types, forceDequeue }, handler) {
91
+ const promises = [];
90
92
  for (let i = 0; i < concurrency; i++) {
91
- void this.processWorker(cancellationSignal, handler, { types, forceDequeue });
93
+ const processPromise = this.processWorker(cancellationSignal, handler, { types, forceDequeue });
94
+ promises.push(processPromise);
92
95
  }
96
+ this.injector.addDisposeHandler(async () => await Promise.all(promises));
93
97
  }
94
98
  getTransactionalContextData() {
95
99
  return this.config;
@@ -1,6 +1,6 @@
1
1
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
2
  import { DependencyJoinMode, TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
3
- import { setupIntegrationTest } from '../../unit-test/index.js';
3
+ import { setupIntegrationTest } from '../../testing/index.js';
4
4
  import { currentTimestamp } from '../../utils/date-time.js';
5
5
  import { timeout } from '../../utils/timing.js';
6
6
  describe('Complex Queue Scenarios', () => {
@@ -1,7 +1,7 @@
1
1
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
2
  import { CancellationToken } from '../../cancellation/index.js';
3
3
  import { DependencyJoinMode, TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
4
- import { setupIntegrationTest } from '../../unit-test/index.js';
4
+ import { setupIntegrationTest } from '../../testing/index.js';
5
5
  import { timeout } from '../../utils/timing.js';
6
6
  describe('Queue Dependencies & Tree Tests', () => {
7
7
  let injector;
@@ -147,9 +147,9 @@ describe('Queue Dependencies & Tree Tests', () => {
147
147
  const token = new CancellationToken();
148
148
  const consumer = queue.getConsumer(token);
149
149
  const t1 = (await consumer.next()).value;
150
- expect(t1.data.val).toBe(1);
150
+ expect(t1.data['val']).toBe(1);
151
151
  const t2 = (await consumer.next()).value;
152
- expect(t2.data.val).toBe(2);
152
+ expect(t2.data['val']).toBe(2);
153
153
  token.set(); // Stop consumer
154
154
  const t3 = await consumer.next();
155
155
  expect(t3.done).toBe(true);
@@ -1,6 +1,6 @@
1
1
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
2
  import { DependencyJoinMode, TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
3
- import { setupIntegrationTest } from '../../unit-test/index.js';
3
+ import { setupIntegrationTest } from '../../testing/index.js';
4
4
  import { timeout } from '../../utils/timing.js';
5
5
  describe('Extensive Task Queue Dependency Tests', () => {
6
6
  let injector;
@@ -1,6 +1,6 @@
1
1
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
2
  import { TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
3
- import { setupIntegrationTest } from '../../unit-test/index.js';
3
+ import { setupIntegrationTest } from '../../testing/index.js';
4
4
  import { currentTimestamp } from '../../utils/date-time.js';
5
5
  import { timeout } from '../../utils/timing.js';
6
6
  describe('Queue Integration Tests', () => {
@@ -1,11 +1,12 @@
1
1
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
2
  import { CancellationToken } from '../../cancellation/index.js';
3
3
  import { TaskProcessResult, TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
4
- import { setupIntegrationTest } from '../../unit-test/index.js';
4
+ import { setupIntegrationTest } from '../../testing/index.js';
5
5
  import { timeout } from '../../utils/timing.js';
6
6
  describe('Worker & Base Class Tests', () => {
7
7
  let injector;
8
8
  let queue;
9
+ let token;
9
10
  beforeAll(async () => {
10
11
  ({ injector } = await setupIntegrationTest({ modules: { taskQueue: true } }));
11
12
  });
@@ -15,8 +16,10 @@ describe('Worker & Base Class Tests', () => {
15
16
  queue = queueProvider.get(queueName, {
16
17
  visibilityTimeout: 500, // Short visibility for testing lease loss
17
18
  });
19
+ token = new CancellationToken();
18
20
  });
19
21
  afterEach(async () => {
22
+ token.set();
20
23
  await queue.clear();
21
24
  const queueProvider = injector.resolve(TaskQueueProvider);
22
25
  await queueProvider.get('other-queue').clear();
@@ -28,7 +31,6 @@ describe('Worker & Base Class Tests', () => {
28
31
  const t1 = await queue.enqueue('work', { val: 1 });
29
32
  const t2 = await queue.enqueue('work', { val: 2 });
30
33
  const processed = [];
31
- const token = new CancellationToken();
32
34
  queue.process({ cancellationSignal: token }, async (context) => {
33
35
  processed.push(context.data['val']);
34
36
  return TaskProcessResult.Complete();
@@ -50,7 +52,6 @@ describe('Worker & Base Class Tests', () => {
50
52
  });
51
53
  it('should handle errors in worker gracefully', async () => {
52
54
  const task = await queue.enqueue('fail', {});
53
- const token = new CancellationToken();
54
55
  queue.process({ cancellationSignal: token }, async () => {
55
56
  throw new Error('worker error');
56
57
  });
@@ -63,7 +64,6 @@ describe('Worker & Base Class Tests', () => {
63
64
  });
64
65
  it('should extend lease (heartbeat) during long processing', async () => {
65
66
  const task = await queue.enqueue('long', {});
66
- const token = new CancellationToken();
67
67
  let executed = false;
68
68
  queue.process({ cancellationSignal: token }, async (_context) => {
69
69
  // Simulate long work > visibilityTimeout (500ms)
@@ -80,7 +80,6 @@ describe('Worker & Base Class Tests', () => {
80
80
  it('should handle TaskResult actions (Fail, Reschedule)', async () => {
81
81
  const tFail = await queue.enqueue('fail-action', {});
82
82
  const tResched = await queue.enqueue('resched-action', {});
83
- const token = new CancellationToken();
84
83
  const processed = new Set();
85
84
  queue.process({ cancellationSignal: token }, async (context) => {
86
85
  processed.add(context.id);
@@ -107,7 +106,6 @@ describe('Worker & Base Class Tests', () => {
107
106
  });
108
107
  it('should exercise TaskContext methods', async () => {
109
108
  const task = await queue.enqueue('context-test', { val: 1 });
110
- const token = new CancellationToken();
111
109
  let executed = false;
112
110
  queue.process({ cancellationSignal: token, types: ['context-test'] }, async (context) => {
113
111
  expect(context.id).toBe(task.id);
@@ -149,7 +147,6 @@ describe('Worker & Base Class Tests', () => {
149
147
  });
150
148
  await testQueue.enqueue('work', {});
151
149
  testQueue.notify();
152
- const token = new CancellationToken();
153
150
  const finalAttemptValues = [];
154
151
  testQueue.process({ cancellationSignal: token }, async (context) => {
155
152
  finalAttemptValues.push(context.isFinalAttempt);
package/test5.js CHANGED
@@ -1,17 +1,61 @@
1
1
  import './polyfills.js';
2
- import { writeFile } from 'node:fs/promises';
2
+ import { buildPrompts, orderedList } from './ai/index.js';
3
+ import { fewShotPrompt } from './ai/prompts/steering.js';
3
4
  import { Application } from './application/application.js';
4
5
  import { provideModule, provideSignalHandler } from './application/index.js';
5
6
  import { PrettyPrintLogFormatter } from './logger/index.js';
6
7
  import { provideConsoleLogTransport } from './logger/transports/console.js';
7
- import { renderTypst } from './typst/render.js';
8
- const template = `
9
- I got an ice cream for
10
- \\$1.50! \\u{1f600}
11
- `;
12
8
  async function main(_cancellationSignal) {
13
- const pdfBytes = await renderTypst(template, { format: 'docx' });
14
- await writeFile('/tmp/output.docx', pdfBytes);
9
+ const prompt = buildPrompts({
10
+ baseSystemInstructions: { Role: 'You are a helpful assistant.' },
11
+ baseUserInstructions: 'Please process the following data.',
12
+ additionalSystemInstructions: 'Make sure to follow the user instructions carefully.',
13
+ additionalUserInstructions: [
14
+ 'The data is in JSON format.',
15
+ fewShotPrompt([
16
+ {
17
+ input: { a: 1, b: 2 },
18
+ output: 3,
19
+ reason: 'This is a positive example showing that the function should add the two numbers.',
20
+ },
21
+ {
22
+ input: { a: 1, b: 2 },
23
+ output: 4,
24
+ isNegative: true,
25
+ reason: 'This is a negative example showing that the function should NOT return 4 for these inputs.',
26
+ },
27
+ ]),
28
+ {
29
+ Foo: {
30
+ Bar: 'This is a nested instruction example.',
31
+ Baz: {
32
+ Qux: 'Make sure to handle nested instructions properly.',
33
+ Bux: orderedList('This is an ordered list of instructions', [
34
+ 'First instruction',
35
+ 'Second instruction',
36
+ 'Third instruction',
37
+ ]),
38
+ },
39
+ },
40
+ },
41
+ ],
42
+ data: {
43
+ context: {
44
+ task: 'Our favorite hikes together',
45
+ location: 'Boulder',
46
+ season: 'spring_2025',
47
+ },
48
+ friends: ['ana', 'luis', 'sam'],
49
+ hikes: [
50
+ { id: 1, name: 'Blue Lake Trail', distanceKm: 7.5, elevationGain: 320, companion: 'ana', wasSunny: true },
51
+ { id: 2, name: 'Ridge Overlook', distanceKm: 9.2, elevationGain: 540, companion: 'luis', wasSunny: false },
52
+ { id: 3, name: 'Wildflower Loop', distanceKm: 5.1, elevationGain: 180, companion: 'sam', wasSunny: true },
53
+ ],
54
+ },
55
+ });
56
+ console.log(prompt.systemPrompt[0].text);
57
+ console.log();
58
+ console.log(prompt.userPrompt[0].text);
15
59
  }
16
60
  Application.run('Test', [
17
61
  provideConsoleLogTransport(PrettyPrintLogFormatter),
@@ -19,6 +19,7 @@ export type IntegrationTestOptions = {
19
19
  audit?: boolean;
20
20
  authentication?: boolean;
21
21
  circuitBreaker?: boolean;
22
+ documentManagement?: boolean;
22
23
  keyValueStore?: boolean;
23
24
  lock?: boolean;
24
25
  messageBus?: boolean;
@@ -5,6 +5,7 @@ import { AuthenticationApiClient } from '../authentication/client/api.client.js'
5
5
  import { configureAuthenticationClient } from '../authentication/client/index.js';
6
6
  import { AuthenticationApiController, configureAuthenticationServer, migrateAuthenticationSchema } from '../authentication/server/index.js';
7
7
  import { configurePostgresCircuitBreaker, migratePostgresCircuitBreaker } from '../circuit-breaker/postgres/module.js';
8
+ import { configureDocumentManagement, migrateDocumentManagementSchema } from '../document-management/server/index.js';
8
9
  import { configureUndiciHttpClientAdapter } from '../http/client/adapters/undici.adapter.js';
9
10
  import { configureHttpClient } from '../http/client/index.js';
10
11
  import { HttpServer } from '../http/server/index.js';
@@ -107,6 +108,18 @@ export async function setupIntegrationTest(options = {}) {
107
108
  configureNotification({ injector });
108
109
  await runInInjectionContext(injector, migrateNotificationSchema);
109
110
  }
111
+ if (options.modules?.documentManagement) {
112
+ configureDocumentManagement({
113
+ ancillaryService: undefined, // Should be overridden by test if needed
114
+ authorizationService: undefined, // Should be overridden by test if needed
115
+ fileObjectStorageModule: 'docs',
116
+ fileUploadObjectStorageModule: 'uploads',
117
+ filePreviewObjectStorageModule: 'previews',
118
+ skipAi: true,
119
+ injector,
120
+ });
121
+ await runInInjectionContext(injector, migrateDocumentManagementSchema);
122
+ }
110
123
  if (options.modules?.objectStorage) {
111
124
  configureS3ObjectStorage({
112
125
  endpoint: configParser.string('S3_ENDPOINT', 'http://127.0.0.1:9000'),
package/utils/base64.d.ts CHANGED
@@ -1,4 +1,11 @@
1
1
  import type { BinaryData } from '../types/index.js';
2
+ /**
3
+ * Converts data to a base64 data URL.
4
+ * @param mimeType The MIME type of the data.
5
+ * @param data The data as Uint8Array.
6
+ * @returns A base64 data URL.
7
+ */
8
+ export declare function encodeDataUrl(mimeType: string, data: Uint8Array): string;
2
9
  export declare function encodeBase64(array: BinaryData, bytesOffset?: number, bytesLength?: number): string;
3
10
  export declare function decodeBase64(base64: string): Uint8Array<ArrayBuffer>;
4
11
  export declare function encodeBase64Url(array: BinaryData, bytesOffset?: number, length?: number): string;
package/utils/base64.js CHANGED
@@ -1,7 +1,16 @@
1
- /* eslint-disable @typescript-eslint/no-magic-numbers, no-bitwise */
2
1
  import { supportsBuffer } from '../supports.js';
3
2
  import { toUint8Array } from './binary.js';
4
3
  import { isArrayBufferLike, isDefined } from './type-guards.js';
4
+ /**
5
+ * Converts data to a base64 data URL.
6
+ * @param mimeType The MIME type of the data.
7
+ * @param data The data as Uint8Array.
8
+ * @returns A base64 data URL.
9
+ */
10
+ export function encodeDataUrl(mimeType, data) {
11
+ const base64 = encodeBase64(data);
12
+ return `data:${mimeType};base64,${base64}`;
13
+ }
5
14
  export function encodeBase64(array, bytesOffset, bytesLength) {
6
15
  let arrayBuffer;
7
16
  let offset;
package/utils/noop.d.ts CHANGED
@@ -1,2 +1,8 @@
1
+ /**
2
+ * A no-operation function that does nothing. Useful as a default placeholder for optional callbacks or functions.
3
+ */
1
4
  export declare function noop(): void;
2
- export declare function noopPass<T>(value: T): T;
5
+ /**
6
+ * A passthrough function that returns the provided value. Useful as a default for optional transformation functions.
7
+ */
8
+ export declare function passthrough<T>(value: T): T;
package/utils/noop.js CHANGED
@@ -1,6 +1,12 @@
1
+ /**
2
+ * A no-operation function that does nothing. Useful as a default placeholder for optional callbacks or functions.
3
+ */
1
4
  export function noop() {
2
5
  // noop
3
6
  }
4
- export function noopPass(value) {
7
+ /**
8
+ * A passthrough function that returns the provided value. Useful as a default for optional transformation functions.
9
+ */
10
+ export function passthrough(value) {
5
11
  return value;
6
12
  }
@@ -1,57 +0,0 @@
1
- import type { Resolvable, resolveArgumentType } from '../injector/interfaces.js';
2
- import { AiModuleOptions } from './module.js';
3
- import { type FileContentPart, type FileInput } from './types.js';
4
- /**
5
- * Options for {@link AiFileService}.
6
- */
7
- export type AiFileServiceOptions = Pick<AiModuleOptions, 'apiKey' | 'keyFile' | 'vertex'>;
8
- export type AiFileServiceArgument = AiFileServiceOptions;
9
- type File = {
10
- id: string;
11
- name: string;
12
- uri: string;
13
- mimeType: string;
14
- };
15
- /**
16
- * Manages file uploads and state for use with AI models.
17
- * Handles both Google Generative AI File API and Google Cloud Storage for Vertex AI.
18
- */
19
- export declare class AiFileService implements Resolvable<AiFileServiceArgument> {
20
- #private;
21
- readonly [resolveArgumentType]: AiFileServiceArgument;
22
- /**
23
- * Uploads and processes a single file, making it available for AI model consumption.
24
- * @param fileInput The file to process.
25
- * @returns A promise that resolves to a {@link FileContentPart} for use in AI requests.
26
- */
27
- processFile(fileInput: FileInput): Promise<FileContentPart>;
28
- /**
29
- * Uploads and processes multiple files in parallel, making them available for AI model consumption.
30
- * @param fileInputs The files to process.
31
- * @returns A promise that resolves to an array of {@link FileContentPart} for use in AI requests.
32
- */
33
- processFiles(fileInputs: FileInput[]): Promise<FileContentPart[]>;
34
- /**
35
- * Retrieves a file by its internal ID.
36
- * The file must have been processed by this service instance before.
37
- * @param id The internal ID of the file.
38
- * @returns The file, or `undefined` if not found.
39
- */
40
- getFileById(id: string): File | undefined;
41
- /**
42
- * Retrieves a file by its URI (e.g., GCS URI).
43
- * The file must have been processed by this service instance before.
44
- * @param uri The URI of the file.
45
- * @returns The file, or `undefined` if not found.
46
- */
47
- getFileByUri(uri: string): File | undefined;
48
- private getFile;
49
- private getFiles;
50
- private uploadFile;
51
- private uploadFileVertex;
52
- private uploadFileGenAi;
53
- private getBucket;
54
- private waitForFileActive;
55
- private waitForFilesActive;
56
- }
57
- export {};
@@ -1,233 +0,0 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- import { createReadStream } from 'node:fs';
8
- import { stat } from 'node:fs/promises';
9
- import { Readable } from 'node:stream';
10
- import { ReadableStream as NodeReadableStream } from 'node:stream/web';
11
- import { Storage } from '@google-cloud/storage';
12
- import { FileState, GoogleGenAI } from '@google/genai';
13
- import { CancellationSignal } from '../cancellation/token.js';
14
- import { AsyncEnumerable } from '../enumerable/async-enumerable.js';
15
- import { DetailsError } from '../errors/details.error.js';
16
- import { Singleton } from '../injector/decorators.js';
17
- import { inject, injectArgument } from '../injector/inject.js';
18
- import { Logger } from '../logger/logger.js';
19
- import { createArray } from '../utils/array/array.js';
20
- import { backoffGenerator } from '../utils/backoff.js';
21
- import { formatBytes } from '../utils/format.js';
22
- import { readBinaryStream } from '../utils/stream/stream-reader.js';
23
- import { assertDefinedPass, isBlob, isDefined, isUndefined } from '../utils/type-guards.js';
24
- import { AiModuleOptions } from './module.js';
25
- import { isPathFileInput } from './types.js';
26
- /**
27
- * Manages file uploads and state for use with AI models.
28
- * Handles both Google Generative AI File API and Google Cloud Storage for Vertex AI.
29
- */
30
- let AiFileService = class AiFileService {
31
- #options = injectArgument(this, { optional: true }) ?? inject(AiModuleOptions);
32
- #genAI = new GoogleGenAI({
33
- vertexai: isDefined(this.#options.vertex?.project),
34
- project: this.#options.vertex?.project,
35
- location: this.#options.vertex?.location,
36
- googleAuthOptions: isDefined(this.#options.vertex?.project) ? { apiKey: this.#options.apiKey, keyFile: this.#options.keyFile } : undefined,
37
- apiKey: isUndefined(this.#options.vertex?.project) ? assertDefinedPass(this.#options.apiKey, 'Api key not defined') : undefined,
38
- });
39
- #storage = isDefined(this.#options.vertex) ? new Storage({ keyFile: assertDefinedPass(this.#options.keyFile, 'Key file not defined'), projectId: this.#options.vertex.project }) : undefined;
40
- #fileMap = new Map();
41
- #fileUriMap = new Map();
42
- #logger = inject(Logger, 'AiFileService');
43
- #cancellationSignal = inject(CancellationSignal);
44
- #backoffOptions = {
45
- cancellationSignal: this.#cancellationSignal,
46
- strategy: 'linear',
47
- initialDelay: 1000,
48
- increase: 500,
49
- jitter: 0.2,
50
- maximumDelay: 10000,
51
- };
52
- #bucket;
53
- /**
54
- * Uploads and processes a single file, making it available for AI model consumption.
55
- * @param fileInput The file to process.
56
- * @returns A promise that resolves to a {@link FileContentPart} for use in AI requests.
57
- */
58
- async processFile(fileInput) {
59
- const file = await this.getFile(fileInput);
60
- this.#fileMap.set(file.id, file);
61
- this.#fileUriMap.set(file.uri, file);
62
- return { file: file.id };
63
- }
64
- /**
65
- * Uploads and processes multiple files in parallel, making them available for AI model consumption.
66
- * @param fileInputs The files to process.
67
- * @returns A promise that resolves to an array of {@link FileContentPart} for use in AI requests.
68
- */
69
- async processFiles(fileInputs) {
70
- const files = await this.getFiles(fileInputs);
71
- return files.map((file) => {
72
- this.#fileMap.set(file.id, file);
73
- this.#fileUriMap.set(file.uri, file);
74
- return { file: file.id };
75
- });
76
- }
77
- /**
78
- * Retrieves a file by its internal ID.
79
- * The file must have been processed by this service instance before.
80
- * @param id The internal ID of the file.
81
- * @returns The file, or `undefined` if not found.
82
- */
83
- getFileById(id) {
84
- return this.#fileMap.get(id);
85
- }
86
- /**
87
- * Retrieves a file by its URI (e.g., GCS URI).
88
- * The file must have been processed by this service instance before.
89
- * @param uri The URI of the file.
90
- * @returns The file, or `undefined` if not found.
91
- */
92
- getFileByUri(uri) {
93
- return this.#fileUriMap.get(uri);
94
- }
95
- async getFile(fileInput) {
96
- const id = crypto.randomUUID();
97
- const file = await this.uploadFile(fileInput, id);
98
- this.#logger.verbose(`Processing file "${id}"...`);
99
- await this.waitForFileActive(file);
100
- return file;
101
- }
102
- async getFiles(fileInputs) {
103
- const ids = createArray(fileInputs.length, () => crypto.randomUUID());
104
- const files = await AsyncEnumerable.from(fileInputs).parallelMap(5, true, async (file, index) => await this.uploadFile(file, ids[index])).toArray();
105
- this.#logger.verbose(`Processing ${fileInputs.length} files...`);
106
- await this.waitForFilesActive(files);
107
- return files;
108
- }
109
- async uploadFile(fileInput, id) {
110
- if (isDefined(this.#storage)) {
111
- return await this.uploadFileVertex(fileInput, id);
112
- }
113
- return await this.uploadFileGenAi(fileInput, id);
114
- }
115
- async uploadFileVertex(fileInput, id) {
116
- const bucket = await this.getBucket();
117
- const file = bucket.file(id);
118
- let stream;
119
- let contentType;
120
- let size;
121
- if (isBlob(fileInput)) {
122
- stream = Readable.fromWeb(fileInput.stream());
123
- contentType = fileInput.type;
124
- size = fileInput.size;
125
- }
126
- else if (isPathFileInput(fileInput)) {
127
- const stats = await stat(fileInput.path);
128
- stream = createReadStream(fileInput.path);
129
- contentType = fileInput.mimeType;
130
- size = stats.size;
131
- }
132
- else {
133
- stream = Readable.fromWeb(fileInput.stream);
134
- contentType = fileInput.mimeType;
135
- size = fileInput.size;
136
- }
137
- this.#logger.verbose(`Uploading file "${id}"${isDefined(size) ? ` (${formatBytes(size)})` : ''}...`);
138
- await file.save(stream, { contentType, resumable: false });
139
- return {
140
- id,
141
- name: id,
142
- uri: `gs://${bucket.name}/${file.name}`,
143
- mimeType: contentType,
144
- };
145
- }
146
- async uploadFileGenAi(fileInput, id) {
147
- let uploadData;
148
- let contentType;
149
- let size;
150
- if (isBlob(fileInput)) {
151
- uploadData = fileInput;
152
- contentType = fileInput.type;
153
- size = fileInput.size;
154
- }
155
- else if (isPathFileInput(fileInput)) {
156
- const fileState = await stat(fileInput.path);
157
- uploadData = fileInput.path;
158
- contentType = fileInput.mimeType;
159
- size = fileState.size;
160
- }
161
- else {
162
- const fileBytes = await readBinaryStream(fileInput.stream);
163
- const blob = new Blob([fileBytes], { type: fileInput.mimeType });
164
- uploadData = blob;
165
- contentType = blob.type;
166
- size = blob.size;
167
- }
168
- this.#logger.verbose(`Uploading file "${id}" (${formatBytes(size)})...`);
169
- // upload supports paths and blobs, but not streams (yet)
170
- const response = await this.#genAI.files.upload({ file: uploadData, config: { mimeType: contentType } });
171
- return {
172
- id,
173
- name: assertDefinedPass(response.name, 'Missing file name'),
174
- uri: assertDefinedPass(response.uri, 'Missing file uri'),
175
- mimeType: assertDefinedPass(response.mimeType, 'Missing file mime type'),
176
- };
177
- }
178
- async getBucket() {
179
- if (isUndefined(this.#options.vertex)) {
180
- throw new Error('Not using Vertex');
181
- }
182
- if (isDefined(this.#bucket)) {
183
- return this.#bucket;
184
- }
185
- const bucketName = assertDefinedPass(this.#options.vertex.bucket, 'Bucket not specified');
186
- const bucket = this.#storage.bucket(bucketName);
187
- const [exists] = await bucket.exists();
188
- if (!exists) {
189
- this.#logger.info(`Bucket "${bucketName}" not found, creating...`);
190
- const [createdBucket] = await this.#storage.createBucket(bucketName, {
191
- location: this.#options.vertex.location,
192
- lifecycle: {
193
- rule: [{
194
- action: { type: 'Delete' },
195
- condition: { age: 1 },
196
- }],
197
- },
198
- });
199
- this.#bucket = createdBucket;
200
- }
201
- else {
202
- this.#bucket = bucket;
203
- }
204
- return this.#bucket;
205
- }
206
- async waitForFileActive(file) {
207
- if (isDefined(this.#options.vertex)) {
208
- // For Vertex, uploads to GCS are instantly "active"
209
- return;
210
- }
211
- for await (const backoff of backoffGenerator(this.#backoffOptions)) {
212
- const state = await this.#genAI.files.get({ name: file.name });
213
- if (state.state == FileState.ACTIVE) {
214
- this.#logger.verbose(`File "${file.id}" is active.`);
215
- return;
216
- }
217
- if (state.state == FileState.FAILED) {
218
- throw new DetailsError(state.error?.message ?? `Failed to process file ${state.name}`, state.error?.details);
219
- }
220
- backoff();
221
- }
222
- }
223
- async waitForFilesActive(files) {
224
- // parallelizing does not help here, as each file upload is independently processed in the background
225
- for (const file of files) {
226
- await this.waitForFileActive(file);
227
- }
228
- }
229
- };
230
- AiFileService = __decorate([
231
- Singleton()
232
- ], AiFileService);
233
- export { AiFileService };
@@ -1,38 +0,0 @@
1
- import type { OneOrMany } from '../types/index.js';
2
- import type { AiService, CallFunctionsOptions, SpecializedGenerationResult } from './ai.service.js';
3
- import type { Content, GenerationRequest, GenerationResult, SchemaFunctionDeclarations, SchemaFunctionDeclarationsResult } from './types.js';
4
- /**
5
- * Represents a conversational session with an AI model.
6
- *
7
- * This class maintains the history of contents (messages) in a conversation,
8
- * allowing for stateful, multi-turn interactions with the AI.
9
- */
10
- export declare class AiSession {
11
- #private;
12
- /**
13
- * The history of contents in the session.
14
- */
15
- readonly contents: Content[];
16
- /**
17
- * Creates an instance of `AiSession`.
18
- * @param aiService The {@link AiService} instance to use for AI interactions.
19
- */
20
- constructor(aiService: AiService);
21
- /**
22
- * Adds new content to the session's history without triggering a generation.
23
- * @param content The content or contents to add.
24
- */
25
- addContent(content: OneOrMany<Content>): void;
26
- /**
27
- * Prompts the model to call one or more functions based on the provided context and session history.
28
- * The new user content and the resulting model response (function calls) are automatically added to the session history.
29
- * @param options The options for the function call request.
30
- */
31
- callFunctions<const T extends SchemaFunctionDeclarations>(options: CallFunctionsOptions<T>): Promise<SpecializedGenerationResult<SchemaFunctionDeclarationsResult<T>[]>>;
32
- /**
33
- * Generates content from the model based on the provided request and the current session history.
34
- * The new user content and the resulting model response are automatically added to the session history.
35
- * @param request The generation request.
36
- */
37
- generate(request: GenerationRequest): Promise<GenerationResult>;
38
- }