@tstdl/base 0.93.123 → 0.93.126
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/ai/genkit/tests/multi-region.test.js +6 -6
- package/ai/index.d.ts +2 -6
- package/ai/index.js +2 -6
- package/ai/parser/index.d.ts +1 -0
- package/ai/parser/index.js +1 -0
- package/ai/parser/parser.d.ts +12 -0
- package/ai/parser/parser.js +28 -0
- package/ai/prompts/build.d.ts +21 -0
- package/ai/prompts/build.js +25 -0
- package/ai/prompts/index.d.ts +2 -0
- package/ai/prompts/index.js +2 -0
- package/ai/prompts/instructions-formatter.d.ts +9 -22
- package/ai/prompts/instructions-formatter.js +20 -7
- package/ai/prompts/instructions.js +1 -1
- package/ai/prompts/steering.d.ts +27 -0
- package/ai/prompts/steering.js +54 -0
- package/ai/tests/instructions-formatter.test.js +115 -0
- package/ai/tests/steering.test.js +37 -0
- package/application/application.d.ts +2 -1
- package/application/application.js +3 -0
- package/authentication/client/module.d.ts +1 -1
- package/authentication/client/module.js +4 -5
- package/authentication/tests/authentication-ancillary.service.test.js +1 -1
- package/authentication/tests/authentication.api-controller.test.js +3 -1
- package/authentication/tests/authentication.api-request-token.provider.test.js +1 -1
- package/authentication/tests/authentication.client-service.test.js +1 -1
- package/authentication/tests/authentication.service.test.js +1 -1
- package/authentication/tests/subject.service.test.js +1 -1
- package/circuit-breaker/tests/circuit-breaker.test.js +1 -1
- package/document-management/api/document-management.api.d.ts +16 -16
- package/document-management/api/document-management.api.js +12 -12
- package/document-management/models/ai-configuration.d.ts +59 -0
- package/document-management/models/ai-configuration.js +1 -0
- package/document-management/models/document-assignment-scope.model.js +2 -4
- package/document-management/models/document-assignment-task.model.js +2 -4
- package/document-management/models/document-collection-assignment.model.js +2 -4
- package/document-management/models/document-collection.model.js +2 -3
- package/document-management/models/document-content.model.d.ts +6 -0
- package/document-management/models/document-content.model.js +32 -0
- package/document-management/models/document-property-value.model.js +1 -2
- package/document-management/models/document-request-collection-assignment.model.js +2 -4
- package/document-management/models/document-request.model.js +2 -4
- package/document-management/models/document-tag-assignment.model.js +2 -3
- package/document-management/models/document-validation-execution-related-document.model.js +2 -4
- package/document-management/models/document-validation-execution.model.js +2 -5
- package/document-management/models/document-workflow.model.d.ts +2 -1
- package/document-management/models/document-workflow.model.js +4 -5
- package/document-management/models/document.model.js +2 -3
- package/document-management/models/index.d.ts +2 -0
- package/document-management/models/index.js +2 -0
- package/document-management/server/api/document-management.api.d.ts +7 -7
- package/document-management/server/api/document-management.api.js +9 -9
- package/document-management/server/configure.d.ts +4 -1
- package/document-management/server/configure.js +9 -4
- package/document-management/server/drizzle/{0000_silly_chimera.sql → 0000_curious_nighthawk.sql} +8 -28
- package/document-management/server/drizzle/meta/0000_snapshot.json +14 -286
- package/document-management/server/drizzle/meta/_journal.json +2 -2
- package/document-management/server/module.d.ts +2 -0
- package/document-management/server/module.js +1 -0
- package/document-management/server/schemas.d.ts +2 -1
- package/document-management/server/services/document-file.service.d.ts +6 -6
- package/document-management/server/services/document-file.service.js +7 -81
- package/document-management/server/services/document-management-ai-provider.service.d.ts +66 -0
- package/document-management/server/services/document-management-ai-provider.service.js +2 -0
- package/document-management/server/services/document-management-ai.service.d.ts +44 -7
- package/document-management/server/services/document-management-ai.service.js +332 -329
- package/document-management/server/services/document-validation.service.d.ts +1 -1
- package/document-management/server/services/document-workflow.service.d.ts +4 -3
- package/document-management/server/services/document-workflow.service.js +26 -9
- package/document-management/server/services/document.service.d.ts +7 -3
- package/document-management/server/services/document.service.js +13 -4
- package/document-management/server/services/index.d.ts +1 -0
- package/document-management/server/services/index.js +1 -0
- package/document-management/server/validators/ai-validation-executor.d.ts +419 -12
- package/document-management/server/validators/ai-validation-executor.js +51 -46
- package/document-management/server/validators/single-document-validation-executor.d.ts +1 -3
- package/document-management/server/validators/single-document-validation-executor.js +2 -4
- package/document-management/service-models/document.service-model.d.ts +3 -3
- package/document-management/service-models/document.service-model.js +1 -1
- package/document-management/tests/ai-config-hierarchy.test.d.ts +1 -0
- package/document-management/tests/ai-config-hierarchy.test.js +64 -0
- package/document-management/tests/ai-config-integration.test.d.ts +1 -0
- package/document-management/tests/ai-config-integration.test.js +125 -0
- package/document-management/tests/ai-config-merge.test.d.ts +1 -0
- package/document-management/tests/ai-config-merge.test.js +38 -0
- package/document-management/tests/document-management-ai-overrides.test.d.ts +1 -0
- package/document-management/tests/document-management-ai-overrides.test.js +64 -0
- package/document-management/tests/document-management-core.test.js +6 -6
- package/document-management/tests/document-management.api.test.js +5 -5
- package/document-management/tests/document-statistics.service.test.js +10 -6
- package/document-management/tests/document-validation-ai-overrides.test.d.ts +1 -0
- package/document-management/tests/document-validation-ai-overrides.test.js +85 -0
- package/document-management/tests/document.service.test.js +15 -11
- package/document-management/tests/enum-helpers.test.js +5 -5
- package/examples/document-management/ai-provider.d.ts +20 -0
- package/examples/document-management/ai-provider.js +74 -0
- package/examples/document-management/main.js +9 -6
- package/examples/injector/graph-example.d.ts +1 -0
- package/examples/injector/graph-example.js +340 -0
- package/injector/decorators.d.ts +4 -4
- package/injector/decorators.js +5 -6
- package/injector/forward-ref.d.ts +15 -0
- package/injector/forward-ref.js +20 -0
- package/injector/graph.d.ts +113 -0
- package/injector/graph.js +631 -0
- package/injector/index.d.ts +2 -0
- package/injector/index.js +2 -0
- package/injector/inject.d.ts +15 -15
- package/injector/injector.d.ts +101 -13
- package/injector/injector.js +103 -59
- package/injector/resolve-chain.d.ts +20 -6
- package/injector/resolve-chain.js +39 -14
- package/injector/tests/advanced.test.d.ts +1 -0
- package/injector/tests/advanced.test.js +116 -0
- package/injector/tests/async-init.test.d.ts +1 -0
- package/injector/tests/async-init.test.js +77 -0
- package/injector/tests/basic.test.d.ts +1 -0
- package/injector/tests/basic.test.js +114 -0
- package/injector/tests/hierarchical.test.d.ts +1 -0
- package/injector/tests/hierarchical.test.js +59 -0
- package/injector/tests/lifecycles.test.d.ts +1 -0
- package/injector/tests/lifecycles.test.js +109 -0
- package/injector/token.d.ts +2 -1
- package/injector/token.js +4 -1
- package/injector/type-info.d.ts +1 -5
- package/injector/types.d.ts +4 -10
- package/logger/tests/pretty-print.test.d.ts +1 -0
- package/logger/{formatters → tests}/pretty-print.test.js +1 -1
- package/logger/transports/console.d.ts +3 -2
- package/logger/transports/console.js +4 -3
- package/notification/api/notification.api.d.ts +26 -6
- package/notification/api/notification.api.js +15 -4
- package/notification/client/notification-client.d.ts +6 -0
- package/notification/client/notification-client.js +13 -3
- package/notification/models/in-app-notification.model.d.ts +9 -3
- package/notification/models/in-app-notification.model.js +32 -11
- package/notification/models/notification-log.model.js +2 -3
- package/notification/server/api/notification.api-controller.d.ts +1 -0
- package/notification/server/api/notification.api-controller.js +7 -1
- package/notification/server/drizzle/{0000_oval_rage.sql → 0000_wise_pyro.sql} +22 -4
- package/notification/server/drizzle/meta/0000_snapshot.json +249 -37
- package/notification/server/drizzle/meta/_journal.json +2 -2
- package/notification/server/module.d.ts +5 -0
- package/notification/server/module.js +6 -1
- package/notification/server/providers/in-app-channel-provider.js +1 -0
- package/notification/server/schemas.d.ts +3 -2
- package/notification/server/schemas.js +3 -2
- package/notification/server/services/notification.service.d.ts +11 -6
- package/notification/server/services/notification.service.js +138 -42
- package/notification/tests/notification-api.test.js +16 -6
- package/notification/tests/notification-client.test.d.ts +1 -0
- package/notification/tests/{unit/notification-client.test.js → notification-client.test.js} +5 -5
- package/notification/tests/notification-flow.test.js +45 -7
- package/notification/tests/notification-sse.service.test.js +1 -1
- package/notification/tests/notification-type.service.test.js +1 -1
- package/object-storage/s3/s3.object-storage.js +3 -0
- package/object-storage/s3/tests/s3.object-storage.integration.test.js +1 -1
- package/orm/server/drizzle/schema-converter.js +5 -3
- package/orm/tests/repository-attributes.test.js +10 -17
- package/orm/tests/repository-cti-mapping.test.js +2 -2
- package/orm/tests/repository-cti-soft-delete.test.js +1 -1
- package/orm/tests/repository-cti.test.js +19 -33
- package/orm/tests/repository-extra-coverage.test.js +1 -1
- package/orm/tests/repository-search.test.js +5 -2
- package/orm/tests/schema-converter.test.js +1 -0
- package/orm/tests/transaction-safety.test.js +1 -1
- package/package.json +7 -9
- package/rate-limit/tests/postgres-rate-limiter.test.js +6 -16
- package/renderer/d2.d.ts +77 -0
- package/renderer/d2.js +68 -0
- package/renderer/graphviz.d.ts +47 -0
- package/renderer/graphviz.js +58 -0
- package/renderer/index.d.ts +4 -0
- package/renderer/index.js +4 -0
- package/renderer/typst.d.ts +57 -0
- package/renderer/typst.js +62 -0
- package/rpc/adapters/readable-stream.adapter.d.ts +3 -0
- package/rpc/adapters/readable-stream.adapter.js +5 -1
- package/rpc/rpc.js +28 -3
- package/rpc/tests/rpc.integration.test.js +3 -1
- package/schema/schemas/nullable.js +1 -1
- package/task-queue/task-queue.d.ts +2 -0
- package/task-queue/task-queue.js +6 -2
- package/task-queue/tests/complex.test.js +1 -1
- package/task-queue/tests/dependencies.test.js +3 -3
- package/task-queue/tests/extensive-dependencies.test.js +1 -1
- package/task-queue/tests/queue.test.js +1 -1
- package/task-queue/tests/worker.test.js +4 -7
- package/test5.js +52 -8
- package/{unit-test → testing}/integration-setup.d.ts +1 -0
- package/{unit-test → testing}/integration-setup.js +13 -0
- package/utils/base64.d.ts +7 -0
- package/utils/base64.js +10 -1
- package/utils/noop.d.ts +7 -1
- package/utils/noop.js +7 -1
- package/ai/ai-file.service.d.ts +0 -57
- package/ai/ai-file.service.js +0 -233
- package/ai/ai-session.d.ts +0 -38
- package/ai/ai-session.js +0 -50
- package/ai/ai.service.d.ts +0 -126
- package/ai/ai.service.js +0 -481
- package/ai/functions.d.ts +0 -9
- package/ai/functions.js +0 -38
- package/ai/module.d.ts +0 -26
- package/ai/module.js +0 -25
- package/ai/types.d.ts +0 -229
- package/ai/types.js +0 -33
- package/latex/index.d.ts +0 -1
- package/latex/index.js +0 -1
- package/typst/index.d.ts +0 -1
- package/typst/index.js +0 -1
- package/typst/render.d.ts +0 -23
- package/typst/render.js +0 -32
- /package/{logger/formatters/pretty-print.test.d.ts → ai/tests/instructions-formatter.test.d.ts} +0 -0
- /package/{notification/tests/unit/notification-client.test.d.ts → ai/tests/steering.test.d.ts} +0 -0
- /package/{latex/render.d.ts → renderer/latex.d.ts} +0 -0
- /package/{latex/render.js → renderer/latex.js} +0 -0
- /package/{unit-test → testing}/index.d.ts +0 -0
- /package/{unit-test → testing}/index.js +0 -0
|
@@ -9,9 +9,9 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { Injector, runInInjectionContext } from '../../injector/index.js';
|
|
11
11
|
import { StringProperty } from '../../schema/index.js';
|
|
12
|
-
import { dropTables, setupIntegrationTest, truncateTables } from '../../
|
|
12
|
+
import { dropTables, setupIntegrationTest, truncateTables } from '../../testing/index.js';
|
|
13
13
|
import { sql } from 'drizzle-orm';
|
|
14
|
-
import { beforeAll, describe, expect, test } from 'vitest';
|
|
14
|
+
import { beforeAll, beforeEach, describe, expect, test } from 'vitest';
|
|
15
15
|
import { ChildEntity, Column, Inheritance, Table } from '../decorators.js';
|
|
16
16
|
import { Entity } from '../entity.js';
|
|
17
17
|
import { Database, injectRepository } from '../server/index.js';
|
|
@@ -137,8 +137,10 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
137
137
|
)
|
|
138
138
|
`);
|
|
139
139
|
});
|
|
140
|
-
|
|
140
|
+
beforeEach(async () => {
|
|
141
141
|
await truncateTables(database, schema, ['users']);
|
|
142
|
+
});
|
|
143
|
+
test('should insert into both parent and child tables', async () => {
|
|
142
144
|
await runInInjectionContext(injector, async () => {
|
|
143
145
|
const repository = injectRepository(Admin);
|
|
144
146
|
const newAdmin = new Admin();
|
|
@@ -154,17 +156,16 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
154
156
|
// Verify DB state
|
|
155
157
|
const { rows: [userRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('users')} WHERE id = ${inserted.id}`);
|
|
156
158
|
expect(userRow).toBeDefined();
|
|
157
|
-
expect(userRow
|
|
158
|
-
expect(userRow
|
|
159
|
-
expect(userRow
|
|
159
|
+
expect(userRow['type']).toBe('admin');
|
|
160
|
+
expect(userRow['name']).toBe('Alice');
|
|
161
|
+
expect(userRow['revision']).toBe(1);
|
|
160
162
|
const { rows: [adminRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('admins')} WHERE id = ${inserted.id}`);
|
|
161
163
|
expect(adminRow).toBeDefined();
|
|
162
|
-
expect(adminRow
|
|
163
|
-
expect(adminRow
|
|
164
|
+
expect(adminRow['type']).toBe('admin');
|
|
165
|
+
expect(adminRow['role']).toBe('SuperAdmin');
|
|
164
166
|
});
|
|
165
167
|
});
|
|
166
168
|
test('should insert many into both parent and child tables', async () => {
|
|
167
|
-
await truncateTables(database, schema, ['users']);
|
|
168
169
|
await runInInjectionContext(injector, async () => {
|
|
169
170
|
const repository = injectRepository(Admin);
|
|
170
171
|
const admins = [
|
|
@@ -179,14 +180,13 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
179
180
|
expect(insertedAdmins[1].type).toBe('admin');
|
|
180
181
|
for (const inserted of insertedAdmins) {
|
|
181
182
|
const { rows: [userRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('users')} WHERE id = ${inserted.id}`);
|
|
182
|
-
expect(userRow
|
|
183
|
+
expect(userRow['name']).toBe(inserted.name);
|
|
183
184
|
const { rows: [adminRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('admins')} WHERE id = ${inserted.id}`);
|
|
184
|
-
expect(adminRow
|
|
185
|
+
expect(adminRow['role']).toBe(inserted.role);
|
|
185
186
|
}
|
|
186
187
|
});
|
|
187
188
|
});
|
|
188
189
|
test('should update both parent and child tables', async () => {
|
|
189
|
-
await truncateTables(database, schema, ['users']);
|
|
190
190
|
await runInInjectionContext(injector, async () => {
|
|
191
191
|
const repository = injectRepository(Admin);
|
|
192
192
|
const newAdmin = Object.assign(new Admin(), { name: 'Alice', role: 'SuperAdmin' });
|
|
@@ -198,14 +198,13 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
198
198
|
expect(updated.metadata.revision).toBe(2);
|
|
199
199
|
// Verify DB state
|
|
200
200
|
const { rows: [userRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('users')} WHERE id = ${inserted.id}`);
|
|
201
|
-
expect(userRow
|
|
202
|
-
expect(userRow
|
|
201
|
+
expect(userRow['name']).toBe('Alice Updated');
|
|
202
|
+
expect(userRow['revision']).toBe(2);
|
|
203
203
|
const { rows: [adminRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('admins')} WHERE id = ${inserted.id}`);
|
|
204
|
-
expect(adminRow
|
|
204
|
+
expect(adminRow['role']).toBe('MegaAdmin');
|
|
205
205
|
});
|
|
206
206
|
});
|
|
207
207
|
test('should soft delete from parent table', async () => {
|
|
208
|
-
await truncateTables(database, schema, ['users']);
|
|
209
208
|
await runInInjectionContext(injector, async () => {
|
|
210
209
|
const repository = injectRepository(Admin);
|
|
211
210
|
const newAdmin = Object.assign(new Admin(), { name: 'Alice', role: 'SuperAdmin' });
|
|
@@ -213,14 +212,13 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
213
212
|
await repository.delete(inserted.id);
|
|
214
213
|
// Verify DB state
|
|
215
214
|
const { rows: [userRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('users')} WHERE id = ${inserted.id}`);
|
|
216
|
-
expect(userRow
|
|
215
|
+
expect(userRow['delete_timestamp']).not.toBeNull();
|
|
217
216
|
// Child table should still have the row (soft delete only affects parent)
|
|
218
217
|
const { rows: [adminRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('admins')} WHERE id = ${inserted.id}`);
|
|
219
218
|
expect(adminRow).toBeDefined();
|
|
220
219
|
});
|
|
221
220
|
});
|
|
222
221
|
test('should hard delete from both tables via cascade', async () => {
|
|
223
|
-
await truncateTables(database, schema, ['users']);
|
|
224
222
|
await runInInjectionContext(injector, async () => {
|
|
225
223
|
const repository = injectRepository(Admin);
|
|
226
224
|
const newAdmin = Object.assign(new Admin(), { name: 'Alice', role: 'SuperAdmin' });
|
|
@@ -234,7 +232,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
234
232
|
});
|
|
235
233
|
});
|
|
236
234
|
test('should load entities polymorphically from parent repository', async () => {
|
|
237
|
-
await truncateTables(database, schema, ['users']);
|
|
238
235
|
await runInInjectionContext(injector, async () => {
|
|
239
236
|
const adminRepository = injectRepository(Admin);
|
|
240
237
|
const guestRepository = injectRepository(Guest);
|
|
@@ -265,7 +262,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
265
262
|
});
|
|
266
263
|
});
|
|
267
264
|
test('should load polymorphic subset from parent repository', async () => {
|
|
268
|
-
await truncateTables(database, schema, ['users']);
|
|
269
265
|
await runInInjectionContext(injector, async () => {
|
|
270
266
|
const adminRepository = injectRepository(Admin);
|
|
271
267
|
const guestRepository = injectRepository(Guest);
|
|
@@ -284,7 +280,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
284
280
|
});
|
|
285
281
|
});
|
|
286
282
|
test('should count and check existence for child entities', async () => {
|
|
287
|
-
await truncateTables(database, schema, ['users']);
|
|
288
283
|
await runInInjectionContext(injector, async () => {
|
|
289
284
|
const adminRepository = injectRepository(Admin);
|
|
290
285
|
const guestRepository = injectRepository(Guest);
|
|
@@ -300,7 +295,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
300
295
|
});
|
|
301
296
|
});
|
|
302
297
|
test('should query child entities by parent fields', async () => {
|
|
303
|
-
await truncateTables(database, schema, ['users']);
|
|
304
298
|
await runInInjectionContext(injector, async () => {
|
|
305
299
|
const adminRepository = injectRepository(Admin);
|
|
306
300
|
await adminRepository.insert(Object.assign(new Admin(), { name: 'TargetAdmin', role: 'Super' }));
|
|
@@ -312,7 +306,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
312
306
|
});
|
|
313
307
|
});
|
|
314
308
|
test('should perform partial updates on parent or child fields', async () => {
|
|
315
|
-
await truncateTables(database, schema, ['users']);
|
|
316
309
|
await runInInjectionContext(injector, async () => {
|
|
317
310
|
const adminRepository = injectRepository(Admin);
|
|
318
311
|
const inserted = await adminRepository.insert(Object.assign(new Admin(), { name: 'Alice', role: 'Super' }));
|
|
@@ -351,7 +344,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
351
344
|
await expect(query).rejects.toThrow();
|
|
352
345
|
});
|
|
353
346
|
test('should support nested inheritance', async () => {
|
|
354
|
-
await truncateTables(database, schema, ['users']);
|
|
355
347
|
await runInInjectionContext(injector, async () => {
|
|
356
348
|
const managerRepository = injectRepository(Manager);
|
|
357
349
|
const manager = new Manager();
|
|
@@ -365,15 +357,14 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
365
357
|
expect(inserted.type).toBe('manager');
|
|
366
358
|
// Verify DB state - all 3 tables should have rows
|
|
367
359
|
const { rows: [userRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('users')} WHERE id = ${inserted.id}`);
|
|
368
|
-
expect(userRow
|
|
360
|
+
expect(userRow['name']).toBe('Big Boss');
|
|
369
361
|
const { rows: [staffRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('staff')} WHERE id = ${inserted.id}`);
|
|
370
|
-
expect(staffRow
|
|
362
|
+
expect(staffRow['employee_id']).toBe('EMP001');
|
|
371
363
|
const { rows: [managerRow] } = await database.execute(sql `SELECT * FROM ${sql.identifier(schema)}.${sql.identifier('managers')} WHERE id = ${inserted.id}`);
|
|
372
|
-
expect(managerRow
|
|
364
|
+
expect(managerRow['department']).toBe('Executive');
|
|
373
365
|
});
|
|
374
366
|
});
|
|
375
367
|
test('should rollback parent insert if child insert fails', async () => {
|
|
376
|
-
await truncateTables(database, schema, ['users']);
|
|
377
368
|
await runInInjectionContext(injector, async () => {
|
|
378
369
|
const adminRepository = injectRepository(Admin);
|
|
379
370
|
// Attempt to insert an admin with a missing role (should fail because of NOT NULL)
|
|
@@ -387,7 +378,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
387
378
|
});
|
|
388
379
|
});
|
|
389
380
|
test('should count and check existence polymorphically', async () => {
|
|
390
|
-
await truncateTables(database, schema, ['users']);
|
|
391
381
|
await runInInjectionContext(injector, async () => {
|
|
392
382
|
const adminRepository = injectRepository(Admin);
|
|
393
383
|
const guestRepository = injectRepository(Guest);
|
|
@@ -400,7 +390,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
400
390
|
});
|
|
401
391
|
});
|
|
402
392
|
test('should load many by IDs polymorphically', async () => {
|
|
403
|
-
await truncateTables(database, schema, ['users']);
|
|
404
393
|
await runInInjectionContext(injector, async () => {
|
|
405
394
|
const adminRepository = injectRepository(Admin);
|
|
406
395
|
const guestRepository = injectRepository(Guest);
|
|
@@ -414,7 +403,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
414
403
|
});
|
|
415
404
|
});
|
|
416
405
|
test('should update many child entities', async () => {
|
|
417
|
-
await truncateTables(database, schema, ['users']);
|
|
418
406
|
await runInInjectionContext(injector, async () => {
|
|
419
407
|
const adminRepository = injectRepository(Admin);
|
|
420
408
|
const a1 = await adminRepository.insert(Object.assign(new Admin(), { name: 'A1', role: 'Old' }));
|
|
@@ -426,7 +414,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
426
414
|
});
|
|
427
415
|
});
|
|
428
416
|
test('should update nested inheritance fields', async () => {
|
|
429
|
-
await truncateTables(database, schema, ['users']);
|
|
430
417
|
await runInInjectionContext(injector, async () => {
|
|
431
418
|
const managerRepository = injectRepository(Manager);
|
|
432
419
|
const manager = Object.assign(new Manager(), { name: 'Boss', employeeId: 'E1', department: 'D1' });
|
|
@@ -439,7 +426,6 @@ describe('ORM Repository CTI (Integration)', () => {
|
|
|
439
426
|
});
|
|
440
427
|
});
|
|
441
428
|
test('should delete from nested inheritance tables', async () => {
|
|
442
|
-
await truncateTables(database, schema, ['users']);
|
|
443
429
|
await runInInjectionContext(injector, async () => {
|
|
444
430
|
const managerRepository = injectRepository(Manager);
|
|
445
431
|
const manager = Object.assign(new Manager(), { name: 'Boss', employeeId: 'E1', department: 'D1' });
|
|
@@ -10,7 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
import { NotFoundError } from '../../errors/not-found.error.js';
|
|
11
11
|
import { Injector, runInInjectionContext } from '../../injector/index.js';
|
|
12
12
|
import { StringProperty } from '../../schema/index.js';
|
|
13
|
-
import { dropTables, setupIntegrationTest, truncateTables } from '../../
|
|
13
|
+
import { dropTables, setupIntegrationTest, truncateTables } from '../../testing/index.js';
|
|
14
14
|
import { toArrayAsync } from '../../utils/async-iterable-helpers/to-array.js';
|
|
15
15
|
import { sql } from 'drizzle-orm';
|
|
16
16
|
import { beforeAll, describe, expect, test } from 'vitest';
|
|
@@ -8,10 +8,10 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
import { sql } from 'drizzle-orm';
|
|
11
|
-
import { beforeAll, describe, expect, test } from 'vitest';
|
|
11
|
+
import { beforeAll, beforeEach, describe, expect, test } from 'vitest';
|
|
12
12
|
import { Injector, runInInjectionContext } from '../../injector/index.js';
|
|
13
13
|
import { StringProperty } from '../../schema/index.js';
|
|
14
|
-
import { dropTables, setupIntegrationTest } from '../../
|
|
14
|
+
import { dropTables, setupIntegrationTest, truncateTables } from '../../testing/index.js';
|
|
15
15
|
import { Table } from '../decorators.js';
|
|
16
16
|
import { Entity } from '../entity.js';
|
|
17
17
|
import { Database } from '../server/index.js';
|
|
@@ -51,6 +51,9 @@ describe('ORM Repository Search', () => {
|
|
|
51
51
|
)
|
|
52
52
|
`);
|
|
53
53
|
});
|
|
54
|
+
beforeEach(async () => {
|
|
55
|
+
await truncateTables(database, schema, ['search_entities']);
|
|
56
|
+
});
|
|
54
57
|
test('should support search with score transformer', async () => {
|
|
55
58
|
await runInInjectionContext(injector, async () => {
|
|
56
59
|
const repository = injectRepository(SearchEntity);
|
|
@@ -42,6 +42,7 @@ describe('ORM Schema Generation - Detailed Configurations', () => {
|
|
|
42
42
|
expect(config.indexes.length).toBeGreaterThan(0);
|
|
43
43
|
const idx = config.indexes[0];
|
|
44
44
|
expect(idx).toBeDefined();
|
|
45
|
+
expect(idx.config.name).toContain('partial_idx');
|
|
45
46
|
expect(idx.config.where).toBeDefined();
|
|
46
47
|
});
|
|
47
48
|
test('should handle index with nulls ordering', () => {
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { Injector, Singleton, runInInjectionContext } from '../../injector/index.js';
|
|
11
11
|
import { StringProperty } from '../../schema/index.js';
|
|
12
|
-
import { dropTables, setupIntegrationTest } from '../../
|
|
12
|
+
import { dropTables, setupIntegrationTest } from '../../testing/index.js';
|
|
13
13
|
import { sql } from 'drizzle-orm';
|
|
14
14
|
import { beforeAll, describe, expect, test } from 'vitest';
|
|
15
15
|
import { Entity, Table } from '../index.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tstdl/base",
|
|
3
|
-
"version": "0.93.
|
|
3
|
+
"version": "0.93.126",
|
|
4
4
|
"author": "Patrick Hein",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -76,7 +76,6 @@
|
|
|
76
76
|
"./jsx": "./jsx/index.js",
|
|
77
77
|
"./key-value-store": "./key-value-store/index.js",
|
|
78
78
|
"./key-value-store/postgres": "./key-value-store/postgres/index.js",
|
|
79
|
-
"./latex": "./latex/index.js",
|
|
80
79
|
"./lock": "./lock/index.js",
|
|
81
80
|
"./lock/postgres": "./lock/postgres/index.js",
|
|
82
81
|
"./lock/web": "./lock/web/index.js",
|
|
@@ -93,8 +92,8 @@
|
|
|
93
92
|
"./object-storage/s3": "./object-storage/s3/index.js",
|
|
94
93
|
"./openid-connect": "./openid-connect/index.js",
|
|
95
94
|
"./orm": "./orm/index.js",
|
|
96
|
-
"./orm/schemas": "./orm/schemas/index.js",
|
|
97
95
|
"./orm/data-types": "./orm/data-types/index.js",
|
|
96
|
+
"./orm/schemas": "./orm/schemas/index.js",
|
|
98
97
|
"./orm/server": "./orm/server/index.js",
|
|
99
98
|
"./password": "./password/index.js",
|
|
100
99
|
"./pdf": "./pdf/index.js",
|
|
@@ -105,6 +104,7 @@
|
|
|
105
104
|
"./rate-limit": "./rate-limit/index.js",
|
|
106
105
|
"./rate-limit/postgres": "./rate-limit/postgres/index.js",
|
|
107
106
|
"./reflection": "./reflection/index.js",
|
|
107
|
+
"./renderer": "./renderer/index.js",
|
|
108
108
|
"./rpc": "./rpc/index.js",
|
|
109
109
|
"./rpc/endpoints": "./rpc/endpoints/index.js",
|
|
110
110
|
"./rxjs-utils": "./rxjs-utils/index.js",
|
|
@@ -125,11 +125,10 @@
|
|
|
125
125
|
"./templates/renderers": "./templates/renderers/index.js",
|
|
126
126
|
"./templates/resolvers": "./templates/resolvers/index.js",
|
|
127
127
|
"./templates/types": "./templates/types/index.js",
|
|
128
|
+
"./testing": "./testing/index.js",
|
|
128
129
|
"./text": "./text/index.js",
|
|
129
130
|
"./threading": "./threading/index.js",
|
|
130
131
|
"./types": "./types/index.js",
|
|
131
|
-
"./typst": "./typst/index.js",
|
|
132
|
-
"./unit-test": "./unit-test/index.js",
|
|
133
132
|
"./utils": "./utils/index.js",
|
|
134
133
|
"./utils/array": "./utils/array/index.js",
|
|
135
134
|
"./utils/async-hook": "./utils/async-hook/index.js",
|
|
@@ -153,7 +152,6 @@
|
|
|
153
152
|
"peerDependencies": {
|
|
154
153
|
"@genkit-ai/google-genai": "^1.28",
|
|
155
154
|
"@google-cloud/storage": "^7.19",
|
|
156
|
-
"@google/genai": "^1.40",
|
|
157
155
|
"@toon-format/toon": "^2.1.0",
|
|
158
156
|
"@tstdl/angular": "^0.93",
|
|
159
157
|
"@zxcvbn-ts/core": "^3.0",
|
|
@@ -164,8 +162,8 @@
|
|
|
164
162
|
"file-type": "^21.3",
|
|
165
163
|
"genkit": "^1.28",
|
|
166
164
|
"handlebars": "^4.7",
|
|
167
|
-
"@aws-sdk/client-s3": "^3.
|
|
168
|
-
"@aws-sdk/s3-request-presigner": "^3.
|
|
165
|
+
"@aws-sdk/client-s3": "^3.990",
|
|
166
|
+
"@aws-sdk/s3-request-presigner": "^3.990",
|
|
169
167
|
"mjml": "^4.18",
|
|
170
168
|
"nodemailer": "^8.0",
|
|
171
169
|
"pg": "^8.18",
|
|
@@ -173,7 +171,7 @@
|
|
|
173
171
|
"preact": "^10.28",
|
|
174
172
|
"preact-render-to-string": "^6.6",
|
|
175
173
|
"sharp": "^0.34",
|
|
176
|
-
"undici": "^7.
|
|
174
|
+
"undici": "^7.22",
|
|
177
175
|
"urlpattern-polyfill": "^10.1",
|
|
178
176
|
"zod": "^3.25"
|
|
179
177
|
},
|
|
@@ -1,26 +1,23 @@
|
|
|
1
|
-
import { afterAll, describe, expect, it } from 'vitest';
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
2
2
|
import { RateLimiterProvider } from '../../rate-limit/index.js';
|
|
3
|
-
import { setupIntegrationTest } from '../../
|
|
3
|
+
import { setupIntegrationTest } from '../../testing/index.js';
|
|
4
4
|
import { timeout } from '../../utils/timing.js';
|
|
5
5
|
describe('PostgresRateLimiter Integration Tests', () => {
|
|
6
6
|
let injector;
|
|
7
7
|
let rateLimiter;
|
|
8
8
|
const limiterName = `test-limiter-${Date.now()}`;
|
|
9
|
-
async
|
|
10
|
-
|
|
11
|
-
({ injector } = await setupIntegrationTest({ modules: { rateLimiter: true } }));
|
|
12
|
-
}
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
({ injector } = await setupIntegrationTest({ modules: { rateLimiter: true } }));
|
|
13
11
|
const rateLimiterProvider = injector.resolve(RateLimiterProvider);
|
|
14
|
-
|
|
12
|
+
rateLimiter = rateLimiterProvider.get(limiterName, {
|
|
15
13
|
burstCapacity: 10,
|
|
16
14
|
refillInterval: 1000, // 10 tokens per second -> 1 token per 100ms
|
|
17
15
|
});
|
|
18
|
-
}
|
|
16
|
+
});
|
|
19
17
|
afterAll(async () => {
|
|
20
18
|
await injector?.dispose();
|
|
21
19
|
}, 15000);
|
|
22
20
|
it('should acquire tokens successfully', async () => {
|
|
23
|
-
rateLimiter = await getLimiter();
|
|
24
21
|
const resource = 'res-1';
|
|
25
22
|
const result = await rateLimiter.tryAcquire(resource, 5);
|
|
26
23
|
expect(result).toBe(true);
|
|
@@ -30,7 +27,6 @@ describe('PostgresRateLimiter Integration Tests', () => {
|
|
|
30
27
|
expect(result3).toBe(false);
|
|
31
28
|
});
|
|
32
29
|
it('should refill tokens over time', async () => {
|
|
33
|
-
rateLimiter = await getLimiter();
|
|
34
30
|
const resource = 'res-2';
|
|
35
31
|
await rateLimiter.tryAcquire(resource, 10);
|
|
36
32
|
expect(await rateLimiter.tryAcquire(resource, 1)).toBe(false);
|
|
@@ -40,7 +36,6 @@ describe('PostgresRateLimiter Integration Tests', () => {
|
|
|
40
36
|
expect(await rateLimiter.tryAcquire(resource, 1)).toBe(false);
|
|
41
37
|
});
|
|
42
38
|
it('should refund tokens', async () => {
|
|
43
|
-
rateLimiter = await getLimiter();
|
|
44
39
|
const resource = 'res-3';
|
|
45
40
|
await rateLimiter.tryAcquire(resource, 10);
|
|
46
41
|
expect(await rateLimiter.tryAcquire(resource, 1)).toBe(false);
|
|
@@ -49,14 +44,12 @@ describe('PostgresRateLimiter Integration Tests', () => {
|
|
|
49
44
|
expect(await rateLimiter.tryAcquire(resource, 1)).toBe(false);
|
|
50
45
|
});
|
|
51
46
|
it('should handle fractional tokens', async () => {
|
|
52
|
-
rateLimiter = await getLimiter();
|
|
53
47
|
const resource = 'res-4';
|
|
54
48
|
await rateLimiter.tryAcquire(resource, 9.5);
|
|
55
49
|
expect(await rateLimiter.tryAcquire(resource, 1)).toBe(false);
|
|
56
50
|
expect(await rateLimiter.tryAcquire(resource, 0.5)).toBe(true);
|
|
57
51
|
});
|
|
58
52
|
it('should handle concurrent acquisitions', async () => {
|
|
59
|
-
rateLimiter = await getLimiter();
|
|
60
53
|
const resource = 'res-concurrent';
|
|
61
54
|
// Initialize
|
|
62
55
|
await rateLimiter.tryAcquire(resource, 0);
|
|
@@ -65,19 +58,16 @@ describe('PostgresRateLimiter Integration Tests', () => {
|
|
|
65
58
|
expect(successCount).toBe(10);
|
|
66
59
|
}, 15000);
|
|
67
60
|
it('should always allow zero or negative cost', async () => {
|
|
68
|
-
rateLimiter = await getLimiter();
|
|
69
61
|
const resource = 'res-zero';
|
|
70
62
|
expect(await rateLimiter.tryAcquire(resource, 0)).toBe(true);
|
|
71
63
|
expect(await rateLimiter.tryAcquire(resource, -1)).toBe(true);
|
|
72
64
|
});
|
|
73
65
|
it('should reject cost greater than burst capacity', async () => {
|
|
74
|
-
rateLimiter = await getLimiter();
|
|
75
66
|
const resource = 'res-large';
|
|
76
67
|
// Burst capacity is 10
|
|
77
68
|
expect(await rateLimiter.tryAcquire(resource, 11)).toBe(false);
|
|
78
69
|
});
|
|
79
70
|
it('should cap tokens at burst capacity on refill', async () => {
|
|
80
|
-
rateLimiter = await getLimiter();
|
|
81
71
|
const resource = 'res-refill-cap';
|
|
82
72
|
// Drain
|
|
83
73
|
await rateLimiter.tryAcquire(resource, 10);
|
package/renderer/d2.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export type D2RenderOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* The layout engine to use.
|
|
4
|
+
* @default 'dagre'
|
|
5
|
+
*/
|
|
6
|
+
layout?: string;
|
|
7
|
+
/**
|
|
8
|
+
* The theme ID to use.
|
|
9
|
+
* @default 0
|
|
10
|
+
*/
|
|
11
|
+
theme?: number;
|
|
12
|
+
/**
|
|
13
|
+
* The dark theme ID to use.
|
|
14
|
+
*/
|
|
15
|
+
darkTheme?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Pixels padded around the rendered diagram.
|
|
18
|
+
* @default 100
|
|
19
|
+
*/
|
|
20
|
+
pad?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Render the diagram to look like it was sketched by hand.
|
|
23
|
+
* @default false
|
|
24
|
+
*/
|
|
25
|
+
sketch?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Center the SVG in the containing viewbox.
|
|
28
|
+
* @default false
|
|
29
|
+
*/
|
|
30
|
+
center?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* if given, multiple boards are packaged as 1 SVG which transitions through each board at the interval (in milliseconds).
|
|
33
|
+
* Can only be used with SVG exports.
|
|
34
|
+
* @default 0
|
|
35
|
+
*/
|
|
36
|
+
animateInterval?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Omit XML tag (<?xml ...?>) from output SVG files.
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
noXmlTag?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Add a salt value to ensure the output uses unique IDs.
|
|
44
|
+
*/
|
|
45
|
+
salt?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Omit D2 version from generated image.
|
|
48
|
+
* @default false
|
|
49
|
+
*/
|
|
50
|
+
omitVersion?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Target board to render.
|
|
53
|
+
* @default '*'
|
|
54
|
+
*/
|
|
55
|
+
target?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Scale the output. E.g., 0.5 to halve the default size.
|
|
58
|
+
* @default -1
|
|
59
|
+
*/
|
|
60
|
+
scale?: number;
|
|
61
|
+
/**
|
|
62
|
+
* The output format for the rendered diagram.
|
|
63
|
+
* @default 'svg'
|
|
64
|
+
*/
|
|
65
|
+
format?: 'svg' | 'png' | 'ascii' | 'txt' | 'pdf' | 'pptx' | 'gif';
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Renders D2 source code to a file in the specified format.
|
|
69
|
+
*
|
|
70
|
+
* ## WARNING
|
|
71
|
+
* **This function should not be used with untrusted D2 source, as it can lead to arbitrary code execution on the system.**
|
|
72
|
+
*
|
|
73
|
+
* Requires d2 to be installed on the system.
|
|
74
|
+
* @param source The D2 source code to render
|
|
75
|
+
* @param options Rendering options
|
|
76
|
+
*/
|
|
77
|
+
export declare function renderD2(source: string, options?: D2RenderOptions): Promise<Uint8Array<ArrayBuffer>>;
|
package/renderer/d2.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { spawnCommand } from '../process/spawn.js';
|
|
2
|
+
import { decodeText } from '../utils/encoding.js';
|
|
3
|
+
import { isDefined } from '../utils/type-guards.js';
|
|
4
|
+
/**
|
|
5
|
+
* Renders D2 source code to a file in the specified format.
|
|
6
|
+
*
|
|
7
|
+
* ## WARNING
|
|
8
|
+
* **This function should not be used with untrusted D2 source, as it can lead to arbitrary code execution on the system.**
|
|
9
|
+
*
|
|
10
|
+
* Requires d2 to be installed on the system.
|
|
11
|
+
* @param source The D2 source code to render
|
|
12
|
+
* @param options Rendering options
|
|
13
|
+
*/
|
|
14
|
+
export async function renderD2(source, options) {
|
|
15
|
+
const format = options?.format ?? 'svg';
|
|
16
|
+
const args = ['-', '-', '--stdout-format', format];
|
|
17
|
+
if (isDefined(options?.layout)) {
|
|
18
|
+
args.push('--layout', options.layout);
|
|
19
|
+
}
|
|
20
|
+
if (isDefined(options?.theme)) {
|
|
21
|
+
args.push('--theme', options.theme.toString());
|
|
22
|
+
}
|
|
23
|
+
if (isDefined(options?.darkTheme)) {
|
|
24
|
+
args.push('--dark-theme', options.darkTheme.toString());
|
|
25
|
+
}
|
|
26
|
+
if (isDefined(options?.pad)) {
|
|
27
|
+
args.push('--pad', options.pad.toString());
|
|
28
|
+
}
|
|
29
|
+
if (options?.sketch == true) {
|
|
30
|
+
args.push('--sketch');
|
|
31
|
+
}
|
|
32
|
+
if (options?.center == true) {
|
|
33
|
+
args.push('--center');
|
|
34
|
+
}
|
|
35
|
+
if (isDefined(options?.animateInterval)) {
|
|
36
|
+
args.push('--animate-interval', options.animateInterval.toString());
|
|
37
|
+
}
|
|
38
|
+
if (options?.noXmlTag == true) {
|
|
39
|
+
args.push('--no-xml-tag');
|
|
40
|
+
}
|
|
41
|
+
if (isDefined(options?.salt)) {
|
|
42
|
+
args.push('--salt', options.salt);
|
|
43
|
+
}
|
|
44
|
+
if (options?.omitVersion == true) {
|
|
45
|
+
args.push('--omit-version');
|
|
46
|
+
}
|
|
47
|
+
if (isDefined(options?.target)) {
|
|
48
|
+
args.push('--target', options.target);
|
|
49
|
+
}
|
|
50
|
+
if (isDefined(options?.scale)) {
|
|
51
|
+
args.push('--scale', options.scale.toString());
|
|
52
|
+
}
|
|
53
|
+
const process = await spawnCommand('d2', args);
|
|
54
|
+
const [{ code, output, error }] = await Promise.all([
|
|
55
|
+
process.waitRead('binary', { throwOnNonZeroExitCode: false }),
|
|
56
|
+
process.write(source),
|
|
57
|
+
]);
|
|
58
|
+
const errorString = decodeText(error);
|
|
59
|
+
if (code !== 0) {
|
|
60
|
+
throw new Error(`
|
|
61
|
+
D2 rendering failed with exit code ${code}.
|
|
62
|
+
|
|
63
|
+
Error Output:
|
|
64
|
+
${errorString}
|
|
65
|
+
`.trim());
|
|
66
|
+
}
|
|
67
|
+
return output;
|
|
68
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type GraphvizRenderOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* The layout engine to use.
|
|
4
|
+
* @default 'dot'
|
|
5
|
+
*/
|
|
6
|
+
engine?: 'dot' | 'neato' | 'fdp' | 'sfdp' | 'twopi' | 'circo';
|
|
7
|
+
/**
|
|
8
|
+
* The output format for the rendered diagram.
|
|
9
|
+
* @default 'svg'
|
|
10
|
+
*/
|
|
11
|
+
format?: 'svg' | 'png' | 'pdf' | 'jpg' | 'gif' | 'bmp' | 'eps' | 'ps' | 'tiff' | 'webp' | 'canon' | 'dot' | 'xdot' | 'plain' | 'plain-ext';
|
|
12
|
+
/**
|
|
13
|
+
* Set graph attributes (equivalent to -Gname=val)
|
|
14
|
+
*/
|
|
15
|
+
graphAttributes?: Record<string, string>;
|
|
16
|
+
/**
|
|
17
|
+
* Set node attributes (equivalent to -Nname=val)
|
|
18
|
+
*/
|
|
19
|
+
nodeAttributes?: Record<string, string>;
|
|
20
|
+
/**
|
|
21
|
+
* Set edge attributes (equivalent to -Ename=val)
|
|
22
|
+
*/
|
|
23
|
+
edgeAttributes?: Record<string, string>;
|
|
24
|
+
/**
|
|
25
|
+
* Scale input by 'scale' (equivalent to -s[scale])
|
|
26
|
+
*/
|
|
27
|
+
scale?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Invert y coordinate in output (equivalent to -y)
|
|
30
|
+
*/
|
|
31
|
+
invertY?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Set level of message suppression (equivalent to -q[quiet])
|
|
34
|
+
*/
|
|
35
|
+
quiet?: boolean | number;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Renders Graphviz (DOT) source code to a file in the specified format.
|
|
39
|
+
*
|
|
40
|
+
* ## WARNING
|
|
41
|
+
* **This function should not be used with untrusted Graphviz source, as it can lead to arbitrary code execution on the system.**
|
|
42
|
+
*
|
|
43
|
+
* Requires Graphviz to be installed on the system.
|
|
44
|
+
* @param source The Graphviz source code to render
|
|
45
|
+
* @param options Rendering options
|
|
46
|
+
*/
|
|
47
|
+
export declare function renderGraphviz(source: string, options?: GraphvizRenderOptions): Promise<Uint8Array<ArrayBuffer>>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { spawnCommand } from '../process/spawn.js';
|
|
2
|
+
import { decodeText } from '../utils/encoding.js';
|
|
3
|
+
import { isDefined } from '../utils/type-guards.js';
|
|
4
|
+
/**
|
|
5
|
+
* Renders Graphviz (DOT) source code to a file in the specified format.
|
|
6
|
+
*
|
|
7
|
+
* ## WARNING
|
|
8
|
+
* **This function should not be used with untrusted Graphviz source, as it can lead to arbitrary code execution on the system.**
|
|
9
|
+
*
|
|
10
|
+
* Requires Graphviz to be installed on the system.
|
|
11
|
+
* @param source The Graphviz source code to render
|
|
12
|
+
* @param options Rendering options
|
|
13
|
+
*/
|
|
14
|
+
export async function renderGraphviz(source, options) {
|
|
15
|
+
const engine = options?.engine ?? 'dot';
|
|
16
|
+
const format = options?.format ?? 'svg';
|
|
17
|
+
const args = [`-T${format}`];
|
|
18
|
+
if (isDefined(options?.graphAttributes)) {
|
|
19
|
+
for (const [name, val] of Object.entries(options.graphAttributes)) {
|
|
20
|
+
args.push(`-G${name}=${val}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (isDefined(options?.nodeAttributes)) {
|
|
24
|
+
for (const [name, val] of Object.entries(options.nodeAttributes)) {
|
|
25
|
+
args.push(`-N${name}=${val}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (isDefined(options?.edgeAttributes)) {
|
|
29
|
+
for (const [name, val] of Object.entries(options.edgeAttributes)) {
|
|
30
|
+
args.push(`-E${name}=${val}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (isDefined(options?.scale)) {
|
|
34
|
+
args.push(`-s${options.scale}`);
|
|
35
|
+
}
|
|
36
|
+
if (options?.invertY == true) {
|
|
37
|
+
args.push('-y');
|
|
38
|
+
}
|
|
39
|
+
if (isDefined(options?.quiet)) {
|
|
40
|
+
const level = (options.quiet == true) ? '' : options.quiet.toString();
|
|
41
|
+
args.push(`-q${level}`);
|
|
42
|
+
}
|
|
43
|
+
const process = await spawnCommand(engine, args);
|
|
44
|
+
const [{ code, output, error }] = await Promise.all([
|
|
45
|
+
process.waitRead('binary', { throwOnNonZeroExitCode: false }),
|
|
46
|
+
process.write(source),
|
|
47
|
+
]);
|
|
48
|
+
const errorString = decodeText(error);
|
|
49
|
+
if (code !== 0) {
|
|
50
|
+
throw new Error(`
|
|
51
|
+
Graphviz rendering failed with exit code ${code}.
|
|
52
|
+
|
|
53
|
+
Error Output:
|
|
54
|
+
${errorString}
|
|
55
|
+
`.trim());
|
|
56
|
+
}
|
|
57
|
+
return output;
|
|
58
|
+
}
|