@superblocksteam/sdk-api 2.0.166 → 2.0.168-next.0
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/README.md +35 -609
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -1
- package/dist/errors.js.map +1 -1
- package/dist/integrations/base/decode-worker-binary-response.d.ts.map +1 -1
- package/dist/integrations/base/decode-worker-binary-response.js +22 -1
- package/dist/integrations/base/decode-worker-binary-response.js.map +1 -1
- package/dist/integrations/base/decode-worker-binary-response.test.js +15 -2
- package/dist/integrations/base/decode-worker-binary-response.test.js.map +1 -1
- package/package.json +2 -2
- package/src/errors.ts +4 -1
- package/src/integrations/base/decode-worker-binary-response.test.ts +25 -5
- package/src/integrations/base/decode-worker-binary-response.ts +26 -1
package/README.md
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
# @superblocksteam/sdk-api
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for defining Superblocks APIs as simple, type-safe functions.
|
|
4
|
-
|
|
5
3
|
## Overview
|
|
6
4
|
|
|
7
5
|
This SDK enables writing Superblocks APIs as TypeScript functions with:
|
|
8
6
|
|
|
9
7
|
- **Zod schemas** for input/output validation
|
|
10
|
-
- **Typed integration clients** for Postgres, Slack, OpenAI, Anthropic, Stripe,
|
|
8
|
+
- **Typed integration clients** for Postgres, Slack, OpenAI, Anthropic, Stripe, and more
|
|
11
9
|
- **Full type inference** for inputs, outputs, and integrations
|
|
12
10
|
- **Upfront integration declarations** for type-safe access and pre-authentication
|
|
13
11
|
- **Runtime schema validation** with detailed error information for debugging
|
|
@@ -329,35 +327,6 @@ Defines a TypeScript-based API with input/output validation. Returns a complete
|
|
|
329
327
|
| `run` | `(ctx, input) => Promise<Output>` | **Required.** The implementation function |
|
|
330
328
|
| `integrations` | `Record<string, IntegrationRef>` | Optional. Integration declarations for the API |
|
|
331
329
|
|
|
332
|
-
```typescript
|
|
333
|
-
import { api, z } from "@superblocksteam/sdk-api";
|
|
334
|
-
|
|
335
|
-
const myApi = api({
|
|
336
|
-
// REQUIRED: name is used for identification in logs and debugging
|
|
337
|
-
name: "GreetUser",
|
|
338
|
-
|
|
339
|
-
// REQUIRED: Zod schema for input validation
|
|
340
|
-
input: z.object({
|
|
341
|
-
name: z.string(),
|
|
342
|
-
count: z.number().int().positive(),
|
|
343
|
-
}),
|
|
344
|
-
|
|
345
|
-
// REQUIRED: Zod schema for output validation
|
|
346
|
-
output: z.object({
|
|
347
|
-
greeting: z.string(),
|
|
348
|
-
items: z.array(z.string()),
|
|
349
|
-
}),
|
|
350
|
-
|
|
351
|
-
// REQUIRED: API implementation
|
|
352
|
-
async run(ctx, { name, count }) {
|
|
353
|
-
return {
|
|
354
|
-
greeting: `Hello, ${name}!`,
|
|
355
|
-
items: Array(count).fill("item"),
|
|
356
|
-
};
|
|
357
|
-
},
|
|
358
|
-
});
|
|
359
|
-
```
|
|
360
|
-
|
|
361
330
|
### Context (`ctx`) and Input
|
|
362
331
|
|
|
363
332
|
The `run` function receives two arguments:
|
|
@@ -377,52 +346,24 @@ async run(ctx, { userId, options }) {
|
|
|
377
346
|
Access typed integration clients via the **name (key)** you declared in the API config: `ctx.integrations.<name>`. For example, if you declared `integrations: { db: postgres(...), notifier: slack(...) }`, use `ctx.integrations.db` and `ctx.integrations.notifier` in your `run` function. Integrations must be declared upfront to enable type-safe access and pre-authentication.
|
|
378
347
|
|
|
379
348
|
```typescript
|
|
380
|
-
import { api,
|
|
349
|
+
import { api, postgres, slack, z } from "@superblocksteam/sdk-api";
|
|
381
350
|
|
|
382
351
|
// Integration IDs from the integrations panel - store in constants
|
|
383
352
|
const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
|
|
384
353
|
const OPS_SLACK = "b2c3d4e5-6789-01ab-cdef-ghijklmnopqr";
|
|
385
|
-
const PROD_OPENAI = "c3d4e5f6-7890-12ab-cdef-ghijklmnopqr";
|
|
386
|
-
const PROD_ANTHROPIC = "d4e5f6g7-8901-23ab-cdef-ghijklmnopqr";
|
|
387
|
-
const PROD_STRIPE = "e5f6g7h8-9012-34ab-cdef-ghijklmnopqr";
|
|
388
|
-
const PROD_GITHUB = "f6g7h8i9-0123-45ab-cdef-ghijklmnopqr";
|
|
389
|
-
const PROD_NOTION = "g7h8i9j0-1234-56ab-cdef-ghijklmnopqr";
|
|
390
354
|
|
|
391
355
|
export default api({
|
|
392
356
|
name: "ProcessUserWorkflow",
|
|
393
|
-
|
|
394
|
-
// Declare all integrations you'll use
|
|
395
357
|
integrations: {
|
|
396
|
-
// Database
|
|
397
358
|
db: postgres(PROD_POSTGRES),
|
|
398
|
-
|
|
399
|
-
// Messaging
|
|
400
359
|
notifier: slack(OPS_SLACK),
|
|
401
|
-
|
|
402
|
-
// AI/LLM
|
|
403
|
-
ai: openai(PROD_OPENAI),
|
|
404
|
-
claude: anthropic(PROD_ANTHROPIC),
|
|
405
|
-
|
|
406
|
-
// Payment Processing
|
|
407
|
-
payments: stripe(PROD_STRIPE),
|
|
408
|
-
|
|
409
|
-
// Developer Tools
|
|
410
|
-
repo: github(PROD_GITHUB),
|
|
411
|
-
|
|
412
|
-
// Productivity
|
|
413
|
-
wiki: notion(PROD_NOTION),
|
|
414
360
|
},
|
|
415
|
-
|
|
416
361
|
input: z.object({ userId: z.string() }),
|
|
417
362
|
output: z.object({ success: z.boolean() }),
|
|
418
|
-
|
|
419
363
|
async run(ctx, { userId }) {
|
|
420
|
-
// Each integration is fully typed based on its declaration
|
|
421
364
|
const users = await ctx.integrations.db.query(...);
|
|
422
365
|
await ctx.integrations.notifier.apiRequest(...);
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
return { success: true };
|
|
366
|
+
return { success: users.length > 0 };
|
|
426
367
|
},
|
|
427
368
|
});
|
|
428
369
|
```
|
|
@@ -516,83 +457,14 @@ interface ApiUser {
|
|
|
516
457
|
}
|
|
517
458
|
```
|
|
518
459
|
|
|
519
|
-
##### Basic Usage
|
|
520
|
-
|
|
521
|
-
```typescript
|
|
522
|
-
import { api, z, postgres } from "@superblocksteam/sdk-api";
|
|
523
|
-
|
|
524
|
-
const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
|
|
525
|
-
|
|
526
|
-
export default api({
|
|
527
|
-
name: "GetMyProfile",
|
|
528
|
-
integrations: { db: postgres(PROD_POSTGRES) },
|
|
529
|
-
input: z.object({}),
|
|
530
|
-
output: z.object({
|
|
531
|
-
email: z.string(),
|
|
532
|
-
name: z.string(),
|
|
533
|
-
}),
|
|
534
|
-
|
|
535
|
-
async run(ctx) {
|
|
536
|
-
// Access user identity securely from the JWT — no frontend input needed
|
|
537
|
-
ctx.log.info("Fetching profile", { userId: ctx.user.userId });
|
|
538
|
-
|
|
539
|
-
const rows = await ctx.integrations.db.query(
|
|
540
|
-
"SELECT email, display_name FROM users WHERE id = $1",
|
|
541
|
-
z.object({ email: z.string(), display_name: z.string() }),
|
|
542
|
-
[ctx.user.userId],
|
|
543
|
-
);
|
|
544
|
-
|
|
545
|
-
if (rows.length === 0) {
|
|
546
|
-
throw new Error("User not found");
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
return { email: rows[0].email, name: rows[0].display_name };
|
|
550
|
-
},
|
|
551
|
-
});
|
|
552
|
-
```
|
|
553
|
-
|
|
554
460
|
##### Role-Based Access Control with Groups
|
|
555
461
|
|
|
556
462
|
Use `ctx.user.groups` to restrict API access based on the user's group memberships. Groups are populated from the user's identity provider (e.g., Okta, Azure AD) via the JWT.
|
|
557
463
|
|
|
558
464
|
```typescript
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
export default api({
|
|
564
|
-
name: "GetAdminDashboard",
|
|
565
|
-
integrations: { db: postgres(PROD_POSTGRES) },
|
|
566
|
-
input: z.object({}),
|
|
567
|
-
output: z.object({
|
|
568
|
-
stats: z.object({
|
|
569
|
-
totalUsers: z.number(),
|
|
570
|
-
activeUsers: z.number(),
|
|
571
|
-
}),
|
|
572
|
-
}),
|
|
573
|
-
|
|
574
|
-
async run(ctx) {
|
|
575
|
-
// Check group membership for authorization
|
|
576
|
-
if (!ctx.user.groups.includes("admin")) {
|
|
577
|
-
throw new Error("Access denied: admin group membership required");
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
const stats = await ctx.integrations.db.query(
|
|
581
|
-
`SELECT
|
|
582
|
-
COUNT(*) as total_users,
|
|
583
|
-
COUNT(*) FILTER (WHERE status = 'active') as active_users
|
|
584
|
-
FROM users`,
|
|
585
|
-
z.object({ total_users: z.number(), active_users: z.number() }),
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
return {
|
|
589
|
-
stats: {
|
|
590
|
-
totalUsers: stats[0].total_users,
|
|
591
|
-
activeUsers: stats[0].active_users,
|
|
592
|
-
},
|
|
593
|
-
};
|
|
594
|
-
},
|
|
595
|
-
});
|
|
465
|
+
if (!ctx.user.groups.includes("admin")) {
|
|
466
|
+
throw new Error("Access denied: admin group membership required");
|
|
467
|
+
}
|
|
596
468
|
```
|
|
597
469
|
|
|
598
470
|
##### Custom Claims
|
|
@@ -606,119 +478,17 @@ export default api({
|
|
|
606
478
|
- Always write defensive code that handles missing custom claims gracefully.
|
|
607
479
|
|
|
608
480
|
```typescript
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
integrations: { db: postgres(PROD_POSTGRES) },
|
|
616
|
-
input: z.object({}),
|
|
617
|
-
output: z.object({
|
|
618
|
-
tenantName: z.string(),
|
|
619
|
-
records: z.array(z.object({ id: z.string(), value: z.string() })),
|
|
620
|
-
}),
|
|
621
|
-
|
|
622
|
-
async run(ctx) {
|
|
623
|
-
// Access custom claims from the JWT — handle missing claims defensively
|
|
624
|
-
const tenantId = ctx.user.customClaims["tenant_id"];
|
|
625
|
-
if (typeof tenantId !== "string") {
|
|
626
|
-
throw new Error(
|
|
627
|
-
"Missing tenant_id claim. Custom claims require enterprise SSO or embed tokens with SSO claims.",
|
|
628
|
-
);
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
const department = ctx.user.customClaims["department"] as
|
|
632
|
-
string | undefined;
|
|
633
|
-
|
|
634
|
-
ctx.log.info("Fetching tenant data", {
|
|
635
|
-
userId: ctx.user.userId,
|
|
636
|
-
tenantId,
|
|
637
|
-
department: department ?? "unknown",
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
const records = await ctx.integrations.db.query(
|
|
641
|
-
"SELECT id, value FROM tenant_data WHERE tenant_id = $1",
|
|
642
|
-
z.object({ id: z.string(), value: z.string() }),
|
|
643
|
-
[tenantId],
|
|
644
|
-
);
|
|
645
|
-
|
|
646
|
-
const tenantNames = await ctx.integrations.db.query(
|
|
647
|
-
"SELECT name FROM tenants WHERE id = $1",
|
|
648
|
-
z.object({ name: z.string() }),
|
|
649
|
-
[tenantId],
|
|
650
|
-
);
|
|
651
|
-
|
|
652
|
-
return {
|
|
653
|
-
tenantName: tenantNames[0]?.name ?? "Unknown Tenant",
|
|
654
|
-
records,
|
|
655
|
-
};
|
|
656
|
-
},
|
|
657
|
-
});
|
|
481
|
+
const tenantId = ctx.user.customClaims["tenant_id"];
|
|
482
|
+
if (typeof tenantId !== "string") {
|
|
483
|
+
throw new Error(
|
|
484
|
+
"Missing tenant_id claim. Custom claims require enterprise SSO or embed tokens with SSO claims.",
|
|
485
|
+
);
|
|
486
|
+
}
|
|
658
487
|
```
|
|
659
488
|
|
|
660
489
|
##### Combining User Context for Row-Level Security
|
|
661
490
|
|
|
662
|
-
|
|
663
|
-
import { api, z, postgres } from "@superblocksteam/sdk-api";
|
|
664
|
-
|
|
665
|
-
const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
|
|
666
|
-
|
|
667
|
-
export default api({
|
|
668
|
-
name: "GetMyOrders",
|
|
669
|
-
integrations: { db: postgres(PROD_POSTGRES) },
|
|
670
|
-
input: z.object({
|
|
671
|
-
status: z.enum(["pending", "completed", "cancelled"]).optional(),
|
|
672
|
-
}),
|
|
673
|
-
output: z.object({
|
|
674
|
-
orders: z.array(
|
|
675
|
-
z.object({
|
|
676
|
-
id: z.string(),
|
|
677
|
-
total: z.number(),
|
|
678
|
-
status: z.string(),
|
|
679
|
-
createdAt: z.string(),
|
|
680
|
-
}),
|
|
681
|
-
),
|
|
682
|
-
}),
|
|
683
|
-
|
|
684
|
-
async run(ctx, { status }) {
|
|
685
|
-
// Use ctx.user for row-level security — users can only see their own orders
|
|
686
|
-
if (!ctx.user.email) {
|
|
687
|
-
throw new Error("User email is required to filter orders");
|
|
688
|
-
}
|
|
689
|
-
const params: unknown[] = [ctx.user.email];
|
|
690
|
-
let query =
|
|
691
|
-
"SELECT id, total, status, created_at FROM orders WHERE user_email = $1";
|
|
692
|
-
|
|
693
|
-
if (status) {
|
|
694
|
-
query += " AND status = $2";
|
|
695
|
-
params.push(status);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
query += " ORDER BY created_at DESC LIMIT 50";
|
|
699
|
-
|
|
700
|
-
const orders = await ctx.integrations.db.query(
|
|
701
|
-
query,
|
|
702
|
-
z.object({
|
|
703
|
-
id: z.string(),
|
|
704
|
-
total: z.number(),
|
|
705
|
-
status: z.string(),
|
|
706
|
-
created_at: z.string(),
|
|
707
|
-
}),
|
|
708
|
-
params,
|
|
709
|
-
);
|
|
710
|
-
|
|
711
|
-
return {
|
|
712
|
-
orders: orders.map((o) => ({
|
|
713
|
-
id: o.id,
|
|
714
|
-
total: o.total,
|
|
715
|
-
status: o.status,
|
|
716
|
-
createdAt: o.created_at,
|
|
717
|
-
})),
|
|
718
|
-
};
|
|
719
|
-
},
|
|
720
|
-
});
|
|
721
|
-
```
|
|
491
|
+
Use `ctx.user.userId` or `ctx.user.email` in parameterized queries to scope rows to the current user. Never accept user identity through API input.
|
|
722
492
|
|
|
723
493
|
## Integration Clients
|
|
724
494
|
|
|
@@ -774,67 +544,31 @@ const users = await ctx.integrations.db.query(
|
|
|
774
544
|
);
|
|
775
545
|
```
|
|
776
546
|
|
|
777
|
-
### Streaming / SSE
|
|
778
|
-
|
|
779
|
-
> **Note:** The SDK's `apiRequest()` method does **not** support streaming or Server-Sent Events (SSE) responses. All AI provider calls (OpenAI, Anthropic, Gemini, Groq, Mistral, Cohere, Perplexity, Fireworks) return complete responses — the SDK waits for the full response before returning. If you are building a chat interface that needs real-time token streaming, you will need to handle streaming at the frontend/UI layer rather than through the SDK.
|
|
780
|
-
|
|
781
547
|
## Generic API Requests
|
|
782
548
|
|
|
783
549
|
All REST API-based integration clients support the `apiRequest()` method for making custom API calls. **Response schema validation is required** for type safety.
|
|
784
550
|
|
|
785
551
|
```typescript
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
552
|
+
const CreateTicketBodySchema = z.object({
|
|
553
|
+
title: z.string(),
|
|
554
|
+
priority: z.enum(["low", "high"]),
|
|
555
|
+
});
|
|
556
|
+
const CreateTicketResponseSchema = z.object({
|
|
557
|
+
id: z.string(),
|
|
558
|
+
createdAt: z.string(),
|
|
559
|
+
});
|
|
793
560
|
|
|
794
|
-
|
|
795
|
-
|
|
561
|
+
const ticket = await ctx.integrations.ticketing.apiRequest(
|
|
562
|
+
{
|
|
563
|
+
method: "POST",
|
|
564
|
+
path: "/tickets",
|
|
565
|
+
body: { title: "Deployment failed", priority: "high" },
|
|
796
566
|
},
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
async run(ctx) {
|
|
802
|
-
// Define request and response schemas
|
|
803
|
-
const PostMessageBodySchema = z.object({
|
|
804
|
-
channel: z.string(),
|
|
805
|
-
text: z.string(),
|
|
806
|
-
blocks: z.array(z.any()).optional(),
|
|
807
|
-
});
|
|
808
|
-
|
|
809
|
-
const PostMessageResponseSchema = z.object({
|
|
810
|
-
ts: z.string(),
|
|
811
|
-
channel: z.string(),
|
|
812
|
-
});
|
|
813
|
-
|
|
814
|
-
// Response schema is REQUIRED
|
|
815
|
-
const result = await ctx.integrations.notifier.apiRequest(
|
|
816
|
-
{
|
|
817
|
-
method: "POST",
|
|
818
|
-
path: "/chat.postMessage",
|
|
819
|
-
body: {
|
|
820
|
-
channel: "#alerts",
|
|
821
|
-
text: "Deployment completed!",
|
|
822
|
-
},
|
|
823
|
-
},
|
|
824
|
-
{
|
|
825
|
-
body: PostMessageBodySchema,
|
|
826
|
-
response: PostMessageResponseSchema, // Required!
|
|
827
|
-
},
|
|
828
|
-
);
|
|
829
|
-
|
|
830
|
-
if (!result.ok) {
|
|
831
|
-
throw new Error(`Slack API error: ${result.error}`);
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// result is fully typed as { ok: true } & PostMessageResponseSchema
|
|
835
|
-
return { ts: result.ts };
|
|
567
|
+
{
|
|
568
|
+
body: CreateTicketBodySchema,
|
|
569
|
+
response: CreateTicketResponseSchema,
|
|
836
570
|
},
|
|
837
|
-
|
|
571
|
+
);
|
|
838
572
|
```
|
|
839
573
|
|
|
840
574
|
### ApiRequestOptions
|
|
@@ -890,33 +624,11 @@ await ctx.integrations.db.execute(
|
|
|
890
624
|
[userId],
|
|
891
625
|
{ label: "Update last login" },
|
|
892
626
|
);
|
|
893
|
-
|
|
894
|
-
// REST API clients (apiRequest)
|
|
895
|
-
const result = await ctx.integrations.slack.apiRequest(
|
|
896
|
-
{ method: "POST", path: "/chat.postMessage", body: { channel, text } },
|
|
897
|
-
{ body: BodySchema, response: ResponseSchema },
|
|
898
|
-
{ label: "Send alert to Slack" },
|
|
899
|
-
);
|
|
900
|
-
|
|
901
|
-
// Other clients (MongoDB, S3, etc.)
|
|
902
|
-
const doc = await ctx.integrations.mongo.run(
|
|
903
|
-
"users",
|
|
904
|
-
"findOne",
|
|
905
|
-
ResultSchema,
|
|
906
|
-
{ filter: { _id: userId } },
|
|
907
|
-
{ label: "Look up user by ID" },
|
|
908
|
-
);
|
|
909
627
|
```
|
|
910
628
|
|
|
911
629
|
### When to Use
|
|
912
630
|
|
|
913
|
-
Trace metadata is optional and
|
|
914
|
-
|
|
915
|
-
- You want labeled traces in the diagnostics panel for easier debugging
|
|
916
|
-
- An API makes multiple calls to the same integration and you need to distinguish them
|
|
917
|
-
- You want to document the intent of each integration call for your team
|
|
918
|
-
|
|
919
|
-
Metadata has no effect on execution - it only appears in diagnostics when `includeDiagnostics` is enabled.
|
|
631
|
+
Trace metadata is optional and has no effect on execution. When `includeDiagnostics` is enabled, use labels and descriptions to distinguish repeated integration calls in the diagnostics panel.
|
|
920
632
|
|
|
921
633
|
## Error Handling
|
|
922
634
|
|
|
@@ -983,47 +695,16 @@ try {
|
|
|
983
695
|
|
|
984
696
|
// The actual data that failed validation
|
|
985
697
|
console.error('Failed data:', error.details.data);
|
|
986
|
-
|
|
987
|
-
// Access Zod utility methods
|
|
988
|
-
const formatted = error.details.zodError.format();
|
|
989
|
-
const flattened = error.details.zodError.flatten();
|
|
990
698
|
}
|
|
991
699
|
}
|
|
992
700
|
```
|
|
993
701
|
|
|
994
|
-
The `RestApiValidationError` provides complete context for debugging:
|
|
995
|
-
|
|
996
|
-
- **Full Zod error object** (`zodError`) with all validation information
|
|
997
|
-
- **Validation codes** (e.g., `"invalid_type"`, `"too_small"`, `"invalid_email"`)
|
|
998
|
-
- **Expected vs received** values for type mismatches
|
|
999
|
-
- **Constraint information** (e.g., minimum/maximum values)
|
|
1000
|
-
- **The actual data** that failed validation
|
|
1001
|
-
- **Utility methods** like `format()` and `flatten()` for different error representations
|
|
1002
|
-
|
|
1003
702
|
## Best Practices
|
|
1004
703
|
|
|
1005
704
|
### Push Filtering Down to the Data Source
|
|
1006
705
|
|
|
1007
706
|
Always filter data at the source (database, API) rather than fetching everything and filtering in code. This reduces network transfer, memory usage, and processing time.
|
|
1008
707
|
|
|
1009
|
-
**Inefficient - Fetching all rows and filtering in code:**
|
|
1010
|
-
|
|
1011
|
-
```typescript
|
|
1012
|
-
const AllUsersSchema = z.array(
|
|
1013
|
-
z.object({ id: z.string(), status: z.string(), region: z.string() }),
|
|
1014
|
-
);
|
|
1015
|
-
|
|
1016
|
-
// Fetches ALL users, then filters - wasteful!
|
|
1017
|
-
async run(ctx, { region }) {
|
|
1018
|
-
const allUsers = await ctx.integrations.db.query("SELECT * FROM users", AllUsersSchema);
|
|
1019
|
-
const activeUsersInRegion = allUsers.filter(
|
|
1020
|
-
(u) => u.status === "active" && u.region === region,
|
|
1021
|
-
);
|
|
1022
|
-
}
|
|
1023
|
-
```
|
|
1024
|
-
|
|
1025
|
-
**Efficient - Filtering in the database query:**
|
|
1026
|
-
|
|
1027
708
|
```typescript
|
|
1028
709
|
const UserSchema = z.object({
|
|
1029
710
|
id: z.string(),
|
|
@@ -1041,22 +722,7 @@ async run(ctx, { region }) {
|
|
|
1041
722
|
}
|
|
1042
723
|
```
|
|
1043
724
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
**Inefficient:**
|
|
1047
|
-
|
|
1048
|
-
```typescript
|
|
1049
|
-
// Fetches all issues, then filters client-side
|
|
1050
|
-
const allIssues = await ctx.integrations.repo.apiRequest(
|
|
1051
|
-
{ method: "GET", path: "/repos/owner/repo/issues" },
|
|
1052
|
-
{ response: IssuesSchema },
|
|
1053
|
-
);
|
|
1054
|
-
const openBugs = allIssues.filter(
|
|
1055
|
-
(i) => i.state === "open" && i.labels.includes("bug"),
|
|
1056
|
-
);
|
|
1057
|
-
```
|
|
1058
|
-
|
|
1059
|
-
**Efficient:**
|
|
725
|
+
Use query parameters to filter REST API responses at the source:
|
|
1060
726
|
|
|
1061
727
|
```typescript
|
|
1062
728
|
// API does the filtering - only matching issues returned
|
|
@@ -1074,20 +740,6 @@ const openBugs = await ctx.integrations.repo.apiRequest(
|
|
|
1074
740
|
|
|
1075
741
|
Avoid `SELECT *` in database queries. Fetch only the columns you actually use to reduce data transfer and improve query performance.
|
|
1076
742
|
|
|
1077
|
-
**Inefficient:**
|
|
1078
|
-
|
|
1079
|
-
```typescript
|
|
1080
|
-
// Fetches all columns even though we only need id and email
|
|
1081
|
-
const users = await ctx.integrations.db.query(
|
|
1082
|
-
"SELECT * FROM users WHERE status = $1",
|
|
1083
|
-
UserSchema,
|
|
1084
|
-
["active"],
|
|
1085
|
-
);
|
|
1086
|
-
const emails = users.map((u) => u.email);
|
|
1087
|
-
```
|
|
1088
|
-
|
|
1089
|
-
**Efficient:**
|
|
1090
|
-
|
|
1091
743
|
```typescript
|
|
1092
744
|
const UserEmailSchema = z.object({ id: z.string(), email: z.string() });
|
|
1093
745
|
|
|
@@ -1134,28 +786,7 @@ while (hasMore) {
|
|
|
1134
786
|
|
|
1135
787
|
### Avoid N+1 Query Patterns
|
|
1136
788
|
|
|
1137
|
-
Don't fetch related data one item at a time in a loop. Use JOINs or batch queries instead.
|
|
1138
|
-
|
|
1139
|
-
**N+1 Problem - One query per order:**
|
|
1140
|
-
|
|
1141
|
-
```typescript
|
|
1142
|
-
const orders = await ctx.integrations.db.query(
|
|
1143
|
-
"SELECT * FROM orders",
|
|
1144
|
-
OrderSchema,
|
|
1145
|
-
);
|
|
1146
|
-
|
|
1147
|
-
// BAD: This makes N additional queries!
|
|
1148
|
-
for (const order of orders) {
|
|
1149
|
-
const items = await ctx.integrations.db.query(
|
|
1150
|
-
"SELECT * FROM order_items WHERE order_id = $1",
|
|
1151
|
-
ItemSchema,
|
|
1152
|
-
[order.id],
|
|
1153
|
-
);
|
|
1154
|
-
order.items = items;
|
|
1155
|
-
}
|
|
1156
|
-
```
|
|
1157
|
-
|
|
1158
|
-
**Efficient - Single query with JOIN:**
|
|
789
|
+
Don't fetch related data one item at a time in a loop (the N+1 problem: one query for the parent rows, then one more per row). Use JOINs or batch queries instead.
|
|
1159
790
|
|
|
1160
791
|
```typescript
|
|
1161
792
|
const OrderWithItemsSchema = z.object({
|
|
@@ -1176,66 +807,11 @@ const rows = await ctx.integrations.db.query(
|
|
|
1176
807
|
);
|
|
1177
808
|
```
|
|
1178
809
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
```typescript
|
|
1182
|
-
const orders = await ctx.integrations.db.query(
|
|
1183
|
-
"SELECT * FROM orders",
|
|
1184
|
-
OrderSchema,
|
|
1185
|
-
);
|
|
1186
|
-
const orderIds = orders.map((o) => o.id);
|
|
1187
|
-
|
|
1188
|
-
// Single query for all items
|
|
1189
|
-
const items = await ctx.integrations.db.query(
|
|
1190
|
-
"SELECT * FROM order_items WHERE order_id = ANY($1)",
|
|
1191
|
-
ItemSchema,
|
|
1192
|
-
[orderIds],
|
|
1193
|
-
);
|
|
1194
|
-
```
|
|
810
|
+
When a join does not fit, fetch related rows in one batch with `WHERE id = ANY($1)`.
|
|
1195
811
|
|
|
1196
812
|
### Batch API Requests When Possible
|
|
1197
813
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
**Inefficient - Individual requests:**
|
|
1201
|
-
|
|
1202
|
-
```typescript
|
|
1203
|
-
// Makes 100 separate API calls!
|
|
1204
|
-
for (const userId of userIds) {
|
|
1205
|
-
await ctx.integrations.notifier.apiRequest(
|
|
1206
|
-
{
|
|
1207
|
-
method: "POST",
|
|
1208
|
-
path: "/chat.postMessage",
|
|
1209
|
-
body: { channel: userId, text: "Hello!" },
|
|
1210
|
-
},
|
|
1211
|
-
{ response: MessageSchema },
|
|
1212
|
-
);
|
|
1213
|
-
}
|
|
1214
|
-
```
|
|
1215
|
-
|
|
1216
|
-
**Efficient - Use batch endpoints when available:**
|
|
1217
|
-
|
|
1218
|
-
```typescript
|
|
1219
|
-
// Check if the API supports batch operations
|
|
1220
|
-
// For Slack, consider using chat.postMessage with multiple channel webhooks
|
|
1221
|
-
// or processing in controlled parallel batches
|
|
1222
|
-
const BATCH_SIZE = 10;
|
|
1223
|
-
for (let i = 0; i < userIds.length; i += BATCH_SIZE) {
|
|
1224
|
-
const batch = userIds.slice(i, i + BATCH_SIZE);
|
|
1225
|
-
await Promise.all(
|
|
1226
|
-
batch.map((userId) =>
|
|
1227
|
-
ctx.integrations.notifier.apiRequest(
|
|
1228
|
-
{
|
|
1229
|
-
method: "POST",
|
|
1230
|
-
path: "/chat.postMessage",
|
|
1231
|
-
body: { channel: userId, text: "Hello!" },
|
|
1232
|
-
},
|
|
1233
|
-
{ response: MessageSchema },
|
|
1234
|
-
),
|
|
1235
|
-
),
|
|
1236
|
-
);
|
|
1237
|
-
}
|
|
1238
|
-
```
|
|
814
|
+
Prefer a documented batch endpoint. When none exists, cap concurrency with small batches; do not launch one unbounded request per row.
|
|
1239
815
|
|
|
1240
816
|
### Use Precise Schemas
|
|
1241
817
|
|
|
@@ -1329,156 +905,6 @@ const getUsers = useApi<typeof GetUsersApi>("GetUsers");
|
|
|
1329
905
|
const listProfiles = useApi<typeof ListProfiles>("ListProfiles");
|
|
1330
906
|
```
|
|
1331
907
|
|
|
1332
|
-
## Complete Example
|
|
1333
|
-
|
|
1334
|
-
```typescript
|
|
1335
|
-
import { api, z, postgres, slack, openai } from "@superblocksteam/sdk-api";
|
|
1336
|
-
|
|
1337
|
-
// Integration IDs from the integrations panel
|
|
1338
|
-
const PROD_POSTGRES = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr";
|
|
1339
|
-
const OPS_SLACK = "b2c3d4e5-6789-01ab-cdef-ghijklmnopqr";
|
|
1340
|
-
const PROD_OPENAI = "c3d4e5f6-7890-12ab-cdef-ghijklmnopqr";
|
|
1341
|
-
|
|
1342
|
-
// Define reusable schemas
|
|
1343
|
-
const OrderItemSchema = z.object({
|
|
1344
|
-
productId: z.string(),
|
|
1345
|
-
quantity: z.number().int().positive(),
|
|
1346
|
-
});
|
|
1347
|
-
|
|
1348
|
-
const CompletionRequestSchema = z.object({
|
|
1349
|
-
model: z.string(),
|
|
1350
|
-
messages: z.array(
|
|
1351
|
-
z.object({
|
|
1352
|
-
role: z.string(),
|
|
1353
|
-
content: z.string(),
|
|
1354
|
-
}),
|
|
1355
|
-
),
|
|
1356
|
-
});
|
|
1357
|
-
|
|
1358
|
-
const CompletionResponseSchema = z.object({
|
|
1359
|
-
choices: z.array(
|
|
1360
|
-
z.object({
|
|
1361
|
-
message: z.object({
|
|
1362
|
-
content: z.string(),
|
|
1363
|
-
}),
|
|
1364
|
-
}),
|
|
1365
|
-
),
|
|
1366
|
-
});
|
|
1367
|
-
|
|
1368
|
-
const CustomerSchema = z.object({
|
|
1369
|
-
id: z.string(),
|
|
1370
|
-
email: z.string().email(),
|
|
1371
|
-
});
|
|
1372
|
-
|
|
1373
|
-
const SlackPostMessageResponseSchema = z.object({
|
|
1374
|
-
ts: z.string().optional(),
|
|
1375
|
-
});
|
|
1376
|
-
|
|
1377
|
-
export default api({
|
|
1378
|
-
name: "CreateOrder",
|
|
1379
|
-
|
|
1380
|
-
// Declare all integrations upfront
|
|
1381
|
-
integrations: {
|
|
1382
|
-
db: postgres(PROD_POSTGRES),
|
|
1383
|
-
notifier: slack(OPS_SLACK),
|
|
1384
|
-
ai: openai(PROD_OPENAI),
|
|
1385
|
-
},
|
|
1386
|
-
|
|
1387
|
-
input: z.object({
|
|
1388
|
-
customerId: z.string().uuid(),
|
|
1389
|
-
items: z.array(OrderItemSchema).min(1),
|
|
1390
|
-
sendAiSummary: z.boolean().default(false),
|
|
1391
|
-
}),
|
|
1392
|
-
|
|
1393
|
-
output: z.object({
|
|
1394
|
-
orderId: z.string(),
|
|
1395
|
-
total: z.number(),
|
|
1396
|
-
itemCount: z.number(),
|
|
1397
|
-
aiSummary: z.string().optional(),
|
|
1398
|
-
}),
|
|
1399
|
-
|
|
1400
|
-
async run(ctx, { customerId, items, sendAiSummary }) {
|
|
1401
|
-
ctx.log.info("Creating order", { customerId, itemCount: items.length });
|
|
1402
|
-
|
|
1403
|
-
// Get customer from database
|
|
1404
|
-
const customers = await ctx.integrations.db.query(
|
|
1405
|
-
"SELECT id, email FROM customers WHERE id = $1",
|
|
1406
|
-
CustomerSchema,
|
|
1407
|
-
[customerId],
|
|
1408
|
-
);
|
|
1409
|
-
const customer = customers[0];
|
|
1410
|
-
|
|
1411
|
-
if (!customer) {
|
|
1412
|
-
throw new Error(`Customer not found: ${customerId}`);
|
|
1413
|
-
}
|
|
1414
|
-
|
|
1415
|
-
// Calculate total
|
|
1416
|
-
const total = items.reduce((sum, item) => {
|
|
1417
|
-
// In real code, you'd look up prices
|
|
1418
|
-
return sum + item.quantity * 10;
|
|
1419
|
-
}, 0);
|
|
1420
|
-
|
|
1421
|
-
// Insert order
|
|
1422
|
-
await ctx.integrations.db.execute(
|
|
1423
|
-
"INSERT INTO orders (customer_id, total, item_count) VALUES ($1, $2, $3)",
|
|
1424
|
-
[customerId, total, items.length],
|
|
1425
|
-
);
|
|
1426
|
-
|
|
1427
|
-
const orderId = `ord_${Date.now()}`;
|
|
1428
|
-
|
|
1429
|
-
// Notify via Slack
|
|
1430
|
-
const slackResult = await ctx.integrations.notifier.apiRequest(
|
|
1431
|
-
{
|
|
1432
|
-
method: "POST",
|
|
1433
|
-
path: "/chat.postMessage",
|
|
1434
|
-
body: {
|
|
1435
|
-
channel: "#orders",
|
|
1436
|
-
text: `New order ${orderId} from ${customer.email}: $${total.toFixed(2)}`,
|
|
1437
|
-
},
|
|
1438
|
-
},
|
|
1439
|
-
{ response: SlackPostMessageResponseSchema },
|
|
1440
|
-
);
|
|
1441
|
-
|
|
1442
|
-
if (!slackResult.ok) {
|
|
1443
|
-
throw new Error(`Slack API error: ${slackResult.error}`);
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
// Generate AI summary if requested
|
|
1447
|
-
let aiSummary: string | undefined;
|
|
1448
|
-
if (sendAiSummary) {
|
|
1449
|
-
const completion = await ctx.integrations.ai.apiRequest(
|
|
1450
|
-
{
|
|
1451
|
-
method: "POST",
|
|
1452
|
-
path: "/chat/completions",
|
|
1453
|
-
body: {
|
|
1454
|
-
model: "gpt-4",
|
|
1455
|
-
messages: [
|
|
1456
|
-
{
|
|
1457
|
-
role: "user",
|
|
1458
|
-
content: `Summarize this order: ${items.length} items, total $${total}`,
|
|
1459
|
-
},
|
|
1460
|
-
],
|
|
1461
|
-
},
|
|
1462
|
-
},
|
|
1463
|
-
{
|
|
1464
|
-
body: CompletionRequestSchema,
|
|
1465
|
-
response: CompletionResponseSchema,
|
|
1466
|
-
},
|
|
1467
|
-
);
|
|
1468
|
-
|
|
1469
|
-
aiSummary = completion.choices[0]?.message.content;
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
return {
|
|
1473
|
-
orderId,
|
|
1474
|
-
total,
|
|
1475
|
-
itemCount: items.length,
|
|
1476
|
-
aiSummary,
|
|
1477
|
-
};
|
|
1478
|
-
},
|
|
1479
|
-
});
|
|
1480
|
-
```
|
|
1481
|
-
|
|
1482
908
|
## Development
|
|
1483
909
|
|
|
1484
910
|
```bash
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,6BAA6B;;;EAGxC,CAAC;AAUH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,WAAW,GAAG,eAAe,CAExC;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,6BAA6B;;;EAGxC,CAAC;AAUH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,WAAW,GAAG,eAAe,CAExC;AA2CD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GACnC,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,SAAS,CAEjC;AAyHD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAEzE;AA0FD;;;;;;;;;;;;GAYG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,OAAO,EAAE;QAChB,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC;KACtB,CAAC;gBAGA,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QACP,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC;KACtB;CAUJ;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,CAAC,CAAC,QAAQ,EACpB,IAAI,EAAE,OAAO,GACZ,sBAAsB,CAMxB;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;aAG3B,OAAO,EAAE;QACvB,wDAAwD;QACxD,QAAQ,EAAE,MAAM,CAAC;QACjB,sDAAsD;QACtD,MAAM,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC9D,8CAA8C;QAC9C,GAAG,EAAE,OAAO,CAAC;KACd;gBARD,OAAO,EAAE,MAAM,EACC,OAAO,EAAE;QACvB,wDAAwD;QACxD,QAAQ,EAAE,MAAM,CAAC;QACjB,sDAAsD;QACtD,MAAM,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC9D,8CAA8C;QAC9C,GAAG,EAAE,OAAO,CAAC;KACd;CAOJ;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;aAGzB,OAAO,CAAC,EAAE;QACxB,iCAAiC;QACjC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,4CAA4C;QAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,+CAA+C;QAC/C,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,gDAAgD;QAChD,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC;QACtB,gDAAgD;QAChD,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB;gBAZD,OAAO,EAAE,MAAM,EACC,OAAO,CAAC,EAAE;QACxB,iCAAiC;QACjC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,4CAA4C;QAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,+CAA+C;QAC/C,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,gDAAgD;QAChD,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC;QACtB,gDAAgD;QAChD,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,YAAA;CAOJ"}
|
package/dist/errors.js
CHANGED
|
@@ -20,7 +20,8 @@ function isWorkerBufferJson(data) {
|
|
|
20
20
|
if (!("data" in data) || !("type" in data)) {
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
|
-
return data.type === "Buffer" &&
|
|
23
|
+
return (data.type === "Buffer" &&
|
|
24
|
+
(Array.isArray(data.data) || typeof data.data === "string"));
|
|
24
25
|
}
|
|
25
26
|
function containsBinary(data, seen, depth = 0) {
|
|
26
27
|
if (typeof data !== "object" || data === null)
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAM,CAAC,MAAM,CAAC;IACzD,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,IAAI;CACf,CAAC,CAAC;AAEH,4EAA4E;AAC5E,4EAA4E;AAC5E,yEAAyE;AACzE,6EAA6E;AAC7E,6EAA6E;AAC7E,qDAAqD;AACrD,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAElC,MAAM,UAAU,YAAY,CAC1B,KAAc;IAEd,OAAO,KAAK,YAAY,WAAW,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAY;IACtC,IAAI,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decode-worker-binary-response.d.ts","sourceRoot":"","sources":["../../../src/integrations/base/decode-worker-binary-response.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,6BAA6B,EAE9B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,6BAA6B,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"decode-worker-binary-response.d.ts","sourceRoot":"","sources":["../../../src/integrations/base/decode-worker-binary-response.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,6BAA6B,EAE9B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,6BAA6B,EAAE,CAAC;AAmEzC,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAUrE"}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { REDACTED_BINARY_RESPONSE_DATA, restApiValidationErrorFromZodError, } from "../../errors.js";
|
|
3
3
|
export { REDACTED_BINARY_RESPONSE_DATA };
|
|
4
|
+
// Node's decoder accepts missing and misplaced padding. A worker payload is
|
|
5
|
+
// a canonical padded string, so reject anything else before decoding.
|
|
6
|
+
const BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
7
|
+
function decodeBase64(data) {
|
|
8
|
+
if (!BASE64.test(data)) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return new Uint8Array(Buffer.from(data, "base64"));
|
|
12
|
+
}
|
|
4
13
|
const workerBinaryResponseSchema = z.union([
|
|
5
14
|
z.instanceof(Uint8Array).transform((value) => new Uint8Array(value)),
|
|
6
15
|
z
|
|
@@ -9,10 +18,22 @@ const workerBinaryResponseSchema = z.union([
|
|
|
9
18
|
type: z.literal("Buffer"),
|
|
10
19
|
})
|
|
11
20
|
.transform((value, context) => {
|
|
21
|
+
if (typeof value.data === "string") {
|
|
22
|
+
const bytes = decodeBase64(value.data);
|
|
23
|
+
if (!bytes) {
|
|
24
|
+
context.addIssue({
|
|
25
|
+
code: z.ZodIssueCode.custom,
|
|
26
|
+
message: "data must be base64",
|
|
27
|
+
path: ["data"],
|
|
28
|
+
});
|
|
29
|
+
return z.NEVER;
|
|
30
|
+
}
|
|
31
|
+
return bytes;
|
|
32
|
+
}
|
|
12
33
|
if (!Array.isArray(value.data)) {
|
|
13
34
|
context.addIssue({
|
|
14
35
|
code: z.ZodIssueCode.custom,
|
|
15
|
-
message: "data must be an array of bytes",
|
|
36
|
+
message: "data must be an array of bytes or a base64 string",
|
|
16
37
|
path: ["data"],
|
|
17
38
|
});
|
|
18
39
|
return z.NEVER;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decode-worker-binary-response.js","sourceRoot":"","sources":["../../../src/integrations/base/decode-worker-binary-response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EACL,6BAA6B,EAC7B,kCAAkC,GACnC,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,6BAA6B,EAAE,CAAC;AAEzC,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC;IACzC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IACpE,CAAC;SACE,MAAM,CAAC;QACN,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE;QACjB,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;KAC1B,CAAC;SACD,SAAS,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,OAAO,CAAC,QAAQ,CAAC;gBACf,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM;gBAC3B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"decode-worker-binary-response.js","sourceRoot":"","sources":["../../../src/integrations/base/decode-worker-binary-response.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,19 +19,32 @@ describe("decodeWorkerBinaryResponse", () => {
|
|
|
19
19
|
expect(result.constructor).toBe(Uint8Array);
|
|
20
20
|
expect(Array.from(result)).toEqual([37, 80, 68, 70]);
|
|
21
21
|
});
|
|
22
|
+
it("converts base64 Buffer JSON into a plain Uint8Array", () => {
|
|
23
|
+
const result = decodeWorkerBinaryResponse({
|
|
24
|
+
type: "Buffer",
|
|
25
|
+
data: Buffer.from([37, 80, 68, 70]).toString("base64"),
|
|
26
|
+
});
|
|
27
|
+
expect(result.constructor).toBe(Uint8Array);
|
|
28
|
+
expect(Array.from(result)).toEqual([37, 80, 68, 70]);
|
|
29
|
+
});
|
|
22
30
|
it("converts empty Buffer JSON into a zero-length Uint8Array", () => {
|
|
23
31
|
const result = decodeWorkerBinaryResponse({ type: "Buffer", data: [] });
|
|
24
32
|
expect(result).toBeInstanceOf(Uint8Array);
|
|
25
33
|
expect(result.byteLength).toBe(0);
|
|
26
34
|
});
|
|
35
|
+
it("decodes an empty base64 string to a zero-length Uint8Array", () => {
|
|
36
|
+
const result = decodeWorkerBinaryResponse({ type: "Buffer", data: "" });
|
|
37
|
+
expect(result).toBeInstanceOf(Uint8Array);
|
|
38
|
+
expect(result).toHaveLength(0);
|
|
39
|
+
});
|
|
27
40
|
it("rejects a missing discriminant", () => {
|
|
28
41
|
expect(() => decodeWorkerBinaryResponse({ data: [1] })).toThrow(RestApiValidationError);
|
|
29
42
|
});
|
|
30
43
|
it("rejects a non-Buffer discriminant", () => {
|
|
31
44
|
expect(() => decodeWorkerBinaryResponse({ type: "Array", data: [1] })).toThrow(RestApiValidationError);
|
|
32
45
|
});
|
|
33
|
-
it("rejects
|
|
34
|
-
expect(() => decodeWorkerBinaryResponse({ type: "Buffer", data
|
|
46
|
+
it.each(["@@@@", "A=BC", "====", "QUI"])("rejects non-base64 string data %j", (data) => {
|
|
47
|
+
expect(() => decodeWorkerBinaryResponse({ type: "Buffer", data })).toThrow(RestApiValidationError);
|
|
35
48
|
});
|
|
36
49
|
it("rejects a byte outside 0 through 255", () => {
|
|
37
50
|
expect(() => decodeWorkerBinaryResponse({ type: "Buffer", data: [256] })).toThrow(RestApiValidationError);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decode-worker-binary-response.test.js","sourceRoot":"","sources":["../../../src/integrations/base/decode-worker-binary-response.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAEhF,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAE3C,MAAM,MAAM,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;QAElD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACd,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,MAAM,GAAG,0BAA0B,CAAC;YACxC,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;SACvB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,MAAM,GAAG,0BAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAExE,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,GAAG,EAAE,CAAC,0BAA0B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAC7D,sBAAsB,CACvB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,GAAG,EAAE,CACV,0BAA0B,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACzD,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"decode-worker-binary-response.test.js","sourceRoot":"","sources":["../../../src/integrations/base/decode-worker-binary-response.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAEhF,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAE3C,MAAM,MAAM,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;QAElD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACd,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,MAAM,GAAG,0BAA0B,CAAC;YACxC,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;SACvB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,MAAM,GAAG,0BAA0B,CAAC;YACxC,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;SACvD,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,MAAM,GAAG,0BAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAExE,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,MAAM,GAAG,0BAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAExE,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,GAAG,EAAE,CAAC,0BAA0B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAC7D,sBAAsB,CACvB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,GAAG,EAAE,CACV,0BAA0B,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACzD,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CACtC,mCAAmC,EACnC,CAAC,IAAI,EAAE,EAAE;QACP,MAAM,CAAC,GAAG,EAAE,CACV,0BAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CACrD,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACpC,CAAC,CACF,CAAC;IAEF,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,GAAG,EAAE,CACV,0BAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAC5D,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,GAAG,EAAE,CACV,0BAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAC3D,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,IAAI,CAAC;YACH,0BAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,sBAAsB,CAAC,CAAC;YACrD,IAAI,CAAC,CAAC,KAAK,YAAY,sBAAsB,CAAC,EAAE,CAAC;gBAC/C,MAAM,KAAK,CAAC;YACd,CAAC;YACD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;gBAC5C,MAAM,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC;aAC/C,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;QAEjB,IAAI,CAAC;YACH,0BAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,sBAAsB,CAAC,CAAC;YACrD,IAAI,CAAC,CAAC,KAAK,YAAY,sBAAsB,CAAC,EAAE,CAAC;gBAC/C,MAAM,KAAK,CAAC;YACd,CAAC;YACD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;gBACjC,QAAQ,EAAE,QAAQ;gBAClB,QAAQ,EAAE,IAAI;aACf,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,GAAG,EAAE,CAAC,0BAA0B,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAC5D,sBAAsB,CACvB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superblocksteam/sdk-api",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.168-next.0",
|
|
4
4
|
"description": "Superblocks SDK for TypeScript-based API definitions",
|
|
5
5
|
"license": "Superblocks Community Software License",
|
|
6
6
|
"files": [
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@bufbuild/protobuf": "1.3.0",
|
|
24
24
|
"@eslint/js": "^9.39.1",
|
|
25
|
-
"@superblocksteam/shared": "0.
|
|
25
|
+
"@superblocksteam/shared": "0.9616.0",
|
|
26
26
|
"@types/node": "^20",
|
|
27
27
|
"dotenv": "^16.4.5",
|
|
28
28
|
"eslint": "^9.39.1",
|
package/src/errors.ts
CHANGED
|
@@ -27,7 +27,10 @@ function isWorkerBufferJson(data: object): boolean {
|
|
|
27
27
|
if (!("data" in data) || !("type" in data)) {
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
30
|
-
return
|
|
30
|
+
return (
|
|
31
|
+
data.type === "Buffer" &&
|
|
32
|
+
(Array.isArray(data.data) || typeof data.data === "string")
|
|
33
|
+
);
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
function containsBinary(
|
|
@@ -26,6 +26,16 @@ describe("decodeWorkerBinaryResponse", () => {
|
|
|
26
26
|
expect(Array.from(result)).toEqual([37, 80, 68, 70]);
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
+
it("converts base64 Buffer JSON into a plain Uint8Array", () => {
|
|
30
|
+
const result = decodeWorkerBinaryResponse({
|
|
31
|
+
type: "Buffer",
|
|
32
|
+
data: Buffer.from([37, 80, 68, 70]).toString("base64"),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(result.constructor).toBe(Uint8Array);
|
|
36
|
+
expect(Array.from(result)).toEqual([37, 80, 68, 70]);
|
|
37
|
+
});
|
|
38
|
+
|
|
29
39
|
it("converts empty Buffer JSON into a zero-length Uint8Array", () => {
|
|
30
40
|
const result = decodeWorkerBinaryResponse({ type: "Buffer", data: [] });
|
|
31
41
|
|
|
@@ -33,6 +43,13 @@ describe("decodeWorkerBinaryResponse", () => {
|
|
|
33
43
|
expect(result.byteLength).toBe(0);
|
|
34
44
|
});
|
|
35
45
|
|
|
46
|
+
it("decodes an empty base64 string to a zero-length Uint8Array", () => {
|
|
47
|
+
const result = decodeWorkerBinaryResponse({ type: "Buffer", data: "" });
|
|
48
|
+
|
|
49
|
+
expect(result).toBeInstanceOf(Uint8Array);
|
|
50
|
+
expect(result).toHaveLength(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
36
53
|
it("rejects a missing discriminant", () => {
|
|
37
54
|
expect(() => decodeWorkerBinaryResponse({ data: [1] })).toThrow(
|
|
38
55
|
RestApiValidationError,
|
|
@@ -45,11 +62,14 @@ describe("decodeWorkerBinaryResponse", () => {
|
|
|
45
62
|
).toThrow(RestApiValidationError);
|
|
46
63
|
});
|
|
47
64
|
|
|
48
|
-
it("
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
65
|
+
it.each(["@@@@", "A=BC", "====", "QUI"])(
|
|
66
|
+
"rejects non-base64 string data %j",
|
|
67
|
+
(data) => {
|
|
68
|
+
expect(() =>
|
|
69
|
+
decodeWorkerBinaryResponse({ type: "Buffer", data }),
|
|
70
|
+
).toThrow(RestApiValidationError);
|
|
71
|
+
},
|
|
72
|
+
);
|
|
53
73
|
|
|
54
74
|
it("rejects a byte outside 0 through 255", () => {
|
|
55
75
|
expect(() =>
|
|
@@ -7,6 +7,18 @@ import {
|
|
|
7
7
|
|
|
8
8
|
export { REDACTED_BINARY_RESPONSE_DATA };
|
|
9
9
|
|
|
10
|
+
// Node's decoder accepts missing and misplaced padding. A worker payload is
|
|
11
|
+
// a canonical padded string, so reject anything else before decoding.
|
|
12
|
+
const BASE64 =
|
|
13
|
+
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
14
|
+
|
|
15
|
+
function decodeBase64(data: string): Uint8Array | undefined {
|
|
16
|
+
if (!BASE64.test(data)) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
return new Uint8Array(Buffer.from(data, "base64"));
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
const workerBinaryResponseSchema = z.union([
|
|
11
23
|
z.instanceof(Uint8Array).transform((value) => new Uint8Array(value)),
|
|
12
24
|
z
|
|
@@ -15,10 +27,23 @@ const workerBinaryResponseSchema = z.union([
|
|
|
15
27
|
type: z.literal("Buffer"),
|
|
16
28
|
})
|
|
17
29
|
.transform((value, context) => {
|
|
30
|
+
if (typeof value.data === "string") {
|
|
31
|
+
const bytes = decodeBase64(value.data);
|
|
32
|
+
if (!bytes) {
|
|
33
|
+
context.addIssue({
|
|
34
|
+
code: z.ZodIssueCode.custom,
|
|
35
|
+
message: "data must be base64",
|
|
36
|
+
path: ["data"],
|
|
37
|
+
});
|
|
38
|
+
return z.NEVER;
|
|
39
|
+
}
|
|
40
|
+
return bytes;
|
|
41
|
+
}
|
|
42
|
+
|
|
18
43
|
if (!Array.isArray(value.data)) {
|
|
19
44
|
context.addIssue({
|
|
20
45
|
code: z.ZodIssueCode.custom,
|
|
21
|
-
message: "data must be an array of bytes",
|
|
46
|
+
message: "data must be an array of bytes or a base64 string",
|
|
22
47
|
path: ["data"],
|
|
23
48
|
});
|
|
24
49
|
return z.NEVER;
|