@link-assistant/web-capture 1.7.4 → 1.7.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.
package/src/index.js CHANGED
@@ -11,7 +11,6 @@ import { docxHandler } from './docx.js';
11
11
  import { animationHandler } from './animation.js';
12
12
  import { figuresHandler } from './figures.js';
13
13
  import { themedImageHandler } from './themed-image.js';
14
- import { gdocsHandler } from './gdocs.js';
15
14
 
16
15
  const app = express();
17
16
  const port = process.env.PORT || 3000;
@@ -28,7 +27,6 @@ app.get('/docx', docxHandler);
28
27
  app.get('/animation', animationHandler);
29
28
  app.get('/figures', figuresHandler);
30
29
  app.get('/themed-image', themedImageHandler);
31
- app.get('/gdocs', gdocsHandler);
32
30
 
33
31
  // Start the server if this is the main module
34
32
  const isMainModule =
@@ -28,6 +28,17 @@ afterEach(() => {
28
28
  });
29
29
 
30
30
  describe('API Endpoint Tests', () => {
31
+ describe('unsupported routes', () => {
32
+ it('does not expose /gdocs as an output-format endpoint', async () => {
33
+ await request(app)
34
+ .get('/gdocs')
35
+ .query({
36
+ url: 'https://docs.google.com/document/d/test-doc/edit',
37
+ })
38
+ .expect(404);
39
+ });
40
+ });
41
+
31
42
  describe('GET /image', () => {
32
43
  it('rejects invalid format', async () => {
33
44
  await request(app)
@@ -222,6 +222,7 @@ describe('CLI', () => {
222
222
  expect(result.stdout).toContain('/html');
223
223
  expect(result.stdout).toContain('/markdown');
224
224
  expect(result.stdout).toContain('/image');
225
+ expect(result.stdout).not.toContain('/gdocs');
225
226
  });
226
227
  });
227
228
 
@@ -3,7 +3,13 @@ import {
3
3
  isGoogleDocsUrl,
4
4
  extractDocumentId,
5
5
  buildExportUrl,
6
+ buildEditUrl,
7
+ buildDocsApiUrl,
6
8
  extractBase64Images,
9
+ parseGoogleDocsModelChunks,
10
+ renderGoogleDocsCapture,
11
+ renderDocsApiDocument,
12
+ selectGoogleDocsCaptureMethod,
7
13
  GDOCS_EXPORT_FORMATS,
8
14
  } from '../../src/gdocs.js';
9
15
  import {
@@ -149,6 +155,161 @@ describe('gdocs', () => {
149
155
  });
150
156
  });
151
157
 
158
+ describe('capture method selection (issue #72)', () => {
159
+ it('honors explicit browser capture for Google Docs URLs', () => {
160
+ expect(selectGoogleDocsCaptureMethod('browser')).toBe('browser-model');
161
+ });
162
+
163
+ it('uses public export for API capture without token', () => {
164
+ expect(selectGoogleDocsCaptureMethod('api')).toBe('public-export');
165
+ });
166
+
167
+ it('uses Docs REST API for API capture with token', () => {
168
+ expect(selectGoogleDocsCaptureMethod('api', 'token-123')).toBe(
169
+ 'docs-api'
170
+ );
171
+ });
172
+ });
173
+
174
+ describe('buildEditUrl', () => {
175
+ it('builds the fresh Google Docs editor URL', () => {
176
+ expect(buildEditUrl('abc123')).toBe(
177
+ 'https://docs.google.com/document/d/abc123/edit'
178
+ );
179
+ });
180
+ });
181
+
182
+ describe('buildDocsApiUrl', () => {
183
+ it('builds the Google Docs REST API URL', () => {
184
+ expect(buildDocsApiUrl('abc123')).toBe(
185
+ 'https://docs.googleapis.com/v1/documents/abc123'
186
+ );
187
+ });
188
+ });
189
+
190
+ describe('parseGoogleDocsModelChunks (issue #72)', () => {
191
+ it('includes regular and suggested text from DOCS_modelChunk data', () => {
192
+ const capture = parseGoogleDocsModelChunks([
193
+ {
194
+ chunk: [
195
+ { ty: 'is', s: 'Stable ' },
196
+ { ty: 'iss', s: 'suggested\n' },
197
+ ],
198
+ },
199
+ ]);
200
+
201
+ expect(capture.text).toContain('Stable suggested');
202
+ expect(renderGoogleDocsCapture(capture, 'markdown')).toContain(
203
+ 'Stable suggested'
204
+ );
205
+ });
206
+
207
+ it('extracts table cells and suggested images from DOCS_modelChunk data', () => {
208
+ const chunks = [
209
+ {
210
+ chunk: [
211
+ { ty: 'is', s: String.fromCharCode(0x10) },
212
+ { ty: 'is', s: String.fromCharCode(0x12) },
213
+ { ty: 'is', s: String.fromCharCode(0x1c) },
214
+ { ty: 'is', s: 'Cell A' },
215
+ { ty: 'is', s: String.fromCharCode(0x1c) },
216
+ { ty: 'is', s: '*' },
217
+ { ty: 'is', s: '\n' },
218
+ { ty: 'is', s: String.fromCharCode(0x11) },
219
+ {
220
+ ty: 'ase',
221
+ id: 'suggested-image',
222
+ epm: { ee_eo: { i_cid: 'cid_12345678901234567890' } },
223
+ },
224
+ { ty: 'ste', id: 'suggested-image', spi: 10 },
225
+ ],
226
+ },
227
+ ];
228
+ const capture = parseGoogleDocsModelChunks(chunks, {
229
+ cid_12345678901234567890:
230
+ 'https://docs.google.com/docs-images-rt/image-id',
231
+ });
232
+ const markdown = renderGoogleDocsCapture(capture, 'markdown');
233
+
234
+ expect(capture.tables).toHaveLength(1);
235
+ expect(capture.tables[0].rows[0].cells).toHaveLength(2);
236
+ expect(markdown).toContain('Cell A');
237
+ expect(markdown).toContain(
238
+ '![suggested image](https://docs.google.com/docs-images-rt/image-id)'
239
+ );
240
+ });
241
+ });
242
+
243
+ describe('renderDocsApiDocument (issue #72)', () => {
244
+ it('renders Google Docs REST API paragraphs, tables, and inline images', () => {
245
+ const apiDocument = {
246
+ title: 'API Doc',
247
+ body: {
248
+ content: [
249
+ {
250
+ paragraph: {
251
+ elements: [{ textRun: { content: 'Intro paragraph\n' } }],
252
+ },
253
+ },
254
+ {
255
+ table: {
256
+ tableRows: [
257
+ {
258
+ tableCells: [
259
+ {
260
+ content: [
261
+ {
262
+ paragraph: {
263
+ elements: [{ textRun: { content: 'Name\n' } }],
264
+ },
265
+ },
266
+ ],
267
+ },
268
+ {
269
+ content: [
270
+ {
271
+ paragraph: {
272
+ elements: [
273
+ {
274
+ inlineObjectElement: {
275
+ inlineObjectId: 'image-1',
276
+ },
277
+ },
278
+ ],
279
+ },
280
+ },
281
+ ],
282
+ },
283
+ ],
284
+ },
285
+ ],
286
+ },
287
+ },
288
+ ],
289
+ },
290
+ inlineObjects: {
291
+ 'image-1': {
292
+ inlineObjectProperties: {
293
+ embeddedObject: {
294
+ title: 'Diagram',
295
+ imageProperties: {
296
+ contentUri: 'https://example.com/diagram.png',
297
+ },
298
+ },
299
+ },
300
+ },
301
+ },
302
+ };
303
+
304
+ const rendered = renderDocsApiDocument(apiDocument);
305
+
306
+ expect(rendered.markdown).toContain('Intro paragraph');
307
+ expect(rendered.markdown).toContain('| Name | ![Diagram]');
308
+ expect(rendered.html).toContain('<table>');
309
+ expect(rendered.html).toContain('src="https://example.com/diagram.png"');
310
+ });
311
+ });
312
+
152
313
  describe('GDOCS_EXPORT_FORMATS', () => {
153
314
  it('contains all expected formats', () => {
154
315
  expect(GDOCS_EXPORT_FORMATS).toEqual({