@taprootio/integration-sdk 0.0.0-stage → 0.1.2

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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -2
  3. package/fixtures/manifest.valid.json +28 -0
  4. package/fixtures/webhooks/v1/common.schema.json +45 -0
  5. package/fixtures/webhooks/v1/examples/installation.created.json +1 -0
  6. package/fixtures/webhooks/v1/examples/installation.resumed.json +1 -0
  7. package/fixtures/webhooks/v1/examples/installation.suspended.json +1 -0
  8. package/fixtures/webhooks/v1/examples/installation.uninstalled.json +1 -0
  9. package/fixtures/webhooks/v1/examples/installation.updated.json +1 -0
  10. package/fixtures/webhooks/v1/examples/page.deleted.json +1 -0
  11. package/fixtures/webhooks/v1/examples/page.published.json +1 -0
  12. package/fixtures/webhooks/v1/examples/ping.json +1 -0
  13. package/fixtures/webhooks/v1/examples/placement.created.json +1 -0
  14. package/fixtures/webhooks/v1/examples/placement.deleted.json +1 -0
  15. package/fixtures/webhooks/v1/examples/placement.updated.json +1 -0
  16. package/fixtures/webhooks/v1/examples/publish.completed.json +1 -0
  17. package/fixtures/webhooks/v1/examples/publish.failed.json +1 -0
  18. package/fixtures/webhooks/v1/examples/sync.requested.json +1 -0
  19. package/fixtures/webhooks/v1/installation.created.schema.json +22 -0
  20. package/fixtures/webhooks/v1/installation.resumed.schema.json +8 -0
  21. package/fixtures/webhooks/v1/installation.suspended.schema.json +8 -0
  22. package/fixtures/webhooks/v1/installation.uninstalled.schema.json +8 -0
  23. package/fixtures/webhooks/v1/installation.updated.schema.json +8 -0
  24. package/fixtures/webhooks/v1/page.deleted.schema.json +23 -0
  25. package/fixtures/webhooks/v1/page.published.schema.json +23 -0
  26. package/fixtures/webhooks/v1/ping.schema.json +18 -0
  27. package/fixtures/webhooks/v1/placement.created.schema.json +23 -0
  28. package/fixtures/webhooks/v1/placement.deleted.schema.json +23 -0
  29. package/fixtures/webhooks/v1/placement.updated.schema.json +23 -0
  30. package/fixtures/webhooks/v1/publish.completed.schema.json +21 -0
  31. package/fixtures/webhooks/v1/publish.failed.schema.json +22 -0
  32. package/fixtures/webhooks/v1/schema-index.json +19 -0
  33. package/fixtures/webhooks/v1/signature-vector.json +9 -0
  34. package/fixtures/webhooks/v1/sync.requested.schema.json +22 -0
  35. package/index.d.ts +258 -0
  36. package/package.json +42 -4
  37. package/schema/taproot-integration-manifest.v1.schema.json +82 -0
  38. package/src/bytes.js +51 -0
  39. package/src/client.js +409 -0
  40. package/src/constants.js +17 -0
  41. package/src/errors.js +18 -0
  42. package/src/handoff.js +223 -0
  43. package/src/index.js +16 -0
  44. package/src/json.js +56 -0
  45. package/src/manifest.js +284 -0
  46. package/src/openapi-contract.d.ts +73 -0
  47. package/src/openapi-contract.js +1035 -0
  48. package/src/webhooks.js +269 -0
package/index.d.ts ADDED
@@ -0,0 +1,258 @@
1
+ import type { GeneratedWebhookEvent, GeneratedWebhookEventType, IntegrationOpenApiOperationMap } from "./src/openapi-contract.js";
2
+
3
+ export type { GeneratedWebhookEvent, GeneratedWebhookEventMap, GeneratedWebhookEventType, IntegrationOpenApiOperationMap } from "./src/openapi-contract.js";
4
+ export { INTEGRATION_OPENAPI_OPERATIONS, INTEGRATION_OPENAPI_SCHEMAS, INTEGRATION_WEBHOOK_EVENTS } from "./src/openapi-contract.js";
5
+
6
+ type OperationRequest<Name extends keyof IntegrationOpenApiOperationMap> = IntegrationOpenApiOperationMap[Name]["request"];
7
+ type OperationResponse<Name extends keyof IntegrationOpenApiOperationMap> = IntegrationOpenApiOperationMap[Name]["response"];
8
+
9
+ export const INTEGRATION_SDK_VERSION: "0.1.2";
10
+ export const WEBHOOK_VERSION: "1";
11
+ export const WEBHOOK_EVENT_TYPES: readonly WebhookEventType[];
12
+ export const INTEGRATION_CAPABILITIES: readonly IntegrationCapability[];
13
+ export const HANDOFF_JWKS_PATH: ".well-known/taproot-integrations-jwks.json";
14
+ export const HANDOFF_LIFETIME_SECONDS: 300;
15
+ export const DEFAULT_TIMESTAMP_TOLERANCE_SECONDS: 300;
16
+ export const DEFAULT_RETRY_ATTEMPTS: 3;
17
+
18
+ export type IntegrationCapability =
19
+ | "pages.manage_own"
20
+ | "data.manage_own"
21
+ | "media.manage_own"
22
+ | "publish"
23
+ | "site.pages.read"
24
+ | "fragments.manage_own";
25
+
26
+ export class IntegrationSdkError extends Error {
27
+ readonly code: string;
28
+ readonly status?: number;
29
+ readonly details?: unknown;
30
+ }
31
+
32
+ export class IntegrationApiError extends IntegrationSdkError {
33
+ readonly status: number;
34
+ }
35
+
36
+ export interface IntegrationConfigurationSchema {
37
+ type: "object" | "array" | "string" | "number" | "integer" | "boolean";
38
+ properties?: Record<string, IntegrationConfigurationSchema>;
39
+ required?: string[];
40
+ items?: IntegrationConfigurationSchema;
41
+ enum?: Array<string | number | boolean>;
42
+ additionalProperties?: false;
43
+ minLength?: number;
44
+ maxLength?: number;
45
+ minimum?: number;
46
+ maximum?: number;
47
+ }
48
+
49
+ export interface IntegrationManifestComponent {
50
+ id: string;
51
+ name: string;
52
+ configSchema: IntegrationConfigurationSchema;
53
+ }
54
+
55
+ export interface IntegrationPricingOption {
56
+ id: string;
57
+ version: string;
58
+ quantityMin: number;
59
+ quantityMax: number | null;
60
+ cadence: "free" | "monthly" | "annual";
61
+ catalogueItemKey: string | null;
62
+ }
63
+
64
+ export interface IntegrationManifest {
65
+ slug: string;
66
+ displayName: string;
67
+ webhookEndpointUrl: string;
68
+ settingsUrl: string;
69
+ requestedCapabilities: IntegrationCapability[];
70
+ offeredComponents: IntegrationManifestComponent[];
71
+ externalScriptOrigins: string[];
72
+ externalConnectOrigins?: string[];
73
+ externalScriptOriginReasons?: Record<string, string>;
74
+ externalConnectOriginReasons?: Record<string, string>;
75
+ defaultPublishPolicy: "auto" | "stage";
76
+ recommendedUninstallDisposition: "freeze" | "remove";
77
+ contractVersion: string;
78
+ pricingOptions: IntegrationPricingOption[];
79
+ description?: string;
80
+ }
81
+
82
+ export interface ManifestValidationError {
83
+ field: string;
84
+ code: string;
85
+ }
86
+
87
+ export type ManifestValidationResult =
88
+ | { ok: true; value: IntegrationManifest }
89
+ | { ok: false; errors: ManifestValidationError[] };
90
+
91
+ export function validateManifest(input: unknown): ManifestValidationResult;
92
+ export function assertValidManifest(input: unknown): IntegrationManifest;
93
+
94
+ export interface FragmentPreflightError {
95
+ code: "invalid_html" | "size_exceeded" | "element_not_allowed" | "attribute_not_allowed" | "invalid_url";
96
+ message: string;
97
+ }
98
+
99
+ /** Advisory only. Taproot's write-time sanitizer is authoritative. */
100
+ export function validateFragment(html: unknown): { ok: true } | { ok: false; errors: FragmentPreflightError[] };
101
+
102
+ /** Canonical v1 event union emitted from the reviewed webhook JSON Schemas. */
103
+ export type WebhookEvent = GeneratedWebhookEvent;
104
+ export type WebhookEventType = GeneratedWebhookEventType;
105
+ export type WebhookHeaders = Headers | Map<string, string> | Record<string, string | string[] | undefined>;
106
+
107
+ export interface VerifiedWebhookEvent {
108
+ event: WebhookEvent;
109
+ deliveryId: string;
110
+ eventType: WebhookEventType;
111
+ installationId: string;
112
+ keyId: string;
113
+ timestamp: number;
114
+ body: Uint8Array;
115
+ bodySha256: string;
116
+ }
117
+
118
+ export interface VerifyWebhookSignatureOptions {
119
+ headers: WebhookHeaders;
120
+ /** The request body exactly as received, before JSON parsing or reformatting. */
121
+ body: Uint8Array | ArrayBuffer | string;
122
+ /** Active and overlapping retired key ids mapped to base64 HMAC secrets or decoded secret bytes. */
123
+ signingSecrets: Map<string, string | Uint8Array> | Record<string, string | Uint8Array>;
124
+ now?: Date | number;
125
+ timestampToleranceSeconds?: number;
126
+ }
127
+
128
+ export function parseWebhookEvent(body: Uint8Array | ArrayBuffer | string): WebhookEvent;
129
+ export function verifyWebhookSignature(options: VerifyWebhookSignatureOptions): VerifiedWebhookEvent;
130
+
131
+ export interface WebhookReceipt {
132
+ deliveryId: string;
133
+ eventType: WebhookEventType;
134
+ installationId: string;
135
+ integrationId: string;
136
+ occurredAt: string;
137
+ keyId: string;
138
+ bodySha256: string;
139
+ receivedAt: string;
140
+ }
141
+
142
+ export type WebhookReceiptClaim =
143
+ | { state: "claimed"; token: string }
144
+ | { state: "duplicate" }
145
+ | { state: "in_progress" }
146
+ | { state: "conflict" };
147
+
148
+ /**
149
+ * Implement with an atomic, durable database transaction. An in-memory Set is not sufficient.
150
+ * Return `duplicate` only for a delivered receipt. Failed receipts and expired processing leases
151
+ * must return `claimed` with a fresh token so Taproot can redeliver them safely.
152
+ */
153
+ export interface WebhookReceiptStore {
154
+ claim(receipt: WebhookReceipt): Promise<WebhookReceiptClaim> | WebhookReceiptClaim;
155
+ finish(completion: {
156
+ deliveryId: string;
157
+ bodySha256: string;
158
+ token: string;
159
+ status: "delivered" | "failed";
160
+ failureCode?: string;
161
+ completedAt: string;
162
+ }): Promise<void> | void;
163
+ }
164
+
165
+ export function claimWebhookReceipt(options: { store: WebhookReceiptStore; verified: VerifiedWebhookEvent; now?: Date | number }): Promise<{ receipt: WebhookReceipt } & WebhookReceiptClaim>;
166
+ export function finishWebhookReceipt(options: {
167
+ store: WebhookReceiptStore;
168
+ verified: VerifiedWebhookEvent;
169
+ token: string;
170
+ status: "delivered" | "failed";
171
+ failureCode?: string;
172
+ now?: Date | number;
173
+ }): Promise<void>;
174
+
175
+ /** Exact OpenAPI types emitted from the reviewed integration-operation allowlist. */
176
+ export type ClaimInstallationResponse = OperationResponse<"claimInstallation">;
177
+ export type IntegrationInstallation = ClaimInstallationResponse extends { installation?: infer Value } ? NonNullable<Value> : never;
178
+ export type IntegrationCredential = ClaimInstallationResponse extends { credential?: infer Value } ? NonNullable<Value> : never;
179
+ export type ValidateInstallationResponse = OperationResponse<"validateInstallation">;
180
+ export type IntegrationSyncResult = OperationResponse<"createSync">;
181
+ export type TaprootImage = OperationResponse<"confirmMediaUpload"> extends { image?: infer Value } ? NonNullable<Value> : never;
182
+ export type RequestMediaUploadResponse = OperationResponse<"requestMediaUpload">;
183
+ export type IntegrationPlacement = OperationResponse<"listPlacements"> extends { placements?: Array<infer Value> } ? Value : never;
184
+ export type IntegrationPlacementFragmentResult = OperationResponse<"putFragment">;
185
+ export type IntegrationBundle = OperationResponse<"confirmBundleUpload">;
186
+ export type RequestBundleUploadResponse = OperationResponse<"requestBundleUpload">;
187
+ export type IntegrationManagedPageContent = OperationRequest<"putManagedPage"> extends { page?: infer Value } ? NonNullable<Value> : never;
188
+
189
+ export interface TaprootIntegrationClientOptions {
190
+ /** Canonical API base, such as https://app.example/api; do not include /v1. */
191
+ baseUrl: string;
192
+ fetch?: typeof globalThis.fetch;
193
+ installationCredential?: string;
194
+ publisherCredential?: string;
195
+ timeoutMs?: number;
196
+ maxAttempts?: 1 | 2 | 3;
197
+ maxConcurrentRequests?: number;
198
+ maxQueuedRequests?: number;
199
+ }
200
+
201
+ export class TaprootIntegrationClient {
202
+ constructor(options: TaprootIntegrationClientOptions);
203
+ claimInstallation(input: { installationId: string; claimCode: string }): Promise<ClaimInstallationResponse>;
204
+ validateInstallation(input: { installationId: string }): Promise<ValidateInstallationResponse>;
205
+ createSync(input: { installationId: string; mode: string; requestedSyncId?: string; idempotencyKey: string }): Promise<IntegrationSyncResult>;
206
+ completeSync(input: { installationId: string; syncId: string; summary?: string; idempotencyKey: string }): Promise<IntegrationSyncResult>;
207
+ abortSync(input: { installationId: string; syncId: string; summary?: string; idempotencyKey: string }): Promise<IntegrationSyncResult>;
208
+ putManagedPage(input: { installationId: string; syncId?: string; page: IntegrationManagedPageContent; idempotencyKey: string }): Promise<IntegrationSyncResult>;
209
+ deleteManagedPage(input: { installationId: string; syncId?: string; externalId: string; idempotencyKey: string }): Promise<IntegrationSyncResult>;
210
+ putDataDocument(input: { installationId: string; syncId?: string; key: string; json: string; idempotencyKey: string }): Promise<IntegrationSyncResult>;
211
+ deleteDataDocument(input: { installationId: string; syncId?: string; key: string; idempotencyKey: string }): Promise<IntegrationSyncResult>;
212
+ requestMediaUpload(input: { installationId: string; fileName: string; contentType: string; fileSize: number; contentHash: string; width: number; height: number }): Promise<RequestMediaUploadResponse>;
213
+ confirmMediaUpload(input: { installationId: string; uploadId: string }): Promise<OperationResponse<"confirmMediaUpload">>;
214
+ putMedia(input: { installationId: string; fileName: string; contentType: string; fileSize: number; contentHash: string; width: number; height: number; body: Uint8Array | ArrayBuffer | string }): Promise<TaprootImage>;
215
+ uploadPresigned(input: { url: string; requiredHeaders?: Record<string, string>; body: Uint8Array | ArrayBuffer | string }): Promise<void>;
216
+ listPlacements(input: { installationId: string }): Promise<{ placements: IntegrationPlacement[] }>;
217
+ putFragment(input: { installationId: string; placementId: string; configurationRevision: number; expectedFragmentRevision?: number; html: string; structuredData?: string[]; syncId?: string; idempotencyKey: string }): Promise<IntegrationPlacementFragmentResult>;
218
+ deleteFragment(input: { installationId: string; placementId: string; configurationRevision: number; expectedFragmentRevision?: number; syncId?: string; idempotencyKey: string }): Promise<IntegrationPlacementFragmentResult>;
219
+ requestBundleUpload(input: { integrationId: string; contentHash: string; byteLength: number; runtimeMajor: number }): Promise<RequestBundleUploadResponse>;
220
+ confirmBundleUpload(input: { integrationId: string; uploadId: string }): Promise<IntegrationBundle>;
221
+ confirmHandoff(input: { installationId: string; token: string }): Promise<ConfirmedHandoff>;
222
+ }
223
+
224
+ export function createIdempotencyKey(): string;
225
+
226
+ export interface HandoffClaims {
227
+ userId: string;
228
+ siteId: string;
229
+ installationId: string;
230
+ integrationId: string;
231
+ credentialId: string;
232
+ jti: string;
233
+ role: "owner" | "engagement";
234
+ engagementId: string | null;
235
+ issuedAt: string;
236
+ expiresAt: string;
237
+ }
238
+ export type ConfirmedHandoff = OperationResponse<"confirmHandoff">;
239
+ export interface HandoffJwk { kty: "RSA"; use: "sig"; alg: "RS256"; kid: string; n: string; e: string; }
240
+ export interface HandoffJwks { keys: HandoffJwk[]; }
241
+ export function verifyHandoff(options: {
242
+ token: string;
243
+ issuer: string;
244
+ audience: string;
245
+ installationId: string;
246
+ jwksUrl?: string;
247
+ jwks?: HandoffJwks;
248
+ fetch?: typeof globalThis.fetch;
249
+ now?: Date | number;
250
+ }): Promise<HandoffClaims>;
251
+ export function confirmHandoff(options: {
252
+ client: TaprootIntegrationClient;
253
+ claims: HandoffClaims;
254
+ token: string;
255
+ now?: Date | number;
256
+ maximumSessionLifetimeSeconds?: number;
257
+ }): Promise<HandoffClaims & { sessionExpiresAt: string }>;
258
+ export function clearHandoffJwksCache(jwksUrl?: string): void;
package/package.json CHANGED
@@ -1,6 +1,44 @@
1
1
  {
2
2
  "name": "@taprootio/integration-sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.2",
4
+ "description": "Public contracts and client for Taproot push integrations",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "types": "./index.d.ts",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/taprootio/trunk.git",
12
+ "directory": "packages/integration-sdk"
13
+ },
14
+ "engines": {
15
+ "node": ">=22"
16
+ },
17
+ "exports": {
18
+ ".": {
19
+ "types": "./index.d.ts",
20
+ "import": "./src/index.js"
21
+ },
22
+ "./schema": "./schema/taproot-integration-manifest.v1.schema.json",
23
+ "./fixtures/manifest.valid.json": "./fixtures/manifest.valid.json",
24
+ "./fixtures/webhooks/v1/schema-index.json": "./fixtures/webhooks/v1/schema-index.json",
25
+ "./fixtures/webhooks/v1/signature-vector.json": "./fixtures/webhooks/v1/signature-vector.json",
26
+ "./fixtures/*": "./fixtures/*"
27
+ },
28
+ "files": [
29
+ "fixtures/",
30
+ "schema/",
31
+ "src/",
32
+ "LICENSE",
33
+ "README.md",
34
+ "index.d.ts"
35
+ ],
36
+ "scripts": {
37
+ "test": "node --test",
38
+ "prepack": "npm test"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public",
42
+ "provenance": true
43
+ }
44
+ }
@@ -0,0 +1,82 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.taproot.io/integrations/manifest.v1.schema.json",
4
+ "title": "Taproot integration manifest v1",
5
+ "description": "The public declarative integration registry contract. validateManifest applies additional bounded semantic checks; Taproot remains authoritative for DNS and registration policy.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "slug", "displayName", "webhookEndpointUrl", "settingsUrl", "requestedCapabilities",
10
+ "offeredComponents", "externalScriptOrigins", "defaultPublishPolicy",
11
+ "recommendedUninstallDisposition", "contractVersion", "pricingOptions"
12
+ ],
13
+ "properties": {
14
+ "slug": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$", "minLength": 1, "maxLength": 64 },
15
+ "displayName": { "type": "string", "minLength": 1, "maxLength": 100 },
16
+ "description": { "type": "string", "minLength": 1, "maxLength": 2000 },
17
+ "webhookEndpointUrl": { "$ref": "#/$defs/endpoint" },
18
+ "settingsUrl": { "$ref": "#/$defs/endpoint" },
19
+ "requestedCapabilities": {
20
+ "type": "array", "uniqueItems": true, "maxItems": 32,
21
+ "items": { "enum": ["pages.manage_own", "data.manage_own", "media.manage_own", "publish", "site.pages.read", "fragments.manage_own"] }
22
+ },
23
+ "offeredComponents": { "type": "array", "maxItems": 32, "items": { "$ref": "#/$defs/component" } },
24
+ "externalScriptOrigins": { "$ref": "#/$defs/origins" },
25
+ "externalConnectOrigins": { "$ref": "#/$defs/origins" },
26
+ "externalScriptOriginReasons": { "$ref": "#/$defs/originReasons" },
27
+ "externalConnectOriginReasons": { "$ref": "#/$defs/originReasons" },
28
+ "defaultPublishPolicy": { "enum": ["auto", "stage"] },
29
+ "recommendedUninstallDisposition": { "enum": ["freeze", "remove"] },
30
+ "contractVersion": { "type": "string", "pattern": "^[A-Za-z0-9.-]+$", "minLength": 1, "maxLength": 64 },
31
+ "pricingOptions": { "type": "array", "maxItems": 16, "items": { "$ref": "#/$defs/pricingOption" } }
32
+ },
33
+ "$defs": {
34
+ "endpoint": {
35
+ "type": "string", "format": "uri", "maxLength": 2048,
36
+ "pattern": "^https://"
37
+ },
38
+ "origins": {
39
+ "type": "array", "uniqueItems": true, "maxItems": 16,
40
+ "items": { "type": "string", "format": "uri", "pattern": "^https://", "maxLength": 2048 }
41
+ },
42
+ "originReasons": {
43
+ "type": "object", "additionalProperties": { "type": "string", "minLength": 1, "maxLength": 500 }
44
+ },
45
+ "component": {
46
+ "type": "object", "additionalProperties": false, "required": ["id", "name", "configSchema"],
47
+ "properties": {
48
+ "id": { "$ref": "#/$defs/identifier64" },
49
+ "name": { "type": "string", "minLength": 1, "maxLength": 100 },
50
+ "configSchema": { "$ref": "#/$defs/configSchema" }
51
+ }
52
+ },
53
+ "identifier64": { "type": "string", "pattern": "^[A-Za-z0-9_.-]+$", "minLength": 1, "maxLength": 64 },
54
+ "pricingOption": {
55
+ "type": "object", "additionalProperties": false,
56
+ "required": ["id", "version", "quantityMin", "quantityMax", "cadence", "catalogueItemKey"],
57
+ "properties": {
58
+ "id": { "$ref": "#/$defs/identifier64" },
59
+ "version": { "type": "string", "pattern": "^[A-Za-z0-9.-]+$", "minLength": 1, "maxLength": 64 },
60
+ "quantityMin": { "type": "integer", "minimum": 1, "maximum": 100000 },
61
+ "quantityMax": { "type": ["integer", "null"], "minimum": 1, "maximum": 100000 },
62
+ "cadence": { "enum": ["free", "monthly", "annual"] },
63
+ "catalogueItemKey": { "type": ["string", "null"], "maxLength": 128 }
64
+ }
65
+ },
66
+ "configSchema": {
67
+ "type": "object", "additionalProperties": false, "required": ["type"],
68
+ "properties": {
69
+ "type": { "enum": ["object", "array", "string", "number", "integer", "boolean"] },
70
+ "properties": { "type": "object", "maxProperties": 32, "additionalProperties": { "$ref": "#/$defs/configSchema" } },
71
+ "required": { "type": "array", "maxItems": 32, "uniqueItems": true, "items": { "type": "string", "minLength": 1, "maxLength": 64 } },
72
+ "items": { "$ref": "#/$defs/configSchema" },
73
+ "enum": { "type": "array", "minItems": 1, "maxItems": 64, "items": { "type": ["string", "number", "boolean"] } },
74
+ "additionalProperties": { "const": false },
75
+ "minLength": { "type": "integer", "minimum": 0, "maximum": 100000 },
76
+ "maxLength": { "type": "integer", "minimum": 0, "maximum": 100000 },
77
+ "minimum": { "type": "number" },
78
+ "maximum": { "type": "number" }
79
+ }
80
+ }
81
+ }
82
+ }
package/src/bytes.js ADDED
@@ -0,0 +1,51 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import { IntegrationSdkError } from "./errors.js";
4
+
5
+ export const MAX_WEBHOOK_BODY_BYTES = 1024 * 1024;
6
+ export const MAX_API_RESPONSE_BYTES = 1024 * 1024;
7
+ export const MAX_API_REQUEST_BYTES = 1024 * 1024;
8
+
9
+ export function asBytes(value, code = "invalid_body") {
10
+ if (value instanceof Uint8Array) return value;
11
+ if (value instanceof ArrayBuffer) return new Uint8Array(value);
12
+ if (typeof value === "string") return new TextEncoder().encode(value);
13
+ throw new IntegrationSdkError(code, "Expected exact bytes, an ArrayBuffer, or a UTF-8 string.");
14
+ }
15
+
16
+ export function sha256Hex(value) {
17
+ return createHash("sha256").update(asBytes(value)).digest("hex");
18
+ }
19
+
20
+ export function isPlainObject(value) {
21
+ return value !== null && typeof value === "object" && !Array.isArray(value) &&
22
+ Object.getPrototypeOf(value) === Object.prototype;
23
+ }
24
+
25
+ export function readHeader(headers, name) {
26
+ const target = name.toLowerCase();
27
+ if (headers instanceof Headers) return headers.get(name);
28
+ if (headers instanceof Map) {
29
+ for (const [key, value] of headers) if (String(key).toLowerCase() === target) return String(value);
30
+ return undefined;
31
+ }
32
+ if (headers && typeof headers === "object") {
33
+ for (const [key, value] of Object.entries(headers)) if (key.toLowerCase() === target) return String(value);
34
+ }
35
+ return undefined;
36
+ }
37
+
38
+ export function parseJsonBytes(bytes, code = "invalid_json") {
39
+ try {
40
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
41
+ } catch {
42
+ throw new IntegrationSdkError(code, "The value is not valid UTF-8 JSON.");
43
+ }
44
+ }
45
+
46
+ export function ensureBoundedText(value, maximum, code, field) {
47
+ if (typeof value !== "string" || value.length === 0 || value.length > maximum || /[\u0000-\u001f\u007f]/.test(value)) {
48
+ throw new IntegrationSdkError(code, `Invalid ${field}.`);
49
+ }
50
+ return value;
51
+ }