pdfdancer-client-typescript 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +5 -5
  2. package/dist/__tests__/e2e/test-helpers.d.ts +2 -1
  3. package/dist/__tests__/e2e/test-helpers.d.ts.map +1 -1
  4. package/dist/__tests__/e2e/test-helpers.js +7 -3
  5. package/dist/__tests__/e2e/test-helpers.js.map +1 -1
  6. package/dist/client-v1.js +1 -1
  7. package/dist/client-v1.js.map +1 -1
  8. package/dist/client-v2.d.ts +129 -0
  9. package/dist/client-v2.d.ts.map +1 -0
  10. package/dist/client-v2.js +696 -0
  11. package/dist/client-v2.js.map +1 -0
  12. package/dist/image-builder.d.ts +13 -0
  13. package/dist/image-builder.d.ts.map +1 -0
  14. package/dist/image-builder.js +44 -0
  15. package/dist/image-builder.js.map +1 -0
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +3 -3
  19. package/dist/index.js.map +1 -1
  20. package/dist/models.d.ts +5 -10
  21. package/dist/models.d.ts.map +1 -1
  22. package/dist/models.js +3 -13
  23. package/dist/models.js.map +1 -1
  24. package/dist/paragraph-builder.d.ts +19 -10
  25. package/dist/paragraph-builder.d.ts.map +1 -1
  26. package/dist/paragraph-builder.js +65 -26
  27. package/dist/paragraph-builder.js.map +1 -1
  28. package/dist/pdfdancer_v1.d.ts +202 -0
  29. package/dist/pdfdancer_v1.d.ts.map +1 -0
  30. package/dist/pdfdancer_v1.js +702 -0
  31. package/dist/pdfdancer_v1.js.map +1 -0
  32. package/dist/types.d.ts +56 -0
  33. package/dist/types.d.ts.map +1 -0
  34. package/dist/types.js +92 -0
  35. package/dist/types.js.map +1 -0
  36. package/package.json +1 -1
  37. package/scripts/release.js +1 -1
  38. package/src/__tests__/client-v1.test.ts +46 -87
  39. package/src/__tests__/e2e/acroform.test.ts +60 -57
  40. package/src/__tests__/e2e/form_x_object.test.ts +17 -16
  41. package/src/__tests__/e2e/image.test.ts +53 -56
  42. package/src/__tests__/e2e/line.test.ts +47 -48
  43. package/src/__tests__/e2e/page.test.ts +37 -36
  44. package/src/__tests__/e2e/paragraph.test.ts +107 -101
  45. package/src/__tests__/e2e/path.test.ts +67 -64
  46. package/src/__tests__/e2e/test-helpers.ts +71 -67
  47. package/src/__tests__/e2e/token_from_env.test.ts +35 -0
  48. package/src/image-builder.ts +52 -0
  49. package/src/index.ts +1 -1
  50. package/src/models.ts +5 -21
  51. package/src/paragraph-builder.ts +217 -162
  52. package/src/{client-v1.ts → pdfdancer_v1.ts} +248 -53
  53. package/src/types.ts +133 -0
  54. package/example.ts +0 -99
@@ -1,22 +1,21 @@
1
1
  /**
2
- * E2E tests for paragraph operations
2
+ * E2E tests for paragraph operations — new PDFDancer API
3
3
  */
4
4
 
5
- import {ClientV1, Color, Font, Position} from '../../index';
5
+ import {Color, PDFDancer} from '../../index';
6
6
  import {readFontFixture, requireEnvAndFixture} from './test-helpers';
7
- import {expectWithin} from "../assertions";
7
+ import {expectWithin} from '../assertions';
8
8
 
9
- describe('Paragraph E2E Tests', () => {
10
- // Tests should fail properly if environment is not configured
9
+ describe('Paragraph E2E Tests (v2 API)', () => {
11
10
 
12
11
  test('find paragraphs by position', async () => {
13
12
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
14
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
13
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
15
14
 
16
- const paras = await client.findParagraphs();
15
+ const paras = await pdf.selectParagraphs();
17
16
  expect(paras).toHaveLength(172);
18
17
 
19
- const parasPage0 = await client.findParagraphs(Position.atPage(0));
18
+ const parasPage0 = await pdf.page(0).selectParagraphs();
20
19
  expect(parasPage0).toHaveLength(2);
21
20
 
22
21
  const first = parasPage0[0];
@@ -34,10 +33,9 @@ describe('Paragraph E2E Tests', () => {
34
33
 
35
34
  test('find paragraphs by text', async () => {
36
35
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
37
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
36
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
38
37
 
39
- const pos = Position.atPage(0).withTextStarts('The Complete');
40
- const paras = await client.findParagraphs(pos);
38
+ const paras = await pdf.page(0).selectParagraphsStartingWith('The Complete');
41
39
  expect(paras).toHaveLength(1);
42
40
 
43
41
  const p = paras[0];
@@ -47,147 +45,155 @@ describe('Paragraph E2E Tests', () => {
47
45
  expectWithin(p.position.boundingRect?.y, 496, 2);
48
46
  });
49
47
 
48
+ test('find paragraphs by pattern', async () => {
49
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
50
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
51
+
52
+ const paras = await pdf.page(0).selectParagraphsMatching('.*Complete.*');
53
+ expect(paras).toHaveLength(1);
54
+ const p = paras[0];
55
+ expect(p.internalId).toBe('PARAGRAPH_000004');
56
+
57
+ const paras2 = await pdf.page(0).selectParagraphsMatching('.*NOT FOUND.*');
58
+ expect(paras2).toHaveLength(0);
59
+
60
+ const paras3 = await pdf.page(0).selectParagraphsMatching('.*');
61
+ expect(paras3).toHaveLength(2);
62
+ });
63
+
50
64
  test('delete paragraph', async () => {
51
65
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
52
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
66
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
53
67
 
54
- const posDel = Position.atPage(0).withTextStarts('The Complete');
55
- const ref = (await client.findParagraphs(posDel))[0];
56
- expect(await client.delete(ref)).toBe(true);
68
+ const [target] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
69
+ await target.delete();
57
70
 
58
- const posDel2 = Position.atPage(0).withTextStarts('The Complete');
59
- expect(await client.findParagraphs(posDel2)).toHaveLength(0);
71
+ const remaining = await pdf.page(0).selectParagraphsStartingWith('The Complete');
72
+ expect(remaining).toHaveLength(0);
60
73
  });
61
74
 
62
75
  test('move paragraph', async () => {
63
76
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
64
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
77
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
65
78
 
66
- const posMove = Position.atPage(0).withTextStarts('The Complete');
67
- const ref = (await client.findParagraphs(posMove))[0];
68
- const newPos = Position.atPageCoordinates(0, 0.1, 300);
69
- expect(await client.move(ref, newPos)).toBe(true);
79
+ const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
80
+ await para.moveTo(0.1, 300);
70
81
 
71
- const ref2 = (await client.findParagraphs(newPos))[0];
72
- expect(ref2).toBeDefined();
82
+ const moved = await pdf.page(0).selectParagraphsAt(0.1, 300);
83
+ expect(moved.length).toBeGreaterThan(0);
73
84
  });
74
85
 
75
- async function assertNewParagraphExists(client: ClientV1): Promise<void> {
76
- // Validate via find_text_lines for text starting with 'Awesomely'
77
- const pos = Position.atPage(0).withTextStarts('Awesomely');
78
- const lines = await client.findTextLines(pos);
86
+ async function assertNewParagraphExists(pdf: PDFDancer) {
87
+ const lines = await pdf.page(0).selectTextLinesStartingWith('Awesomely');
79
88
  expect(lines.length).toBeGreaterThanOrEqual(1);
80
89
  }
81
90
 
82
91
  test('modify paragraph', async () => {
83
92
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
84
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
93
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
85
94
 
86
- const posMod = Position.atPage(0).withTextStarts('The Complete');
87
- const ref = (await client.findParagraphs(posMod))[0];
95
+ const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
88
96
 
89
- const newParagraph = client.paragraphBuilder()
90
- .fromString('Awesomely\\nObvious!')
91
- .withFont(new Font('Helvetica', 14))
92
- .withLineSpacing(0.7)
93
- .withPosition(Position.atPageCoordinates(0, 300.1, 500))
94
- .build();
97
+ await para.edit().replace('Awesomely\nObvious!')
98
+ .font("Helvetica", 12)
99
+ .lineSpacing(0.7)
100
+ .moveTo(300.1, 500)
101
+ .apply()
95
102
 
96
- expect(await client.modifyParagraph(ref, newParagraph)).toBe(true);
97
- await assertNewParagraphExists(client);
103
+ await assertNewParagraphExists(pdf);
98
104
  });
99
105
 
100
- test('modify paragraph simple', async () => {
106
+ test('modify paragraph (simple)', async () => {
101
107
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
102
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
108
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
103
109
 
104
- const posMod2 = Position.atPage(0).withTextStarts('The Complete');
105
- const ref = (await client.findParagraphs(posMod2))[0];
106
- expect(await client.modifyParagraph(ref, 'Awesomely\\nObvious!')).toBe(true);
107
- await assertNewParagraphExists(client);
110
+ const [para] = await pdf.page(0).selectParagraphsStartingWith('The Complete');
111
+ await para.edit().replace('Awesomely\nObvious!').apply();
112
+ await assertNewParagraphExists(pdf);
108
113
  });
109
114
 
110
- test('add paragraph with custom font - expect not found', async () => {
115
+ test('add paragraph with missing font (expect error)', async () => {
111
116
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
112
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
113
-
114
- const pb = client.paragraphBuilder()
115
- .fromString('Awesomely\\nObvious!')
116
- .withFont(new Font('Roboto', 14))
117
- .withLineSpacing(0.7)
118
- .withPosition(Position.atPageCoordinates(0, 300.1, 500));
119
-
120
- await expect(client.addParagraph(pb.build())).rejects.toThrow('Font not found');
117
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
118
+
119
+ await expect(
120
+ pdf.page(0).newParagraph()
121
+ .text('Awesomely\nObvious!')
122
+ .font('Roboto', 14)
123
+ .lineSpacing(0.7)
124
+ .at(300.1, 500)
125
+ .apply()
126
+ ).rejects.toThrow('Font not found');
121
127
  });
122
128
 
123
- test('add paragraph with custom font - Roboto-Regular', async () => {
129
+ test('add paragraph with known font', async () => {
124
130
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
125
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
131
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
126
132
 
127
- const pb = client.paragraphBuilder()
128
- .fromString('Awesomely\\nObvious!')
129
- .withFont(new Font('Roboto-Regular', 14))
130
- .withLineSpacing(0.7)
131
- .withPosition(Position.atPageCoordinates(0, 300.1, 500));
133
+ const success = await pdf.page(0).newParagraph()
134
+ .text('Awesomely\nObvious!')
135
+ .font('Roboto-Regular', 14)
136
+ .lineSpacing(0.7)
137
+ .at(300.1, 500)
138
+ .apply()
132
139
 
133
- expect(await client.addParagraph(pb.build())).toBe(true);
134
- await assertNewParagraphExists(client);
140
+ expect(success).toBe(true);
141
+ await assertNewParagraphExists(pdf);
135
142
  });
136
143
 
137
- test('add paragraph with found font', async () => {
144
+ test('add paragraph using font lookup', async () => {
138
145
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
139
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
146
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
140
147
 
141
- const fonts = await client.findFonts('Roboto', 14);
148
+ const fonts = await pdf.findFonts('Roboto', 14);
142
149
  expect(fonts.length).toBeGreaterThan(0);
143
- expect(fonts[0].name).toBe('Roboto-Regular');
144
-
145
150
  const roboto = fonts[0];
146
- const pb = client.paragraphBuilder()
147
- .fromString('Awesomely\\nObvious!')
148
- .withFont(roboto)
149
- .withLineSpacing(0.7)
150
- .withPosition(Position.atPageCoordinates(0, 300.1, 500));
151
-
152
- expect(await client.addParagraph(pb.build())).toBe(true);
153
- await assertNewParagraphExists(client);
151
+
152
+ const success = await pdf.page(0).newParagraph()
153
+ .text('Awesomely\nObvious!')
154
+ .font(roboto)
155
+ .lineSpacing(0.7)
156
+ .at(300.1, 500)
157
+ .apply();
158
+
159
+ expect(success).toBe(true);
160
+ await assertNewParagraphExists(pdf);
154
161
  });
155
162
 
156
163
  test('add paragraph with Asimovian font', async () => {
157
164
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
158
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
165
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
159
166
 
160
- const fonts = await client.findFonts('Asimovian', 14);
167
+ const fonts = await pdf.findFonts('Asimovian', 14);
161
168
  expect(fonts.length).toBeGreaterThan(0);
162
- expect(fonts[0].name).toBe('Asimovian-Regular');
163
-
164
169
  const asimovian = fonts[0];
165
- const pb = client.paragraphBuilder()
166
- .fromString('Awesomely\\nObvious!')
167
- .withFont(asimovian)
168
- .withLineSpacing(0.7)
169
- .withPosition(Position.atPageCoordinates(0, 300.1, 500));
170
-
171
- expect(await client.addParagraph(pb.build())).toBe(true);
172
- await assertNewParagraphExists(client);
170
+
171
+ const success = await pdf.page(0).newParagraph()
172
+ .text('Awesomely\nObvious!')
173
+ .font(asimovian)
174
+ .lineSpacing(0.7)
175
+ .at(300.1, 500)
176
+ .apply();
177
+
178
+ expect(success).toBe(true);
179
+ await assertNewParagraphExists(pdf);
173
180
  });
174
181
 
175
182
  test('add paragraph with custom TTF font', async () => {
176
183
  const [baseUrl, token, pdfData] = await requireEnvAndFixture('ObviouslyAwesome.pdf');
177
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
178
-
179
- // Use DancingScript-Regular.ttf from fixtures directory
180
- const ttfData = readFontFixture('DancingScript-Regular.ttf');
184
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl);
181
185
 
182
- const pb = client.paragraphBuilder()
183
- .fromString('Awesomely\\nObvious!')
184
- .withLineSpacing(1.8)
185
- .withColor(new Color(0, 0, 255))
186
- .withPosition(Position.atPageCoordinates(0, 300.1, 500));
186
+ const ttf = readFontFixture('DancingScript-Regular.ttf');
187
187
 
188
- await pb.withFontFile(ttfData, 24);
188
+ const success = await pdf.page(0).newParagraph()
189
+ .text('Awesomely\nObvious!')
190
+ .fontFile(ttf, 24)
191
+ .lineSpacing(1.8)
192
+ .color(new Color(0, 0, 255))
193
+ .at(300.1, 500)
194
+ .apply();
189
195
 
190
- expect(await client.addParagraph(pb.build())).toBe(true);
191
- await assertNewParagraphExists(client);
196
+ expect(success).toBe(true);
197
+ await assertNewParagraphExists(pdf);
192
198
  });
193
199
  });
@@ -1,68 +1,71 @@
1
1
  /**
2
- * E2E tests for path operations
2
+ * E2E tests for path operations (new PDFDancer API)
3
3
  */
4
4
 
5
- import { ClientV1, Position, ObjectType } from '../../index';
6
- import { requireEnvAndFixture } from './test-helpers';
7
-
8
- describe('Path E2E Tests', () => {
9
- // Remove the misleading beforeAll - tests should fail properly if not configured
10
-
11
- test('find paths', async () => {
12
- const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
13
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
14
-
15
- const paths = await client.findPaths();
16
- expect(paths).toHaveLength(9);
17
- expect(paths[0].type).toBe(ObjectType.PATH);
18
-
19
- const p1 = paths[0];
20
- expect(p1).toBeDefined();
21
- expect(p1.internalId).toBe('PATH_000001');
22
- expect(p1.position.boundingRect?.x).toBeCloseTo(80, 1);
23
- expect(p1.position.boundingRect?.y).toBeCloseTo(720, 1);
24
- });
25
-
26
- test('find paths by position', async () => {
27
- const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
28
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
29
-
30
- const paths = await client.findPaths(Position.atPageCoordinates(0, 80, 720));
31
- expect(paths).toHaveLength(1);
32
- expect(paths[0].internalId).toBe('PATH_000001');
33
- });
34
-
35
- test('delete path', async () => {
36
- const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
37
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
38
-
39
- let paths = await client.findPaths(Position.atPageCoordinates(0, 80, 720));
40
- expect(paths).toHaveLength(1);
41
- expect(paths[0].internalId).toBe('PATH_000001');
42
- expect(await client.delete(paths[0])).toBe(true);
43
-
44
- expect(await client.findPaths(Position.atPageCoordinates(0, 80, 720))).toHaveLength(0);
45
- expect(await client.findPaths()).toHaveLength(8);
46
- });
47
-
48
- test('move path', async () => {
49
- const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
50
- const client = await ClientV1.create(token, pdfData, baseUrl, 30000);
51
-
52
- let paths = await client.findPaths(Position.atPageCoordinates(0, 80, 720));
53
- const ref = paths[0];
54
- const pos = ref.position;
55
- expect(pos.boundingRect?.x).toBeCloseTo(80, 1);
56
- expect(pos.boundingRect?.y).toBeCloseTo(720, 1);
57
-
58
- expect(await client.move(ref, Position.atPageCoordinates(0, 50.1, 100))).toBe(true);
59
-
60
- expect(await client.findPaths(Position.atPageCoordinates(0, 80, 720))).toHaveLength(0);
61
-
62
- paths = await client.findPaths(Position.atPageCoordinates(0, 50.1, 100));
63
- const movedRef = paths[0];
64
- const newPos = movedRef.position;
65
- expect(newPos.boundingRect?.x).toBeCloseTo(50.1, 0.05);
66
- expect(newPos.boundingRect?.y).toBeCloseTo(100, 0.05);
67
- });
5
+ import {requireEnvAndFixture} from './test-helpers';
6
+ import {PDFDancer} from "../../pdfdancer_v1";
7
+
8
+ describe('Path E2E Tests (New API)', () => {
9
+
10
+ test('select paths', async () => {
11
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
12
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
13
+
14
+ const paths = await pdf.selectPaths();
15
+ expect(paths).toHaveLength(9);
16
+ expect(paths[0].type).toBe('PATH');
17
+
18
+ const p1 = paths[0];
19
+ expect(p1).toBeDefined();
20
+ expect(p1.internalId).toBe('PATH_000001');
21
+ expect(p1.position.getX()).toBeCloseTo(80, 1);
22
+ expect(p1.position.getY()).toBeCloseTo(720, 1);
23
+ });
24
+
25
+ test('select paths by position', async () => {
26
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
27
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
28
+
29
+ const paths = await pdf.page(0).selectPathsAt(80, 720);
30
+ expect(paths).toHaveLength(1);
31
+ expect(paths[0].internalId).toBe('PATH_000001');
32
+ });
33
+
34
+ test('delete path', async () => {
35
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
36
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
37
+
38
+ let paths = await pdf.page(0).selectPathsAt(80, 720);
39
+ expect(paths).toHaveLength(1);
40
+ expect(paths[0].internalId).toBe('PATH_000001');
41
+
42
+ const path = paths[0];
43
+ await path.delete();
44
+
45
+ const remainingAtOldPos = await pdf.page(0).selectPathsAt(80, 720);
46
+ expect(remainingAtOldPos).toHaveLength(0);
47
+
48
+ const allPaths = await pdf.selectPaths();
49
+ expect(allPaths).toHaveLength(8);
50
+ });
51
+
52
+ test('move path', async () => {
53
+ const [baseUrl, token, pdfData] = await requireEnvAndFixture('basic-paths.pdf');
54
+ const pdf = await PDFDancer.open(pdfData, token, baseUrl, 30000);
55
+
56
+ const [path] = await pdf.page(0).selectPathsAt(80, 720);
57
+ const pos = path.position;
58
+ expect(pos.getX()).toBeCloseTo(80, 1);
59
+ expect(pos.getY()).toBeCloseTo(720, 1);
60
+
61
+ await path.moveTo(50.1, 100);
62
+
63
+ const oldPos = await pdf.page(0).selectPathsAt(80, 720);
64
+ expect(oldPos).toHaveLength(0);
65
+
66
+ const moved = await pdf.page(0).selectPathsAt(50.1, 100);
67
+ const movedPos = moved[0].position;
68
+ expect(movedPos.getX()).toBeCloseTo(50.1, 1);
69
+ expect(movedPos.getY()).toBeCloseTo(100, 1);
70
+ });
68
71
  });
@@ -9,124 +9,128 @@ import * as path from 'path';
9
9
  * Get the base URL from environment variable or default
10
10
  */
11
11
  export function getBaseUrl(): string {
12
- return process.env.PDFDANCER_BASE_URL || 'http://localhost:8080';
12
+ return process.env.PDFDANCER_BASE_URL || 'http://localhost:8080';
13
13
  }
14
14
 
15
15
  /**
16
16
  * Read authentication token from environment or token files
17
17
  */
18
18
  export function readToken(): string | null {
19
- const token = process.env.PDFDANCER_TOKEN;
20
- if (token) {
21
- return token.trim();
22
- }
19
+ const token = process.env.PDFDANCER_TOKEN;
20
+ if (token) {
21
+ return token.trim();
22
+ }
23
23
 
24
- // Try common token files in project root
25
- const projectRoot = path.resolve(__dirname, '../../..');
26
- const candidates = findFiles(projectRoot, 'jwt-token-*.txt');
24
+ // Try common token files in project root
25
+ const projectRoot = path.resolve(__dirname, '../../..');
26
+ const candidates = findFiles(projectRoot, 'jwt-token-*.txt');
27
27
 
28
- for (const file of candidates) {
29
- try {
30
- return fs.readFileSync(file, 'utf-8').trim();
31
- } catch {
32
- continue;
28
+ for (const file of candidates) {
29
+ try {
30
+ return fs.readFileSync(file, 'utf-8').trim();
31
+ } catch {
32
+ continue;
33
+ }
33
34
  }
34
- }
35
35
 
36
- return null;
36
+ return null;
37
37
  }
38
38
 
39
39
  /**
40
40
  * Check if server is up and running
41
41
  */
42
42
  export async function serverUp(baseUrl: string): Promise<boolean> {
43
- try {
44
- const response = await fetch(`${baseUrl}/ping`, {
45
- signal: AbortSignal.timeout(3000)
46
- });
47
- const text = await response.text();
48
- return response.status === 200 && text.includes('Pong');
49
- } catch {
50
- return false;
51
- }
43
+ try {
44
+ const response = await fetch(`${baseUrl}/ping`, {
45
+ signal: AbortSignal.timeout(3000)
46
+ });
47
+ const text = await response.text();
48
+ return response.status === 200 && text.includes('Pong');
49
+ } catch {
50
+ return false;
51
+ }
52
52
  }
53
53
 
54
54
  /**
55
55
  * Helper to find files matching a pattern
56
56
  */
57
57
  function findFiles(dir: string, pattern: string): string[] {
58
- try {
59
- const files = fs.readdirSync(dir);
60
- const regex = new RegExp(pattern.replace('*', '.*'));
61
- return files
62
- .filter(file => regex.test(file))
63
- .map(file => path.join(dir, file));
64
- } catch {
65
- return [];
66
- }
58
+ try {
59
+ const files = fs.readdirSync(dir);
60
+ const regex = new RegExp(pattern.replace('*', '.*'));
61
+ return files
62
+ .filter(file => regex.test(file))
63
+ .map(file => path.join(dir, file));
64
+ } catch {
65
+ return [];
66
+ }
67
67
  }
68
68
 
69
69
  /**
70
70
  * Require environment variables and PDF fixture for testing
71
71
  */
72
72
  export async function requireEnvAndFixture(pdfFilename: string): Promise<[string, string, Uint8Array]> {
73
- const baseUrl = getBaseUrl();
74
- const token = readToken();
73
+ const baseUrl = getBaseUrl();
74
+ const token = readToken();
75
75
 
76
- if (!await serverUp(baseUrl)) {
77
- throw new Error(`PDFDancer server not reachable at ${baseUrl}; set PDFDANCER_BASE_URL or start server`);
78
- }
76
+ if (!await serverUp(baseUrl)) {
77
+ throw new Error(`PDFDancer server not reachable at ${baseUrl}; set PDFDANCER_BASE_URL or start server`);
78
+ }
79
79
 
80
- if (!token) {
81
- throw new Error('PDFDANCER_TOKEN not set and no token file found; set env or place jwt-token-*.txt in repo');
82
- }
80
+ if (!token) {
81
+ throw new Error('PDFDANCER_TOKEN not set and no token file found; set env or place jwt-token-*.txt in repo');
82
+ }
83
83
 
84
- // Look for PDF fixture file
85
- const fixturesDir = path.resolve(__dirname, '../../../fixtures');
86
- const pdfPath = path.join(fixturesDir, pdfFilename);
84
+ // Look for PDF fixture file
85
+ const fixturesDir = path.resolve(__dirname, '../../../fixtures');
86
+ const pdfPath = path.join(fixturesDir, pdfFilename);
87
87
 
88
- if (!fs.existsSync(pdfPath)) {
89
- throw new Error(`${pdfFilename} fixture not found at ${pdfPath}`);
90
- }
88
+ if (!fs.existsSync(pdfPath)) {
89
+ throw new Error(`${pdfFilename} fixture not found at ${pdfPath}`);
90
+ }
91
91
 
92
- const pdfData = fs.readFileSync(pdfPath);
93
- return [baseUrl, token, new Uint8Array(pdfData)];
92
+ const pdfData = fs.readFileSync(pdfPath);
93
+ return [baseUrl, token, new Uint8Array(pdfData)];
94
94
  }
95
95
 
96
96
  /**
97
- * Helper to create temporary file path (for Node.js environment)
97
+ * Helper to open temporary file path (for Node.js environment)
98
98
  */
99
99
  export function createTempPath(filename: string): string {
100
- const tmpDir = process.env.TMPDIR || '/tmp';
101
- return path.join(tmpDir, filename);
100
+ const tmpDir = process.env.TMPDIR || '/tmp';
101
+ return path.join(tmpDir, filename);
102
102
  }
103
103
 
104
104
  /**
105
105
  * Helper to read image file for tests
106
106
  */
107
107
  export function readImageFixture(filename: string): Uint8Array {
108
- const fixturesDir = path.resolve(__dirname, '../../../fixtures');
109
- const imagePath = path.join(fixturesDir, filename);
108
+ const imagePath = getImagePath(filename);
109
+
110
+ if (!fs.existsSync(imagePath)) {
111
+ throw new Error(`Image fixture not found: ${filename}`);
112
+ }
110
113
 
111
- if (!fs.existsSync(imagePath)) {
112
- throw new Error(`Image fixture not found: ${filename}`);
113
- }
114
+ const imageData = fs.readFileSync(imagePath);
115
+ return new Uint8Array(imageData);
116
+ }
114
117
 
115
- const imageData = fs.readFileSync(imagePath);
116
- return new Uint8Array(imageData);
118
+ export function getImagePath(filename: string): string {
119
+ const fixturesDir = path.resolve(__dirname, '../../../fixtures');
120
+ return path.join(fixturesDir, filename);
117
121
  }
118
122
 
119
123
  /**
120
124
  * Helper to read font file for tests
121
125
  */
122
126
  export function readFontFixture(filename: string): Uint8Array {
123
- const fixturesDir = path.resolve(__dirname, '../../../fixtures');
124
- const fontPath = path.join(fixturesDir, filename);
127
+ const fixturesDir = path.resolve(__dirname, '../../../fixtures');
128
+ const fontPath = path.join(fixturesDir, filename);
125
129
 
126
- if (!fs.existsSync(fontPath)) {
127
- throw new Error(`Font fixture not found: ${filename}`);
128
- }
130
+ if (!fs.existsSync(fontPath)) {
131
+ throw new Error(`Font fixture not found: ${filename}`);
132
+ }
129
133
 
130
- const fontData = fs.readFileSync(fontPath);
131
- return new Uint8Array(fontData);
132
- }
134
+ const fontData = fs.readFileSync(fontPath);
135
+ return new Uint8Array(fontData);
136
+ }