@hammr/cdn 1.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.
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Type definitions for CDN storage system
3
+ */
4
+ /**
5
+ * Metadata for stored artifacts
6
+ */
7
+ interface ArtifactMetadata {
8
+ /**
9
+ * Content-Type header (e.g., 'image/png', 'application/json')
10
+ */
11
+ contentType?: string;
12
+ /**
13
+ * Original filename (if provided)
14
+ */
15
+ filename?: string;
16
+ /**
17
+ * File size in bytes
18
+ */
19
+ size?: number;
20
+ /**
21
+ * Upload timestamp (Unix milliseconds)
22
+ */
23
+ uploadedAt?: number;
24
+ /**
25
+ * Custom metadata fields
26
+ */
27
+ customMetadata?: Record<string, string>;
28
+ }
29
+ /**
30
+ * Stored artifact with content and metadata
31
+ */
32
+ interface StoredArtifact {
33
+ /**
34
+ * Content-addressable hash (SHA256)
35
+ */
36
+ hash: string;
37
+ /**
38
+ * Artifact content (raw bytes)
39
+ */
40
+ body: ArrayBuffer | ReadableStream | Uint8Array;
41
+ /**
42
+ * Metadata about the artifact
43
+ */
44
+ metadata: ArtifactMetadata;
45
+ }
46
+ /**
47
+ * Result from uploading an artifact
48
+ */
49
+ interface UploadResult {
50
+ /**
51
+ * Content-addressable hash
52
+ */
53
+ hash: string;
54
+ /**
55
+ * Public URL to access the artifact
56
+ */
57
+ url: string;
58
+ /**
59
+ * Whether this was a new upload (true) or already existed (false)
60
+ */
61
+ created: boolean;
62
+ /**
63
+ * Artifact metadata
64
+ */
65
+ metadata: ArtifactMetadata;
66
+ }
67
+ /**
68
+ * Storage adapter interface
69
+ *
70
+ * Implement this to support different storage backends (R2, S3, etc.)
71
+ */
72
+ interface StorageAdapter {
73
+ /**
74
+ * Store artifact content with hash as key
75
+ *
76
+ * @param hash - Content-addressable hash (SHA256)
77
+ * @param content - Raw bytes to store
78
+ * @param metadata - Optional metadata
79
+ */
80
+ put(hash: string, content: ArrayBuffer | Uint8Array, metadata?: ArtifactMetadata): Promise<void>;
81
+ /**
82
+ * Retrieve artifact by hash
83
+ *
84
+ * @param hash - Content-addressable hash
85
+ * @returns Artifact or null if not found
86
+ */
87
+ get(hash: string): Promise<StoredArtifact | null>;
88
+ /**
89
+ * Delete artifact by hash
90
+ *
91
+ * @param hash - Content-addressable hash
92
+ * @returns true if deleted, false if not found
93
+ */
94
+ delete(hash: string): Promise<boolean>;
95
+ /**
96
+ * Check if artifact exists
97
+ *
98
+ * @param hash - Content-addressable hash
99
+ * @returns true if exists, false otherwise
100
+ */
101
+ exists(hash: string): Promise<boolean>;
102
+ /**
103
+ * List all artifact hashes (optional, for admin/debugging)
104
+ *
105
+ * @param options - List options (limit, cursor, etc.)
106
+ * @returns Array of hashes
107
+ */
108
+ list?(options?: {
109
+ limit?: number;
110
+ cursor?: string;
111
+ }): Promise<string[]>;
112
+ }
113
+ /**
114
+ * Options for CDN
115
+ */
116
+ interface CDNOptions {
117
+ /**
118
+ * Storage adapter (R2, S3, Memory, etc.)
119
+ */
120
+ storage: StorageAdapter;
121
+ /**
122
+ * Base URL for generating artifact URLs
123
+ * @example 'https://cdn.example.com'
124
+ */
125
+ baseUrl: string;
126
+ /**
127
+ * Cache-Control max-age in seconds
128
+ * @default 31536000 (1 year)
129
+ */
130
+ cacheMaxAge?: number;
131
+ /**
132
+ * Default content type if not detected
133
+ * @default 'application/octet-stream'
134
+ */
135
+ defaultContentType?: string;
136
+ /**
137
+ * Enable CORS headers
138
+ * @default true
139
+ */
140
+ cors?: boolean;
141
+ }
142
+ /**
143
+ * Content type map for common extensions
144
+ */
145
+ declare const CONTENT_TYPES: Record<string, string>;
146
+ /**
147
+ * Detect content type from filename extension
148
+ */
149
+ declare function detectContentType(filename: string, defaultType?: string): string;
150
+
151
+ /**
152
+ * CDN - Content-Addressable Storage System
153
+ *
154
+ * Features:
155
+ * - Content-addressable (SHA256 hashing via @sygnl/normalizer)
156
+ * - Storage abstraction (R2, S3, Memory, etc.)
157
+ * - Automatic content-type detection
158
+ * - Immutable artifacts (hash-based URLs)
159
+ * - Cache-friendly headers
160
+ */
161
+
162
+ declare class CDN {
163
+ private storage;
164
+ private baseUrl;
165
+ private cacheMaxAge;
166
+ private defaultContentType;
167
+ private cors;
168
+ constructor(options: CDNOptions);
169
+ /**
170
+ * Upload an artifact and get content-addressable URL
171
+ *
172
+ * @param content - Raw bytes to upload
173
+ * @param metadata - Optional metadata (filename, contentType, etc.)
174
+ * @returns Upload result with hash and URL
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * const result = await cdn.put(imageBytes, {
179
+ * filename: 'logo.png',
180
+ * contentType: 'image/png'
181
+ * });
182
+ *
183
+ * console.log(result.url); // https://cdn.example.com/a/5e88489...
184
+ * ```
185
+ */
186
+ put(content: ArrayBuffer | Uint8Array, metadata?: Partial<ArtifactMetadata>): Promise<UploadResult>;
187
+ /**
188
+ * Retrieve an artifact by hash
189
+ *
190
+ * @param hash - Content-addressable hash
191
+ * @returns Artifact or null if not found
192
+ *
193
+ * @example
194
+ * ```typescript
195
+ * const artifact = await cdn.get('5e884898...');
196
+ * if (artifact) {
197
+ * console.log(artifact.metadata.contentType);
198
+ * }
199
+ * ```
200
+ */
201
+ get(hash: string): Promise<StoredArtifact | null>;
202
+ /**
203
+ * Delete an artifact by hash
204
+ *
205
+ * @param hash - Content-addressable hash
206
+ * @returns true if deleted, false if not found
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * const deleted = await cdn.delete('5e884898...');
211
+ * ```
212
+ */
213
+ delete(hash: string): Promise<boolean>;
214
+ /**
215
+ * Check if artifact exists
216
+ *
217
+ * @param hash - Content-addressable hash
218
+ * @returns true if exists
219
+ */
220
+ exists(hash: string): Promise<boolean>;
221
+ /**
222
+ * List all artifacts (if supported by storage adapter)
223
+ *
224
+ * @param options - List options
225
+ * @returns Array of hashes
226
+ */
227
+ list(options?: {
228
+ limit?: number;
229
+ cursor?: string;
230
+ }): Promise<string[]>;
231
+ /**
232
+ * Handle HTTP request (for Cloudflare Workers, Express, etc.)
233
+ *
234
+ * Routes:
235
+ * - PUT /artifact - Upload artifact
236
+ * - GET /a/:hash(.ext) - Retrieve artifact
237
+ * - DELETE /a/:hash - Delete artifact
238
+ *
239
+ * @param request - HTTP request
240
+ * @returns HTTP response
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * export default {
245
+ * async fetch(request, env) {
246
+ * const cdn = new CDN({
247
+ * storage: new R2Storage(env.ARTIFACTS),
248
+ * baseUrl: 'https://cdn.example.com'
249
+ * });
250
+ * return cdn.handleRequest(request);
251
+ * }
252
+ * }
253
+ * ```
254
+ */
255
+ handleRequest(request: Request): Promise<Response>;
256
+ /**
257
+ * Get CORS headers if enabled
258
+ */
259
+ private getCORSHeaders;
260
+ }
261
+
262
+ /**
263
+ * In-memory storage adapter
264
+ *
265
+ * Useful for:
266
+ * - Testing
267
+ * - Development
268
+ * - Temporary caching
269
+ *
270
+ * NOT for production (data lost on restart)
271
+ */
272
+
273
+ declare class MemoryStorage implements StorageAdapter {
274
+ private storage;
275
+ put(hash: string, content: ArrayBuffer | Uint8Array, metadata?: ArtifactMetadata): Promise<void>;
276
+ get(hash: string): Promise<StoredArtifact | null>;
277
+ delete(hash: string): Promise<boolean>;
278
+ exists(hash: string): Promise<boolean>;
279
+ list(options?: {
280
+ limit?: number;
281
+ cursor?: string;
282
+ }): Promise<string[]>;
283
+ /**
284
+ * Clear all stored artifacts (useful for testing)
285
+ */
286
+ clear(): void;
287
+ /**
288
+ * Get storage size (number of artifacts)
289
+ */
290
+ size(): number;
291
+ }
292
+
293
+ /**
294
+ * Cloudflare R2 storage adapter
295
+ *
296
+ * Uses Cloudflare R2 for artifact storage.
297
+ *
298
+ * Usage:
299
+ * ```typescript
300
+ * const storage = new R2Storage(env.ARTIFACTS);
301
+ * ```
302
+ */
303
+
304
+ /**
305
+ * Minimal R2 bucket interface
306
+ * (matches Cloudflare Workers R2Bucket type)
307
+ */
308
+ interface R2Bucket {
309
+ put(key: string, value: ArrayBuffer | Uint8Array | ReadableStream, options?: {
310
+ httpMetadata?: {
311
+ contentType?: string;
312
+ };
313
+ customMetadata?: Record<string, string>;
314
+ }): Promise<any>;
315
+ get(key: string): Promise<{
316
+ body?: ReadableStream | ArrayBuffer;
317
+ httpMetadata?: {
318
+ contentType?: string;
319
+ };
320
+ customMetadata?: Record<string, string>;
321
+ size?: number;
322
+ uploaded?: Date;
323
+ } | null>;
324
+ delete(key: string): Promise<void>;
325
+ head(key: string): Promise<{
326
+ httpMetadata?: {
327
+ contentType?: string;
328
+ };
329
+ customMetadata?: Record<string, string>;
330
+ size?: number;
331
+ uploaded?: Date;
332
+ } | null>;
333
+ list?(options?: {
334
+ limit?: number;
335
+ cursor?: string;
336
+ }): Promise<{
337
+ objects: Array<{
338
+ key: string;
339
+ }>;
340
+ truncated: boolean;
341
+ cursor?: string;
342
+ }>;
343
+ }
344
+ declare class R2Storage implements StorageAdapter {
345
+ private bucket;
346
+ constructor(bucket: R2Bucket);
347
+ put(hash: string, content: ArrayBuffer | Uint8Array, metadata?: ArtifactMetadata): Promise<void>;
348
+ get(hash: string): Promise<StoredArtifact | null>;
349
+ delete(hash: string): Promise<boolean>;
350
+ exists(hash: string): Promise<boolean>;
351
+ list(options?: {
352
+ limit?: number;
353
+ cursor?: string;
354
+ }): Promise<string[]>;
355
+ }
356
+
357
+ export { type ArtifactMetadata, CDN, type CDNOptions, CONTENT_TYPES, MemoryStorage, type R2Bucket, R2Storage, type StorageAdapter, type StoredArtifact, type UploadResult, detectContentType };
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Type definitions for CDN storage system
3
+ */
4
+ /**
5
+ * Metadata for stored artifacts
6
+ */
7
+ interface ArtifactMetadata {
8
+ /**
9
+ * Content-Type header (e.g., 'image/png', 'application/json')
10
+ */
11
+ contentType?: string;
12
+ /**
13
+ * Original filename (if provided)
14
+ */
15
+ filename?: string;
16
+ /**
17
+ * File size in bytes
18
+ */
19
+ size?: number;
20
+ /**
21
+ * Upload timestamp (Unix milliseconds)
22
+ */
23
+ uploadedAt?: number;
24
+ /**
25
+ * Custom metadata fields
26
+ */
27
+ customMetadata?: Record<string, string>;
28
+ }
29
+ /**
30
+ * Stored artifact with content and metadata
31
+ */
32
+ interface StoredArtifact {
33
+ /**
34
+ * Content-addressable hash (SHA256)
35
+ */
36
+ hash: string;
37
+ /**
38
+ * Artifact content (raw bytes)
39
+ */
40
+ body: ArrayBuffer | ReadableStream | Uint8Array;
41
+ /**
42
+ * Metadata about the artifact
43
+ */
44
+ metadata: ArtifactMetadata;
45
+ }
46
+ /**
47
+ * Result from uploading an artifact
48
+ */
49
+ interface UploadResult {
50
+ /**
51
+ * Content-addressable hash
52
+ */
53
+ hash: string;
54
+ /**
55
+ * Public URL to access the artifact
56
+ */
57
+ url: string;
58
+ /**
59
+ * Whether this was a new upload (true) or already existed (false)
60
+ */
61
+ created: boolean;
62
+ /**
63
+ * Artifact metadata
64
+ */
65
+ metadata: ArtifactMetadata;
66
+ }
67
+ /**
68
+ * Storage adapter interface
69
+ *
70
+ * Implement this to support different storage backends (R2, S3, etc.)
71
+ */
72
+ interface StorageAdapter {
73
+ /**
74
+ * Store artifact content with hash as key
75
+ *
76
+ * @param hash - Content-addressable hash (SHA256)
77
+ * @param content - Raw bytes to store
78
+ * @param metadata - Optional metadata
79
+ */
80
+ put(hash: string, content: ArrayBuffer | Uint8Array, metadata?: ArtifactMetadata): Promise<void>;
81
+ /**
82
+ * Retrieve artifact by hash
83
+ *
84
+ * @param hash - Content-addressable hash
85
+ * @returns Artifact or null if not found
86
+ */
87
+ get(hash: string): Promise<StoredArtifact | null>;
88
+ /**
89
+ * Delete artifact by hash
90
+ *
91
+ * @param hash - Content-addressable hash
92
+ * @returns true if deleted, false if not found
93
+ */
94
+ delete(hash: string): Promise<boolean>;
95
+ /**
96
+ * Check if artifact exists
97
+ *
98
+ * @param hash - Content-addressable hash
99
+ * @returns true if exists, false otherwise
100
+ */
101
+ exists(hash: string): Promise<boolean>;
102
+ /**
103
+ * List all artifact hashes (optional, for admin/debugging)
104
+ *
105
+ * @param options - List options (limit, cursor, etc.)
106
+ * @returns Array of hashes
107
+ */
108
+ list?(options?: {
109
+ limit?: number;
110
+ cursor?: string;
111
+ }): Promise<string[]>;
112
+ }
113
+ /**
114
+ * Options for CDN
115
+ */
116
+ interface CDNOptions {
117
+ /**
118
+ * Storage adapter (R2, S3, Memory, etc.)
119
+ */
120
+ storage: StorageAdapter;
121
+ /**
122
+ * Base URL for generating artifact URLs
123
+ * @example 'https://cdn.example.com'
124
+ */
125
+ baseUrl: string;
126
+ /**
127
+ * Cache-Control max-age in seconds
128
+ * @default 31536000 (1 year)
129
+ */
130
+ cacheMaxAge?: number;
131
+ /**
132
+ * Default content type if not detected
133
+ * @default 'application/octet-stream'
134
+ */
135
+ defaultContentType?: string;
136
+ /**
137
+ * Enable CORS headers
138
+ * @default true
139
+ */
140
+ cors?: boolean;
141
+ }
142
+ /**
143
+ * Content type map for common extensions
144
+ */
145
+ declare const CONTENT_TYPES: Record<string, string>;
146
+ /**
147
+ * Detect content type from filename extension
148
+ */
149
+ declare function detectContentType(filename: string, defaultType?: string): string;
150
+
151
+ /**
152
+ * CDN - Content-Addressable Storage System
153
+ *
154
+ * Features:
155
+ * - Content-addressable (SHA256 hashing via @sygnl/normalizer)
156
+ * - Storage abstraction (R2, S3, Memory, etc.)
157
+ * - Automatic content-type detection
158
+ * - Immutable artifacts (hash-based URLs)
159
+ * - Cache-friendly headers
160
+ */
161
+
162
+ declare class CDN {
163
+ private storage;
164
+ private baseUrl;
165
+ private cacheMaxAge;
166
+ private defaultContentType;
167
+ private cors;
168
+ constructor(options: CDNOptions);
169
+ /**
170
+ * Upload an artifact and get content-addressable URL
171
+ *
172
+ * @param content - Raw bytes to upload
173
+ * @param metadata - Optional metadata (filename, contentType, etc.)
174
+ * @returns Upload result with hash and URL
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * const result = await cdn.put(imageBytes, {
179
+ * filename: 'logo.png',
180
+ * contentType: 'image/png'
181
+ * });
182
+ *
183
+ * console.log(result.url); // https://cdn.example.com/a/5e88489...
184
+ * ```
185
+ */
186
+ put(content: ArrayBuffer | Uint8Array, metadata?: Partial<ArtifactMetadata>): Promise<UploadResult>;
187
+ /**
188
+ * Retrieve an artifact by hash
189
+ *
190
+ * @param hash - Content-addressable hash
191
+ * @returns Artifact or null if not found
192
+ *
193
+ * @example
194
+ * ```typescript
195
+ * const artifact = await cdn.get('5e884898...');
196
+ * if (artifact) {
197
+ * console.log(artifact.metadata.contentType);
198
+ * }
199
+ * ```
200
+ */
201
+ get(hash: string): Promise<StoredArtifact | null>;
202
+ /**
203
+ * Delete an artifact by hash
204
+ *
205
+ * @param hash - Content-addressable hash
206
+ * @returns true if deleted, false if not found
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * const deleted = await cdn.delete('5e884898...');
211
+ * ```
212
+ */
213
+ delete(hash: string): Promise<boolean>;
214
+ /**
215
+ * Check if artifact exists
216
+ *
217
+ * @param hash - Content-addressable hash
218
+ * @returns true if exists
219
+ */
220
+ exists(hash: string): Promise<boolean>;
221
+ /**
222
+ * List all artifacts (if supported by storage adapter)
223
+ *
224
+ * @param options - List options
225
+ * @returns Array of hashes
226
+ */
227
+ list(options?: {
228
+ limit?: number;
229
+ cursor?: string;
230
+ }): Promise<string[]>;
231
+ /**
232
+ * Handle HTTP request (for Cloudflare Workers, Express, etc.)
233
+ *
234
+ * Routes:
235
+ * - PUT /artifact - Upload artifact
236
+ * - GET /a/:hash(.ext) - Retrieve artifact
237
+ * - DELETE /a/:hash - Delete artifact
238
+ *
239
+ * @param request - HTTP request
240
+ * @returns HTTP response
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * export default {
245
+ * async fetch(request, env) {
246
+ * const cdn = new CDN({
247
+ * storage: new R2Storage(env.ARTIFACTS),
248
+ * baseUrl: 'https://cdn.example.com'
249
+ * });
250
+ * return cdn.handleRequest(request);
251
+ * }
252
+ * }
253
+ * ```
254
+ */
255
+ handleRequest(request: Request): Promise<Response>;
256
+ /**
257
+ * Get CORS headers if enabled
258
+ */
259
+ private getCORSHeaders;
260
+ }
261
+
262
+ /**
263
+ * In-memory storage adapter
264
+ *
265
+ * Useful for:
266
+ * - Testing
267
+ * - Development
268
+ * - Temporary caching
269
+ *
270
+ * NOT for production (data lost on restart)
271
+ */
272
+
273
+ declare class MemoryStorage implements StorageAdapter {
274
+ private storage;
275
+ put(hash: string, content: ArrayBuffer | Uint8Array, metadata?: ArtifactMetadata): Promise<void>;
276
+ get(hash: string): Promise<StoredArtifact | null>;
277
+ delete(hash: string): Promise<boolean>;
278
+ exists(hash: string): Promise<boolean>;
279
+ list(options?: {
280
+ limit?: number;
281
+ cursor?: string;
282
+ }): Promise<string[]>;
283
+ /**
284
+ * Clear all stored artifacts (useful for testing)
285
+ */
286
+ clear(): void;
287
+ /**
288
+ * Get storage size (number of artifacts)
289
+ */
290
+ size(): number;
291
+ }
292
+
293
+ /**
294
+ * Cloudflare R2 storage adapter
295
+ *
296
+ * Uses Cloudflare R2 for artifact storage.
297
+ *
298
+ * Usage:
299
+ * ```typescript
300
+ * const storage = new R2Storage(env.ARTIFACTS);
301
+ * ```
302
+ */
303
+
304
+ /**
305
+ * Minimal R2 bucket interface
306
+ * (matches Cloudflare Workers R2Bucket type)
307
+ */
308
+ interface R2Bucket {
309
+ put(key: string, value: ArrayBuffer | Uint8Array | ReadableStream, options?: {
310
+ httpMetadata?: {
311
+ contentType?: string;
312
+ };
313
+ customMetadata?: Record<string, string>;
314
+ }): Promise<any>;
315
+ get(key: string): Promise<{
316
+ body?: ReadableStream | ArrayBuffer;
317
+ httpMetadata?: {
318
+ contentType?: string;
319
+ };
320
+ customMetadata?: Record<string, string>;
321
+ size?: number;
322
+ uploaded?: Date;
323
+ } | null>;
324
+ delete(key: string): Promise<void>;
325
+ head(key: string): Promise<{
326
+ httpMetadata?: {
327
+ contentType?: string;
328
+ };
329
+ customMetadata?: Record<string, string>;
330
+ size?: number;
331
+ uploaded?: Date;
332
+ } | null>;
333
+ list?(options?: {
334
+ limit?: number;
335
+ cursor?: string;
336
+ }): Promise<{
337
+ objects: Array<{
338
+ key: string;
339
+ }>;
340
+ truncated: boolean;
341
+ cursor?: string;
342
+ }>;
343
+ }
344
+ declare class R2Storage implements StorageAdapter {
345
+ private bucket;
346
+ constructor(bucket: R2Bucket);
347
+ put(hash: string, content: ArrayBuffer | Uint8Array, metadata?: ArtifactMetadata): Promise<void>;
348
+ get(hash: string): Promise<StoredArtifact | null>;
349
+ delete(hash: string): Promise<boolean>;
350
+ exists(hash: string): Promise<boolean>;
351
+ list(options?: {
352
+ limit?: number;
353
+ cursor?: string;
354
+ }): Promise<string[]>;
355
+ }
356
+
357
+ export { type ArtifactMetadata, CDN, type CDNOptions, CONTENT_TYPES, MemoryStorage, type R2Bucket, R2Storage, type StorageAdapter, type StoredArtifact, type UploadResult, detectContentType };