@superblocksteam/sdk-api 0.0.7 → 0.0.9

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 (62) hide show
  1. package/dist/errors.d.ts +6 -6
  2. package/dist/errors.d.ts.map +1 -1
  3. package/dist/errors.js +23 -2
  4. package/dist/errors.js.map +1 -1
  5. package/dist/integrations/base/decode-worker-binary-response.d.ts +4 -0
  6. package/dist/integrations/base/decode-worker-binary-response.d.ts.map +1 -0
  7. package/dist/integrations/base/decode-worker-binary-response.js +49 -0
  8. package/dist/integrations/base/decode-worker-binary-response.js.map +1 -0
  9. package/dist/integrations/base/decode-worker-binary-response.test.d.ts +2 -0
  10. package/dist/integrations/base/decode-worker-binary-response.test.d.ts.map +1 -0
  11. package/dist/integrations/base/decode-worker-binary-response.test.js +81 -0
  12. package/dist/integrations/base/decode-worker-binary-response.test.js.map +1 -0
  13. package/dist/integrations/base/index.d.ts +1 -1
  14. package/dist/integrations/base/index.d.ts.map +1 -1
  15. package/dist/integrations/base/rest-api-client-base.d.ts +0 -27
  16. package/dist/integrations/base/rest-api-client-base.d.ts.map +1 -1
  17. package/dist/integrations/base/rest-api-client-base.js +34 -37
  18. package/dist/integrations/base/rest-api-client-base.js.map +1 -1
  19. package/dist/integrations/base/rest-api-integration-client.d.ts +11 -17
  20. package/dist/integrations/base/rest-api-integration-client.d.ts.map +1 -1
  21. package/dist/integrations/base/rest-api-integration-client.js +18 -17
  22. package/dist/integrations/base/rest-api-integration-client.js.map +1 -1
  23. package/dist/integrations/base/types.d.ts +72 -13
  24. package/dist/integrations/base/types.d.ts.map +1 -1
  25. package/dist/integrations/base/types.js +1 -4
  26. package/dist/integrations/base/types.js.map +1 -1
  27. package/dist/integrations/documentation-resolver.test.js +173 -1
  28. package/dist/integrations/documentation-resolver.test.js.map +1 -1
  29. package/dist/integrations/documentation.d.ts +1 -0
  30. package/dist/integrations/documentation.d.ts.map +1 -1
  31. package/dist/integrations/documentation.js +31 -8
  32. package/dist/integrations/documentation.js.map +1 -1
  33. package/dist/integrations/postgres/client.d.ts +0 -7
  34. package/dist/integrations/postgres/client.d.ts.map +1 -1
  35. package/dist/integrations/restapiintegration/client.test.d.ts +2 -0
  36. package/dist/integrations/restapiintegration/client.test.d.ts.map +1 -0
  37. package/dist/integrations/restapiintegration/client.test.js +313 -0
  38. package/dist/integrations/restapiintegration/client.test.js.map +1 -0
  39. package/dist/integrations/slack/client.test.js +26 -1
  40. package/dist/integrations/slack/client.test.js.map +1 -1
  41. package/dist/integrations/slack/types.d.ts +2 -2
  42. package/dist/integrations/slack/types.d.ts.map +1 -1
  43. package/package.json +6 -6
  44. package/src/errors.ts +33 -5
  45. package/src/integrations/base/decode-worker-binary-response.test.ts +107 -0
  46. package/src/integrations/base/decode-worker-binary-response.ts +62 -0
  47. package/src/integrations/base/index.ts +1 -0
  48. package/src/integrations/base/rest-api-client-base.ts +42 -46
  49. package/src/integrations/base/rest-api-integration-client.ts +51 -21
  50. package/src/integrations/base/types.ts +85 -15
  51. package/src/integrations/documentation-resolver.test.ts +197 -1
  52. package/src/integrations/documentation.ts +63 -11
  53. package/src/integrations/graphql/docs.manifest.json +6 -1
  54. package/src/integrations/graphql/overlays/dynamic-headers.md +34 -0
  55. package/src/integrations/postgres/client.ts +1 -1
  56. package/src/integrations/restapiintegration/client.test.ts +480 -0
  57. package/src/integrations/restapiintegration/docs.manifest.json +14 -1
  58. package/src/integrations/restapiintegration/overlays/response-types-binary.md +51 -0
  59. package/src/integrations/restapiintegration/overlays/response-types-unsupported.md +7 -0
  60. package/src/integrations/restapiintegration/overlays/response-types.md +26 -0
  61. package/src/integrations/slack/client.test.ts +36 -1
  62. package/src/integrations/slack/types.ts +2 -2
@@ -659,7 +659,27 @@ describe("resolveIntegrationDocumentation", () => {
659
659
  }
660
660
  });
661
661
 
662
- it("throws when overlay entry has empty versionRange", async () => {
662
+ it("throws when overlay entry has neither versionRange nor sdkVersionRange", async () => {
663
+ const integrationsDirectory = createPluginDocsFixture("dropbox", {
664
+ "README.md": "base",
665
+ "docs.manifest.json": JSON.stringify({
666
+ overlays: [{ file: "overlays/01.md" }],
667
+ }),
668
+ });
669
+
670
+ try {
671
+ await expect(
672
+ resolveIntegrationDocumentation("dropbox", {
673
+ pluginVersion: "0.4.0",
674
+ integrationsDirectory,
675
+ }),
676
+ ).rejects.toThrowError(/Invalid overlay entry/);
677
+ } finally {
678
+ rmSync(integrationsDirectory, { recursive: true, force: true });
679
+ }
680
+ });
681
+
682
+ it("throws when overlay entry has empty versionRange and no sdkVersionRange", async () => {
663
683
  const integrationsDirectory = createPluginDocsFixture("dropbox", {
664
684
  "README.md": "base",
665
685
  "docs.manifest.json": JSON.stringify({
@@ -680,6 +700,182 @@ describe("resolveIntegrationDocumentation", () => {
680
700
  });
681
701
  });
682
702
 
703
+ describe("sdkVersionRange", () => {
704
+ it("applies overlay when sdkVersion matches sdkVersionRange", async () => {
705
+ const integrationsDirectory = createPluginDocsFixture("graphql", {
706
+ "README.md": "base-graphql-docs",
707
+ "docs.manifest.json": JSON.stringify({
708
+ overlays: [
709
+ { file: "overlays/headers.md", sdkVersionRange: ">=0.0.2" },
710
+ ],
711
+ }),
712
+ "overlays/headers.md": "dynamic-headers-docs",
713
+ });
714
+
715
+ try {
716
+ const docs = await resolveIntegrationDocumentation("graphql", {
717
+ sdkVersion: "0.0.2",
718
+ integrationsDirectory,
719
+ });
720
+ expect(docs).toBe("base-graphql-docs\n\ndynamic-headers-docs");
721
+ } finally {
722
+ rmSync(integrationsDirectory, { recursive: true, force: true });
723
+ }
724
+ });
725
+
726
+ it("skips overlay when sdkVersion does not match sdkVersionRange", async () => {
727
+ const integrationsDirectory = createPluginDocsFixture("graphql", {
728
+ "README.md": "base-graphql-docs",
729
+ "docs.manifest.json": JSON.stringify({
730
+ overlays: [
731
+ { file: "overlays/headers.md", sdkVersionRange: ">=0.0.2" },
732
+ ],
733
+ }),
734
+ "overlays/headers.md": "dynamic-headers-docs",
735
+ });
736
+
737
+ try {
738
+ const docs = await resolveIntegrationDocumentation("graphql", {
739
+ sdkVersion: "0.0.1",
740
+ integrationsDirectory,
741
+ });
742
+ expect(docs).toBe("base-graphql-docs");
743
+ } finally {
744
+ rmSync(integrationsDirectory, { recursive: true, force: true });
745
+ }
746
+ });
747
+
748
+ it("skips overlay when sdkVersion is not provided", async () => {
749
+ const integrationsDirectory = createPluginDocsFixture("graphql", {
750
+ "README.md": "base-graphql-docs",
751
+ "docs.manifest.json": JSON.stringify({
752
+ overlays: [
753
+ { file: "overlays/headers.md", sdkVersionRange: ">=0.0.2" },
754
+ ],
755
+ }),
756
+ "overlays/headers.md": "dynamic-headers-docs",
757
+ });
758
+
759
+ try {
760
+ const docs = await resolveIntegrationDocumentation("graphql", {
761
+ pluginVersion: "0.0.10",
762
+ integrationsDirectory,
763
+ });
764
+ expect(docs).toBe("base-graphql-docs");
765
+ } finally {
766
+ rmSync(integrationsDirectory, { recursive: true, force: true });
767
+ }
768
+ });
769
+
770
+ it("requires both versionRange and sdkVersionRange to match when both are specified", async () => {
771
+ const integrationsDirectory = createPluginDocsFixture("graphql", {
772
+ "README.md": "base",
773
+ "docs.manifest.json": JSON.stringify({
774
+ overlays: [
775
+ {
776
+ file: "overlays/both.md",
777
+ versionRange: ">=0.0.10",
778
+ sdkVersionRange: ">=0.0.2",
779
+ },
780
+ ],
781
+ }),
782
+ "overlays/both.md": "both-match-overlay",
783
+ });
784
+
785
+ try {
786
+ // Both match
787
+ expect(
788
+ await resolveIntegrationDocumentation("graphql", {
789
+ pluginVersion: "0.0.10",
790
+ sdkVersion: "0.0.2",
791
+ integrationsDirectory,
792
+ }),
793
+ ).toBe("base\n\nboth-match-overlay");
794
+
795
+ // Only plugin matches
796
+ expect(
797
+ await resolveIntegrationDocumentation("graphql", {
798
+ pluginVersion: "0.0.10",
799
+ sdkVersion: "0.0.1",
800
+ integrationsDirectory,
801
+ }),
802
+ ).toBe("base");
803
+
804
+ // Only sdk matches
805
+ expect(
806
+ await resolveIntegrationDocumentation("graphql", {
807
+ pluginVersion: "0.0.9",
808
+ sdkVersion: "0.0.2",
809
+ integrationsDirectory,
810
+ }),
811
+ ).toBe("base");
812
+ } finally {
813
+ rmSync(integrationsDirectory, { recursive: true, force: true });
814
+ }
815
+ });
816
+
817
+ it("allows overlay with only sdkVersionRange (no versionRange)", async () => {
818
+ const integrationsDirectory = createPluginDocsFixture("graphql", {
819
+ "README.md": "base",
820
+ "docs.manifest.json": JSON.stringify({
821
+ overlays: [
822
+ { file: "overlays/sdk-only.md", sdkVersionRange: ">=0.0.2" },
823
+ ],
824
+ }),
825
+ "overlays/sdk-only.md": "sdk-gated-overlay",
826
+ });
827
+
828
+ try {
829
+ // No pluginVersion needed — only sdkVersion matters
830
+ const docs = await resolveIntegrationDocumentation("graphql", {
831
+ sdkVersion: "0.0.3",
832
+ integrationsDirectory,
833
+ });
834
+ expect(docs).toBe("base\n\nsdk-gated-overlay");
835
+ } finally {
836
+ rmSync(integrationsDirectory, { recursive: true, force: true });
837
+ }
838
+ });
839
+ });
840
+
841
+ describe("restapiintegration responseType gating (real docs)", () => {
842
+ it("documents text responseType for agents whose sdk-api supports text only", async () => {
843
+ const docs = await resolveIntegrationDocumentation("restapiintegration", {
844
+ sdkVersion: "0.0.3",
845
+ });
846
+
847
+ expect(docs).toContain('responseType: "text"');
848
+ expect(docs).not.toContain('responseType: "binary"');
849
+ expect(docs).not.toContain("does not support the `responseType`");
850
+ });
851
+
852
+ it("documents binary responseType only for agents whose sdk-api supports it", async () => {
853
+ const docs = await resolveIntegrationDocumentation("restapiintegration", {
854
+ sdkVersion: "0.0.4",
855
+ });
856
+
857
+ expect(docs).toContain('responseType: "text"');
858
+ expect(docs).toContain('responseType: "binary"');
859
+ expect(docs).not.toContain("does not support the `responseType`");
860
+ });
861
+
862
+ it("notes non-support instead of documenting responseType on older agents", async () => {
863
+ const docs = await resolveIntegrationDocumentation("restapiintegration", {
864
+ sdkVersion: "0.0.2",
865
+ });
866
+
867
+ expect(docs).toContain("does not support the `responseType`");
868
+ expect(docs).not.toContain('responseType: "text"');
869
+ expect(docs).not.toContain('responseType: "binary"');
870
+ });
871
+
872
+ it("stays silent about responseType when the agent reports no sdk-api version", async () => {
873
+ const docs = await resolveIntegrationDocumentation("restapiintegration");
874
+
875
+ expect(docs).not.toContain("responseType");
876
+ });
877
+ });
878
+
683
879
  it("blocks overlay paths outside the plugin directory", async () => {
684
880
  const integrationsDirectory = createPluginDocsFixture("dropbox", {
685
881
  "README.md": "base",
@@ -11,7 +11,13 @@ const DOC_DIRECTORY_ALIASES = {
11
11
 
12
12
  interface DocumentationOverlay {
13
13
  file: string;
14
- versionRange: string;
14
+ versionRange?: string;
15
+ /** When set, checks against the sdk-api version ("javascriptsdkapi") running
16
+ * in the orchestrator instead of the plugin-specific version. Use this to
17
+ * gate overlays on sdk-api features that ship independently of plugin
18
+ * version bumps. Both versionRange and sdkVersionRange can be specified
19
+ * together — the overlay is applied only when all specified ranges match. */
20
+ sdkVersionRange?: string;
15
21
  }
16
22
 
17
23
  interface DocumentationManifest {
@@ -32,6 +38,7 @@ type OverlayOperation =
32
38
 
33
39
  export interface ResolveIntegrationDocumentationOptions {
34
40
  pluginVersion?: SemVer;
41
+ sdkVersion?: SemVer;
35
42
  integrationsDirectory?: string;
36
43
  }
37
44
 
@@ -82,16 +89,17 @@ async function parseDocumentationManifest(
82
89
  typeof overlay !== "object" ||
83
90
  overlay === null ||
84
91
  !("file" in overlay) ||
85
- !("versionRange" in overlay)
92
+ (!("versionRange" in overlay) && !("sdkVersionRange" in overlay))
86
93
  ) {
87
94
  throw new Error(
88
95
  `Invalid overlay entry at index ${index} in ${manifestPath}.`,
89
96
  );
90
97
  }
91
98
 
92
- const { file, versionRange } = overlay as {
99
+ const { file, versionRange, sdkVersionRange } = overlay as {
93
100
  file: unknown;
94
101
  versionRange: unknown;
102
+ sdkVersionRange: unknown;
95
103
  };
96
104
 
97
105
  if (typeof file !== "string" || file.trim().length === 0) {
@@ -99,18 +107,35 @@ async function parseDocumentationManifest(
99
107
  `Invalid "file" for overlay index ${index} in ${manifestPath}.`,
100
108
  );
101
109
  }
102
- if (
103
- typeof versionRange !== "string" ||
104
- versionRange.trim().length === 0
105
- ) {
110
+
111
+ const hasVersionRange =
112
+ typeof versionRange === "string" && versionRange.trim().length > 0;
113
+ const hasSdkVersionRange =
114
+ typeof sdkVersionRange === "string" &&
115
+ sdkVersionRange.trim().length > 0;
116
+
117
+ if ("versionRange" in overlay && !hasVersionRange) {
106
118
  throw new Error(
107
119
  `Invalid "versionRange" for overlay index ${index} in ${manifestPath}.`,
108
120
  );
109
121
  }
122
+ if ("sdkVersionRange" in overlay && !hasSdkVersionRange) {
123
+ throw new Error(
124
+ `Invalid "sdkVersionRange" for overlay index ${index} in ${manifestPath}.`,
125
+ );
126
+ }
127
+ if (!hasVersionRange && !hasSdkVersionRange) {
128
+ throw new Error(
129
+ `Overlay at index ${index} in ${manifestPath} must specify at least one of "versionRange" or "sdkVersionRange".`,
130
+ );
131
+ }
110
132
 
111
133
  return {
112
134
  file,
113
- versionRange,
135
+ ...(hasVersionRange ? { versionRange: versionRange as string } : {}),
136
+ ...(hasSdkVersionRange
137
+ ? { sdkVersionRange: sdkVersionRange as string }
138
+ : {}),
114
139
  };
115
140
  },
116
141
  );
@@ -380,11 +405,38 @@ function ensureOverlayPathIsWithinPluginDir(
380
405
  }
381
406
  }
382
407
 
408
+ function overlayMatchesVersions(
409
+ overlay: DocumentationOverlay,
410
+ pluginVersion: SemVer | undefined,
411
+ sdkVersion: SemVer | undefined,
412
+ ): boolean {
413
+ if (
414
+ overlay.versionRange &&
415
+ (!pluginVersion ||
416
+ !versionMatchesRange(pluginVersion, overlay.versionRange))
417
+ ) {
418
+ return false;
419
+ }
420
+
421
+ if (
422
+ overlay.sdkVersionRange &&
423
+ (!sdkVersion || !versionMatchesRange(sdkVersion, overlay.sdkVersionRange))
424
+ ) {
425
+ return false;
426
+ }
427
+
428
+ return true;
429
+ }
430
+
383
431
  export async function resolveIntegrationDocumentation(
384
432
  pluginId: string,
385
433
  options: ResolveIntegrationDocumentationOptions = {},
386
434
  ): Promise<string> {
387
- const { pluginVersion, integrationsDirectory = INTEGRATIONS_DIR } = options;
435
+ const {
436
+ pluginVersion,
437
+ sdkVersion,
438
+ integrationsDirectory = INTEGRATIONS_DIR,
439
+ } = options;
388
440
 
389
441
  const pluginDirectory = resolve(
390
442
  integrationsDirectory,
@@ -397,13 +449,13 @@ export async function resolveIntegrationDocumentation(
397
449
  const baseDocumentationPath = resolve(pluginDirectory, baseFileName);
398
450
  const baseDocumentation = await readFile(baseDocumentationPath, "utf8");
399
451
 
400
- if (!manifest || !pluginVersion) {
452
+ if (!manifest) {
401
453
  return baseDocumentation;
402
454
  }
403
455
 
404
456
  let resolvedDocumentation = baseDocumentation;
405
457
  for (const overlay of manifest.overlays) {
406
- if (!versionMatchesRange(pluginVersion, overlay.versionRange)) {
458
+ if (!overlayMatchesVersions(overlay, pluginVersion, sdkVersion)) {
407
459
  continue;
408
460
  }
409
461
 
@@ -1,5 +1,10 @@
1
1
  {
2
2
  "pluginId": "graphql",
3
3
  "base": "README.md",
4
- "overlays": []
4
+ "overlays": [
5
+ {
6
+ "file": "overlays/dynamic-headers.md",
7
+ "sdkVersionRange": ">=0.0.2"
8
+ }
9
+ ]
5
10
  }
@@ -0,0 +1,34 @@
1
+ ## @replace: Methods
2
+
3
+ | Method | Description |
4
+ | ---------------------------------------------------------------- | ---------------------------------------------------------- |
5
+ | `query<T>(query, schema, variables?, metadata?, headers?)` | Execute a GraphQL query with required schema validation |
6
+ | `mutation<T>(mutation, schema, variables?, metadata?, headers?)` | Execute a GraphQL mutation with required schema validation |
7
+
8
+ ## @replace: Trace Metadata
9
+
10
+ All methods accept an optional `metadata` parameter for diagnostics labeling. See the [root SDK README](../../../README.md#trace-metadata) for details.
11
+
12
+ ## Dynamic Headers
13
+
14
+ Static headers (e.g. a fixed `X-API-Version`) and auth headers (e.g. Bearer tokens, API keys) should be configured on the GraphQL integration in the Superblocks UI so they apply to every call automatically.
15
+
16
+ For values that change per request — for example a bearer token derived from the API's input or from `ctx.env` — pass an optional `headers` map as the final argument to `query()` or `mutation()`:
17
+
18
+ ```typescript
19
+ const MeResponseSchema = z.object({
20
+ data: z.object({
21
+ me: z.object({ id: z.string(), email: z.string() }),
22
+ }),
23
+ });
24
+
25
+ const result = await ctx.integrations.graphql.query(
26
+ `query { me { id email } }`,
27
+ { response: MeResponseSchema },
28
+ undefined, // no variables
29
+ undefined, // no trace metadata
30
+ { Authorization: `Bearer ${ctx.env.UPSTREAM_TOKEN}` },
31
+ );
32
+ ```
33
+
34
+ Since both `variables` and `metadata` accept plain objects, pass `undefined` for any of them that you do not need.
@@ -20,7 +20,7 @@ import type { PostgresClient } from "./types.js";
20
20
  * PostgreSQL request type derived from proto definition.
21
21
  * Using PartialMessage allows optional fields.
22
22
  */
23
- export type PostgresRequest = PartialMessage<PostgresPlugin>;
23
+ type PostgresRequest = PartialMessage<PostgresPlugin>;
24
24
 
25
25
  /**
26
26
  * Internal implementation of PostgresClient.