@tstdl/base 0.93.140 → 0.93.141
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.
- package/application/application.d.ts +1 -1
- package/application/application.js +1 -1
- package/application/providers.d.ts +20 -2
- package/application/providers.js +34 -7
- package/audit/module.d.ts +5 -0
- package/audit/module.js +9 -1
- package/authentication/server/module.d.ts +5 -0
- package/authentication/server/module.js +9 -1
- package/authentication/tests/authentication.api-controller.test.js +1 -1
- package/authentication/tests/authentication.api-request-token.provider.test.js +1 -1
- package/authentication/tests/authentication.client-service.test.js +1 -1
- package/circuit-breaker/postgres/module.d.ts +1 -0
- package/circuit-breaker/postgres/module.js +5 -1
- package/document-management/server/configure.js +5 -1
- package/document-management/server/module.d.ts +1 -1
- package/document-management/server/module.js +1 -1
- package/document-management/server/services/document-management-ancillary.service.js +1 -1
- package/document-management/tests/ai-config-hierarchy.test.js +0 -5
- package/document-management/tests/document-management-ai-overrides.test.js +0 -1
- package/document-management/tests/document-validation-ai-overrides.test.js +0 -1
- package/examples/document-management/main.d.ts +1 -0
- package/examples/document-management/main.js +14 -11
- package/key-value-store/postgres/module.d.ts +1 -0
- package/key-value-store/postgres/module.js +5 -1
- package/lock/postgres/module.d.ts +1 -0
- package/lock/postgres/module.js +5 -1
- package/mail/module.d.ts +5 -1
- package/mail/module.js +11 -6
- package/module/modules/web-server.module.js +2 -3
- package/notification/server/module.d.ts +1 -0
- package/notification/server/module.js +5 -1
- package/notification/tests/notification-flow.test.js +2 -2
- package/orm/decorators.d.ts +5 -1
- package/orm/decorators.js +1 -1
- package/orm/server/drizzle/schema-converter.js +17 -30
- package/orm/server/encryption.d.ts +0 -1
- package/orm/server/encryption.js +1 -4
- package/orm/server/index.d.ts +1 -6
- package/orm/server/index.js +1 -6
- package/orm/server/migration.d.ts +19 -0
- package/orm/server/migration.js +72 -0
- package/orm/server/repository.d.ts +1 -1
- package/orm/server/transaction.d.ts +5 -10
- package/orm/server/transaction.js +22 -26
- package/orm/server/transactional.js +3 -3
- package/orm/tests/database-migration.test.d.ts +1 -0
- package/orm/tests/database-migration.test.js +82 -0
- package/orm/tests/encryption.test.js +3 -4
- package/orm/utils.d.ts +17 -2
- package/orm/utils.js +49 -1
- package/package.json +4 -3
- package/rate-limit/postgres/module.d.ts +1 -0
- package/rate-limit/postgres/module.js +5 -1
- package/reflection/decorator-data.js +11 -12
- package/task-queue/README.md +2 -9
- package/task-queue/postgres/drizzle/{0000_simple_invisible_woman.sql → 0000_wakeful_sunspot.sql} +22 -14
- package/task-queue/postgres/drizzle/meta/0000_snapshot.json +160 -82
- package/task-queue/postgres/drizzle/meta/_journal.json +2 -2
- package/task-queue/postgres/module.d.ts +1 -0
- package/task-queue/postgres/module.js +5 -1
- package/task-queue/postgres/schemas.d.ts +9 -6
- package/task-queue/postgres/schemas.js +4 -3
- package/task-queue/postgres/task-queue.d.ts +2 -12
- package/task-queue/postgres/task-queue.js +431 -354
- package/task-queue/postgres/task.model.d.ts +12 -5
- package/task-queue/postgres/task.model.js +51 -25
- package/task-queue/task-context.d.ts +2 -2
- package/task-queue/task-context.js +7 -7
- package/task-queue/task-queue.d.ts +36 -19
- package/task-queue/task-queue.js +18 -10
- package/task-queue/tests/cascading-cancellations.test.d.ts +1 -0
- package/task-queue/tests/cascading-cancellations.test.js +38 -0
- package/task-queue/tests/complex.test.js +44 -228
- package/task-queue/tests/coverage-branch.test.d.ts +1 -0
- package/task-queue/tests/coverage-branch.test.js +407 -0
- package/task-queue/tests/coverage-enhancement.test.d.ts +1 -0
- package/task-queue/tests/coverage-enhancement.test.js +144 -0
- package/task-queue/tests/dag-dependencies.test.d.ts +1 -0
- package/task-queue/tests/dag-dependencies.test.js +41 -0
- package/task-queue/tests/dependencies.test.js +26 -26
- package/task-queue/tests/extensive-dependencies.test.js +64 -139
- package/task-queue/tests/fan-out-spawning.test.d.ts +1 -0
- package/task-queue/tests/fan-out-spawning.test.js +53 -0
- package/task-queue/tests/idempotent-replacement.test.d.ts +1 -0
- package/task-queue/tests/idempotent-replacement.test.js +61 -0
- package/task-queue/tests/missing-idempotent-tasks.test.d.ts +1 -0
- package/task-queue/tests/missing-idempotent-tasks.test.js +38 -0
- package/task-queue/tests/queue.test.js +33 -24
- package/task-queue/tests/worker.test.js +20 -5
- package/task-queue/tests/zombie-parent.test.d.ts +1 -0
- package/task-queue/tests/zombie-parent.test.js +45 -0
- package/task-queue/tests/zombie-recovery.test.d.ts +1 -0
- package/task-queue/tests/zombie-recovery.test.js +51 -0
- package/test5.js +5 -5
- package/testing/integration-setup.d.ts +4 -4
- package/testing/integration-setup.js +54 -29
- package/text/localization.service.js +2 -2
- package/utils/file-reader.js +1 -2
|
@@ -59,7 +59,7 @@ describe('Worker & Base Class Tests', () => {
|
|
|
59
59
|
// Wait until task is processed (error recorded and status is Pending)
|
|
60
60
|
for (let i = 0; i < 50; i++) {
|
|
61
61
|
const updated = await queue.getTask(task.id);
|
|
62
|
-
if (updated?.tries == 1) {
|
|
62
|
+
if (updated?.tries == 1 && updated.status == TaskStatus.Pending) {
|
|
63
63
|
break;
|
|
64
64
|
}
|
|
65
65
|
await timeout(20);
|
|
@@ -134,16 +134,31 @@ describe('Worker & Base Class Tests', () => {
|
|
|
134
134
|
// Other queue spawn
|
|
135
135
|
const otherQueue = injector.resolve(TaskQueueProvider).get('other-queue');
|
|
136
136
|
const otherChild = await context.spawn(otherQueue, 'other', { x: 1 });
|
|
137
|
-
expect(otherChild.parentId).toBe(task.id);
|
|
138
137
|
expect(otherChild.namespace).toBe('other-queue');
|
|
139
|
-
|
|
140
|
-
expect(otherChildren[0]?.parentId).toBe(task.id);
|
|
138
|
+
await context.spawnMany(otherQueue, [{ type: 'other', data: { x: 2 } }]);
|
|
141
139
|
executed = true;
|
|
142
140
|
return TaskProcessResult.Complete();
|
|
143
141
|
});
|
|
144
|
-
|
|
142
|
+
// Complete children so parent can finalize
|
|
143
|
+
void (async () => {
|
|
144
|
+
while (!executed) {
|
|
145
|
+
await timeout(50);
|
|
146
|
+
}
|
|
147
|
+
// At this point parent should be WaitingChildren if children are not done
|
|
148
|
+
const midTask = await queue.getTask(task.id);
|
|
149
|
+
expect(midTask?.status).toBe(TaskStatus.WaitingChildren);
|
|
150
|
+
while (true) {
|
|
151
|
+
const dChild = await queue.dequeue({ types: ['child'] });
|
|
152
|
+
if (!dChild)
|
|
153
|
+
break;
|
|
154
|
+
await queue.complete(dChild);
|
|
155
|
+
}
|
|
156
|
+
})();
|
|
157
|
+
await queue.waitForTasks([task.id], { interval: 50, timeout: 5000 });
|
|
145
158
|
token.set();
|
|
146
159
|
expect(executed).toBe(true);
|
|
160
|
+
const finalTask = await queue.getTask(task.id);
|
|
161
|
+
expect(finalTask?.status).toBe(TaskStatus.Completed);
|
|
147
162
|
});
|
|
148
163
|
it('should correctly report isFinalAttempt in TaskContext', async () => {
|
|
149
164
|
const queueProvider = injector.resolve(TaskQueueProvider);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
|
|
3
|
+
import { setupIntegrationTest } from '../../testing/index.js';
|
|
4
|
+
import { timeout } from '../../utils/timing.js';
|
|
5
|
+
describe('Zombie Parent Deadlock', () => {
|
|
6
|
+
let injector;
|
|
7
|
+
let queue;
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
({ injector } = await setupIntegrationTest({ modules: { taskQueue: true } }));
|
|
10
|
+
});
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
const queueProvider = injector.resolve(TaskQueueProvider);
|
|
13
|
+
const queueName = `zombie-queue-${Date.now()}-${Math.random()}`;
|
|
14
|
+
queue = queueProvider.get(queueName, {
|
|
15
|
+
visibilityTimeout: 1000,
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
afterEach(async () => {
|
|
19
|
+
await queue.clear();
|
|
20
|
+
});
|
|
21
|
+
afterAll(async () => {
|
|
22
|
+
await injector?.dispose();
|
|
23
|
+
});
|
|
24
|
+
it('should resolve parent even if child fails (failFast: false)', async () => {
|
|
25
|
+
const parent = await queue.enqueue('parent', {});
|
|
26
|
+
const dParent = await queue.dequeue();
|
|
27
|
+
// Spawn a child that will fail. Parent has failFast: false by default.
|
|
28
|
+
const [child] = await queue.enqueueMany([{ type: 'child', data: {}, parentId: parent.id, failFast: false }], { returnTasks: true });
|
|
29
|
+
await queue.complete(dParent);
|
|
30
|
+
// Parent should be WaitingChildren
|
|
31
|
+
const uParent = await queue.getTask(parent.id);
|
|
32
|
+
expect(uParent?.status).toBe(TaskStatus.WaitingChildren);
|
|
33
|
+
// Fail the child fatally
|
|
34
|
+
const dChild = await queue.dequeue();
|
|
35
|
+
await queue.fail(dChild, new Error('child failed'), { fatal: true });
|
|
36
|
+
// Verify child is Dead
|
|
37
|
+
const uChild = await queue.getTask(child.id);
|
|
38
|
+
expect(uChild?.status).toBe(TaskStatus.Dead);
|
|
39
|
+
// Wait a bit for dependency resolution
|
|
40
|
+
await timeout(200);
|
|
41
|
+
// Parent should NOT be stuck. It should transition to Completed because the child is terminal.
|
|
42
|
+
const fParent = await queue.getTask(parent.id);
|
|
43
|
+
expect(fParent?.status).toBe(TaskStatus.Completed);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
|
|
3
|
+
import { setupIntegrationTest } from '../../testing/index.js';
|
|
4
|
+
import { timeout } from '../../utils/timing.js';
|
|
5
|
+
describe('Zombie Recovery Race Condition', () => {
|
|
6
|
+
let injector;
|
|
7
|
+
let queue;
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
({ injector } = await setupIntegrationTest({ modules: { taskQueue: true } }));
|
|
10
|
+
});
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
const queueProvider = injector.resolve(TaskQueueProvider);
|
|
13
|
+
const queueName = `zombie-recovery-queue-${Date.now()}-${Math.random()}`;
|
|
14
|
+
queue = queueProvider.get(queueName, {
|
|
15
|
+
visibilityTimeout: 100, // Very short visibility timeout
|
|
16
|
+
retryDelayMinimum: 0,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
await queue.clear();
|
|
21
|
+
});
|
|
22
|
+
afterAll(async () => {
|
|
23
|
+
await injector?.dispose();
|
|
24
|
+
});
|
|
25
|
+
it('should NOT prematurely complete a recovered parent when children finish', async () => {
|
|
26
|
+
// 1. Enqueue parent
|
|
27
|
+
const parent = await queue.enqueue('parent', {});
|
|
28
|
+
// 2. Dequeue parent (it is now Running, startTimestamp is set)
|
|
29
|
+
const dParent = await queue.dequeue();
|
|
30
|
+
expect(dParent?.id).toBe(parent.id);
|
|
31
|
+
expect(dParent?.startTimestamp).not.toBeNull();
|
|
32
|
+
// 3. Parent spawns a child that it waits for
|
|
33
|
+
const [child] = await queue.enqueueMany([{ type: 'child', data: {}, parentId: parent.id }], { returnTasks: true });
|
|
34
|
+
// 4. Simulate crash: Wait for visibility timeout so it becomes a zombie
|
|
35
|
+
await timeout(200);
|
|
36
|
+
// 5. Run maintenance to recover the zombie parent
|
|
37
|
+
await queue.maintenance();
|
|
38
|
+
// Verify it was recovered to Pending
|
|
39
|
+
const recoveredParent = await queue.getTask(parent.id);
|
|
40
|
+
expect(recoveredParent?.status).toBe(TaskStatus.Pending);
|
|
41
|
+
// If the bug exists, startTimestamp is still set here.
|
|
42
|
+
// 6. Complete the child while parent is still Pending
|
|
43
|
+
const dChild = await queue.dequeue({ types: ['child'] });
|
|
44
|
+
await queue.complete(dChild);
|
|
45
|
+
// 7. Verify parent status
|
|
46
|
+
// BUG: evaluateTaskStatus will see unresolvedCompleteDependencies=0 and startTimestamp != null,
|
|
47
|
+
// and incorrectly transition the Pending parent to Completed.
|
|
48
|
+
const finalParent = await queue.getTask(parent.id);
|
|
49
|
+
expect(finalParent?.status).toBe(TaskStatus.Pending); // It should still be pending execution!
|
|
50
|
+
});
|
|
51
|
+
});
|
package/test5.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import './polyfills.js';
|
|
2
|
-
import { promptBuilder } from './ai/index.js';
|
|
3
2
|
import { Application } from './application/application.js';
|
|
4
3
|
import { provideModule, provideSignalHandler } from './application/index.js';
|
|
5
4
|
import { PrettyPrintLogFormatter } from './logger/index.js';
|
|
6
5
|
import { provideConsoleLogTransport } from './logger/transports/console.js';
|
|
7
6
|
async function main(_cancellationSignal) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
const arr = [1, 2, 3, 4, 5];
|
|
8
|
+
for (const item of arr) {
|
|
9
|
+
console.log(item);
|
|
10
|
+
arr.push(arr.at(-1) + 1);
|
|
11
|
+
}
|
|
12
12
|
}
|
|
13
13
|
Application.run('Test', [
|
|
14
14
|
provideConsoleLogTransport(PrettyPrintLogFormatter),
|
|
@@ -3,6 +3,7 @@ import { type AuthenticationAncillaryService } from '../authentication/server/in
|
|
|
3
3
|
import { Injector } from '../injector/index.js';
|
|
4
4
|
import { LogLevel } from '../logger/index.js';
|
|
5
5
|
import { type S3ObjectStorageProviderConfig } from '../object-storage/s3/index.js';
|
|
6
|
+
import type { EntityType } from '../orm/entity.js';
|
|
6
7
|
import { Database } from '../orm/server/index.js';
|
|
7
8
|
import type { Type } from '../types/index.js';
|
|
8
9
|
export type IntegrationTestOptions = {
|
|
@@ -30,7 +31,6 @@ export type IntegrationTestOptions = {
|
|
|
30
31
|
rateLimiter?: boolean;
|
|
31
32
|
signals?: boolean;
|
|
32
33
|
taskQueue?: boolean;
|
|
33
|
-
test?: boolean;
|
|
34
34
|
webServer?: boolean;
|
|
35
35
|
};
|
|
36
36
|
authenticationAncillaryService?: Type<AuthenticationAncillaryService>;
|
|
@@ -47,14 +47,14 @@ export declare function setupIntegrationTest(options?: IntegrationTestOptions):
|
|
|
47
47
|
* Helper to truncate specific tables in a schema.
|
|
48
48
|
* Useful in beforeEach() to reset state.
|
|
49
49
|
*/
|
|
50
|
-
export declare function truncateTables(database: Database, schema: string, tables: string[]): Promise<void>;
|
|
50
|
+
export declare function truncateTables(database: Database, schema: string, tables: (string | EntityType)[]): Promise<void>;
|
|
51
51
|
/**
|
|
52
52
|
* Helper to delete data for a specific tenant from specific tables.
|
|
53
53
|
* Useful for parallel tests to avoid interference.
|
|
54
54
|
*/
|
|
55
|
-
export declare function clearTenantData(database: Database, schema: string, tables: string[], tenantId: string): Promise<void>;
|
|
55
|
+
export declare function clearTenantData(database: Database, schema: string | undefined, tables: (string | EntityType)[], tenantId: string): Promise<void>;
|
|
56
56
|
/**
|
|
57
57
|
* Helper to drop specific tables.
|
|
58
58
|
* Useful in beforeAll() cleanups.
|
|
59
59
|
*/
|
|
60
|
-
export declare function dropTables(database: Database, schema: string, tables: string[]): Promise<void>;
|
|
60
|
+
export declare function dropTables(database: Database, schema: string | undefined, tables: (string | EntityType)[]): Promise<void>;
|
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
import { sql } from 'drizzle-orm';
|
|
2
2
|
import { configureApiServer } from '../api/server/index.js';
|
|
3
|
-
import { configureAudit
|
|
3
|
+
import { configureAudit } from '../audit/index.js';
|
|
4
4
|
import { AuthenticationApiClient } from '../authentication/client/api.client.js';
|
|
5
5
|
import { configureAuthenticationClient } from '../authentication/client/index.js';
|
|
6
|
-
import { AuthenticationApiController, configureAuthenticationServer
|
|
7
|
-
import { configurePostgresCircuitBreaker
|
|
8
|
-
import { configureDocumentManagement
|
|
6
|
+
import { AuthenticationApiController, configureAuthenticationServer } from '../authentication/server/index.js';
|
|
7
|
+
import { configurePostgresCircuitBreaker } from '../circuit-breaker/postgres/module.js';
|
|
8
|
+
import { configureDocumentManagement } from '../document-management/server/index.js';
|
|
9
9
|
import { configureUndiciHttpClientAdapter } from '../http/client/adapters/undici.adapter.js';
|
|
10
10
|
import { configureHttpClient } from '../http/client/index.js';
|
|
11
11
|
import { HttpServer } from '../http/server/index.js';
|
|
12
12
|
import { configureNodeHttpServer } from '../http/server/node/module.js';
|
|
13
13
|
import { Injector, runInInjectionContext } from '../injector/index.js';
|
|
14
|
-
import { configurePostgresKeyValueStore
|
|
15
|
-
import { configurePostgresLock
|
|
14
|
+
import { configurePostgresKeyValueStore } from '../key-value-store/postgres/module.js';
|
|
15
|
+
import { configurePostgresLock } from '../lock/postgres/module.js';
|
|
16
16
|
import { ConsoleLogTransport, DEFAULT_LOG_LEVEL, LogFormatter, LogLevel, LogManager, LogTransport, PrettyPrintLogFormatter } from '../logger/index.js';
|
|
17
17
|
import { configureLocalMessageBus } from '../message-bus/index.js';
|
|
18
18
|
import { configureWebServerModule, WebServerModule } from '../module/modules/web-server.module.js';
|
|
19
|
-
import { configureNotification
|
|
19
|
+
import { configureNotification } from '../notification/server/index.js';
|
|
20
20
|
import { configureS3ObjectStorage } from '../object-storage/s3/index.js';
|
|
21
|
-
import { configureOrm, Database } from '../orm/server/index.js';
|
|
22
|
-
import {
|
|
21
|
+
import { configureOrm, Database, runDatabaseMigrations } from '../orm/server/index.js';
|
|
22
|
+
import { getEntitySchema, getEntityTableName } from '../orm/utils.js';
|
|
23
|
+
import { configurePostgresRateLimiter } from '../rate-limit/postgres/module.js';
|
|
23
24
|
import { configureDefaultSignalsImplementation } from '../signals/implementation/configure.js';
|
|
24
|
-
import { configurePostgresTaskQueue
|
|
25
|
+
import { configurePostgresTaskQueue } from '../task-queue/postgres/index.js';
|
|
25
26
|
import * as configParser from '../utils/config-parser.js';
|
|
26
27
|
import { objectEntries } from '../utils/object/object.js';
|
|
27
|
-
import { isDefined, isNotNull } from '../utils/type-guards.js';
|
|
28
|
+
import { assertDefinedPass, isDefined, isNotNull, isString } from '../utils/type-guards.js';
|
|
28
29
|
/**
|
|
29
30
|
* Standard setup for integration tests.
|
|
30
31
|
*/
|
|
@@ -51,6 +52,8 @@ export async function setupIntegrationTest(options = {}) {
|
|
|
51
52
|
...options.dbConfig,
|
|
52
53
|
};
|
|
53
54
|
// 4. Configure ORM
|
|
55
|
+
// We disable autoMigrate here because APPLICATION_INITIALIZER is not used in integration tests
|
|
56
|
+
// We manually run migrations via runDatabaseMigrations below
|
|
54
57
|
configureOrm({
|
|
55
58
|
repositoryConfig: { schema: options.orm?.schema ?? 'test' },
|
|
56
59
|
connection: dbConfig,
|
|
@@ -66,35 +69,29 @@ export async function setupIntegrationTest(options = {}) {
|
|
|
66
69
|
await database.execute(sql `CREATE SCHEMA IF NOT EXISTS ${sql.identifier(options.orm.schema)}`);
|
|
67
70
|
}
|
|
68
71
|
// 7. Optional Modules
|
|
69
|
-
if (options.modules?.messageBus ?? options.modules?.taskQueue ?? options.modules?.authentication ?? options.modules?.
|
|
72
|
+
if (options.modules?.messageBus ?? options.modules?.taskQueue ?? options.modules?.authentication ?? options.modules?.notification) {
|
|
70
73
|
configureLocalMessageBus({ injector });
|
|
71
74
|
}
|
|
72
75
|
if (options.modules?.taskQueue) {
|
|
73
76
|
configurePostgresTaskQueue({ injector });
|
|
74
|
-
await runInInjectionContext(injector, migratePostgresTaskQueueSchema);
|
|
75
77
|
}
|
|
76
78
|
if (options.modules?.circuitBreaker ?? options.modules?.taskQueue) {
|
|
77
79
|
configurePostgresCircuitBreaker({ injector });
|
|
78
|
-
await runInInjectionContext(injector, migratePostgresCircuitBreaker);
|
|
79
80
|
}
|
|
80
81
|
if (options.modules?.rateLimiter ?? options.modules?.taskQueue) {
|
|
81
82
|
configurePostgresRateLimiter({ injector });
|
|
82
|
-
await runInInjectionContext(injector, migratePostgresRateLimiterSchema);
|
|
83
83
|
}
|
|
84
84
|
if (options.modules?.keyValueStore ?? options.modules?.authentication) {
|
|
85
85
|
configurePostgresKeyValueStore({ injector });
|
|
86
|
-
await runInInjectionContext(injector, migratePostgresKeyValueStoreSchema);
|
|
87
86
|
}
|
|
88
87
|
if (options.modules?.lock ?? options.modules?.authentication) {
|
|
89
88
|
configurePostgresLock({ injector });
|
|
90
|
-
await runInInjectionContext(injector, migratePostgresLockSchema);
|
|
91
89
|
}
|
|
92
|
-
if (options.modules?.signals ?? options.modules?.authentication ?? options.modules?.
|
|
90
|
+
if (options.modules?.signals ?? options.modules?.authentication ?? options.modules?.notification) {
|
|
93
91
|
configureDefaultSignalsImplementation();
|
|
94
92
|
}
|
|
95
93
|
if (options.modules?.audit ?? options.modules?.authentication) {
|
|
96
94
|
configureAudit({ injector });
|
|
97
|
-
await runInInjectionContext(injector, migrateAuditSchema);
|
|
98
95
|
}
|
|
99
96
|
if (options.modules?.authentication) {
|
|
100
97
|
configureAuthenticationServer({
|
|
@@ -106,11 +103,9 @@ export async function setupIntegrationTest(options = {}) {
|
|
|
106
103
|
authenticationAncillaryService: options.authenticationAncillaryService,
|
|
107
104
|
injector,
|
|
108
105
|
});
|
|
109
|
-
await runInInjectionContext(injector, migrateAuthenticationSchema);
|
|
110
106
|
}
|
|
111
107
|
if (options.modules?.notification) {
|
|
112
108
|
configureNotification({ injector });
|
|
113
|
-
await runInInjectionContext(injector, migrateNotificationSchema);
|
|
114
109
|
}
|
|
115
110
|
if (options.modules?.documentManagement) {
|
|
116
111
|
configureDocumentManagement({
|
|
@@ -122,8 +117,8 @@ export async function setupIntegrationTest(options = {}) {
|
|
|
122
117
|
skipAi: true,
|
|
123
118
|
injector,
|
|
124
119
|
});
|
|
125
|
-
await runInInjectionContext(injector, migrateDocumentManagementSchema);
|
|
126
120
|
}
|
|
121
|
+
await runInInjectionContext(injector, runDatabaseMigrations);
|
|
127
122
|
if (options.modules?.objectStorage) {
|
|
128
123
|
const bucketPerModule = options.s3?.bucketPerModule ?? configParser.boolean('S3_BUCKET_PER_MODULE', true);
|
|
129
124
|
configureS3ObjectStorage({
|
|
@@ -190,9 +185,18 @@ export async function truncateTables(database, schema, tables) {
|
|
|
190
185
|
if (tables.length == 0) {
|
|
191
186
|
return;
|
|
192
187
|
}
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
|
|
188
|
+
const lockId = 987654321; // Different lock ID for table maintenance
|
|
189
|
+
await database.execute(sql `SELECT pg_advisory_lock(${lockId})`);
|
|
190
|
+
try {
|
|
191
|
+
// Using CASCADE to handle foreign keys automatically
|
|
192
|
+
for (const table of tables) {
|
|
193
|
+
const tableName = isString(table) ? table : getEntityTableName(table);
|
|
194
|
+
const tableSchema = isString(table) ? schema : getEntitySchema(table, schema);
|
|
195
|
+
await database.execute(sql `TRUNCATE TABLE ${sql.identifier(tableSchema)}.${sql.identifier(tableName)} CASCADE`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
await database.execute(sql `SELECT pg_advisory_unlock(${lockId})`);
|
|
196
200
|
}
|
|
197
201
|
}
|
|
198
202
|
/**
|
|
@@ -203,8 +207,17 @@ export async function clearTenantData(database, schema, tables, tenantId) {
|
|
|
203
207
|
if (tables.length == 0) {
|
|
204
208
|
return;
|
|
205
209
|
}
|
|
206
|
-
|
|
207
|
-
|
|
210
|
+
const lockId = 987654321;
|
|
211
|
+
await database.execute(sql `SELECT pg_advisory_lock(${lockId})`);
|
|
212
|
+
try {
|
|
213
|
+
for (const table of tables) {
|
|
214
|
+
const tableName = isString(table) ? table : getEntityTableName(table);
|
|
215
|
+
const tableSchema = isString(table) ? assertDefinedPass(schema, 'Schema not provided') : getEntitySchema(table, schema);
|
|
216
|
+
await database.execute(sql `DELETE FROM ${sql.identifier(tableSchema)}.${sql.identifier(tableName)} WHERE tenant_id = ${tenantId}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
await database.execute(sql `SELECT pg_advisory_unlock(${lockId})`);
|
|
208
221
|
}
|
|
209
222
|
}
|
|
210
223
|
/**
|
|
@@ -212,7 +225,19 @@ export async function clearTenantData(database, schema, tables, tenantId) {
|
|
|
212
225
|
* Useful in beforeAll() cleanups.
|
|
213
226
|
*/
|
|
214
227
|
export async function dropTables(database, schema, tables) {
|
|
215
|
-
|
|
216
|
-
|
|
228
|
+
if (tables.length == 0) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const lockId = 987654321;
|
|
232
|
+
await database.execute(sql `SELECT pg_advisory_lock(${lockId})`);
|
|
233
|
+
try {
|
|
234
|
+
for (const table of tables) {
|
|
235
|
+
const tableName = isString(table) ? table : getEntityTableName(table);
|
|
236
|
+
const tableSchema = isString(table) ? assertDefinedPass(schema, 'Schema not provided') : getEntitySchema(table, schema);
|
|
237
|
+
await database.execute(sql `DROP TABLE IF EXISTS ${sql.identifier(tableSchema)}.${sql.identifier(tableName)} CASCADE`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
await database.execute(sql `SELECT pg_advisory_unlock(${lockId})`);
|
|
217
242
|
}
|
|
218
243
|
}
|
|
@@ -192,7 +192,7 @@ function buildMappedLocalization({ language, keys, enums }) {
|
|
|
192
192
|
const mappedLocalization = {
|
|
193
193
|
language,
|
|
194
194
|
keys: new Map(deepObjectEntries(keys)),
|
|
195
|
-
enums: new Map(enumsEntries)
|
|
195
|
+
enums: new Map(enumsEntries),
|
|
196
196
|
};
|
|
197
197
|
return mappedLocalization;
|
|
198
198
|
}
|
|
@@ -215,7 +215,7 @@ function mergeMappedLocalization(a, b, force = false) {
|
|
|
215
215
|
return {
|
|
216
216
|
language: b.language,
|
|
217
217
|
keys: new Map([...a.keys, ...b.keys]),
|
|
218
|
-
enums: new Map([...a.enums, ...b.enums])
|
|
218
|
+
enums: new Map([...a.enums, ...b.enums]),
|
|
219
219
|
};
|
|
220
220
|
}
|
|
221
221
|
export function isLocalizeItem(value) {
|
package/utils/file-reader.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DetailsError } from '../errors/details.error.js';
|
|
2
1
|
export async function readAsText(blob, encoding) {
|
|
3
2
|
return await setup((reader) => reader.readAsText(blob, encoding));
|
|
4
3
|
}
|
|
@@ -12,7 +11,7 @@ async function setup(dispatcher) {
|
|
|
12
11
|
return await new Promise((resolve, reject) => {
|
|
13
12
|
const reader = new FileReader();
|
|
14
13
|
reader.onload = () => resolve(reader.result);
|
|
15
|
-
reader.onerror = () => reject(new
|
|
14
|
+
reader.onerror = () => reject(new Error('FileReader error', { cause: reader.error }));
|
|
16
15
|
dispatcher(reader);
|
|
17
16
|
});
|
|
18
17
|
}
|