@tstdl/base 0.93.129 → 0.93.131
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/document-management/models/ai-configuration.d.ts +1 -1
- package/document-management/server/services/document-management-ai.service.js +4 -3
- package/document-management/tests/ai-config-merge.test.js +8 -0
- package/package.json +4 -4
- package/pdf/tests/utils.test.d.ts +1 -0
- package/pdf/tests/utils.test.js +187 -0
- package/pdf/utils.js +10 -27
- package/process/spawn.js +36 -20
- package/process/tests/spawn.test.d.ts +1 -0
- package/process/tests/spawn.test.js +182 -0
- package/renderer/d2.js +3 -14
- package/renderer/graphviz.js +3 -14
- package/renderer/tests/renderer.test.d.ts +1 -0
- package/renderer/tests/renderer.test.js +88 -0
- package/renderer/typst.js +6 -13
- package/test5.js +2 -52
|
@@ -9,7 +9,7 @@ var DocumentManagementAiService_1;
|
|
|
9
9
|
import { and, isNull as drizzleIsNull, eq, inArray } from 'drizzle-orm';
|
|
10
10
|
import { P, match } from 'ts-pattern';
|
|
11
11
|
import { genkitGenerationOptions, injectGenkit, injectModel } from '../../../ai/genkit/index.js';
|
|
12
|
-
import { buildPrompts, jsonOutputInstructions, orderedList } from '../../../ai/prompts/index.js';
|
|
12
|
+
import { buildPrompts, formatInstructions, jsonOutputInstructions, orderedList } from '../../../ai/prompts/index.js';
|
|
13
13
|
import { inject } from '../../../injector/inject.js';
|
|
14
14
|
import { Logger } from '../../../logger/logger.js';
|
|
15
15
|
import { arrayAgg } from '../../../orm/index.js';
|
|
@@ -497,11 +497,12 @@ export function mergeInstructions(base, overrides, options = {}) {
|
|
|
497
497
|
}
|
|
498
498
|
if (isDefined(override.content)) {
|
|
499
499
|
const strategy = override.strategy ?? 'append';
|
|
500
|
+
const content = isString(override.content) ? override.content : formatInstructions(override.content);
|
|
500
501
|
if (strategy == 'replace') {
|
|
501
|
-
result =
|
|
502
|
+
result = content;
|
|
502
503
|
}
|
|
503
504
|
else {
|
|
504
|
-
result = `${result}\n\n${
|
|
505
|
+
result = `${result}\n\n${content}`;
|
|
505
506
|
}
|
|
506
507
|
}
|
|
507
508
|
}
|
|
@@ -35,4 +35,12 @@ describe('AI Instruction Merging', () => {
|
|
|
35
35
|
const result = mergeInstructions(base, overrides);
|
|
36
36
|
expect(result).toContain('[ID] - [Name]');
|
|
37
37
|
});
|
|
38
|
+
test('should handle Instructions in content', () => {
|
|
39
|
+
const base = 'Base';
|
|
40
|
+
const overrides = [{ content: { Detail: 'More info' }, strategy: 'append' }];
|
|
41
|
+
const result = mergeInstructions(base, overrides);
|
|
42
|
+
expect(result).toContain('Base');
|
|
43
|
+
expect(result).toContain('# Detail');
|
|
44
|
+
expect(result).toContain('More info');
|
|
45
|
+
});
|
|
38
46
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tstdl/base",
|
|
3
|
-
"version": "0.93.
|
|
3
|
+
"version": "0.93.131",
|
|
4
4
|
"author": "Patrick Hein",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -162,8 +162,8 @@
|
|
|
162
162
|
"file-type": "^21.3",
|
|
163
163
|
"genkit": "^1.28",
|
|
164
164
|
"handlebars": "^4.7",
|
|
165
|
-
"@aws-sdk/client-s3": "^3.
|
|
166
|
-
"@aws-sdk/s3-request-presigner": "^3.
|
|
165
|
+
"@aws-sdk/client-s3": "^3.991",
|
|
166
|
+
"@aws-sdk/s3-request-presigner": "^3.991",
|
|
167
167
|
"mjml": "^4.18",
|
|
168
168
|
"nodemailer": "^8.0",
|
|
169
169
|
"pg": "^8.18",
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
"typedoc-plugin-markdown": "4.10",
|
|
200
200
|
"typedoc-plugin-missing-exports": "4.1",
|
|
201
201
|
"typescript": "5.9",
|
|
202
|
-
"typescript-eslint": "8.
|
|
202
|
+
"typescript-eslint": "8.56",
|
|
203
203
|
"vite-tsconfig-paths": "6.1",
|
|
204
204
|
"vitest": "4.0"
|
|
205
205
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
53
|
+
import { describe, expect, it } from 'vitest';
|
|
54
|
+
import { TemporaryFile } from '../../file/server/temporary-file.js';
|
|
55
|
+
import { spawnCommand } from '../../process/spawn.js';
|
|
56
|
+
import { extractPdfPages, getPdfPageCount, mergePdfs, overlayPdfs, pdfToImage } from '../utils.js';
|
|
57
|
+
describe('pdf utils', () => {
|
|
58
|
+
async function createPdf(pages) {
|
|
59
|
+
const file = TemporaryFile.create('.pdf');
|
|
60
|
+
const source = Array.from({ length: pages }, (_, i) => `Page ${i + 1}`).join('\n#pagebreak()\n');
|
|
61
|
+
const process = await spawnCommand('typst', ['compile', '-', file.path]);
|
|
62
|
+
await process.write(source);
|
|
63
|
+
await process.wait();
|
|
64
|
+
return file;
|
|
65
|
+
}
|
|
66
|
+
it('getPdfPageCount should return correct page count', async () => {
|
|
67
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
68
|
+
try {
|
|
69
|
+
const pdf = __addDisposableResource(env_1, await createPdf(5), true);
|
|
70
|
+
const count = await getPdfPageCount(pdf.path);
|
|
71
|
+
expect(count).toBe(5);
|
|
72
|
+
}
|
|
73
|
+
catch (e_1) {
|
|
74
|
+
env_1.error = e_1;
|
|
75
|
+
env_1.hasError = true;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
const result_1 = __disposeResources(env_1);
|
|
79
|
+
if (result_1)
|
|
80
|
+
await result_1;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
it('extractPdfPages should extract specific pages', async () => {
|
|
84
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
85
|
+
try {
|
|
86
|
+
const pdf = __addDisposableResource(env_2, await createPdf(10), true);
|
|
87
|
+
const extracted = __addDisposableResource(env_2, await extractPdfPages(pdf.path, [1, { from: 3, to: 5 }]), true);
|
|
88
|
+
const count = await getPdfPageCount(extracted.path);
|
|
89
|
+
expect(count).toBe(4); // 1 + (5-3+1) = 4
|
|
90
|
+
}
|
|
91
|
+
catch (e_2) {
|
|
92
|
+
env_2.error = e_2;
|
|
93
|
+
env_2.hasError = true;
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
const result_2 = __disposeResources(env_2);
|
|
97
|
+
if (result_2)
|
|
98
|
+
await result_2;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
it('mergePdfs should handle different input types and page ranges', async () => {
|
|
102
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
103
|
+
try {
|
|
104
|
+
const pdf1 = __addDisposableResource(env_3, await createPdf(2), true);
|
|
105
|
+
const pdf2 = __addDisposableResource(env_3, await createPdf(3), true);
|
|
106
|
+
const pdf2Bytes = await pdf1.read();
|
|
107
|
+
// Test with path, Uint8Array, and page range object
|
|
108
|
+
const merged = __addDisposableResource(env_3, await mergePdfs([
|
|
109
|
+
pdf1.path,
|
|
110
|
+
pdf2Bytes,
|
|
111
|
+
{ input: pdf2.path, pages: [1, { from: 2, to: 3 }] },
|
|
112
|
+
]), true);
|
|
113
|
+
const count = await getPdfPageCount(merged.path);
|
|
114
|
+
expect(count).toBe(2 + 2 + 3); // 2 (path) + 2 (bytes) + 3 (page range)
|
|
115
|
+
}
|
|
116
|
+
catch (e_3) {
|
|
117
|
+
env_3.error = e_3;
|
|
118
|
+
env_3.hasError = true;
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
const result_3 = __disposeResources(env_3);
|
|
122
|
+
if (result_3)
|
|
123
|
+
await result_3;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
it('pdfToImage should work with binary inputs', async () => {
|
|
127
|
+
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
128
|
+
try {
|
|
129
|
+
const pdf = __addDisposableResource(env_4, await createPdf(1), true);
|
|
130
|
+
const pdfBytes = await pdf.read();
|
|
131
|
+
// pdftocairo fails in some environments if no fonts are available,
|
|
132
|
+
// but here we just want to test the branching logic of writing bytes
|
|
133
|
+
const result = await pdfToImage(pdfBytes, 1, 100, 'png');
|
|
134
|
+
expect(result).toBeInstanceOf(Uint8Array);
|
|
135
|
+
}
|
|
136
|
+
catch (e_4) {
|
|
137
|
+
env_4.error = e_4;
|
|
138
|
+
env_4.hasError = true;
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
const result_4 = __disposeResources(env_4);
|
|
142
|
+
if (result_4)
|
|
143
|
+
await result_4;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
it('overlayPdfs should overlay two pdfs', async () => {
|
|
147
|
+
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
148
|
+
try {
|
|
149
|
+
const pdf1 = __addDisposableResource(env_5, await createPdf(1), true);
|
|
150
|
+
const pdf2 = __addDisposableResource(env_5, await createPdf(1), true);
|
|
151
|
+
const overlaid = __addDisposableResource(env_5, await overlayPdfs(pdf1.path, pdf2.path), true);
|
|
152
|
+
const count = await getPdfPageCount(overlaid.path);
|
|
153
|
+
expect(count).toBe(1);
|
|
154
|
+
}
|
|
155
|
+
catch (e_5) {
|
|
156
|
+
env_5.error = e_5;
|
|
157
|
+
env_5.hasError = true;
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
const result_5 = __disposeResources(env_5);
|
|
161
|
+
if (result_5)
|
|
162
|
+
await result_5;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
it('overlayPdfs with repeat should overlay and respect repeat option', async () => {
|
|
166
|
+
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
167
|
+
try {
|
|
168
|
+
const pdf1 = __addDisposableResource(env_6, await createPdf(1), true);
|
|
169
|
+
const pdf2 = __addDisposableResource(env_6, await createPdf(1), true);
|
|
170
|
+
const overlaid = __addDisposableResource(env_6, await overlayPdfs(pdf1.path, pdf2.path, { repeat: true }), true);
|
|
171
|
+
const count = await getPdfPageCount(overlaid.path);
|
|
172
|
+
expect(count).toBe(1);
|
|
173
|
+
}
|
|
174
|
+
catch (e_6) {
|
|
175
|
+
env_6.error = e_6;
|
|
176
|
+
env_6.hasError = true;
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
const result_6 = __disposeResources(env_6);
|
|
180
|
+
if (result_6)
|
|
181
|
+
await result_6;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
it('should throw descriptive error on qpdf failure', async () => {
|
|
185
|
+
await expect(getPdfPageCount('non-existent.pdf')).rejects.toThrow(/qpdf: open non-existent.pdf: No such file or directory/);
|
|
186
|
+
});
|
|
187
|
+
});
|
package/pdf/utils.js
CHANGED
|
@@ -51,7 +51,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
51
51
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
52
|
});
|
|
53
53
|
import { TemporaryFile } from '../file/server/temporary-file.js';
|
|
54
|
-
import { spawnCommand,
|
|
54
|
+
import { spawnCommand, spawnWaitCommand } from '../process/spawn.js';
|
|
55
55
|
import { hasOwnProperty } from '../utils/object/object.js';
|
|
56
56
|
import { isNotString, isNumber, isObject, isReadableStream, isString, isUint8Array, isUndefined } from '../utils/type-guards.js';
|
|
57
57
|
export async function getPdfPageCount(file) {
|
|
@@ -60,13 +60,8 @@ export async function getPdfPageCount(file) {
|
|
|
60
60
|
const stack = __addDisposableResource(env_1, new AsyncDisposableStack(), true);
|
|
61
61
|
const [sourceFile] = await getPdfSourceFiles([file], stack);
|
|
62
62
|
const process = await spawnCommand('qpdf', ['--show-npages', sourceFile]);
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
const errorOutput = await process.readError();
|
|
66
|
-
throw new Error(errorOutput.trim());
|
|
67
|
-
}
|
|
68
|
-
const output = await process.readOutput();
|
|
69
|
-
return Number(output);
|
|
63
|
+
const result = await process.waitRead('string');
|
|
64
|
+
return Number(result.output);
|
|
70
65
|
}
|
|
71
66
|
catch (e_1) {
|
|
72
67
|
env_1.error = e_1;
|
|
@@ -85,12 +80,8 @@ export async function extractPdfPages(file, pages) {
|
|
|
85
80
|
const [sourceFile] = await getPdfSourceFiles([file], stack);
|
|
86
81
|
const resultFile = TemporaryFile.create();
|
|
87
82
|
const pagesString = toQpdfPageRangeString(pages);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
await resultFile.dispose();
|
|
91
|
-
throw new Error(processResult.error.trim());
|
|
92
|
-
}
|
|
93
|
-
return resultFile;
|
|
83
|
+
await spawnWaitCommand('qpdf', ['--empty', '--pages', sourceFile, pagesString, '--', resultFile.path]);
|
|
84
|
+
return resultFile.keep();
|
|
94
85
|
}
|
|
95
86
|
catch (e_2) {
|
|
96
87
|
env_2.error = e_2;
|
|
@@ -117,11 +108,8 @@ export async function mergePdfs(sourceFiles) {
|
|
|
117
108
|
const pagesString = toQpdfPageRangeString(pageRanges);
|
|
118
109
|
return [path, pagesString];
|
|
119
110
|
});
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
throw new Error(processResult.error);
|
|
123
|
-
}
|
|
124
|
-
return resultFile;
|
|
111
|
+
await spawnWaitCommand('qpdf', ['--empty', '--pages', ...sourceArguments, '--', resultFile.path]);
|
|
112
|
+
return resultFile.keep();
|
|
125
113
|
}
|
|
126
114
|
catch (e_3) {
|
|
127
115
|
env_3.error = e_3;
|
|
@@ -137,19 +125,15 @@ export async function overlayPdfs(base, overlay, options) {
|
|
|
137
125
|
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
138
126
|
try {
|
|
139
127
|
const stack = __addDisposableResource(env_4, new AsyncDisposableStack(), true);
|
|
128
|
+
const resultFile = __addDisposableResource(env_4, TemporaryFile.create(), true);
|
|
140
129
|
const [baseFilePath, overlayFilePath] = await getPdfSourceFiles([base, overlay], stack);
|
|
141
|
-
const resultFile = TemporaryFile.create();
|
|
142
|
-
stack.use(resultFile);
|
|
143
130
|
const args = [baseFilePath, '--overlay', overlayFilePath];
|
|
144
131
|
if (options?.repeat) {
|
|
145
132
|
args.push('--repeat=1');
|
|
146
133
|
}
|
|
147
134
|
args.push('--', resultFile.path);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
throw new Error(processResult.error);
|
|
151
|
-
}
|
|
152
|
-
return resultFile;
|
|
135
|
+
await spawnWaitCommand('qpdf', args);
|
|
136
|
+
return resultFile.keep();
|
|
153
137
|
}
|
|
154
138
|
catch (e_4) {
|
|
155
139
|
env_4.error = e_4;
|
|
@@ -172,7 +156,6 @@ export async function overlayPdfs(base, overlay, options) {
|
|
|
172
156
|
export async function pdfToImage(file, page, size, format) {
|
|
173
157
|
const path = isString(file) ? file : '-';
|
|
174
158
|
const process = await spawnCommand('pdftocairo', ['-f', String(page), '-l', String(page), '-scale-to', String(size), '-singlefile', `-${format}`, path, '-']);
|
|
175
|
-
process.handleNonZeroExitCode();
|
|
176
159
|
if (isNotString(file)) {
|
|
177
160
|
process.writeInBackground(file);
|
|
178
161
|
}
|
package/process/spawn.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { dynamicImport } from '../import.js';
|
|
2
2
|
import { LazyPromise } from '../promise/lazy-promise.js';
|
|
3
|
-
import {
|
|
4
|
-
import { readBinaryStream
|
|
3
|
+
import { decodeText, encodeUtf8Stream } from '../utils/encoding.js';
|
|
4
|
+
import { readBinaryStream } from '../utils/stream/stream-reader.js';
|
|
5
5
|
import { toReadableStream } from '../utils/stream/to-readable-stream.js';
|
|
6
|
-
import { assertNotNullOrUndefinedPass, isArray, isReadableStream, isString, isUint8Array } from '../utils/type-guards.js';
|
|
6
|
+
import { assertNotNullOrUndefinedPass, isArray, isNotNull, isReadableStream, isString, isUint8Array } from '../utils/type-guards.js';
|
|
7
7
|
export async function spawnWaitCommand(command, argsOrOptions, optionsOrNothing) {
|
|
8
|
-
const [args, options] = isArray(argsOrOptions) ? [argsOrOptions, optionsOrNothing] : [
|
|
8
|
+
const [args, options] = isArray(argsOrOptions) ? [argsOrOptions, optionsOrNothing] : [argsOrOptions?.arguments, argsOrOptions];
|
|
9
9
|
const process = await spawnCommand(command, args, options);
|
|
10
10
|
return await process.wait({ throwOnNonZeroExitCode: options?.throwOnNonZeroExitCode });
|
|
11
11
|
}
|
|
@@ -17,7 +17,7 @@ export async function spawnWaitReadCommand(format, command, argsOrOptions, optio
|
|
|
17
17
|
export async function spawnCommand(command, argsOrOptions, optionsOrNothing) {
|
|
18
18
|
const { spawn } = await dynamicImport('node:child_process');
|
|
19
19
|
const { Readable, Writable } = await dynamicImport('node:stream');
|
|
20
|
-
const [args, options] = isArray(argsOrOptions) ? [argsOrOptions, optionsOrNothing] : [
|
|
20
|
+
const [args, options] = isArray(argsOrOptions) ? [argsOrOptions, optionsOrNothing] : [argsOrOptions?.arguments, argsOrOptions];
|
|
21
21
|
const process = spawn(command, args, { stdio: 'pipe', cwd: options?.workingDirectory, env: options?.environment });
|
|
22
22
|
await Promise.race([
|
|
23
23
|
new Promise((resolve) => process.on('spawn', resolve)),
|
|
@@ -40,29 +40,41 @@ export async function spawnCommand(command, argsOrOptions, optionsOrNothing) {
|
|
|
40
40
|
/** Writes data in the background. Must be used with care because of potential unhandled errors */
|
|
41
41
|
function writeInBackground(data, options) {
|
|
42
42
|
write(data, options).catch((error) => {
|
|
43
|
-
readable.cancel(error).catch(() => { });
|
|
44
|
-
writable.abort(error).catch(() => { });
|
|
43
|
+
void readable.cancel(error).catch(() => { });
|
|
44
|
+
void writable.abort(error).catch(() => { });
|
|
45
|
+
void stderr.cancel(error).catch(() => { });
|
|
45
46
|
});
|
|
46
47
|
}
|
|
48
|
+
const outputBytes = new LazyPromise(async () => await readBinaryStream(readable));
|
|
49
|
+
const errorBytes = new LazyPromise(async () => await readBinaryStream(stderr));
|
|
50
|
+
const outputText = new LazyPromise(async () => decodeText(await outputBytes));
|
|
51
|
+
const errorText = new LazyPromise(async () => decodeText(await errorBytes));
|
|
47
52
|
async function readOutputBytes() {
|
|
48
|
-
return await
|
|
53
|
+
return await outputBytes;
|
|
49
54
|
}
|
|
50
55
|
async function readOutput() {
|
|
51
|
-
return await
|
|
56
|
+
return await outputText;
|
|
52
57
|
}
|
|
53
58
|
async function readErrorBytes() {
|
|
54
|
-
return await
|
|
59
|
+
return await errorBytes;
|
|
55
60
|
}
|
|
56
61
|
async function readError() {
|
|
57
|
-
return await
|
|
62
|
+
return await errorText;
|
|
58
63
|
}
|
|
59
|
-
const signalPromise = new Promise((resolve) => process.on('close', (code, signal) =>
|
|
64
|
+
const signalPromise = new Promise((resolve) => process.on('close', (code, signal) => {
|
|
65
|
+
const result = { code, signal };
|
|
66
|
+
resolve(result);
|
|
67
|
+
if ((code != 0) || isNotNull(signal)) {
|
|
68
|
+
void handleNonZeroExitCode();
|
|
69
|
+
}
|
|
70
|
+
}));
|
|
60
71
|
const nonZeroExitCodeError = new LazyPromise(async () => {
|
|
61
72
|
const result = await signalPromise;
|
|
62
|
-
if (result.code != 0) {
|
|
73
|
+
if ((result.code != 0) || isNotNull(result.signal)) {
|
|
63
74
|
try {
|
|
64
|
-
const errorOutput = await
|
|
65
|
-
|
|
75
|
+
const errorOutput = await errorText;
|
|
76
|
+
const message = errorOutput.trim();
|
|
77
|
+
return new Error(message.length > 0 ? message : `Process exited with code ${result.code} and signal ${result.signal}.`);
|
|
66
78
|
}
|
|
67
79
|
catch {
|
|
68
80
|
return new Error(`Process exited with code ${result.code} and signal ${result.signal}.`);
|
|
@@ -75,12 +87,12 @@ export async function spawnCommand(command, argsOrOptions, optionsOrNothing) {
|
|
|
75
87
|
if (error) {
|
|
76
88
|
await writable.abort(error).catch(() => { });
|
|
77
89
|
await readable.cancel(error).catch(() => { });
|
|
90
|
+
await stderr.cancel(error).catch(() => { });
|
|
78
91
|
}
|
|
79
92
|
}
|
|
80
93
|
async function wait({ throwOnNonZeroExitCode = true } = {}) {
|
|
81
94
|
const result = await signalPromise;
|
|
82
|
-
|
|
83
|
-
if (handleNonZeroExitCode) {
|
|
95
|
+
if (((result.code != 0) || isNotNull(result.signal)) && throwOnNonZeroExitCode) {
|
|
84
96
|
const error = await nonZeroExitCodeError;
|
|
85
97
|
throw assertNotNullOrUndefinedPass(error);
|
|
86
98
|
}
|
|
@@ -88,10 +100,14 @@ export async function spawnCommand(command, argsOrOptions, optionsOrNothing) {
|
|
|
88
100
|
}
|
|
89
101
|
async function waitRead(format, { throwOnNonZeroExitCode = true } = {}) {
|
|
90
102
|
const [result, output, error] = await Promise.all([
|
|
91
|
-
wait({ throwOnNonZeroExitCode }),
|
|
92
|
-
(format === 'string') ?
|
|
93
|
-
(format === 'string') ?
|
|
103
|
+
wait({ throwOnNonZeroExitCode: false }),
|
|
104
|
+
(format === 'string') ? outputText : outputBytes,
|
|
105
|
+
(format === 'string') ? errorText : errorBytes,
|
|
94
106
|
]);
|
|
107
|
+
if (((result.code != 0) || isNotNull(result.signal)) && throwOnNonZeroExitCode) {
|
|
108
|
+
const error = await nonZeroExitCodeError;
|
|
109
|
+
throw assertNotNullOrUndefinedPass(error);
|
|
110
|
+
}
|
|
95
111
|
return {
|
|
96
112
|
...result,
|
|
97
113
|
output: output,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { encodeUtf8 } from '../../utils/encoding.js';
|
|
3
|
+
import { toReadableStream } from '../../utils/stream/to-readable-stream.js';
|
|
4
|
+
import { spawnCommand, spawnWaitCommand, spawnWaitReadCommand } from '../spawn.js';
|
|
5
|
+
describe('spawn', () => {
|
|
6
|
+
describe('spawnCommand', () => {
|
|
7
|
+
it('should spawn a command and wait for completion', async () => {
|
|
8
|
+
const process = await spawnCommand('sh', ['-c', 'exit 0']);
|
|
9
|
+
const result = await process.wait();
|
|
10
|
+
expect(result.code).toBe(0);
|
|
11
|
+
});
|
|
12
|
+
it('should throw on non-zero exit code', async () => {
|
|
13
|
+
const process = await spawnCommand('sh', ['-c', 'echo "error message" >&2; exit 1']);
|
|
14
|
+
await expect(process.wait()).rejects.toThrow('error message');
|
|
15
|
+
});
|
|
16
|
+
it('should handle process terminated by signal', async () => {
|
|
17
|
+
const process = await spawnCommand('sh', ['-c', 'kill -9 $$']);
|
|
18
|
+
await expect(process.wait()).rejects.toThrow('Process exited with code null and signal SIGKILL');
|
|
19
|
+
});
|
|
20
|
+
it('should buffer stdout and allow multiple reads', async () => {
|
|
21
|
+
const process = await spawnCommand('sh', ['-c', 'echo "hello world"']);
|
|
22
|
+
await process.wait();
|
|
23
|
+
const output1 = await process.readOutput();
|
|
24
|
+
const output2 = await process.readOutput();
|
|
25
|
+
const outputBytes = await process.readOutputBytes();
|
|
26
|
+
expect(output1.trim()).toBe('hello world');
|
|
27
|
+
expect(output1).toBe(output2);
|
|
28
|
+
expect(new TextDecoder().decode(outputBytes).trim()).toBe('hello world');
|
|
29
|
+
});
|
|
30
|
+
it('should buffer stderr and allow multiple reads', async () => {
|
|
31
|
+
const process = await spawnCommand('sh', ['-c', 'echo "error message" >&2']);
|
|
32
|
+
await process.wait();
|
|
33
|
+
const error1 = await process.readError();
|
|
34
|
+
const error2 = await process.readError();
|
|
35
|
+
const errorBytes = await process.readErrorBytes();
|
|
36
|
+
expect(error1.trim()).toBe('error message');
|
|
37
|
+
expect(error1).toBe(error2);
|
|
38
|
+
expect(new TextDecoder().decode(errorBytes).trim()).toBe('error message');
|
|
39
|
+
});
|
|
40
|
+
it('should handle different write data types', async () => {
|
|
41
|
+
const process1 = await spawnCommand('cat');
|
|
42
|
+
await process1.write(toReadableStream(encodeUtf8('stream\n')));
|
|
43
|
+
expect(await process1.readOutput()).toBe('stream\n');
|
|
44
|
+
const process2 = await spawnCommand('cat');
|
|
45
|
+
await process2.write(encodeUtf8('bytes\n'));
|
|
46
|
+
expect(await process2.readOutput()).toBe('bytes\n');
|
|
47
|
+
const process3 = await spawnCommand('cat');
|
|
48
|
+
await process3.write('string\n');
|
|
49
|
+
expect(await process3.readOutput()).toBe('string\n');
|
|
50
|
+
});
|
|
51
|
+
it('should handle background writes and error propagation', async () => {
|
|
52
|
+
const process = await spawnCommand('cat');
|
|
53
|
+
process.writeInBackground('test data');
|
|
54
|
+
const result = await process.waitRead('string');
|
|
55
|
+
expect(result.output).toBe('test data');
|
|
56
|
+
});
|
|
57
|
+
it('should handle errors in writeInBackground', async () => {
|
|
58
|
+
const process = await spawnCommand('sh', ['-c', 'exit 0']);
|
|
59
|
+
await process.writable.close();
|
|
60
|
+
process.writeInBackground('this should fail');
|
|
61
|
+
});
|
|
62
|
+
it('should handle non-zero exit code with empty stderr', async () => {
|
|
63
|
+
const process = await spawnCommand('sh', ['-c', 'exit 1']);
|
|
64
|
+
await expect(process.wait()).rejects.toThrow('Process exited with code 1 and signal null.');
|
|
65
|
+
});
|
|
66
|
+
it('should support manual handleNonZeroExitCode', async () => {
|
|
67
|
+
const process = await spawnCommand('sh', ['-c', 'exit 1']);
|
|
68
|
+
process.handleNonZeroExitCode();
|
|
69
|
+
await expect(process.wait()).rejects.toThrow();
|
|
70
|
+
});
|
|
71
|
+
it('should handle errors when reading error text in nonZeroExitCodeError', async () => {
|
|
72
|
+
// Mock TextDecoder to throw
|
|
73
|
+
const originalTextDecoder = global.TextDecoder;
|
|
74
|
+
global.TextDecoder = vi.fn().mockImplementation(() => ({
|
|
75
|
+
decode: () => { throw new Error('mock error'); },
|
|
76
|
+
}));
|
|
77
|
+
try {
|
|
78
|
+
const process = await spawnCommand('sh', ['-c', 'exit 1']);
|
|
79
|
+
await expect(process.wait()).rejects.toThrow('Process exited with code 1 and signal null.');
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
global.TextDecoder = originalTextDecoder;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
it('should support passing options as second argument', async () => {
|
|
86
|
+
const process = await spawnCommand('sh', { arguments: ['-c', 'exit 0'] });
|
|
87
|
+
const result = await process.wait();
|
|
88
|
+
expect(result.code).toBe(0);
|
|
89
|
+
});
|
|
90
|
+
it('should support separate args and options', async () => {
|
|
91
|
+
const process = await spawnCommand('sh', ['-c', 'exit 0'], { workingDirectory: '.' });
|
|
92
|
+
const result = await process.wait();
|
|
93
|
+
expect(result.code).toBe(0);
|
|
94
|
+
});
|
|
95
|
+
it('should handle explicit undefined options', async () => {
|
|
96
|
+
const process = await spawnCommand('sh', ['-c', 'exit 0'], undefined);
|
|
97
|
+
const result = await process.wait();
|
|
98
|
+
expect(result.code).toBe(0);
|
|
99
|
+
});
|
|
100
|
+
it('should wait without options', async () => {
|
|
101
|
+
const process = await spawnCommand('sh', ['-c', 'exit 0']);
|
|
102
|
+
const result = await process.wait(undefined);
|
|
103
|
+
expect(result.code).toBe(0);
|
|
104
|
+
});
|
|
105
|
+
it('should handle command only', async () => {
|
|
106
|
+
const process = await spawnCommand('ls');
|
|
107
|
+
const result = await process.wait();
|
|
108
|
+
expect(result.code).toBe(0);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe('spawnWaitCommand', () => {
|
|
112
|
+
it('should wait for command and return result', async () => {
|
|
113
|
+
const result = await spawnWaitCommand('sh', ['-c', 'exit 0']);
|
|
114
|
+
expect(result.code).toBe(0);
|
|
115
|
+
});
|
|
116
|
+
it('should throw on error by default', async () => {
|
|
117
|
+
await expect(spawnWaitCommand('sh', ['-c', 'exit 1'])).rejects.toThrow();
|
|
118
|
+
});
|
|
119
|
+
it('should support separate args and options', async () => {
|
|
120
|
+
const result = await spawnWaitCommand('sh', ['-c', 'exit 0'], { workingDirectory: '.' });
|
|
121
|
+
expect(result.code).toBe(0);
|
|
122
|
+
});
|
|
123
|
+
it('should support passing options directly', async () => {
|
|
124
|
+
const result = await spawnWaitCommand('sh', { arguments: ['-c', 'exit 0'] });
|
|
125
|
+
expect(result.code).toBe(0);
|
|
126
|
+
});
|
|
127
|
+
it('should support command only', async () => {
|
|
128
|
+
const result = await spawnWaitCommand('ls');
|
|
129
|
+
expect(result.code).toBe(0);
|
|
130
|
+
});
|
|
131
|
+
it('should handle explicit undefined options', async () => {
|
|
132
|
+
const result = await spawnWaitCommand('sh', ['-c', 'exit 0'], undefined);
|
|
133
|
+
expect(result.code).toBe(0);
|
|
134
|
+
});
|
|
135
|
+
it('should NOT throw on error if throwOnNonZeroExitCode is false', async () => {
|
|
136
|
+
const result = await spawnWaitCommand('sh', ['-c', 'exit 1'], { throwOnNonZeroExitCode: false });
|
|
137
|
+
expect(result.code).toBe(1);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
describe('spawnWaitReadCommand', () => {
|
|
141
|
+
it('should wait and read output', async () => {
|
|
142
|
+
const result = await spawnWaitReadCommand('string', 'sh', ['-c', 'echo "test"']);
|
|
143
|
+
expect(result.output.trim()).toBe('test');
|
|
144
|
+
});
|
|
145
|
+
it('should read binary output', async () => {
|
|
146
|
+
const result = await spawnWaitReadCommand('binary', 'sh', ['-c', 'echo "test"']);
|
|
147
|
+
expect(result.output).toBeInstanceOf(Uint8Array);
|
|
148
|
+
});
|
|
149
|
+
it('should include error in exception', async () => {
|
|
150
|
+
await expect(spawnWaitReadCommand('string', 'sh', ['-c', 'echo "fail" >&2; exit 1'])).rejects.toThrow('fail');
|
|
151
|
+
});
|
|
152
|
+
it('should support options object', async () => {
|
|
153
|
+
const result = await spawnWaitReadCommand('string', 'sh', { arguments: ['-c', 'echo "test"'] });
|
|
154
|
+
expect(result.output.trim()).toBe('test');
|
|
155
|
+
});
|
|
156
|
+
it('should support separate args and options', async () => {
|
|
157
|
+
const result = await spawnWaitReadCommand('string', 'sh', ['-c', 'echo "test"'], { workingDirectory: '.' });
|
|
158
|
+
expect(result.output.trim()).toBe('test');
|
|
159
|
+
});
|
|
160
|
+
it('should support command only', async () => {
|
|
161
|
+
const result = await spawnWaitReadCommand('string', 'ls');
|
|
162
|
+
expect(result.code).toBe(0);
|
|
163
|
+
});
|
|
164
|
+
it('should NOT throw on error if throwOnNonZeroExitCode is false', async () => {
|
|
165
|
+
const result = await spawnWaitReadCommand('string', 'sh', ['-c', 'echo "fail" >&2; exit 1'], { throwOnNonZeroExitCode: false });
|
|
166
|
+
expect(result.code).toBe(1);
|
|
167
|
+
expect(result.error.trim()).toBe('fail');
|
|
168
|
+
});
|
|
169
|
+
it('should handle explicit undefined options', async () => {
|
|
170
|
+
const result = await spawnWaitReadCommand('string', 'sh', ['-c', 'echo "test"'], undefined);
|
|
171
|
+
expect(result.output.trim()).toBe('test');
|
|
172
|
+
});
|
|
173
|
+
it('should wait and read binary output without options', async () => {
|
|
174
|
+
const result = await spawnWaitReadCommand('binary', 'sh', ['-c', 'echo "test"']);
|
|
175
|
+
expect(result.output).toBeInstanceOf(Uint8Array);
|
|
176
|
+
});
|
|
177
|
+
it('should wait and read with explicit undefined options', async () => {
|
|
178
|
+
const result = await spawnWaitReadCommand('string', 'sh', ['-c', 'echo "test"'], undefined);
|
|
179
|
+
expect(result.output.trim()).toBe('test');
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
package/renderer/d2.js
CHANGED
|
@@ -51,18 +51,7 @@ export async function renderD2(source, options) {
|
|
|
51
51
|
args.push('--scale', options.scale.toString());
|
|
52
52
|
}
|
|
53
53
|
const process = await spawnCommand('d2', args);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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;
|
|
54
|
+
process.writeInBackground(source);
|
|
55
|
+
const result = await process.waitRead('binary');
|
|
56
|
+
return result.output;
|
|
68
57
|
}
|
package/renderer/graphviz.js
CHANGED
|
@@ -41,18 +41,7 @@ export async function renderGraphviz(source, options) {
|
|
|
41
41
|
args.push(`-q${level}`);
|
|
42
42
|
}
|
|
43
43
|
const process = await spawnCommand(engine, args);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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;
|
|
44
|
+
process.writeInBackground(source);
|
|
45
|
+
const result = await process.waitRead('binary');
|
|
46
|
+
return result.output;
|
|
58
47
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { renderD2 } from '../d2.js';
|
|
3
|
+
import { renderGraphviz } from '../graphviz.js';
|
|
4
|
+
import { renderTypst } from '../typst.js';
|
|
5
|
+
describe('renderer', () => {
|
|
6
|
+
describe('renderGraphviz', () => {
|
|
7
|
+
it('should render a simple graph with options', async () => {
|
|
8
|
+
const source = 'graph { a -- b }';
|
|
9
|
+
const result = await renderGraphviz(source, {
|
|
10
|
+
format: 'svg',
|
|
11
|
+
engine: 'dot',
|
|
12
|
+
graphAttributes: { rankdir: 'LR' },
|
|
13
|
+
nodeAttributes: { color: 'red' },
|
|
14
|
+
edgeAttributes: { style: 'dashed' },
|
|
15
|
+
scale: 2,
|
|
16
|
+
quiet: true,
|
|
17
|
+
});
|
|
18
|
+
const svg = new TextDecoder().decode(result);
|
|
19
|
+
expect(svg).toContain('<svg');
|
|
20
|
+
expect(svg).toContain('stroke="red"');
|
|
21
|
+
expect(svg).toContain('stroke-dasharray');
|
|
22
|
+
});
|
|
23
|
+
it('should throw on invalid graphviz source', async () => {
|
|
24
|
+
const source = 'invalid graph {';
|
|
25
|
+
await expect(renderGraphviz(source)).rejects.toThrow();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('renderD2', () => {
|
|
29
|
+
it('should render a simple d2 diagram with options', async () => {
|
|
30
|
+
const source = 'x -> y';
|
|
31
|
+
const result = await renderD2(source, {
|
|
32
|
+
format: 'svg',
|
|
33
|
+
layout: 'dagre',
|
|
34
|
+
theme: 1,
|
|
35
|
+
darkTheme: 200,
|
|
36
|
+
pad: 20,
|
|
37
|
+
sketch: true,
|
|
38
|
+
center: true,
|
|
39
|
+
animateInterval: 1000,
|
|
40
|
+
noXmlTag: true,
|
|
41
|
+
salt: 'test',
|
|
42
|
+
omitVersion: true,
|
|
43
|
+
target: '*',
|
|
44
|
+
scale: 0.5,
|
|
45
|
+
});
|
|
46
|
+
const svg = new TextDecoder().decode(result);
|
|
47
|
+
expect(svg).toContain('<svg');
|
|
48
|
+
});
|
|
49
|
+
it('should throw on invalid d2 source', async () => {
|
|
50
|
+
const source = 'invalid {';
|
|
51
|
+
await expect(renderD2(source)).rejects.toThrow();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('renderTypst', () => {
|
|
55
|
+
it('should render a simple typst document with options', async () => {
|
|
56
|
+
const source = 'Hello World';
|
|
57
|
+
const result = await renderTypst(source, {
|
|
58
|
+
format: 'svg',
|
|
59
|
+
ppi: 300,
|
|
60
|
+
pages: ['1', { from: 1, to: 1 }],
|
|
61
|
+
ignoreSystemFonts: true,
|
|
62
|
+
creationTimestamp: Date.now(),
|
|
63
|
+
root: '/',
|
|
64
|
+
fontPaths: ['/tmp'],
|
|
65
|
+
pdfStandard: '1.7',
|
|
66
|
+
});
|
|
67
|
+
const svg = new TextDecoder().decode(result);
|
|
68
|
+
expect(svg).toContain('<svg');
|
|
69
|
+
expect(svg).toContain('typst-text');
|
|
70
|
+
});
|
|
71
|
+
it('should render to docx (via pandoc) with options', async () => {
|
|
72
|
+
const source = 'Hello World';
|
|
73
|
+
// pandoc --from typst --to docx --output - -
|
|
74
|
+
const result = await renderTypst(source, {
|
|
75
|
+
format: 'docx',
|
|
76
|
+
docxReferenceFile: undefined, // Testing the branch
|
|
77
|
+
});
|
|
78
|
+
expect(result).toBeInstanceOf(Uint8Array);
|
|
79
|
+
// Docx is a zip file, it should start with PK
|
|
80
|
+
expect(result[0]).toBe(0x50); // P
|
|
81
|
+
expect(result[1]).toBe(0x4B); // K
|
|
82
|
+
});
|
|
83
|
+
it('should throw on invalid typst source', async () => {
|
|
84
|
+
const source = '#invalid_func()';
|
|
85
|
+
await expect(renderTypst(source)).rejects.toThrow();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
package/renderer/typst.js
CHANGED
|
@@ -43,24 +43,17 @@ export async function renderTypst(source, options) {
|
|
|
43
43
|
: `${page.from ?? ''}-${page.to ?? ''}`);
|
|
44
44
|
args.push('--pages', pageParts.join(','));
|
|
45
45
|
}
|
|
46
|
+
args.push('-', '-');
|
|
46
47
|
}
|
|
47
48
|
else if (command == 'pandoc') {
|
|
48
|
-
args = ['--from', 'typst', '--to', 'docx'
|
|
49
|
+
args = ['--from', 'typst', '--to', 'docx'];
|
|
49
50
|
if (isDefined(options?.docxReferenceFile)) {
|
|
50
51
|
args.push('--reference-doc', options.docxReferenceFile);
|
|
51
52
|
}
|
|
53
|
+
args.push('--output', '-', '-');
|
|
52
54
|
}
|
|
53
55
|
const process = await spawnCommand(command, args);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
]);
|
|
58
|
-
const errorString = decodeText(error);
|
|
59
|
-
if (code != 0) {
|
|
60
|
-
throw new Error(`
|
|
61
|
-
Typst compilation failed with exit code ${code}.\n
|
|
62
|
-
Error Output:\n${errorString}
|
|
63
|
-
`.trim());
|
|
64
|
-
}
|
|
65
|
-
return output;
|
|
56
|
+
process.writeInBackground(source);
|
|
57
|
+
const result = await process.waitRead('binary');
|
|
58
|
+
return result.output;
|
|
66
59
|
}
|
package/test5.js
CHANGED
|
@@ -1,61 +1,11 @@
|
|
|
1
1
|
import './polyfills.js';
|
|
2
|
-
import { buildPrompts, orderedList } from './ai/index.js';
|
|
3
|
-
import { fewShotPrompt } from './ai/prompts/steering.js';
|
|
4
2
|
import { Application } from './application/application.js';
|
|
5
3
|
import { provideModule, provideSignalHandler } from './application/index.js';
|
|
6
4
|
import { PrettyPrintLogFormatter } from './logger/index.js';
|
|
7
5
|
import { provideConsoleLogTransport } from './logger/transports/console.js';
|
|
6
|
+
import { mergePdfs } from './pdf/utils.js';
|
|
8
7
|
async function main(_cancellationSignal) {
|
|
9
|
-
|
|
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);
|
|
8
|
+
await mergePdfs(['asd', 'bsa']);
|
|
59
9
|
}
|
|
60
10
|
Application.run('Test', [
|
|
61
11
|
provideConsoleLogTransport(PrettyPrintLogFormatter),
|