@stack0/sdk 0.2.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 ADDED
@@ -0,0 +1,388 @@
1
+ # Stack0 SDK
2
+
3
+ Official TypeScript/JavaScript SDK for Stack0 services.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @stack0/sdk
9
+ # or
10
+ yarn add @stack0/sdk
11
+ # or
12
+ pnpm add @stack0/sdk
13
+ # or
14
+ bun add @stack0/sdk
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { Stack0 } from '@stack0/sdk';
21
+
22
+ const stack0 = new Stack0({
23
+ apiKey: 'stack0_...',
24
+ });
25
+
26
+ // Send an email
27
+ const email = await stack0.mail.send({
28
+ from: 'noreply@example.com',
29
+ to: 'user@example.com',
30
+ subject: 'Welcome!',
31
+ html: '<h1>Hello World</h1>',
32
+ });
33
+
34
+ // Upload a file to CDN
35
+ const asset = await stack0.cdn.upload({
36
+ projectSlug: 'my-project',
37
+ file: fileBuffer,
38
+ filename: 'image.jpg',
39
+ mimeType: 'image/jpeg',
40
+ });
41
+
42
+ console.log(asset.cdnUrl); // https://cdn.stack0.com/...
43
+ ```
44
+
45
+ ## Configuration
46
+
47
+ ```typescript
48
+ import { Stack0 } from '@stack0/sdk';
49
+
50
+ const stack0 = new Stack0({
51
+ apiKey: 'stack0_...', // Required: Your API key
52
+ baseUrl: 'https://api.stack0.com/v1', // Optional: Custom API endpoint
53
+ });
54
+ ```
55
+
56
+ ## CDN API
57
+
58
+ Upload, manage, and transform assets with the CDN API.
59
+
60
+ ### Upload File
61
+
62
+ ```typescript
63
+ // Simple upload (handles presigned URL flow automatically)
64
+ const asset = await stack0.cdn.upload({
65
+ projectSlug: 'my-project',
66
+ file: fileBuffer, // Blob, Buffer, or ArrayBuffer
67
+ filename: 'photo.jpg',
68
+ mimeType: 'image/jpeg',
69
+ folder: '/images/avatars', // Optional
70
+ metadata: { userId: 'user_123' }, // Optional
71
+ });
72
+
73
+ console.log(asset.id); // Asset ID
74
+ console.log(asset.cdnUrl); // CDN URL
75
+ console.log(asset.status); // 'ready', 'processing', etc.
76
+ ```
77
+
78
+ ### Manual Upload Flow
79
+
80
+ For more control, use the presigned URL flow:
81
+
82
+ ```typescript
83
+ // 1. Get presigned upload URL
84
+ const { uploadUrl, assetId } = await stack0.cdn.getUploadUrl({
85
+ projectSlug: 'my-project',
86
+ filename: 'document.pdf',
87
+ mimeType: 'application/pdf',
88
+ size: file.size,
89
+ });
90
+
91
+ // 2. Upload directly to S3
92
+ await fetch(uploadUrl, {
93
+ method: 'PUT',
94
+ body: file,
95
+ headers: { 'Content-Type': 'application/pdf' },
96
+ });
97
+
98
+ // 3. Confirm upload
99
+ const asset = await stack0.cdn.confirmUpload(assetId);
100
+ ```
101
+
102
+ ### List Assets
103
+
104
+ ```typescript
105
+ const { assets, total, hasMore } = await stack0.cdn.list({
106
+ projectSlug: 'my-project',
107
+ type: 'image', // Optional: 'image', 'video', 'audio', 'document', 'other'
108
+ folder: '/images', // Optional
109
+ status: 'ready', // Optional
110
+ search: 'avatar', // Optional: search in filename
111
+ tags: ['profile'], // Optional
112
+ sortBy: 'createdAt', // Optional: 'createdAt', 'filename', 'size', 'type'
113
+ sortOrder: 'desc', // Optional: 'asc', 'desc'
114
+ limit: 20, // Optional
115
+ offset: 0, // Optional
116
+ });
117
+
118
+ for (const asset of assets) {
119
+ console.log(asset.filename, asset.cdnUrl);
120
+ }
121
+ ```
122
+
123
+ ### Get Asset
124
+
125
+ ```typescript
126
+ const asset = await stack0.cdn.get('asset-id');
127
+
128
+ console.log(asset.filename);
129
+ console.log(asset.size);
130
+ console.log(asset.mimeType);
131
+ console.log(asset.width, asset.height); // For images/videos
132
+ ```
133
+
134
+ ### Update Asset
135
+
136
+ ```typescript
137
+ const asset = await stack0.cdn.update({
138
+ id: 'asset-id',
139
+ filename: 'new-name.jpg', // Optional
140
+ folder: '/images/archived', // Optional
141
+ tags: ['nature', 'sunset'], // Optional
142
+ alt: 'A beautiful sunset', // Optional
143
+ metadata: { category: 'landscape' }, // Optional
144
+ });
145
+ ```
146
+
147
+ ### Delete Assets
148
+
149
+ ```typescript
150
+ // Delete single asset
151
+ await stack0.cdn.delete('asset-id');
152
+
153
+ // Delete multiple assets
154
+ const { deletedCount } = await stack0.cdn.deleteMany([
155
+ 'asset-1',
156
+ 'asset-2',
157
+ 'asset-3',
158
+ ]);
159
+ ```
160
+
161
+ ### Move Assets
162
+
163
+ ```typescript
164
+ await stack0.cdn.move({
165
+ assetIds: ['asset-1', 'asset-2'],
166
+ folder: '/images/archive', // Use null for root folder
167
+ });
168
+ ```
169
+
170
+ ### Image Transformations
171
+
172
+ Get optimized and transformed image URLs:
173
+
174
+ ```typescript
175
+ const { url } = await stack0.cdn.getTransformUrl({
176
+ assetId: 'asset-id',
177
+ options: {
178
+ width: 800,
179
+ height: 600,
180
+ fit: 'cover', // 'cover', 'contain', 'fill', 'inside', 'outside'
181
+ format: 'webp', // 'webp', 'jpeg', 'png', 'avif'
182
+ quality: 80,
183
+ },
184
+ });
185
+
186
+ // Use in <img> tag
187
+ // <img src={url} alt="Optimized image" />
188
+ ```
189
+
190
+ ### Folders
191
+
192
+ ```typescript
193
+ // Get folder tree
194
+ const tree = await stack0.cdn.getFolderTree({
195
+ projectSlug: 'my-project',
196
+ maxDepth: 3,
197
+ });
198
+
199
+ // Create folder
200
+ const folder = await stack0.cdn.createFolder({
201
+ projectSlug: 'my-project',
202
+ name: 'avatars',
203
+ parentId: 'parent-folder-id', // Optional
204
+ });
205
+
206
+ // Delete folder
207
+ await stack0.cdn.deleteFolder('folder-id');
208
+ await stack0.cdn.deleteFolder('folder-id', true); // Delete with contents
209
+ ```
210
+
211
+ ## Mail API
212
+
213
+ The Mail API is compatible with the Resend API for easy migration.
214
+
215
+ ### Send Email
216
+
217
+ ```typescript
218
+ // Simple email
219
+ await stack0.mail.send({
220
+ from: 'hello@example.com',
221
+ to: 'user@example.com',
222
+ subject: 'Hello',
223
+ html: '<p>Welcome to Stack0!</p>',
224
+ });
225
+
226
+ // With multiple recipients
227
+ await stack0.mail.send({
228
+ from: 'hello@example.com',
229
+ to: ['user1@example.com', 'user2@example.com'],
230
+ subject: 'Newsletter',
231
+ html: '<p>Monthly update</p>',
232
+ });
233
+
234
+ // With name and email
235
+ await stack0.mail.send({
236
+ from: { name: 'Acme Inc', email: 'noreply@acme.com' },
237
+ to: { name: 'John Doe', email: 'john@example.com' },
238
+ subject: 'Important Update',
239
+ html: '<p>Your account has been updated</p>',
240
+ });
241
+
242
+ // With CC and BCC
243
+ await stack0.mail.send({
244
+ from: 'hello@example.com',
245
+ to: 'user@example.com',
246
+ cc: 'manager@example.com',
247
+ bcc: ['audit@example.com', 'compliance@example.com'],
248
+ subject: 'Team Update',
249
+ html: '<p>Quarterly results</p>',
250
+ });
251
+
252
+ // With plain text alternative
253
+ await stack0.mail.send({
254
+ from: 'hello@example.com',
255
+ to: 'user@example.com',
256
+ subject: 'Hello',
257
+ html: '<h1>Welcome!</h1><p>Thanks for joining</p>',
258
+ text: 'Welcome! Thanks for joining',
259
+ });
260
+
261
+ // With tags and metadata
262
+ await stack0.mail.send({
263
+ from: 'hello@example.com',
264
+ to: 'user@example.com',
265
+ subject: 'Order Confirmation',
266
+ html: '<p>Your order #12345 is confirmed</p>',
267
+ tags: ['transactional', 'order'],
268
+ metadata: {
269
+ orderId: '12345',
270
+ customerId: 'cus_abc123',
271
+ },
272
+ });
273
+
274
+ // With template
275
+ await stack0.mail.send({
276
+ from: 'hello@example.com',
277
+ to: 'user@example.com',
278
+ subject: 'Welcome {{name}}',
279
+ templateId: 'template-uuid',
280
+ templateVariables: {
281
+ name: 'John',
282
+ activationUrl: 'https://example.com/activate?token=...',
283
+ },
284
+ });
285
+
286
+ // With attachments
287
+ await stack0.mail.send({
288
+ from: 'hello@example.com',
289
+ to: 'user@example.com',
290
+ subject: 'Invoice',
291
+ html: '<p>Your invoice is attached</p>',
292
+ attachments: [
293
+ {
294
+ filename: 'invoice.pdf',
295
+ content: 'base64-encoded-content',
296
+ contentType: 'application/pdf',
297
+ },
298
+ ],
299
+ });
300
+ ```
301
+
302
+ ### Get Email
303
+
304
+ Retrieve details about a sent email:
305
+
306
+ ```typescript
307
+ const email = await stack0.mail.get('email-id-uuid');
308
+
309
+ console.log(email.status); // 'sent', 'delivered', 'bounced', etc.
310
+ console.log(email.openedAt); // Date or null
311
+ console.log(email.deliveredAt); // Date or null
312
+ ```
313
+
314
+ ## Error Handling
315
+
316
+ ```typescript
317
+ try {
318
+ await stack0.mail.send({
319
+ from: 'hello@example.com',
320
+ to: 'user@example.com',
321
+ subject: 'Test',
322
+ html: '<p>Test</p>',
323
+ });
324
+ } catch (error) {
325
+ if (error.statusCode === 401) {
326
+ console.error('Invalid API key');
327
+ } else if (error.statusCode === 403) {
328
+ console.error('Insufficient permissions');
329
+ } else if (error.statusCode === 400) {
330
+ console.error('Validation error:', error.message);
331
+ } else {
332
+ console.error('Error:', error.message);
333
+ }
334
+ }
335
+ ```
336
+
337
+ ## TypeScript Support
338
+
339
+ The SDK is written in TypeScript and includes full type definitions:
340
+
341
+ ```typescript
342
+ import type {
343
+ // Mail types
344
+ SendEmailRequest,
345
+ SendEmailResponse,
346
+ // CDN types
347
+ Asset,
348
+ UploadUrlRequest,
349
+ ListAssetsRequest,
350
+ TransformOptions,
351
+ } from '@stack0/sdk';
352
+ ```
353
+
354
+ ## Environment Variables
355
+
356
+ For security, store your API key in environment variables:
357
+
358
+ ```typescript
359
+ const stack0 = new Stack0({
360
+ apiKey: process.env.STACK0_API_KEY!,
361
+ });
362
+ ```
363
+
364
+ ## Migration from Resend
365
+
366
+ Stack0 Mail API is designed to be compatible with Resend:
367
+
368
+ ```typescript
369
+ // Before (Resend)
370
+ import { Resend } from 'resend';
371
+ const resend = new Resend('re_...');
372
+ await resend.emails.send({ ... });
373
+
374
+ // After (Stack0)
375
+ import { Stack0 } from '@stack0/sdk';
376
+ const stack0 = new Stack0({ apiKey: 'stack0_...' });
377
+ await stack0.mail.send({ ... });
378
+ ```
379
+
380
+ ## Support
381
+
382
+ - Documentation: https://docs.stack0.com
383
+ - Issues: https://github.com/CampbellVentures/stack0/issues
384
+ - Email: support@stack0.com
385
+
386
+ ## License
387
+
388
+ MIT
@@ -0,0 +1,322 @@
1
+ import { H as HttpClientConfig } from '../http-client-Wr9lXo9_.mjs';
2
+
3
+ /**
4
+ * Type definitions for Stack0 CDN API
5
+ */
6
+ type AssetStatus = "pending" | "processing" | "ready" | "failed" | "deleted";
7
+ type AssetType = "image" | "video" | "audio" | "document" | "other";
8
+ interface Asset {
9
+ id: string;
10
+ filename: string;
11
+ originalFilename: string;
12
+ mimeType: string;
13
+ size: number;
14
+ type: AssetType;
15
+ s3Key: string;
16
+ cdnUrl: string;
17
+ width: number | null;
18
+ height: number | null;
19
+ duration: number | null;
20
+ status: AssetStatus;
21
+ folder: string | null;
22
+ tags: string[] | null;
23
+ metadata: Record<string, unknown> | null;
24
+ alt: string | null;
25
+ createdAt: Date;
26
+ updatedAt: Date | null;
27
+ }
28
+ interface UploadUrlRequest {
29
+ projectSlug: string;
30
+ filename: string;
31
+ mimeType: string;
32
+ size: number;
33
+ folder?: string;
34
+ metadata?: Record<string, unknown>;
35
+ }
36
+ interface UploadUrlResponse {
37
+ uploadUrl: string;
38
+ assetId: string;
39
+ cdnUrl: string;
40
+ expiresAt: Date;
41
+ }
42
+ interface ConfirmUploadRequest {
43
+ assetId: string;
44
+ }
45
+ interface ConfirmUploadResponse {
46
+ asset: Asset;
47
+ }
48
+ interface GetAssetRequest {
49
+ id: string;
50
+ }
51
+ interface UpdateAssetRequest {
52
+ id: string;
53
+ filename?: string;
54
+ folder?: string;
55
+ tags?: string[];
56
+ alt?: string;
57
+ metadata?: Record<string, unknown>;
58
+ }
59
+ interface DeleteAssetRequest {
60
+ id: string;
61
+ }
62
+ interface DeleteAssetsRequest {
63
+ ids: string[];
64
+ }
65
+ interface DeleteAssetsResponse {
66
+ success: boolean;
67
+ deletedCount: number;
68
+ }
69
+ interface ListAssetsRequest {
70
+ projectSlug: string;
71
+ folder?: string | null;
72
+ type?: AssetType;
73
+ status?: AssetStatus;
74
+ search?: string;
75
+ tags?: string[];
76
+ sortBy?: "createdAt" | "filename" | "size" | "type";
77
+ sortOrder?: "asc" | "desc";
78
+ limit?: number;
79
+ offset?: number;
80
+ }
81
+ interface ListAssetsResponse {
82
+ assets: Asset[];
83
+ total: number;
84
+ hasMore: boolean;
85
+ }
86
+ interface MoveAssetsRequest {
87
+ assetIds: string[];
88
+ folder: string | null;
89
+ }
90
+ interface MoveAssetsResponse {
91
+ success: boolean;
92
+ movedCount: number;
93
+ }
94
+ interface TransformOptions {
95
+ width?: number;
96
+ height?: number;
97
+ fit?: "cover" | "contain" | "fill" | "inside" | "outside";
98
+ format?: "webp" | "jpeg" | "png" | "avif";
99
+ quality?: number;
100
+ }
101
+ interface GetTransformUrlRequest {
102
+ assetId: string;
103
+ options: TransformOptions;
104
+ }
105
+ interface GetTransformUrlResponse {
106
+ url: string;
107
+ originalUrl: string;
108
+ width?: number;
109
+ height?: number;
110
+ format?: string;
111
+ }
112
+ interface FolderTreeNode {
113
+ id: string;
114
+ name: string;
115
+ path: string;
116
+ assetCount: number;
117
+ children: FolderTreeNode[];
118
+ }
119
+ interface GetFolderTreeRequest {
120
+ projectSlug: string;
121
+ maxDepth?: number;
122
+ }
123
+ interface CreateFolderRequest {
124
+ projectSlug: string;
125
+ name: string;
126
+ parentId?: string;
127
+ }
128
+ interface Folder {
129
+ id: string;
130
+ name: string;
131
+ path: string;
132
+ parentId: string | null;
133
+ assetCount: number;
134
+ totalSize: number;
135
+ createdAt: Date;
136
+ updatedAt: Date | null;
137
+ }
138
+
139
+ /**
140
+ * Stack0 CDN Client
141
+ * Upload, manage, and transform assets
142
+ */
143
+
144
+ declare class CDN {
145
+ private http;
146
+ constructor(config: HttpClientConfig);
147
+ /**
148
+ * Generate a presigned URL for uploading a file
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * const { uploadUrl, assetId } = await cdn.getUploadUrl({
153
+ * projectSlug: 'my-project',
154
+ * filename: 'image.jpg',
155
+ * mimeType: 'image/jpeg',
156
+ * size: 1024 * 1024,
157
+ * });
158
+ *
159
+ * // Upload file to the presigned URL
160
+ * await fetch(uploadUrl, {
161
+ * method: 'PUT',
162
+ * body: file,
163
+ * headers: { 'Content-Type': 'image/jpeg' },
164
+ * });
165
+ *
166
+ * // Confirm the upload
167
+ * const asset = await cdn.confirmUpload(assetId);
168
+ * ```
169
+ */
170
+ getUploadUrl(request: UploadUrlRequest): Promise<UploadUrlResponse>;
171
+ /**
172
+ * Confirm that an upload has completed
173
+ */
174
+ confirmUpload(assetId: string): Promise<Asset>;
175
+ /**
176
+ * Upload a file directly (handles presigned URL flow automatically)
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * const asset = await cdn.upload({
181
+ * projectSlug: 'my-project',
182
+ * file: fileBuffer,
183
+ * filename: 'image.jpg',
184
+ * mimeType: 'image/jpeg',
185
+ * });
186
+ * ```
187
+ */
188
+ upload(options: {
189
+ projectSlug: string;
190
+ file: Blob | Buffer | ArrayBuffer;
191
+ filename: string;
192
+ mimeType: string;
193
+ folder?: string;
194
+ metadata?: Record<string, unknown>;
195
+ }): Promise<Asset>;
196
+ /**
197
+ * Get an asset by ID
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const asset = await cdn.get('asset-id');
202
+ * console.log(asset.cdnUrl);
203
+ * ```
204
+ */
205
+ get(id: string): Promise<Asset>;
206
+ /**
207
+ * Update asset metadata
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * const asset = await cdn.update({
212
+ * id: 'asset-id',
213
+ * alt: 'A beautiful sunset',
214
+ * tags: ['nature', 'sunset'],
215
+ * });
216
+ * ```
217
+ */
218
+ update(request: UpdateAssetRequest): Promise<Asset>;
219
+ /**
220
+ * Delete an asset
221
+ *
222
+ * @example
223
+ * ```typescript
224
+ * await cdn.delete('asset-id');
225
+ * ```
226
+ */
227
+ delete(id: string): Promise<{
228
+ success: boolean;
229
+ }>;
230
+ /**
231
+ * Delete multiple assets
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * const result = await cdn.deleteMany(['asset-1', 'asset-2']);
236
+ * console.log(`Deleted ${result.deletedCount} assets`);
237
+ * ```
238
+ */
239
+ deleteMany(ids: string[]): Promise<DeleteAssetsResponse>;
240
+ /**
241
+ * List assets with filters and pagination
242
+ *
243
+ * @example
244
+ * ```typescript
245
+ * const { assets, total, hasMore } = await cdn.list({
246
+ * projectSlug: 'my-project',
247
+ * type: 'image',
248
+ * limit: 20,
249
+ * });
250
+ * ```
251
+ */
252
+ list(request: ListAssetsRequest): Promise<ListAssetsResponse>;
253
+ /**
254
+ * Move assets to a different folder
255
+ *
256
+ * @example
257
+ * ```typescript
258
+ * await cdn.move({
259
+ * assetIds: ['asset-1', 'asset-2'],
260
+ * folder: '/images/archive',
261
+ * });
262
+ * ```
263
+ */
264
+ move(request: MoveAssetsRequest): Promise<MoveAssetsResponse>;
265
+ /**
266
+ * Get a transformed image URL
267
+ *
268
+ * @example
269
+ * ```typescript
270
+ * const { url } = await cdn.getTransformUrl({
271
+ * assetId: 'asset-id',
272
+ * options: {
273
+ * width: 800,
274
+ * height: 600,
275
+ * fit: 'cover',
276
+ * format: 'webp',
277
+ * quality: 80,
278
+ * },
279
+ * });
280
+ * ```
281
+ */
282
+ getTransformUrl(request: GetTransformUrlRequest): Promise<GetTransformUrlResponse>;
283
+ /**
284
+ * Get folder tree for navigation
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * const tree = await cdn.getFolderTree({
289
+ * projectSlug: 'my-project',
290
+ * maxDepth: 3,
291
+ * });
292
+ * ```
293
+ */
294
+ getFolderTree(request: GetFolderTreeRequest): Promise<FolderTreeNode[]>;
295
+ /**
296
+ * Create a new folder
297
+ *
298
+ * @example
299
+ * ```typescript
300
+ * const folder = await cdn.createFolder({
301
+ * projectSlug: 'my-project',
302
+ * name: 'images',
303
+ * });
304
+ * ```
305
+ */
306
+ createFolder(request: CreateFolderRequest): Promise<Folder>;
307
+ /**
308
+ * Delete a folder
309
+ *
310
+ * @example
311
+ * ```typescript
312
+ * await cdn.deleteFolder('folder-id');
313
+ * ```
314
+ */
315
+ deleteFolder(id: string, deleteContents?: boolean): Promise<{
316
+ success: boolean;
317
+ }>;
318
+ private convertAssetDates;
319
+ private convertFolderDates;
320
+ }
321
+
322
+ export { type Asset, type AssetStatus, type AssetType, CDN, type ConfirmUploadRequest, type ConfirmUploadResponse, type CreateFolderRequest, type DeleteAssetRequest, type DeleteAssetsRequest, type DeleteAssetsResponse, type Folder, type FolderTreeNode, type GetAssetRequest, type GetFolderTreeRequest, type GetTransformUrlRequest, type GetTransformUrlResponse, type ListAssetsRequest, type ListAssetsResponse, type MoveAssetsRequest, type MoveAssetsResponse, type TransformOptions, type UpdateAssetRequest, type UploadUrlRequest, type UploadUrlResponse };