@canveletedotcom/sdk 2.0.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 +143 -0
- package/dist/client.d.ts +46 -0
- package/dist/client.js +132 -0
- package/dist/errors.d.ts +27 -0
- package/dist/errors.js +48 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +8 -0
- package/dist/resources/apiKeys.d.ts +22 -0
- package/dist/resources/apiKeys.js +15 -0
- package/dist/resources/assets.d.ts +101 -0
- package/dist/resources/assets.js +93 -0
- package/dist/resources/billing.d.ts +96 -0
- package/dist/resources/billing.js +64 -0
- package/dist/resources/canvas.d.ts +36 -0
- package/dist/resources/canvas.js +26 -0
- package/dist/resources/designs.d.ts +42 -0
- package/dist/resources/designs.js +46 -0
- package/dist/resources/index.d.ts +15 -0
- package/dist/resources/index.js +9 -0
- package/dist/resources/render.d.ts +90 -0
- package/dist/resources/render.js +171 -0
- package/dist/resources/templates.d.ts +53 -0
- package/dist/resources/templates.js +76 -0
- package/dist/resources/usage.d.ts +76 -0
- package/dist/resources/usage.js +52 -0
- package/dist/types/index.d.ts +197 -0
- package/dist/types/index.js +4 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/retry.d.ts +22 -0
- package/dist/utils/retry.js +70 -0
- package/dist/utils/validation.d.ts +69 -0
- package/dist/utils/validation.js +126 -0
- package/dist/utils/webhooks.d.ts +31 -0
- package/dist/utils/webhooks.js +70 -0
- package/package.json +57 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element validation utilities
|
|
3
|
+
*/
|
|
4
|
+
import { ValidationError } from '../errors';
|
|
5
|
+
const VALID_ELEMENT_TYPES = [
|
|
6
|
+
'rectangle',
|
|
7
|
+
'circle',
|
|
8
|
+
'text',
|
|
9
|
+
'image',
|
|
10
|
+
'line',
|
|
11
|
+
'polygon',
|
|
12
|
+
'star',
|
|
13
|
+
'svg',
|
|
14
|
+
'bezier',
|
|
15
|
+
'container',
|
|
16
|
+
'table',
|
|
17
|
+
'qr',
|
|
18
|
+
'barcode',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Validate an element's basic properties
|
|
22
|
+
*/
|
|
23
|
+
export function validateElement(element) {
|
|
24
|
+
const errors = [];
|
|
25
|
+
// Required fields
|
|
26
|
+
if (!element.type) {
|
|
27
|
+
errors.push('Element type is required');
|
|
28
|
+
}
|
|
29
|
+
else if (!VALID_ELEMENT_TYPES.includes(element.type)) {
|
|
30
|
+
errors.push(`Invalid element type: ${element.type}. Valid types: ${VALID_ELEMENT_TYPES.join(', ')}`);
|
|
31
|
+
}
|
|
32
|
+
if (typeof element.x !== 'number') {
|
|
33
|
+
errors.push('Element x position must be a number');
|
|
34
|
+
}
|
|
35
|
+
if (typeof element.y !== 'number') {
|
|
36
|
+
errors.push('Element y position must be a number');
|
|
37
|
+
}
|
|
38
|
+
// Type-specific validation
|
|
39
|
+
if (element.type === 'text' && !element.text) {
|
|
40
|
+
errors.push('Text element requires text content');
|
|
41
|
+
}
|
|
42
|
+
if (element.type === 'image' && !element.src) {
|
|
43
|
+
errors.push('Image element requires src URL');
|
|
44
|
+
}
|
|
45
|
+
// Numeric range validation
|
|
46
|
+
if (element.opacity !== undefined && (element.opacity < 0 || element.opacity > 1)) {
|
|
47
|
+
errors.push('Opacity must be between 0 and 1');
|
|
48
|
+
}
|
|
49
|
+
if (element.fontSize !== undefined && element.fontSize <= 0) {
|
|
50
|
+
errors.push('Font size must be positive');
|
|
51
|
+
}
|
|
52
|
+
if (element.strokeWidth !== undefined && element.strokeWidth < 0) {
|
|
53
|
+
errors.push('Stroke width cannot be negative');
|
|
54
|
+
}
|
|
55
|
+
if (element.borderRadius !== undefined && element.borderRadius < 0) {
|
|
56
|
+
errors.push('Border radius cannot be negative');
|
|
57
|
+
}
|
|
58
|
+
if (errors.length > 0) {
|
|
59
|
+
throw new ValidationError(`Element validation failed: ${errors.join('; ')}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validate canvas dimensions
|
|
64
|
+
*/
|
|
65
|
+
export function validateCanvasDimensions(width, height) {
|
|
66
|
+
const errors = [];
|
|
67
|
+
if (typeof width !== 'number' || width <= 0) {
|
|
68
|
+
errors.push('Width must be a positive number');
|
|
69
|
+
}
|
|
70
|
+
if (typeof height !== 'number' || height <= 0) {
|
|
71
|
+
errors.push('Height must be a positive number');
|
|
72
|
+
}
|
|
73
|
+
if (width > 10000) {
|
|
74
|
+
errors.push('Width cannot exceed 10000 pixels');
|
|
75
|
+
}
|
|
76
|
+
if (height > 10000) {
|
|
77
|
+
errors.push('Height cannot exceed 10000 pixels');
|
|
78
|
+
}
|
|
79
|
+
if (errors.length > 0) {
|
|
80
|
+
throw new ValidationError(`Canvas validation failed: ${errors.join('; ')}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Validate render options
|
|
85
|
+
*/
|
|
86
|
+
export function validateRenderOptions(options) {
|
|
87
|
+
const errors = [];
|
|
88
|
+
if (!options.designId && !options.templateId) {
|
|
89
|
+
errors.push('Either designId or templateId is required');
|
|
90
|
+
}
|
|
91
|
+
if (options.format && !['png', 'jpg', 'jpeg', 'pdf', 'svg'].includes(options.format)) {
|
|
92
|
+
errors.push('Invalid format. Valid formats: png, jpg, jpeg, pdf, svg');
|
|
93
|
+
}
|
|
94
|
+
if (options.quality !== undefined && (options.quality < 1 || options.quality > 100)) {
|
|
95
|
+
errors.push('Quality must be between 1 and 100');
|
|
96
|
+
}
|
|
97
|
+
if (errors.length > 0) {
|
|
98
|
+
throw new ValidationError(`Render options validation failed: ${errors.join('; ')}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate color format
|
|
103
|
+
*/
|
|
104
|
+
export function validateColor(color) {
|
|
105
|
+
// Hex color
|
|
106
|
+
if (/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(color)) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
// RGB/RGBA
|
|
110
|
+
if (/^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+(,\s*[\d.]+)?\s*\)$/.test(color)) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
// HSL/HSLA
|
|
114
|
+
if (/^hsla?\(\s*\d+\s*,\s*\d+%\s*,\s*\d+%(,\s*[\d.]+)?\s*\)$/.test(color)) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
// Named colors (basic check)
|
|
118
|
+
const namedColors = [
|
|
119
|
+
'transparent', 'black', 'white', 'red', 'green', 'blue',
|
|
120
|
+
'yellow', 'orange', 'purple', 'pink', 'gray', 'grey',
|
|
121
|
+
];
|
|
122
|
+
if (namedColors.includes(color.toLowerCase())) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook signature verification utilities
|
|
3
|
+
*/
|
|
4
|
+
export interface WebhookEvent {
|
|
5
|
+
id: string;
|
|
6
|
+
type: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
data: Record<string, any>;
|
|
9
|
+
}
|
|
10
|
+
export interface WebhookVerifyOptions {
|
|
11
|
+
payload: string | Buffer;
|
|
12
|
+
signature: string;
|
|
13
|
+
secret: string;
|
|
14
|
+
tolerance?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Verify a webhook signature
|
|
18
|
+
*/
|
|
19
|
+
export declare function verifyWebhookSignature(options: WebhookVerifyOptions): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Parse a webhook payload
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseWebhookPayload(payload: string | Buffer): WebhookEvent;
|
|
24
|
+
/**
|
|
25
|
+
* Construct a webhook event from verified payload
|
|
26
|
+
*/
|
|
27
|
+
export declare function constructWebhookEvent(payload: string | Buffer, signature: string, secret: string): WebhookEvent;
|
|
28
|
+
/**
|
|
29
|
+
* Generate a webhook signature (for testing)
|
|
30
|
+
*/
|
|
31
|
+
export declare function generateWebhookSignature(payload: string, secret: string): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook signature verification utilities
|
|
3
|
+
*/
|
|
4
|
+
import { createHmac, timingSafeEqual } from 'crypto';
|
|
5
|
+
/**
|
|
6
|
+
* Verify a webhook signature
|
|
7
|
+
*/
|
|
8
|
+
export function verifyWebhookSignature(options) {
|
|
9
|
+
const { payload, signature, secret, tolerance = 300 } = options;
|
|
10
|
+
// Parse the signature header (format: t=timestamp,v1=signature)
|
|
11
|
+
const parts = signature.split(',');
|
|
12
|
+
const signatureParts = {};
|
|
13
|
+
for (const part of parts) {
|
|
14
|
+
const [key, value] = part.split('=');
|
|
15
|
+
if (key && value) {
|
|
16
|
+
signatureParts[key] = value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const timestamp = signatureParts['t'];
|
|
20
|
+
const v1Signature = signatureParts['v1'];
|
|
21
|
+
if (!timestamp || !v1Signature) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
// Check timestamp tolerance
|
|
25
|
+
const timestampNum = parseInt(timestamp, 10);
|
|
26
|
+
const now = Math.floor(Date.now() / 1000);
|
|
27
|
+
if (Math.abs(now - timestampNum) > tolerance) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
// Compute expected signature
|
|
31
|
+
const payloadStr = typeof payload === 'string' ? payload : payload.toString('utf8');
|
|
32
|
+
const signedPayload = `${timestamp}.${payloadStr}`;
|
|
33
|
+
const expectedSignature = createHmac('sha256', secret)
|
|
34
|
+
.update(signedPayload)
|
|
35
|
+
.digest('hex');
|
|
36
|
+
// Timing-safe comparison
|
|
37
|
+
try {
|
|
38
|
+
return timingSafeEqual(Buffer.from(v1Signature), Buffer.from(expectedSignature));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse a webhook payload
|
|
46
|
+
*/
|
|
47
|
+
export function parseWebhookPayload(payload) {
|
|
48
|
+
const payloadStr = typeof payload === 'string' ? payload : payload.toString('utf8');
|
|
49
|
+
return JSON.parse(payloadStr);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Construct a webhook event from verified payload
|
|
53
|
+
*/
|
|
54
|
+
export function constructWebhookEvent(payload, signature, secret) {
|
|
55
|
+
if (!verifyWebhookSignature({ payload, signature, secret })) {
|
|
56
|
+
throw new Error('Invalid webhook signature');
|
|
57
|
+
}
|
|
58
|
+
return parseWebhookPayload(payload);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Generate a webhook signature (for testing)
|
|
62
|
+
*/
|
|
63
|
+
export function generateWebhookSignature(payload, secret) {
|
|
64
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
65
|
+
const signedPayload = `${timestamp}.${payload}`;
|
|
66
|
+
const signature = createHmac('sha256', secret)
|
|
67
|
+
.update(signedPayload)
|
|
68
|
+
.digest('hex');
|
|
69
|
+
return `t=${timestamp},v1=${signature}`;
|
|
70
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@canveletedotcom/sdk",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Official TypeScript SDK for the Canvelete API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./utils": {
|
|
15
|
+
"import": "./dist/utils/index.js",
|
|
16
|
+
"types": "./dist/utils/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"test:coverage": "vitest run --coverage",
|
|
28
|
+
"prepublishOnly": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"canvelete",
|
|
32
|
+
"typescript",
|
|
33
|
+
"sdk",
|
|
34
|
+
"api",
|
|
35
|
+
"image",
|
|
36
|
+
"generation",
|
|
37
|
+
"pdf",
|
|
38
|
+
"design",
|
|
39
|
+
"automation"
|
|
40
|
+
],
|
|
41
|
+
"author": "Canvelete",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "https://github.com/canvelete/canvelete-sdk.git"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://docs.canvelete.com",
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"typescript": "^5.0.0",
|
|
50
|
+
"@types/node": "^18.0.0",
|
|
51
|
+
"vitest": "^1.0.0",
|
|
52
|
+
"@vitest/coverage-v8": "^1.0.0"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18.0.0"
|
|
56
|
+
}
|
|
57
|
+
}
|