@shipstatic/types 0.7.4 → 0.7.5

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
@@ -389,12 +389,34 @@ export declare const BLOCKED_EXTENSIONS: ReadonlySet<string>;
389
389
  */
390
390
  export declare function isBlockedExtension(filename: string): boolean;
391
391
  /**
392
- * Directory names that indicate an unbuilt project was uploaded instead of build output.
392
+ * Characters that are unsafe in filenames for static hosting.
393
+ *
394
+ * Blocks only characters that genuinely break the upload→serve round-trip:
395
+ * - # ? % URL round-trip breakers (fragment, query, encoding ambiguity)
396
+ * - \ Path separator confusion (upload splits on backslash)
397
+ * - < > " XSS vectors with zero legitimate use in filenames
398
+ * - \x00-\x1f \x7f Control characters (header injection, display corruption)
399
+ *
400
+ * Everything else is allowed — browser percent-encodes, Worker decodes, R2 matches.
401
+ */
402
+ export declare const UNSAFE_FILENAME_CHARS: RegExp;
403
+ /**
404
+ * Check if a filename contains unsafe characters.
405
+ *
406
+ * @example
407
+ * hasUnsafeChars('saved_resource(1).html') // false — parentheses are safe
408
+ * hasUnsafeChars('page[slug].js') // false — brackets are safe
409
+ * hasUnsafeChars('file#anchor.html') // true — # breaks URL resolution
410
+ * hasUnsafeChars('file<tag>.html') // true — < is an XSS vector
411
+ */
412
+ export declare function hasUnsafeChars(filename: string): boolean;
413
+ /**
414
+ * Path segment names that indicate an unbuilt project was uploaded instead of build output.
393
415
  * Used for early detection in CLI, browser, and server validation.
394
416
  */
395
417
  export declare const UNBUILT_PROJECT_MARKERS: ReadonlySet<string>;
396
418
  /**
397
- * Check if a file path contains an unbuilt project marker directory.
419
+ * Check if a file path contains an unbuilt project marker.
398
420
  *
399
421
  * @example
400
422
  * hasUnbuiltMarker('node_modules/react/index.js') // true
package/dist/index.js CHANGED
@@ -250,10 +250,37 @@ export function isBlockedExtension(filename) {
250
250
  return BLOCKED_EXTENSIONS.has(ext);
251
251
  }
252
252
  // =============================================================================
253
+ // FILENAME CHARACTER VALIDATION
254
+ // =============================================================================
255
+ /**
256
+ * Characters that are unsafe in filenames for static hosting.
257
+ *
258
+ * Blocks only characters that genuinely break the upload→serve round-trip:
259
+ * - # ? % URL round-trip breakers (fragment, query, encoding ambiguity)
260
+ * - \ Path separator confusion (upload splits on backslash)
261
+ * - < > " XSS vectors with zero legitimate use in filenames
262
+ * - \x00-\x1f \x7f Control characters (header injection, display corruption)
263
+ *
264
+ * Everything else is allowed — browser percent-encodes, Worker decodes, R2 matches.
265
+ */
266
+ export const UNSAFE_FILENAME_CHARS = /[\x00-\x1f\x7f#?%\\<>"]/;
267
+ /**
268
+ * Check if a filename contains unsafe characters.
269
+ *
270
+ * @example
271
+ * hasUnsafeChars('saved_resource(1).html') // false — parentheses are safe
272
+ * hasUnsafeChars('page[slug].js') // false — brackets are safe
273
+ * hasUnsafeChars('file#anchor.html') // true — # breaks URL resolution
274
+ * hasUnsafeChars('file<tag>.html') // true — < is an XSS vector
275
+ */
276
+ export function hasUnsafeChars(filename) {
277
+ return UNSAFE_FILENAME_CHARS.test(filename);
278
+ }
279
+ // =============================================================================
253
280
  // UNBUILT PROJECT MARKERS
254
281
  // =============================================================================
255
282
  /**
256
- * Directory names that indicate an unbuilt project was uploaded instead of build output.
283
+ * Path segment names that indicate an unbuilt project was uploaded instead of build output.
257
284
  * Used for early detection in CLI, browser, and server validation.
258
285
  */
259
286
  export const UNBUILT_PROJECT_MARKERS = new Set([
@@ -261,7 +288,7 @@ export const UNBUILT_PROJECT_MARKERS = new Set([
261
288
  'package.json',
262
289
  ]);
263
290
  /**
264
- * Check if a file path contains an unbuilt project marker directory.
291
+ * Check if a file path contains an unbuilt project marker.
265
292
  *
266
293
  * @example
267
294
  * hasUnbuiltMarker('node_modules/react/index.js') // true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/types",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Shared types for Shipstatic platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -571,12 +571,42 @@ export function isBlockedExtension(filename: string): boolean {
571
571
  return BLOCKED_EXTENSIONS.has(ext);
572
572
  }
573
573
 
574
+ // =============================================================================
575
+ // FILENAME CHARACTER VALIDATION
576
+ // =============================================================================
577
+
578
+ /**
579
+ * Characters that are unsafe in filenames for static hosting.
580
+ *
581
+ * Blocks only characters that genuinely break the upload→serve round-trip:
582
+ * - # ? % URL round-trip breakers (fragment, query, encoding ambiguity)
583
+ * - \ Path separator confusion (upload splits on backslash)
584
+ * - < > " XSS vectors with zero legitimate use in filenames
585
+ * - \x00-\x1f \x7f Control characters (header injection, display corruption)
586
+ *
587
+ * Everything else is allowed — browser percent-encodes, Worker decodes, R2 matches.
588
+ */
589
+ export const UNSAFE_FILENAME_CHARS = /[\x00-\x1f\x7f#?%\\<>"]/;
590
+
591
+ /**
592
+ * Check if a filename contains unsafe characters.
593
+ *
594
+ * @example
595
+ * hasUnsafeChars('saved_resource(1).html') // false — parentheses are safe
596
+ * hasUnsafeChars('page[slug].js') // false — brackets are safe
597
+ * hasUnsafeChars('file#anchor.html') // true — # breaks URL resolution
598
+ * hasUnsafeChars('file<tag>.html') // true — < is an XSS vector
599
+ */
600
+ export function hasUnsafeChars(filename: string): boolean {
601
+ return UNSAFE_FILENAME_CHARS.test(filename);
602
+ }
603
+
574
604
  // =============================================================================
575
605
  // UNBUILT PROJECT MARKERS
576
606
  // =============================================================================
577
607
 
578
608
  /**
579
- * Directory names that indicate an unbuilt project was uploaded instead of build output.
609
+ * Path segment names that indicate an unbuilt project was uploaded instead of build output.
580
610
  * Used for early detection in CLI, browser, and server validation.
581
611
  */
582
612
  export const UNBUILT_PROJECT_MARKERS: ReadonlySet<string> = new Set([
@@ -585,7 +615,7 @@ export const UNBUILT_PROJECT_MARKERS: ReadonlySet<string> = new Set([
585
615
  ]);
586
616
 
587
617
  /**
588
- * Check if a file path contains an unbuilt project marker directory.
618
+ * Check if a file path contains an unbuilt project marker.
589
619
  *
590
620
  * @example
591
621
  * hasUnbuiltMarker('node_modules/react/index.js') // true