@superblocksteam/sdk-api 2.0.115-next.0 → 2.0.115-next.1

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 (76) hide show
  1. package/README.md +1 -192
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/integrations/documentation-resolver.test.d.ts +2 -0
  6. package/dist/integrations/documentation-resolver.test.d.ts.map +1 -0
  7. package/dist/integrations/documentation-resolver.test.js +608 -0
  8. package/dist/integrations/documentation-resolver.test.js.map +1 -0
  9. package/dist/integrations/documentation.d.ts +8 -0
  10. package/dist/integrations/documentation.d.ts.map +1 -0
  11. package/dist/integrations/documentation.js +277 -0
  12. package/dist/integrations/documentation.js.map +1 -0
  13. package/package.json +1 -1
  14. package/src/index.ts +4 -0
  15. package/src/integrations/airtable/docs.manifest.json +5 -0
  16. package/src/integrations/anthropic/docs.manifest.json +5 -0
  17. package/src/integrations/asana/docs.manifest.json +5 -0
  18. package/src/integrations/athena/docs.manifest.json +5 -0
  19. package/src/integrations/bigquery/docs.manifest.json +5 -0
  20. package/src/integrations/bitbucket/docs.manifest.json +5 -0
  21. package/src/integrations/box/docs.manifest.json +5 -0
  22. package/src/integrations/circleci/docs.manifest.json +5 -0
  23. package/src/integrations/cockroachdb/docs.manifest.json +5 -0
  24. package/src/integrations/cohere/docs.manifest.json +5 -0
  25. package/src/integrations/confluence/docs.manifest.json +5 -0
  26. package/src/integrations/cosmosdb/docs.manifest.json +5 -0
  27. package/src/integrations/databricks/docs.manifest.json +5 -0
  28. package/src/integrations/datadog/docs.manifest.json +5 -0
  29. package/src/integrations/documentation-resolver.test.ts +704 -0
  30. package/src/integrations/documentation.ts +421 -0
  31. package/src/integrations/dropbox/docs.manifest.json +5 -0
  32. package/src/integrations/dynamodb/docs.manifest.json +5 -0
  33. package/src/integrations/elasticsearch/docs.manifest.json +5 -0
  34. package/src/integrations/fireworks/docs.manifest.json +5 -0
  35. package/src/integrations/front/docs.manifest.json +5 -0
  36. package/src/integrations/gcs/docs.manifest.json +5 -0
  37. package/src/integrations/gemini/docs.manifest.json +5 -0
  38. package/src/integrations/github/docs.manifest.json +5 -0
  39. package/src/integrations/googleanalytics/docs.manifest.json +5 -0
  40. package/src/integrations/googledrive/docs.manifest.json +5 -0
  41. package/src/integrations/graphql/docs.manifest.json +5 -0
  42. package/src/integrations/groq/docs.manifest.json +5 -0
  43. package/src/integrations/gsheets/docs.manifest.json +5 -0
  44. package/src/integrations/hubspot/docs.manifest.json +5 -0
  45. package/src/integrations/intercom/docs.manifest.json +5 -0
  46. package/src/integrations/jira/docs.manifest.json +5 -0
  47. package/src/integrations/lakebase/docs.manifest.json +5 -0
  48. package/src/integrations/launchdarkly/docs.manifest.json +5 -0
  49. package/src/integrations/mariadb/docs.manifest.json +5 -0
  50. package/src/integrations/mistral/docs.manifest.json +5 -0
  51. package/src/integrations/mongodb/docs.manifest.json +5 -0
  52. package/src/integrations/mssql/docs.manifest.json +5 -0
  53. package/src/integrations/mysql/docs.manifest.json +5 -0
  54. package/src/integrations/notion/docs.manifest.json +5 -0
  55. package/src/integrations/openai_v2/docs.manifest.json +5 -0
  56. package/src/integrations/oracledb/docs.manifest.json +5 -0
  57. package/src/integrations/pagerduty/docs.manifest.json +5 -0
  58. package/src/integrations/perplexity/docs.manifest.json +5 -0
  59. package/src/integrations/postgres/docs.manifest.json +5 -0
  60. package/src/integrations/redshift/docs.manifest.json +5 -0
  61. package/src/integrations/restapiintegration/docs.manifest.json +5 -0
  62. package/src/integrations/s3/docs.manifest.json +5 -0
  63. package/src/integrations/salesforce/docs.manifest.json +5 -0
  64. package/src/integrations/segment/docs.manifest.json +5 -0
  65. package/src/integrations/sendgrid/docs.manifest.json +5 -0
  66. package/src/integrations/slack/docs.manifest.json +5 -0
  67. package/src/integrations/smtp/docs.manifest.json +5 -0
  68. package/src/integrations/snowflake/docs.manifest.json +5 -0
  69. package/src/integrations/snowflakecortex/docs.manifest.json +5 -0
  70. package/src/integrations/snowflakepostgres/docs.manifest.json +5 -0
  71. package/src/integrations/stabilityai/docs.manifest.json +5 -0
  72. package/src/integrations/stripe/docs.manifest.json +5 -0
  73. package/src/integrations/superblocks-ocr/docs.manifest.json +5 -0
  74. package/src/integrations/twilio/docs.manifest.json +5 -0
  75. package/src/integrations/zendesk/docs.manifest.json +5 -0
  76. package/src/integrations/zoom/docs.manifest.json +5 -0
@@ -0,0 +1,704 @@
1
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ import { describe, expect, it } from "vitest";
6
+
7
+ import { resolveIntegrationDocumentation } from "./documentation.js";
8
+
9
+ function createPluginDocsFixture(
10
+ pluginId: string,
11
+ files: Record<string, string>,
12
+ ): string {
13
+ const rootDirectory = mkdtempSync(join(tmpdir(), "sdk-docs-"));
14
+ const pluginDirectory = join(rootDirectory, pluginId);
15
+ mkdirSync(pluginDirectory, { recursive: true });
16
+
17
+ for (const [relativePath, content] of Object.entries(files)) {
18
+ const absolutePath = join(pluginDirectory, relativePath);
19
+ mkdirSync(join(absolutePath, ".."), { recursive: true });
20
+ writeFileSync(absolutePath, content);
21
+ }
22
+
23
+ return rootDirectory;
24
+ }
25
+
26
+ describe("resolveIntegrationDocumentation", () => {
27
+ it("returns base README when no manifest exists", async () => {
28
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
29
+ "README.md": "base-docs",
30
+ });
31
+
32
+ try {
33
+ const docs = await resolveIntegrationDocumentation("dropbox", {
34
+ pluginVersion: "0.4.0",
35
+ integrationsDirectory,
36
+ });
37
+ expect(docs).toBe("base-docs");
38
+ } finally {
39
+ rmSync(integrationsDirectory, { recursive: true, force: true });
40
+ }
41
+ });
42
+
43
+ it("applies matching overlays in manifest order", async () => {
44
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
45
+ "README.md": "base-docs",
46
+ "docs.manifest.json": JSON.stringify({
47
+ overlays: [
48
+ { file: "overlays/01.md", versionRange: ">=0.4.0" },
49
+ { file: "overlays/02.md", versionRange: ">=0.5.0" },
50
+ { file: "overlays/03.md", versionRange: ">=0.5.0" },
51
+ ],
52
+ }),
53
+ "overlays/01.md": "overlay-one",
54
+ "overlays/02.md": "overlay-two",
55
+ "overlays/03.md": "overlay-three",
56
+ });
57
+
58
+ try {
59
+ const docs = await resolveIntegrationDocumentation("dropbox", {
60
+ pluginVersion: "0.5.0",
61
+ integrationsDirectory,
62
+ });
63
+
64
+ expect(docs).toBe(
65
+ ["base-docs", "overlay-one", "overlay-two", "overlay-three"].join(
66
+ "\n\n",
67
+ ),
68
+ );
69
+ } finally {
70
+ rmSync(integrationsDirectory, { recursive: true, force: true });
71
+ }
72
+ });
73
+
74
+ it("skips overlays whose version range does not match", async () => {
75
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
76
+ "README.md": "base-docs",
77
+ "docs.manifest.json": JSON.stringify({
78
+ overlays: [
79
+ { file: "overlays/match.md", versionRange: ">=0.4.0 <0.6.0" },
80
+ { file: "overlays/skip.md", versionRange: ">=0.6.0" },
81
+ ],
82
+ }),
83
+ "overlays/match.md": "overlay-match",
84
+ "overlays/skip.md": "overlay-skip",
85
+ });
86
+
87
+ try {
88
+ const docs = await resolveIntegrationDocumentation("dropbox", {
89
+ pluginVersion: "0.5.1",
90
+ integrationsDirectory,
91
+ });
92
+
93
+ expect(docs).toBe(["base-docs", "overlay-match"].join("\n\n"));
94
+ } finally {
95
+ rmSync(integrationsDirectory, { recursive: true, force: true });
96
+ }
97
+ });
98
+
99
+ it("returns base docs when pluginVersion is missing", async () => {
100
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
101
+ "README.md": "base-docs",
102
+ "docs.manifest.json": JSON.stringify({
103
+ overlays: [{ file: "overlays/01.md", versionRange: ">=0.4.0" }],
104
+ }),
105
+ "overlays/01.md": "overlay-one",
106
+ });
107
+
108
+ try {
109
+ const docs = await resolveIntegrationDocumentation("dropbox", {
110
+ integrationsDirectory,
111
+ });
112
+ expect(docs).toBe("base-docs");
113
+ } finally {
114
+ rmSync(integrationsDirectory, { recursive: true, force: true });
115
+ }
116
+ });
117
+
118
+ it("throws on invalid manifest shape", async () => {
119
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
120
+ "README.md": "base-docs",
121
+ "docs.manifest.json": JSON.stringify({
122
+ overlays: [{ versionRange: ">=0.4.0" }],
123
+ }),
124
+ });
125
+
126
+ try {
127
+ await expect(
128
+ resolveIntegrationDocumentation("dropbox", {
129
+ pluginVersion: "0.5.0",
130
+ integrationsDirectory,
131
+ }),
132
+ ).rejects.toThrowError(/Invalid overlay entry/);
133
+ } finally {
134
+ rmSync(integrationsDirectory, { recursive: true, force: true });
135
+ }
136
+ });
137
+
138
+ it("replaces markdown sections via overlay directives", async () => {
139
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
140
+ "README.md": [
141
+ "# Dropbox",
142
+ "",
143
+ "## Authentication",
144
+ "Old auth docs",
145
+ "",
146
+ "## Limits",
147
+ "Old limits docs",
148
+ ].join("\n"),
149
+ "docs.manifest.json": JSON.stringify({
150
+ overlays: [
151
+ { file: "overlays/replace-auth.md", versionRange: ">=0.4.0" },
152
+ ],
153
+ }),
154
+ "overlays/replace-auth.md": [
155
+ "## @replace: Authentication",
156
+ "New auth docs",
157
+ "With multiple lines",
158
+ ].join("\n"),
159
+ });
160
+
161
+ try {
162
+ const docs = await resolveIntegrationDocumentation("dropbox", {
163
+ pluginVersion: "0.4.1",
164
+ integrationsDirectory,
165
+ });
166
+
167
+ expect(docs).toContain(
168
+ "## Authentication\nNew auth docs\nWith multiple lines",
169
+ );
170
+ expect(docs).toContain("## Limits\nOld limits docs");
171
+ expect(docs).not.toContain("Old auth docs");
172
+ } finally {
173
+ rmSync(integrationsDirectory, { recursive: true, force: true });
174
+ }
175
+ });
176
+
177
+ it("applies replace overlays in manifest order", async () => {
178
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
179
+ "README.md": ["# Dropbox", "", "## Authentication", "v1 auth"].join("\n"),
180
+ "docs.manifest.json": JSON.stringify({
181
+ overlays: [
182
+ { file: "overlays/replace-auth-v2.md", versionRange: ">=0.4.0" },
183
+ { file: "overlays/replace-auth-v3.md", versionRange: ">=0.4.0" },
184
+ ],
185
+ }),
186
+ "overlays/replace-auth-v2.md": [
187
+ "## @replace: Authentication",
188
+ "v2 auth",
189
+ ].join("\n"),
190
+ "overlays/replace-auth-v3.md": [
191
+ "## @replace: Authentication",
192
+ "v3 auth",
193
+ ].join("\n"),
194
+ });
195
+
196
+ try {
197
+ const docs = await resolveIntegrationDocumentation("dropbox", {
198
+ pluginVersion: "0.4.1",
199
+ integrationsDirectory,
200
+ });
201
+
202
+ expect(docs).toContain("## Authentication\nv3 auth");
203
+ expect(docs).not.toContain("v1 auth");
204
+ expect(docs).not.toContain("v2 auth");
205
+ } finally {
206
+ rmSync(integrationsDirectory, { recursive: true, force: true });
207
+ }
208
+ });
209
+
210
+ it("throws when replace target section does not exist", async () => {
211
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
212
+ "README.md": ["# Dropbox", "", "## Authentication", "auth docs"].join(
213
+ "\n",
214
+ ),
215
+ "docs.manifest.json": JSON.stringify({
216
+ overlays: [
217
+ { file: "overlays/missing-target.md", versionRange: ">=0.4.0" },
218
+ ],
219
+ }),
220
+ "overlays/missing-target.md": [
221
+ "## @replace: Limits",
222
+ "new limits docs",
223
+ ].join("\n"),
224
+ });
225
+
226
+ try {
227
+ await expect(
228
+ resolveIntegrationDocumentation("dropbox", {
229
+ pluginVersion: "0.4.1",
230
+ integrationsDirectory,
231
+ }),
232
+ ).rejects.toThrowError(/could not find section/i);
233
+ } finally {
234
+ rmSync(integrationsDirectory, { recursive: true, force: true });
235
+ }
236
+ });
237
+
238
+ it("handles mixed append and replace in a single overlay", async () => {
239
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
240
+ "README.md": [
241
+ "# Dropbox",
242
+ "",
243
+ "## Setup",
244
+ "Old setup content",
245
+ "",
246
+ "## Usage",
247
+ "Usage content",
248
+ ].join("\n"),
249
+ "docs.manifest.json": JSON.stringify({
250
+ overlays: [{ file: "overlays/mixed.md", versionRange: ">=0.4.0" }],
251
+ }),
252
+ "overlays/mixed.md": [
253
+ "Appended intro paragraph",
254
+ "",
255
+ "## @replace: Setup",
256
+ "New setup content",
257
+ ].join("\n"),
258
+ });
259
+
260
+ try {
261
+ const docs = await resolveIntegrationDocumentation("dropbox", {
262
+ pluginVersion: "0.5.0",
263
+ integrationsDirectory,
264
+ });
265
+
266
+ expect(docs).toContain("Appended intro paragraph");
267
+ expect(docs).toContain("## Setup\nNew setup content");
268
+ expect(docs).not.toContain("Old setup content");
269
+ expect(docs).toContain("## Usage\nUsage content");
270
+ } finally {
271
+ rmSync(integrationsDirectory, { recursive: true, force: true });
272
+ }
273
+ });
274
+
275
+ it("handles multiple @replace directives in a single overlay", async () => {
276
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
277
+ "README.md": [
278
+ "# Dropbox",
279
+ "",
280
+ "## Auth",
281
+ "Old auth",
282
+ "",
283
+ "## Limits",
284
+ "Old limits",
285
+ "",
286
+ "## Usage",
287
+ "Usage content",
288
+ ].join("\n"),
289
+ "docs.manifest.json": JSON.stringify({
290
+ overlays: [
291
+ { file: "overlays/multi-replace.md", versionRange: ">=0.4.0" },
292
+ ],
293
+ }),
294
+ "overlays/multi-replace.md": [
295
+ "## @replace: Auth",
296
+ "New auth",
297
+ "",
298
+ "## @replace: Limits",
299
+ "New limits",
300
+ ].join("\n"),
301
+ });
302
+
303
+ try {
304
+ const docs = await resolveIntegrationDocumentation("dropbox", {
305
+ pluginVersion: "0.4.0",
306
+ integrationsDirectory,
307
+ });
308
+
309
+ expect(docs).toContain("## Auth\nNew auth");
310
+ expect(docs).toContain("## Limits\nNew limits");
311
+ expect(docs).toContain("## Usage\nUsage content");
312
+ expect(docs).not.toContain("Old auth");
313
+ expect(docs).not.toContain("Old limits");
314
+ } finally {
315
+ rmSync(integrationsDirectory, { recursive: true, force: true });
316
+ }
317
+ });
318
+
319
+ it("replaces the last section in the document", async () => {
320
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
321
+ "README.md": [
322
+ "# Dropbox",
323
+ "",
324
+ "## First",
325
+ "First content",
326
+ "",
327
+ "## Last",
328
+ "Old last content",
329
+ "More old last content",
330
+ ].join("\n"),
331
+ "docs.manifest.json": JSON.stringify({
332
+ overlays: [
333
+ { file: "overlays/replace-last.md", versionRange: ">=0.4.0" },
334
+ ],
335
+ }),
336
+ "overlays/replace-last.md": [
337
+ "## @replace: Last",
338
+ "New last content",
339
+ ].join("\n"),
340
+ });
341
+
342
+ try {
343
+ const docs = await resolveIntegrationDocumentation("dropbox", {
344
+ pluginVersion: "0.4.0",
345
+ integrationsDirectory,
346
+ });
347
+
348
+ expect(docs).toContain("## Last\nNew last content");
349
+ expect(docs).toContain("## First\nFirst content");
350
+ expect(docs).not.toContain("Old last content");
351
+ } finally {
352
+ rmSync(integrationsDirectory, { recursive: true, force: true });
353
+ }
354
+ });
355
+
356
+ it("respects heading hierarchy when replacing nested sections", async () => {
357
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
358
+ "README.md": [
359
+ "# Dropbox",
360
+ "",
361
+ "## Auth",
362
+ "Auth intro",
363
+ "",
364
+ "### OAuth",
365
+ "OAuth details",
366
+ "",
367
+ "### API Key",
368
+ "API key details",
369
+ "",
370
+ "## Usage",
371
+ "Usage content",
372
+ ].join("\n"),
373
+ "docs.manifest.json": JSON.stringify({
374
+ overlays: [
375
+ { file: "overlays/replace-oauth.md", versionRange: ">=0.4.0" },
376
+ ],
377
+ }),
378
+ "overlays/replace-oauth.md": [
379
+ "### @replace: OAuth",
380
+ "New OAuth flow",
381
+ ].join("\n"),
382
+ });
383
+
384
+ try {
385
+ const docs = await resolveIntegrationDocumentation("dropbox", {
386
+ pluginVersion: "0.4.0",
387
+ integrationsDirectory,
388
+ });
389
+
390
+ expect(docs).toContain("### OAuth\nNew OAuth flow");
391
+ expect(docs).toContain("### API Key\nAPI key details");
392
+ expect(docs).toContain("## Usage\nUsage content");
393
+ expect(docs).not.toContain("OAuth details");
394
+ } finally {
395
+ rmSync(integrationsDirectory, { recursive: true, force: true });
396
+ }
397
+ });
398
+
399
+ it("ignores empty overlay content (whitespace only)", async () => {
400
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
401
+ "README.md": "base-docs",
402
+ "docs.manifest.json": JSON.stringify({
403
+ overlays: [{ file: "overlays/empty.md", versionRange: ">=0.4.0" }],
404
+ }),
405
+ "overlays/empty.md": " \n \n ",
406
+ });
407
+
408
+ try {
409
+ const docs = await resolveIntegrationDocumentation("dropbox", {
410
+ pluginVersion: "0.5.0",
411
+ integrationsDirectory,
412
+ });
413
+
414
+ expect(docs).toBe("base-docs");
415
+ } finally {
416
+ rmSync(integrationsDirectory, { recursive: true, force: true });
417
+ }
418
+ });
419
+
420
+ it("resolves documentation for aliased plugin IDs", async () => {
421
+ const integrationsDirectory = createPluginDocsFixture("graphql", {
422
+ "README.md": "graphql-docs",
423
+ });
424
+
425
+ try {
426
+ const docs = await resolveIntegrationDocumentation("graphqlintegration", {
427
+ integrationsDirectory,
428
+ });
429
+ expect(docs).toBe("graphql-docs");
430
+ } finally {
431
+ rmSync(integrationsDirectory, { recursive: true, force: true });
432
+ }
433
+ });
434
+
435
+ describe("semver matching", () => {
436
+ it("matches exact version with = operator", async () => {
437
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
438
+ "README.md": "base",
439
+ "docs.manifest.json": JSON.stringify({
440
+ overlays: [{ file: "overlays/exact.md", versionRange: "=0.4.0" }],
441
+ }),
442
+ "overlays/exact.md": "exact-match",
443
+ });
444
+
445
+ try {
446
+ expect(
447
+ await resolveIntegrationDocumentation("dropbox", {
448
+ pluginVersion: "0.4.0",
449
+ integrationsDirectory,
450
+ }),
451
+ ).toBe("base\n\nexact-match");
452
+
453
+ expect(
454
+ await resolveIntegrationDocumentation("dropbox", {
455
+ pluginVersion: "0.4.1",
456
+ integrationsDirectory,
457
+ }),
458
+ ).toBe("base");
459
+ } finally {
460
+ rmSync(integrationsDirectory, { recursive: true, force: true });
461
+ }
462
+ });
463
+
464
+ it("matches strict less-than with < operator", async () => {
465
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
466
+ "README.md": "base",
467
+ "docs.manifest.json": JSON.stringify({
468
+ overlays: [{ file: "overlays/legacy.md", versionRange: "<1.0.0" }],
469
+ }),
470
+ "overlays/legacy.md": "legacy-overlay",
471
+ });
472
+
473
+ try {
474
+ expect(
475
+ await resolveIntegrationDocumentation("dropbox", {
476
+ pluginVersion: "0.9.9",
477
+ integrationsDirectory,
478
+ }),
479
+ ).toBe("base\n\nlegacy-overlay");
480
+
481
+ expect(
482
+ await resolveIntegrationDocumentation("dropbox", {
483
+ pluginVersion: "1.0.0",
484
+ integrationsDirectory,
485
+ }),
486
+ ).toBe("base");
487
+ } finally {
488
+ rmSync(integrationsDirectory, { recursive: true, force: true });
489
+ }
490
+ });
491
+
492
+ it("matches <= operator at boundary", async () => {
493
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
494
+ "README.md": "base",
495
+ "docs.manifest.json": JSON.stringify({
496
+ overlays: [{ file: "overlays/lte.md", versionRange: "<=1.0.0" }],
497
+ }),
498
+ "overlays/lte.md": "lte-overlay",
499
+ });
500
+
501
+ try {
502
+ expect(
503
+ await resolveIntegrationDocumentation("dropbox", {
504
+ pluginVersion: "1.0.0",
505
+ integrationsDirectory,
506
+ }),
507
+ ).toBe("base\n\nlte-overlay");
508
+
509
+ expect(
510
+ await resolveIntegrationDocumentation("dropbox", {
511
+ pluginVersion: "1.0.1",
512
+ integrationsDirectory,
513
+ }),
514
+ ).toBe("base");
515
+ } finally {
516
+ rmSync(integrationsDirectory, { recursive: true, force: true });
517
+ }
518
+ });
519
+
520
+ it("matches > operator", async () => {
521
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
522
+ "README.md": "base",
523
+ "docs.manifest.json": JSON.stringify({
524
+ overlays: [{ file: "overlays/gt.md", versionRange: ">1.0.0" }],
525
+ }),
526
+ "overlays/gt.md": "gt-overlay",
527
+ });
528
+
529
+ try {
530
+ expect(
531
+ await resolveIntegrationDocumentation("dropbox", {
532
+ pluginVersion: "1.0.0",
533
+ integrationsDirectory,
534
+ }),
535
+ ).toBe("base");
536
+
537
+ expect(
538
+ await resolveIntegrationDocumentation("dropbox", {
539
+ pluginVersion: "1.0.1",
540
+ integrationsDirectory,
541
+ }),
542
+ ).toBe("base\n\ngt-overlay");
543
+ } finally {
544
+ rmSync(integrationsDirectory, { recursive: true, force: true });
545
+ }
546
+ });
547
+
548
+ it("matches compound range with multiple comparators", async () => {
549
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
550
+ "README.md": "base",
551
+ "docs.manifest.json": JSON.stringify({
552
+ overlays: [
553
+ { file: "overlays/range.md", versionRange: ">=1.0.0 <2.0.0" },
554
+ ],
555
+ }),
556
+ "overlays/range.md": "range-overlay",
557
+ });
558
+
559
+ try {
560
+ expect(
561
+ await resolveIntegrationDocumentation("dropbox", {
562
+ pluginVersion: "0.9.9",
563
+ integrationsDirectory,
564
+ }),
565
+ ).toBe("base");
566
+
567
+ expect(
568
+ await resolveIntegrationDocumentation("dropbox", {
569
+ pluginVersion: "1.5.0",
570
+ integrationsDirectory,
571
+ }),
572
+ ).toBe("base\n\nrange-overlay");
573
+
574
+ expect(
575
+ await resolveIntegrationDocumentation("dropbox", {
576
+ pluginVersion: "2.0.0",
577
+ integrationsDirectory,
578
+ }),
579
+ ).toBe("base");
580
+ } finally {
581
+ rmSync(integrationsDirectory, { recursive: true, force: true });
582
+ }
583
+ });
584
+
585
+ it("handles spaced operators in version ranges", async () => {
586
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
587
+ "README.md": "base",
588
+ "docs.manifest.json": JSON.stringify({
589
+ overlays: [
590
+ { file: "overlays/spaced.md", versionRange: ">= 0.4.0 < 1.0.0" },
591
+ ],
592
+ }),
593
+ "overlays/spaced.md": "spaced-overlay",
594
+ });
595
+
596
+ try {
597
+ expect(
598
+ await resolveIntegrationDocumentation("dropbox", {
599
+ pluginVersion: "0.3.9",
600
+ integrationsDirectory,
601
+ }),
602
+ ).toBe("base");
603
+
604
+ expect(
605
+ await resolveIntegrationDocumentation("dropbox", {
606
+ pluginVersion: "0.5.0",
607
+ integrationsDirectory,
608
+ }),
609
+ ).toBe("base\n\nspaced-overlay");
610
+
611
+ expect(
612
+ await resolveIntegrationDocumentation("dropbox", {
613
+ pluginVersion: "1.0.0",
614
+ integrationsDirectory,
615
+ }),
616
+ ).toBe("base");
617
+ } finally {
618
+ rmSync(integrationsDirectory, { recursive: true, force: true });
619
+ }
620
+ });
621
+ });
622
+
623
+ describe("manifest validation", () => {
624
+ it("throws when overlays key is not an array", async () => {
625
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
626
+ "README.md": "base",
627
+ "docs.manifest.json": JSON.stringify({ overlays: "not-an-array" }),
628
+ });
629
+
630
+ try {
631
+ await expect(
632
+ resolveIntegrationDocumentation("dropbox", {
633
+ pluginVersion: "0.4.0",
634
+ integrationsDirectory,
635
+ }),
636
+ ).rejects.toThrowError(/overlays.*must be an array/i);
637
+ } finally {
638
+ rmSync(integrationsDirectory, { recursive: true, force: true });
639
+ }
640
+ });
641
+
642
+ it("throws when overlay entry has empty file", async () => {
643
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
644
+ "README.md": "base",
645
+ "docs.manifest.json": JSON.stringify({
646
+ overlays: [{ file: " ", versionRange: ">=0.4.0" }],
647
+ }),
648
+ });
649
+
650
+ try {
651
+ await expect(
652
+ resolveIntegrationDocumentation("dropbox", {
653
+ pluginVersion: "0.4.0",
654
+ integrationsDirectory,
655
+ }),
656
+ ).rejects.toThrowError(/Invalid "file"/);
657
+ } finally {
658
+ rmSync(integrationsDirectory, { recursive: true, force: true });
659
+ }
660
+ });
661
+
662
+ it("throws when overlay entry has empty versionRange", async () => {
663
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
664
+ "README.md": "base",
665
+ "docs.manifest.json": JSON.stringify({
666
+ overlays: [{ file: "overlays/01.md", versionRange: " " }],
667
+ }),
668
+ });
669
+
670
+ try {
671
+ await expect(
672
+ resolveIntegrationDocumentation("dropbox", {
673
+ pluginVersion: "0.4.0",
674
+ integrationsDirectory,
675
+ }),
676
+ ).rejects.toThrowError(/Invalid "versionRange"/);
677
+ } finally {
678
+ rmSync(integrationsDirectory, { recursive: true, force: true });
679
+ }
680
+ });
681
+ });
682
+
683
+ it("blocks overlay paths outside the plugin directory", async () => {
684
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
685
+ "README.md": "base",
686
+ "docs.manifest.json": JSON.stringify({
687
+ overlays: [
688
+ { file: "../other-plugin/secret.md", versionRange: ">=0.4.0" },
689
+ ],
690
+ }),
691
+ });
692
+
693
+ try {
694
+ await expect(
695
+ resolveIntegrationDocumentation("dropbox", {
696
+ pluginVersion: "0.4.0",
697
+ integrationsDirectory,
698
+ }),
699
+ ).rejects.toThrowError(/must be inside plugin directory/i);
700
+ } finally {
701
+ rmSync(integrationsDirectory, { recursive: true, force: true });
702
+ }
703
+ });
704
+ });