@shipstatic/types 0.5.4 → 0.6.1

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/dist/index.d.ts CHANGED
@@ -224,6 +224,13 @@ export declare const AccountPlan: {
224
224
  readonly TERMINATED: "terminated";
225
225
  };
226
226
  export type AccountPlanType = typeof AccountPlan[keyof typeof AccountPlan];
227
+ /**
228
+ * Account usage metrics — always available regardless of billing provider.
229
+ */
230
+ export interface AccountUsage {
231
+ /** Number of active custom domains (excludes paused) */
232
+ customDomains: number;
233
+ }
227
234
  /**
228
235
  * Core account object - used in both API responses and SDK
229
236
  * All fields are readonly to prevent accidental mutations
@@ -237,6 +244,8 @@ export interface Account {
237
244
  readonly picture: string | null;
238
245
  /** Account plan status */
239
246
  readonly plan: AccountPlanType;
247
+ /** Account usage metrics (custom domains, etc.) */
248
+ readonly usage: AccountUsage;
240
249
  /** Unix timestamp (seconds) when account was created */
241
250
  readonly created: number;
242
251
  /** Unix timestamp (seconds) when account was activated (first deployment), null if not yet activated */
@@ -345,99 +354,38 @@ export declare class ShipError extends Error {
345
354
  */
346
355
  export declare function isShipError(error: unknown): error is ShipError;
347
356
  /**
348
- * Platform configuration response from API
349
- *
350
- * Contains ONLY dynamic, runtime-specific values (plan-based limits).
351
- * Static constants (MIME types, validation rules) live as exported constants.
357
+ * Dynamic platform configuration returned by the /config endpoint.
358
+ * Contains plan-based limits that vary by account.
352
359
  */
353
360
  export interface ConfigResponse {
354
- /** Maximum individual file size in bytes */
355
361
  maxFileSize: number;
356
- /** Maximum number of files per deployment */
357
362
  maxFilesCount: number;
358
- /** Maximum total deployment size in bytes */
359
363
  maxTotalSize: number;
360
364
  }
361
365
  /**
362
- * Allowed MIME types for static web hosting.
363
- *
364
- * This is a static platform constant, not per-user configuration.
365
- * Safe to share across frontend/backend due to atomic deploys.
366
- *
367
- * Validation rules:
368
- * - Exact match: 'application/json' allows only 'application/json'
369
- * - Prefix match: 'image/' allows all image types (png, jpeg, webp, etc.)
370
- *
371
- * Coverage: 100% of browser-renderable web content
372
- * - Core web (HTML, CSS, JS, WASM)
373
- * - Media (images, audio, video, fonts)
374
- * - Documents (PDF, Markdown, data formats)
375
- * - Modern web (PWA, 3D, structured data)
376
- *
377
- * ============================================================================
378
- * INTENTIONALLY EXCLUDED (Security & Platform Integrity)
379
- * ============================================================================
366
+ * Blocked file extensions files that cannot be uploaded.
380
367
  *
381
- * We are a WEB HOSTING platform, not a file distribution service.
382
- * GitHub Pages-style parity for renderable content, more restrictive for downloads.
368
+ * We accept any file type by default and derive Content-Type from the
369
+ * extension at serve time (via mime-db in the API worker). Unknown extensions
370
+ * are served as `application/octet-stream` with `X-Content-Type-Options: nosniff`.
383
371
  *
384
- * 1. EXECUTABLES (Malware Distribution)
385
- * .exe, .msi, .dmg, .deb, .rpm, .app, .apk, .jar
386
- * → Reason: Direct malware delivery vector
387
- * → Alternative: Use GitHub Releases or dedicated software distribution CDN
388
- *
389
- * 2. ARCHIVES (Piracy & Abuse)
390
- * → .zip, .rar, .tar, .gz, .7z, .bz2
391
- * → Reason: File sharing abuse, can contain executables, no web rendering
392
- * → Alternative: Use file hosting service (Dropbox, Google Drive) or GitHub Releases
393
- *
394
- * 3. SERVER-SIDE SCRIPTS (Credential Leakage)
395
- * → .php, .asp, .jsp, .cgi
396
- * → Reason: Source code exposure (database passwords, API keys, secrets)
397
- * → Alternative: Static hosting only - use serverless functions for backends
398
- *
399
- * 4. SHELL SCRIPTS (OS Execution)
400
- * → .sh, .bash, .bat, .cmd, .ps1, .vbs
401
- * → Reason: Execute on user's OS outside browser sandbox, social engineering risk
402
- * → Alternative: Embed code examples in HTML <pre><code> or link to GitHub repo
403
- *
404
- * 5. PROGRAMMING LANGUAGE SOURCE (Platform Scope)
405
- * → .py, .rb, .pl, .java, .c, .cpp, .cs, .go, .rs
406
- * → Reason: Not web-renderable, better served by GitHub/GitLab/Bitbucket
407
- * → Alternative: Use GitHub for code hosting, link to repository
408
- *
409
- * 6. OFFICE DOCUMENTS (Macro Malware)
410
- * → .doc, .docx, .xls, .xlsx, .ppt, .pptx
411
- * → Reason: Can contain VBA macros, active exploits in the wild
412
- * → Alternative: Use PDF for documents (fully supported)
413
- *
414
- * 7. GENERIC BINARIES (Unvalidatable)
415
- * → application/octet-stream
416
- * → Reason: Too broad - allows any binary format, cannot moderate effectively
417
- * → Alternative: Use specific MIME types for known formats
418
- *
419
- * ============================================================================
420
- * Security Model:
421
- * - Browser sandbox (JS/WASM execute safely in controlled environment)
422
- * - AI content moderation (scans text/image content for abuse)
423
- * - No server-side execution (static files only)
424
- * - Explicit allowlist (only approved formats, reject unknown)
425
- * ============================================================================
372
+ * The blocklist targets file types that pose direct security risks when hosted:
373
+ * executables, disk images, malware vectors, dangerous scripts, and shortcuts.
426
374
  */
427
- export declare const ALLOWED_MIME_TYPES: readonly ["text/html", "text/css", "text/plain", "text/markdown", "text/xml", "text/csv", "text/tab-separated-values", "text/yaml", "text/vcard", "text/mdx", "text/x-mdx", "text/vtt", "text/srt", "text/calendar", "text/javascript", "text/typescript", "application/x-typescript", "text/tsx", "text/jsx", "text/x-scss", "text/x-sass", "text/less", "text/x-less", "text/stylus", "text/x-vue", "text/x-svelte", "text/x-sql", "text/x-diff", "text/x-patch", "text/x-protobuf", "text/x-ini", "text/x-tex", "text/x-latex", "text/x-bibtex", "text/x-r-markdown", "image/", "audio/", "video/", "font/", "application/javascript", "application/ecmascript", "application/x-javascript", "application/wasm", "application/json", "application/ld+json", "application/geo+json", "application/manifest+json", "application/x-ipynb+json", "application/x-ndjson", "application/ndjson", "text/x-ndjson", "application/jsonl", "text/jsonl", "application/json5", "text/json5", "application/schema+json", "application/source-map", "application/xml", "application/xhtml+xml", "application/rss+xml", "application/atom+xml", "application/feed+json", "application/vnd.google-earth.kml+xml", "application/yaml", "application/toml", "application/pdf", "application/x-subrip", "application/sql", "application/graphql", "application/graphql+json", "application/x-protobuf", "application/x-ini", "application/x-tex", "application/x-bibtex", "model/gltf+json", "model/gltf-binary", "application/mp4", "application/font-woff", "application/font-woff2", "application/x-font-woff", "application/x-woff", "application/vnd.ms-fontobject", "application/x-font-ttf", "application/x-font-truetype", "application/x-font-otf", "application/x-font-opentype"];
375
+ export declare const BLOCKED_EXTENSIONS: ReadonlySet<string>;
428
376
  /**
429
- * Check if a MIME type is allowed for upload.
430
- *
431
- * Supports both exact matches and prefix matches:
432
- * - 'application/json' matches 'application/json' exactly
433
- * - 'text/' matches 'text/plain', 'text/html', etc.
377
+ * Check if a filename has a blocked extension.
378
+ * Extracts the extension from the filename and checks against the blocklist.
379
+ * Case-insensitive. Returns false for files without extensions.
434
380
  *
435
381
  * @example
436
- * isAllowedMimeType('text/plain') // true (prefix match)
437
- * isAllowedMimeType('application/json') // true (exact match)
438
- * isAllowedMimeType('application/wasm') // false (not allowed)
382
+ * isBlockedExtension('virus.exe') // true
383
+ * isBlockedExtension('app.dmg') // true
384
+ * isBlockedExtension('style.css') // false
385
+ * isBlockedExtension('data.custom') // false
386
+ * isBlockedExtension('README') // false
439
387
  */
440
- export declare function isAllowedMimeType(mimeType: string): boolean;
388
+ export declare function isBlockedExtension(filename: string): boolean;
441
389
  /**
442
390
  * Simple ping response for health checks
443
391
  */
@@ -651,8 +599,6 @@ export interface BillingStatus {
651
599
  billing: string | null;
652
600
  /** Number of billing units (1 unit = 1 custom domain), null if no billing */
653
601
  units: number | null;
654
- /** Number of custom domains currently in use, null if no billing */
655
- usage: number | null;
656
602
  /** Billing status from Creem (active, trialing, canceled, etc.), null if no billing */
657
603
  status: string | null;
658
604
  /** Link to Creem customer portal for billing management, null if unavailable */
@@ -788,7 +734,6 @@ export interface ValidationIssue {
788
734
  export interface ValidatableFile {
789
735
  name: string;
790
736
  size: number;
791
- type: string;
792
737
  status?: FileValidationStatusType;
793
738
  statusMessage?: string;
794
739
  }
package/dist/index.js CHANGED
@@ -199,215 +199,55 @@ export function isShipError(error) {
199
199
  error.name === 'ShipError' &&
200
200
  'status' in error);
201
201
  }
202
+ // =============================================================================
203
+ // EXTENSION BLOCKLIST
204
+ // =============================================================================
202
205
  /**
203
- * Allowed MIME types for static web hosting.
204
- *
205
- * This is a static platform constant, not per-user configuration.
206
- * Safe to share across frontend/backend due to atomic deploys.
207
- *
208
- * Validation rules:
209
- * - Exact match: 'application/json' allows only 'application/json'
210
- * - Prefix match: 'image/' allows all image types (png, jpeg, webp, etc.)
211
- *
212
- * Coverage: 100% of browser-renderable web content
213
- * - Core web (HTML, CSS, JS, WASM)
214
- * - Media (images, audio, video, fonts)
215
- * - Documents (PDF, Markdown, data formats)
216
- * - Modern web (PWA, 3D, structured data)
217
- *
218
- * ============================================================================
219
- * INTENTIONALLY EXCLUDED (Security & Platform Integrity)
220
- * ============================================================================
221
- *
222
- * We are a WEB HOSTING platform, not a file distribution service.
223
- * GitHub Pages-style parity for renderable content, more restrictive for downloads.
206
+ * Blocked file extensions files that cannot be uploaded.
224
207
  *
225
- * 1. EXECUTABLES (Malware Distribution)
226
- * .exe, .msi, .dmg, .deb, .rpm, .app, .apk, .jar
227
- * Reason: Direct malware delivery vector
228
- * → Alternative: Use GitHub Releases or dedicated software distribution CDN
208
+ * We accept any file type by default and derive Content-Type from the
209
+ * extension at serve time (via mime-db in the API worker). Unknown extensions
210
+ * are served as `application/octet-stream` with `X-Content-Type-Options: nosniff`.
229
211
  *
230
- * 2. ARCHIVES (Piracy & Abuse)
231
- * .zip, .rar, .tar, .gz, .7z, .bz2
232
- * → Reason: File sharing abuse, can contain executables, no web rendering
233
- * → Alternative: Use file hosting service (Dropbox, Google Drive) or GitHub Releases
234
- *
235
- * 3. SERVER-SIDE SCRIPTS (Credential Leakage)
236
- * → .php, .asp, .jsp, .cgi
237
- * → Reason: Source code exposure (database passwords, API keys, secrets)
238
- * → Alternative: Static hosting only - use serverless functions for backends
239
- *
240
- * 4. SHELL SCRIPTS (OS Execution)
241
- * → .sh, .bash, .bat, .cmd, .ps1, .vbs
242
- * → Reason: Execute on user's OS outside browser sandbox, social engineering risk
243
- * → Alternative: Embed code examples in HTML <pre><code> or link to GitHub repo
244
- *
245
- * 5. PROGRAMMING LANGUAGE SOURCE (Platform Scope)
246
- * → .py, .rb, .pl, .java, .c, .cpp, .cs, .go, .rs
247
- * → Reason: Not web-renderable, better served by GitHub/GitLab/Bitbucket
248
- * → Alternative: Use GitHub for code hosting, link to repository
249
- *
250
- * 6. OFFICE DOCUMENTS (Macro Malware)
251
- * → .doc, .docx, .xls, .xlsx, .ppt, .pptx
252
- * → Reason: Can contain VBA macros, active exploits in the wild
253
- * → Alternative: Use PDF for documents (fully supported)
254
- *
255
- * 7. GENERIC BINARIES (Unvalidatable)
256
- * → application/octet-stream
257
- * → Reason: Too broad - allows any binary format, cannot moderate effectively
258
- * → Alternative: Use specific MIME types for known formats
259
- *
260
- * ============================================================================
261
- * Security Model:
262
- * - Browser sandbox (JS/WASM execute safely in controlled environment)
263
- * - AI content moderation (scans text/image content for abuse)
264
- * - No server-side execution (static files only)
265
- * - Explicit allowlist (only approved formats, reject unknown)
266
- * ============================================================================
212
+ * The blocklist targets file types that pose direct security risks when hosted:
213
+ * executables, disk images, malware vectors, dangerous scripts, and shortcuts.
267
214
  */
268
- export const ALLOWED_MIME_TYPES = [
269
- // =========================================================================
270
- // TEXT CONTENT (explicit list - no prefix matching for security)
271
- // =========================================================================
272
- // Core web documents
273
- 'text/html', // HTML pages
274
- 'text/css', // Stylesheets
275
- 'text/plain', // Plain text (robots.txt, .well-known/*, LICENSE, README.txt)
276
- 'text/markdown', // Markdown files (.md)
277
- 'text/xml', // XML files
278
- // Data formats
279
- 'text/csv', // CSV data files
280
- 'text/tab-separated-values', // TSV data files
281
- 'text/yaml', // YAML config files
282
- 'text/vcard', // VCard contact files (.vcf)
283
- // Modern documentation formats
284
- 'text/mdx', // MDX (Markdown with JSX) - Next.js, Docusaurus, Nextra
285
- 'text/x-mdx', // MDX (alternative MIME type)
286
- // Web-specific formats
287
- 'text/vtt', // WebVTT video subtitles/captions (accessibility)
288
- 'text/srt', // SRT subtitles (SubRip format, legacy video captions)
289
- 'text/calendar', // iCalendar (.ics) event files
290
- // JavaScript (legacy MIME type, still widely used by ~50% of servers)
291
- 'text/javascript',
292
- // Modern web development formats (uncompiled source)
293
- 'text/typescript', // TypeScript source (.ts)
294
- 'application/x-typescript', // TypeScript (alternative MIME type, .d.ts declarations)
295
- 'text/tsx', // TypeScript JSX (.tsx)
296
- 'text/jsx', // React JSX (.jsx)
297
- 'text/x-scss', // SCSS preprocessor
298
- 'text/x-sass', // Sass preprocessor
299
- 'text/less', // Less preprocessor
300
- 'text/x-less', // Less preprocessor (alternative)
301
- 'text/stylus', // Stylus preprocessor
302
- 'text/x-vue', // Vue single-file components (.vue)
303
- 'text/x-svelte', // Svelte components (.svelte)
304
- // Developer documentation formats
305
- 'text/x-sql', // SQL files (database schemas, migrations)
306
- 'text/x-diff', // Diff files (code comparisons, patches)
307
- 'text/x-patch', // Patch files (version upgrades, migrations)
308
- 'text/x-protobuf', // Protocol Buffers text format (gRPC schemas)
309
- 'text/x-ini', // INI configuration files
310
- // Academic/research formats
311
- 'text/x-tex', // LaTeX documents
312
- 'text/x-latex', // LaTeX documents (alternative)
313
- 'text/x-bibtex', // BibTeX citations
314
- 'text/x-r-markdown', // R Markdown (statistical documentation)
315
- // =========================================================================
316
- // MEDIA (prefix matching - covers all common subtypes)
317
- // =========================================================================
318
- // Images: PNG, JPEG, GIF, SVG, WebP, AVIF, HEIC, BMP, TIFF, ICO, etc.
319
- 'image/',
320
- // Audio: MP3, OGG, WAV, WebM, AAC, FLAC, Opus, etc.
321
- 'audio/',
322
- // Video: MP4, WebM, OGG, QuickTime, etc.
323
- 'video/',
324
- // Modern fonts: WOFF2, WOFF, TTF, OTF
325
- 'font/',
326
- // =========================================================================
327
- // CORE WEB APPLICATION TYPES
328
- // =========================================================================
329
- // JavaScript (multiple MIME types for compatibility)
330
- 'application/javascript', // Modern standard (RFC 9239)
331
- 'application/ecmascript', // ECMAScript (legacy but still used)
332
- 'application/x-javascript', // Legacy variant (old CDNs, Apache configs)
333
- // WebAssembly (modern web apps, games, compute-heavy workloads)
334
- 'application/wasm',
335
- // JSON and structured data
336
- 'application/json',
337
- 'application/ld+json', // JSON-LD for structured data / SEO (Schema.org, Open Graph)
338
- 'application/geo+json', // GeoJSON for mapping (Leaflet, Mapbox)
339
- 'application/manifest+json', // PWA web app manifests
340
- 'application/x-ipynb+json', // Jupyter Notebooks (data science, ML tutorials)
341
- // JSON variants (AI/ML, streaming data, configs)
342
- 'application/x-ndjson', // Newline-Delimited JSON (training datasets, logs)
343
- 'application/ndjson', // NDJSON (alternative MIME type)
344
- 'text/x-ndjson', // NDJSON (text variant)
345
- 'application/jsonl', // JSON Lines (Hugging Face, OpenAI fine-tuning)
346
- 'text/jsonl', // JSON Lines (text variant)
347
- 'application/json5', // JSON5 (JSON with comments, trailing commas)
348
- 'text/json5', // JSON5 (text variant)
349
- 'application/schema+json', // JSON Schema (API specs, model definitions)
350
- // Development tools
351
- 'application/source-map', // Source maps (.js.map, .css.map) for debugging
352
- // XML and feeds
353
- 'application/xml',
354
- 'application/xhtml+xml', // XHTML - XML-compliant HTML (legacy sites)
355
- 'application/rss+xml', // RSS feeds (blogs, podcasts)
356
- 'application/atom+xml', // Atom feeds
357
- 'application/feed+json', // JSON Feed (modern RSS alternative)
358
- 'application/vnd.google-earth.kml+xml', // KML for mapping (Google Earth, GIS)
359
- // Configuration formats
360
- 'application/yaml', // YAML configs (static site generators)
361
- 'application/toml', // TOML configs (Cargo, Netlify, Rust projects)
362
- // Documents
363
- 'application/pdf', // PDF documents
364
- // Media metadata
365
- 'application/x-subrip', // SRT subtitles (SubRip format)
366
- // Developer tools and schemas
367
- 'application/sql', // SQL files (database schemas, queries)
368
- 'application/graphql', // GraphQL schemas (API documentation)
369
- 'application/graphql+json', // GraphQL with JSON encoding
370
- 'application/x-protobuf', // Protocol Buffers binary (gRPC)
371
- 'application/x-ini', // INI configuration files
372
- // Academic formats
373
- 'application/x-tex', // LaTeX documents
374
- 'application/x-bibtex', // BibTeX citations
375
- // =========================================================================
376
- // 3D FORMATS (industry standard only)
377
- // =========================================================================
378
- // glTF - Khronos standard for 3D web content
379
- 'model/gltf+json', // glTF JSON format
380
- 'model/gltf-binary', // GLB binary format
381
- // =========================================================================
382
- // LEGACY COMPATIBILITY
383
- // =========================================================================
384
- // Video (some tools detect MP4 as application/mp4)
385
- 'application/mp4',
386
- // Legacy font MIME types (Bootstrap, Font Awesome, IE compatibility)
387
- 'application/font-woff',
388
- 'application/font-woff2',
389
- 'application/x-font-woff',
390
- 'application/x-woff',
391
- 'application/vnd.ms-fontobject', // EOT files (Internet Explorer)
392
- 'application/x-font-ttf',
393
- 'application/x-font-truetype',
394
- 'application/x-font-otf',
395
- 'application/x-font-opentype',
396
- ];
215
+ export const BLOCKED_EXTENSIONS = new Set([
216
+ // Executables
217
+ 'exe', 'msi', 'dll', 'scr', 'bat', 'cmd', 'com', 'pif', 'app', 'deb', 'rpm',
218
+ // Installers
219
+ 'pkg', 'mpkg',
220
+ // Disk images
221
+ 'dmg', 'iso', 'img',
222
+ // Malware vectors
223
+ 'cab', 'cpl', 'chm',
224
+ // Dangerous scripts
225
+ 'ps1', 'vbs', 'vbe', 'ws', 'wsf', 'wsc', 'wsh', 'reg',
226
+ // Java
227
+ 'jar', 'jnlp',
228
+ // Mobile/browser packages
229
+ 'apk', 'crx',
230
+ // Shortcut/link
231
+ 'lnk', 'inf', 'hta',
232
+ ]);
397
233
  /**
398
- * Check if a MIME type is allowed for upload.
399
- *
400
- * Supports both exact matches and prefix matches:
401
- * - 'application/json' matches 'application/json' exactly
402
- * - 'text/' matches 'text/plain', 'text/html', etc.
234
+ * Check if a filename has a blocked extension.
235
+ * Extracts the extension from the filename and checks against the blocklist.
236
+ * Case-insensitive. Returns false for files without extensions.
403
237
  *
404
238
  * @example
405
- * isAllowedMimeType('text/plain') // true (prefix match)
406
- * isAllowedMimeType('application/json') // true (exact match)
407
- * isAllowedMimeType('application/wasm') // false (not allowed)
239
+ * isBlockedExtension('virus.exe') // true
240
+ * isBlockedExtension('app.dmg') // true
241
+ * isBlockedExtension('style.css') // false
242
+ * isBlockedExtension('data.custom') // false
243
+ * isBlockedExtension('README') // false
408
244
  */
409
- export function isAllowedMimeType(mimeType) {
410
- return ALLOWED_MIME_TYPES.some(allowed => mimeType === allowed || mimeType.startsWith(allowed));
245
+ export function isBlockedExtension(filename) {
246
+ const dotIndex = filename.lastIndexOf('.');
247
+ if (dotIndex === -1 || dotIndex === filename.length - 1)
248
+ return false;
249
+ const ext = filename.slice(dotIndex + 1).toLowerCase();
250
+ return BLOCKED_EXTENSIONS.has(ext);
411
251
  }
412
252
  // API Key Configuration
413
253
  export const API_KEY_PREFIX = 'ship-';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/types",
3
- "version": "0.5.4",
3
+ "version": "0.6.1",
4
4
  "description": "Shared types for Shipstatic platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -260,6 +260,14 @@ export const AccountPlan = {
260
260
 
261
261
  export type AccountPlanType = typeof AccountPlan[keyof typeof AccountPlan];
262
262
 
263
+ /**
264
+ * Account usage metrics — always available regardless of billing provider.
265
+ */
266
+ export interface AccountUsage {
267
+ /** Number of active custom domains (excludes paused) */
268
+ customDomains: number;
269
+ }
270
+
263
271
  /**
264
272
  * Core account object - used in both API responses and SDK
265
273
  * All fields are readonly to prevent accidental mutations
@@ -273,6 +281,8 @@ export interface Account {
273
281
  readonly picture: string | null;
274
282
  /** Account plan status */
275
283
  readonly plan: AccountPlanType;
284
+ /** Account usage metrics (custom domains, etc.) */
285
+ readonly usage: AccountUsage;
276
286
  /** Unix timestamp (seconds) when account was created */
277
287
  readonly created: number;
278
288
  /** Unix timestamp (seconds) when account was activated (first deployment), null if not yet activated */
@@ -498,266 +508,69 @@ export function isShipError(error: unknown): error is ShipError {
498
508
  // =============================================================================
499
509
 
500
510
  /**
501
- * Platform configuration response from API
502
- *
503
- * Contains ONLY dynamic, runtime-specific values (plan-based limits).
504
- * Static constants (MIME types, validation rules) live as exported constants.
511
+ * Dynamic platform configuration returned by the /config endpoint.
512
+ * Contains plan-based limits that vary by account.
505
513
  */
506
514
  export interface ConfigResponse {
507
- /** Maximum individual file size in bytes */
508
515
  maxFileSize: number;
509
- /** Maximum number of files per deployment */
510
516
  maxFilesCount: number;
511
- /** Maximum total deployment size in bytes */
512
517
  maxTotalSize: number;
513
518
  }
514
519
 
520
+ // =============================================================================
521
+ // EXTENSION BLOCKLIST
522
+ // =============================================================================
523
+
515
524
  /**
516
- * Allowed MIME types for static web hosting.
517
- *
518
- * This is a static platform constant, not per-user configuration.
519
- * Safe to share across frontend/backend due to atomic deploys.
520
- *
521
- * Validation rules:
522
- * - Exact match: 'application/json' allows only 'application/json'
523
- * - Prefix match: 'image/' allows all image types (png, jpeg, webp, etc.)
524
- *
525
- * Coverage: 100% of browser-renderable web content
526
- * - Core web (HTML, CSS, JS, WASM)
527
- * - Media (images, audio, video, fonts)
528
- * - Documents (PDF, Markdown, data formats)
529
- * - Modern web (PWA, 3D, structured data)
525
+ * Blocked file extensions files that cannot be uploaded.
530
526
  *
531
- * ============================================================================
532
- * INTENTIONALLY EXCLUDED (Security & Platform Integrity)
533
- * ============================================================================
527
+ * We accept any file type by default and derive Content-Type from the
528
+ * extension at serve time (via mime-db in the API worker). Unknown extensions
529
+ * are served as `application/octet-stream` with `X-Content-Type-Options: nosniff`.
534
530
  *
535
- * We are a WEB HOSTING platform, not a file distribution service.
536
- * GitHub Pages-style parity for renderable content, more restrictive for downloads.
537
- *
538
- * 1. EXECUTABLES (Malware Distribution)
539
- * → .exe, .msi, .dmg, .deb, .rpm, .app, .apk, .jar
540
- * → Reason: Direct malware delivery vector
541
- * → Alternative: Use GitHub Releases or dedicated software distribution CDN
542
- *
543
- * 2. ARCHIVES (Piracy & Abuse)
544
- * → .zip, .rar, .tar, .gz, .7z, .bz2
545
- * → Reason: File sharing abuse, can contain executables, no web rendering
546
- * → Alternative: Use file hosting service (Dropbox, Google Drive) or GitHub Releases
547
- *
548
- * 3. SERVER-SIDE SCRIPTS (Credential Leakage)
549
- * → .php, .asp, .jsp, .cgi
550
- * → Reason: Source code exposure (database passwords, API keys, secrets)
551
- * → Alternative: Static hosting only - use serverless functions for backends
552
- *
553
- * 4. SHELL SCRIPTS (OS Execution)
554
- * → .sh, .bash, .bat, .cmd, .ps1, .vbs
555
- * → Reason: Execute on user's OS outside browser sandbox, social engineering risk
556
- * → Alternative: Embed code examples in HTML <pre><code> or link to GitHub repo
557
- *
558
- * 5. PROGRAMMING LANGUAGE SOURCE (Platform Scope)
559
- * .py, .rb, .pl, .java, .c, .cpp, .cs, .go, .rs
560
- * Reason: Not web-renderable, better served by GitHub/GitLab/Bitbucket
561
- * → Alternative: Use GitHub for code hosting, link to repository
562
- *
563
- * 6. OFFICE DOCUMENTS (Macro Malware)
564
- * → .doc, .docx, .xls, .xlsx, .ppt, .pptx
565
- * → Reason: Can contain VBA macros, active exploits in the wild
566
- * → Alternative: Use PDF for documents (fully supported)
567
- *
568
- * 7. GENERIC BINARIES (Unvalidatable)
569
- * → application/octet-stream
570
- * → Reason: Too broad - allows any binary format, cannot moderate effectively
571
- * → Alternative: Use specific MIME types for known formats
572
- *
573
- * ============================================================================
574
- * Security Model:
575
- * - Browser sandbox (JS/WASM execute safely in controlled environment)
576
- * - AI content moderation (scans text/image content for abuse)
577
- * - No server-side execution (static files only)
578
- * - Explicit allowlist (only approved formats, reject unknown)
579
- * ============================================================================
580
- */
581
- export const ALLOWED_MIME_TYPES = [
582
- // =========================================================================
583
- // TEXT CONTENT (explicit list - no prefix matching for security)
584
- // =========================================================================
585
-
586
- // Core web documents
587
- 'text/html', // HTML pages
588
- 'text/css', // Stylesheets
589
- 'text/plain', // Plain text (robots.txt, .well-known/*, LICENSE, README.txt)
590
- 'text/markdown', // Markdown files (.md)
591
- 'text/xml', // XML files
592
-
593
- // Data formats
594
- 'text/csv', // CSV data files
595
- 'text/tab-separated-values', // TSV data files
596
- 'text/yaml', // YAML config files
597
- 'text/vcard', // VCard contact files (.vcf)
598
-
599
- // Modern documentation formats
600
- 'text/mdx', // MDX (Markdown with JSX) - Next.js, Docusaurus, Nextra
601
- 'text/x-mdx', // MDX (alternative MIME type)
602
-
603
- // Web-specific formats
604
- 'text/vtt', // WebVTT video subtitles/captions (accessibility)
605
- 'text/srt', // SRT subtitles (SubRip format, legacy video captions)
606
- 'text/calendar', // iCalendar (.ics) event files
607
-
608
- // JavaScript (legacy MIME type, still widely used by ~50% of servers)
609
- 'text/javascript',
610
-
611
- // Modern web development formats (uncompiled source)
612
- 'text/typescript', // TypeScript source (.ts)
613
- 'application/x-typescript', // TypeScript (alternative MIME type, .d.ts declarations)
614
- 'text/tsx', // TypeScript JSX (.tsx)
615
- 'text/jsx', // React JSX (.jsx)
616
- 'text/x-scss', // SCSS preprocessor
617
- 'text/x-sass', // Sass preprocessor
618
- 'text/less', // Less preprocessor
619
- 'text/x-less', // Less preprocessor (alternative)
620
- 'text/stylus', // Stylus preprocessor
621
- 'text/x-vue', // Vue single-file components (.vue)
622
- 'text/x-svelte', // Svelte components (.svelte)
623
-
624
- // Developer documentation formats
625
- 'text/x-sql', // SQL files (database schemas, migrations)
626
- 'text/x-diff', // Diff files (code comparisons, patches)
627
- 'text/x-patch', // Patch files (version upgrades, migrations)
628
- 'text/x-protobuf', // Protocol Buffers text format (gRPC schemas)
629
- 'text/x-ini', // INI configuration files
630
-
631
- // Academic/research formats
632
- 'text/x-tex', // LaTeX documents
633
- 'text/x-latex', // LaTeX documents (alternative)
634
- 'text/x-bibtex', // BibTeX citations
635
- 'text/x-r-markdown', // R Markdown (statistical documentation)
636
-
637
- // =========================================================================
638
- // MEDIA (prefix matching - covers all common subtypes)
639
- // =========================================================================
640
-
641
- // Images: PNG, JPEG, GIF, SVG, WebP, AVIF, HEIC, BMP, TIFF, ICO, etc.
642
- 'image/',
643
-
644
- // Audio: MP3, OGG, WAV, WebM, AAC, FLAC, Opus, etc.
645
- 'audio/',
646
-
647
- // Video: MP4, WebM, OGG, QuickTime, etc.
648
- 'video/',
649
-
650
- // Modern fonts: WOFF2, WOFF, TTF, OTF
651
- 'font/',
652
-
653
- // =========================================================================
654
- // CORE WEB APPLICATION TYPES
655
- // =========================================================================
656
-
657
- // JavaScript (multiple MIME types for compatibility)
658
- 'application/javascript', // Modern standard (RFC 9239)
659
- 'application/ecmascript', // ECMAScript (legacy but still used)
660
- 'application/x-javascript', // Legacy variant (old CDNs, Apache configs)
661
-
662
- // WebAssembly (modern web apps, games, compute-heavy workloads)
663
- 'application/wasm',
664
-
665
- // JSON and structured data
666
- 'application/json',
667
- 'application/ld+json', // JSON-LD for structured data / SEO (Schema.org, Open Graph)
668
- 'application/geo+json', // GeoJSON for mapping (Leaflet, Mapbox)
669
- 'application/manifest+json', // PWA web app manifests
670
- 'application/x-ipynb+json', // Jupyter Notebooks (data science, ML tutorials)
671
-
672
- // JSON variants (AI/ML, streaming data, configs)
673
- 'application/x-ndjson', // Newline-Delimited JSON (training datasets, logs)
674
- 'application/ndjson', // NDJSON (alternative MIME type)
675
- 'text/x-ndjson', // NDJSON (text variant)
676
- 'application/jsonl', // JSON Lines (Hugging Face, OpenAI fine-tuning)
677
- 'text/jsonl', // JSON Lines (text variant)
678
- 'application/json5', // JSON5 (JSON with comments, trailing commas)
679
- 'text/json5', // JSON5 (text variant)
680
- 'application/schema+json', // JSON Schema (API specs, model definitions)
681
-
682
- // Development tools
683
- 'application/source-map', // Source maps (.js.map, .css.map) for debugging
684
-
685
- // XML and feeds
686
- 'application/xml',
687
- 'application/xhtml+xml', // XHTML - XML-compliant HTML (legacy sites)
688
- 'application/rss+xml', // RSS feeds (blogs, podcasts)
689
- 'application/atom+xml', // Atom feeds
690
- 'application/feed+json', // JSON Feed (modern RSS alternative)
691
- 'application/vnd.google-earth.kml+xml', // KML for mapping (Google Earth, GIS)
692
-
693
- // Configuration formats
694
- 'application/yaml', // YAML configs (static site generators)
695
- 'application/toml', // TOML configs (Cargo, Netlify, Rust projects)
696
-
697
- // Documents
698
- 'application/pdf', // PDF documents
699
-
700
- // Media metadata
701
- 'application/x-subrip', // SRT subtitles (SubRip format)
702
-
703
- // Developer tools and schemas
704
- 'application/sql', // SQL files (database schemas, queries)
705
- 'application/graphql', // GraphQL schemas (API documentation)
706
- 'application/graphql+json', // GraphQL with JSON encoding
707
- 'application/x-protobuf', // Protocol Buffers binary (gRPC)
708
- 'application/x-ini', // INI configuration files
709
-
710
- // Academic formats
711
- 'application/x-tex', // LaTeX documents
712
- 'application/x-bibtex', // BibTeX citations
713
-
714
- // =========================================================================
715
- // 3D FORMATS (industry standard only)
716
- // =========================================================================
717
-
718
- // glTF - Khronos standard for 3D web content
719
- 'model/gltf+json', // glTF JSON format
720
- 'model/gltf-binary', // GLB binary format
721
-
722
- // =========================================================================
723
- // LEGACY COMPATIBILITY
724
- // =========================================================================
725
-
726
- // Video (some tools detect MP4 as application/mp4)
727
- 'application/mp4',
728
-
729
- // Legacy font MIME types (Bootstrap, Font Awesome, IE compatibility)
730
- 'application/font-woff',
731
- 'application/font-woff2',
732
- 'application/x-font-woff',
733
- 'application/x-woff',
734
- 'application/vnd.ms-fontobject', // EOT files (Internet Explorer)
735
- 'application/x-font-ttf',
736
- 'application/x-font-truetype',
737
- 'application/x-font-otf',
738
- 'application/x-font-opentype',
739
- ] as const;
740
-
741
- /**
742
- * Check if a MIME type is allowed for upload.
743
- *
744
- * Supports both exact matches and prefix matches:
745
- * - 'application/json' matches 'application/json' exactly
746
- * - 'text/' matches 'text/plain', 'text/html', etc.
531
+ * The blocklist targets file types that pose direct security risks when hosted:
532
+ * executables, disk images, malware vectors, dangerous scripts, and shortcuts.
533
+ */
534
+ export const BLOCKED_EXTENSIONS: ReadonlySet<string> = new Set([
535
+ // Executables
536
+ 'exe', 'msi', 'dll', 'scr', 'bat', 'cmd', 'com', 'pif', 'app', 'deb', 'rpm',
537
+ // Installers
538
+ 'pkg', 'mpkg',
539
+ // Disk images
540
+ 'dmg', 'iso', 'img',
541
+ // Malware vectors
542
+ 'cab', 'cpl', 'chm',
543
+ // Dangerous scripts
544
+ 'ps1', 'vbs', 'vbe', 'ws', 'wsf', 'wsc', 'wsh', 'reg',
545
+ // Java
546
+ 'jar', 'jnlp',
547
+ // Mobile/browser packages
548
+ 'apk', 'crx',
549
+ // Shortcut/link
550
+ 'lnk', 'inf', 'hta',
551
+ ]);
552
+
553
+ /**
554
+ * Check if a filename has a blocked extension.
555
+ * Extracts the extension from the filename and checks against the blocklist.
556
+ * Case-insensitive. Returns false for files without extensions.
747
557
  *
748
558
  * @example
749
- * isAllowedMimeType('text/plain') // true (prefix match)
750
- * isAllowedMimeType('application/json') // true (exact match)
751
- * isAllowedMimeType('application/wasm') // false (not allowed)
752
- */
753
- export function isAllowedMimeType(mimeType: string): boolean {
754
- return ALLOWED_MIME_TYPES.some(allowed =>
755
- mimeType === allowed || mimeType.startsWith(allowed)
756
- );
559
+ * isBlockedExtension('virus.exe') // true
560
+ * isBlockedExtension('app.dmg') // true
561
+ * isBlockedExtension('style.css') // false
562
+ * isBlockedExtension('data.custom') // false
563
+ * isBlockedExtension('README') // false
564
+ */
565
+ export function isBlockedExtension(filename: string): boolean {
566
+ const dotIndex = filename.lastIndexOf('.');
567
+ if (dotIndex === -1 || dotIndex === filename.length - 1) return false;
568
+ const ext = filename.slice(dotIndex + 1).toLowerCase();
569
+ return BLOCKED_EXTENSIONS.has(ext);
757
570
  }
758
571
 
759
572
  // =============================================================================
760
- // COMMON RESPONSE PATTERNS
573
+ // COMMON RESPONSE PATTERNS
761
574
  // =============================================================================
762
575
 
763
576
  /**
@@ -1068,8 +881,6 @@ export interface BillingStatus {
1068
881
  billing: string | null;
1069
882
  /** Number of billing units (1 unit = 1 custom domain), null if no billing */
1070
883
  units: number | null;
1071
- /** Number of custom domains currently in use, null if no billing */
1072
- usage: number | null;
1073
884
  /** Billing status from Creem (active, trialing, canceled, etc.), null if no billing */
1074
885
  status: string | null;
1075
886
  /** Link to Creem customer portal for billing management, null if unavailable */
@@ -1294,7 +1105,6 @@ export interface ValidationIssue {
1294
1105
  export interface ValidatableFile {
1295
1106
  name: string;
1296
1107
  size: number;
1297
- type: string;
1298
1108
  status?: FileValidationStatusType;
1299
1109
  statusMessage?: string;
1300
1110
  }