@shipstatic/drop 0.2.0 → 0.2.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.cts CHANGED
@@ -1,169 +1,7 @@
1
- import { ValidationError } from '@shipstatic/types';
2
- import { Ship, formatFileSize as formatFileSize$1 } from '@shipstatic/ship';
3
-
4
- /**
5
- * Core types for @shipstatic/drop
6
- * Imports types from @shipstatic/types (single source of truth)
7
- * and defines drop-specific types
8
- */
9
-
10
- /**
11
- * Extended File interface with webkitRelativePath
12
- * This property is set by browsers for folder uploads and drag-drop
13
- * https://developer.mozilla.org/en-US/docs/Web/API/File/webkitRelativePath
14
- */
15
- interface FileWithPath extends File {
16
- readonly webkitRelativePath: string;
17
- }
18
- declare const FILE_STATUSES: {
19
- readonly PROCESSING: "processing";
20
- readonly UPLOADING: "uploading";
21
- readonly COMPLETE: "complete";
22
- readonly ERROR: "error";
23
- readonly PENDING: "pending";
24
- readonly PROCESSING_ERROR: "processing_error";
25
- readonly EMPTY_FILE: "empty_file";
26
- readonly VALIDATION_FAILED: "validation_failed";
27
- readonly READY: "ready";
28
- };
29
- type FileStatus = (typeof FILE_STATUSES)[keyof typeof FILE_STATUSES];
30
- /**
31
- * Client-side error structure
32
- * Matches ValidationError from @shipstatic/ship for consistency
33
- */
34
- type ClientError = ValidationError;
35
- /**
36
- * Processed file entry ready for upload
37
- * Contains both the File object and UI-specific metadata
38
- * Use `file` property to access the underlying File for SDK operations
39
- */
40
- interface ProcessedFile {
41
- /** Unique identifier for React keys and tracking */
42
- id: string;
43
- /** The File object - pass this to ship.deployments.create() */
44
- file: File;
45
- /** Relative path for deployment (e.g., "images/photo.jpg") */
46
- path: string;
47
- /** File size in bytes */
48
- size: number;
49
- /** MD5 hash (optional - Ship SDK calculates during deployment if not provided) */
50
- md5?: string;
51
- /** Filename without path */
52
- name: string;
53
- /** MIME type for UI icons/previews */
54
- type: string;
55
- /** Last modified timestamp */
56
- lastModified: number;
57
- /** Current processing/upload status */
58
- status: FileStatus;
59
- /** Human-readable status message for UI */
60
- statusMessage?: string;
61
- /** Upload progress (0-100) - only set during upload */
62
- progress?: number;
63
- }
64
- /**
65
- * State machine values for the drop hook
66
- */
67
- type DropStateValue = 'idle' | 'dragging' | 'processing' | 'ready' | 'error';
68
- /**
69
- * Status information with title and details
70
- */
71
- interface DropStatus {
72
- title: string;
73
- details: string;
74
- errors?: string[];
75
- }
76
- /**
77
- * State machine state for the drop hook
78
- */
79
- interface DropState {
80
- value: DropStateValue;
81
- files: ProcessedFile[];
82
- sourceName: string;
83
- status: DropStatus | null;
84
- }
85
-
86
- interface DropOptions {
87
- /** Ship SDK instance (required for validation) */
88
- ship: Ship;
89
- /** Callback when files are processed and ready */
90
- onFilesReady?: (files: ProcessedFile[]) => void;
91
- /** Callback when validation fails */
92
- onValidationError?: (error: ClientError) => void;
93
- /** Whether to strip common directory prefix from paths (default: true) */
94
- stripPrefix?: boolean;
95
- }
96
- /** Options for getDropzoneProps() */
97
- interface DropzonePropsOptions {
98
- /** Whether clicking the dropzone opens the file picker (default: true) */
99
- clickable?: boolean;
100
- }
101
- interface DropReturn {
102
- /** Current phase of the state machine */
103
- phase: DropStateValue;
104
- /** Whether currently processing files (ZIP extraction, etc.) */
105
- isProcessing: boolean;
106
- /** Whether user is currently dragging over the dropzone */
107
- isDragging: boolean;
108
- /** Whether the dropzone is interactive (idle, dragging, or ready - not processing or error) */
109
- isInteractive: boolean;
110
- /** Whether an error occurred during processing */
111
- hasError: boolean;
112
- /** Flattened access to files */
113
- files: ProcessedFile[];
114
- /** Flattened access to source name */
115
- sourceName: string;
116
- /** Flattened access to status */
117
- status: {
118
- title: string;
119
- details: string;
120
- errors?: string[];
121
- } | null;
122
- /** Get props to spread on dropzone element (handles drag & drop, optionally click) */
123
- getDropzoneProps: (options?: DropzonePropsOptions) => {
124
- onDragOver: (e: React.DragEvent) => void;
125
- onDragLeave: (e: React.DragEvent) => void;
126
- onDrop: (e: React.DragEvent) => void;
127
- onClick?: () => void;
128
- };
129
- /** Get props to spread on hidden file input element */
130
- getInputProps: () => {
131
- ref: React.RefObject<HTMLInputElement | null>;
132
- type: 'file';
133
- style: {
134
- display: string;
135
- };
136
- multiple: boolean;
137
- webkitdirectory: string;
138
- onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
139
- };
140
- /** Programmatically trigger file picker */
141
- open: () => void;
142
- /** Manually process files (for advanced usage) */
143
- processFiles: (files: File[]) => Promise<void>;
144
- /** Reset state and clear all files */
145
- reset: () => void;
146
- /** Get only valid files ready for upload */
147
- validFiles: ProcessedFile[];
148
- /** Get raw File objects ready for Ship SDK upload */
149
- getFilesForUpload: () => File[];
150
- }
151
- /**
152
- * Headless drop hook for file upload workflows
153
- *
154
- * @example
155
- * ```tsx
156
- * const drop = useDrop({ ship });
157
- *
158
- * return (
159
- * <div {...drop.getDropzoneProps()} style={{...}}>
160
- * <input {...drop.getInputProps()} />
161
- * {drop.isDragging ? "📂 Drop" : "📁 Click"}
162
- * </div>
163
- * );
164
- * ```
165
- */
166
- declare function useDrop(options: DropOptions): DropReturn;
1
+ import { P as ProcessedFile } from './useDrop-CRUVkVzW.cjs';
2
+ export { C as ClientError, D as DropOptions, a as DropReturn, b as DropState, c as DropStateValue, d as DropStatus, e as DropzonePropsOptions, F as FILE_STATUSES, f as FileStatus, g as FileWithPath, u as useDrop } from './useDrop-CRUVkVzW.cjs';
3
+ import { formatFileSize as formatFileSize$1 } from '@shipstatic/ship';
4
+ import '@shipstatic/types';
167
5
 
168
6
  /**
169
7
  * Unified file processing utilities
@@ -232,4 +70,4 @@ declare function isZipFile(file: File): boolean;
232
70
  */
233
71
  declare function getMimeType(path: string): string;
234
72
 
235
- export { type ClientError, type DropOptions, type DropReturn, type DropState, type DropStateValue, type DropStatus, type DropzonePropsOptions, FILE_STATUSES, type FileStatus, type FileWithPath, type ProcessedFile, type ZipExtractionResult, createProcessedFile, extractZipToFiles, formatFileSize, getMimeType, isZipFile, normalizePath, stripCommonPrefix, traverseFileTree, useDrop };
73
+ export { ProcessedFile, type ZipExtractionResult, createProcessedFile, extractZipToFiles, formatFileSize, getMimeType, isZipFile, normalizePath, stripCommonPrefix, traverseFileTree };
package/dist/index.d.ts CHANGED
@@ -1,169 +1,7 @@
1
- import { ValidationError } from '@shipstatic/types';
2
- import { Ship, formatFileSize as formatFileSize$1 } from '@shipstatic/ship';
3
-
4
- /**
5
- * Core types for @shipstatic/drop
6
- * Imports types from @shipstatic/types (single source of truth)
7
- * and defines drop-specific types
8
- */
9
-
10
- /**
11
- * Extended File interface with webkitRelativePath
12
- * This property is set by browsers for folder uploads and drag-drop
13
- * https://developer.mozilla.org/en-US/docs/Web/API/File/webkitRelativePath
14
- */
15
- interface FileWithPath extends File {
16
- readonly webkitRelativePath: string;
17
- }
18
- declare const FILE_STATUSES: {
19
- readonly PROCESSING: "processing";
20
- readonly UPLOADING: "uploading";
21
- readonly COMPLETE: "complete";
22
- readonly ERROR: "error";
23
- readonly PENDING: "pending";
24
- readonly PROCESSING_ERROR: "processing_error";
25
- readonly EMPTY_FILE: "empty_file";
26
- readonly VALIDATION_FAILED: "validation_failed";
27
- readonly READY: "ready";
28
- };
29
- type FileStatus = (typeof FILE_STATUSES)[keyof typeof FILE_STATUSES];
30
- /**
31
- * Client-side error structure
32
- * Matches ValidationError from @shipstatic/ship for consistency
33
- */
34
- type ClientError = ValidationError;
35
- /**
36
- * Processed file entry ready for upload
37
- * Contains both the File object and UI-specific metadata
38
- * Use `file` property to access the underlying File for SDK operations
39
- */
40
- interface ProcessedFile {
41
- /** Unique identifier for React keys and tracking */
42
- id: string;
43
- /** The File object - pass this to ship.deployments.create() */
44
- file: File;
45
- /** Relative path for deployment (e.g., "images/photo.jpg") */
46
- path: string;
47
- /** File size in bytes */
48
- size: number;
49
- /** MD5 hash (optional - Ship SDK calculates during deployment if not provided) */
50
- md5?: string;
51
- /** Filename without path */
52
- name: string;
53
- /** MIME type for UI icons/previews */
54
- type: string;
55
- /** Last modified timestamp */
56
- lastModified: number;
57
- /** Current processing/upload status */
58
- status: FileStatus;
59
- /** Human-readable status message for UI */
60
- statusMessage?: string;
61
- /** Upload progress (0-100) - only set during upload */
62
- progress?: number;
63
- }
64
- /**
65
- * State machine values for the drop hook
66
- */
67
- type DropStateValue = 'idle' | 'dragging' | 'processing' | 'ready' | 'error';
68
- /**
69
- * Status information with title and details
70
- */
71
- interface DropStatus {
72
- title: string;
73
- details: string;
74
- errors?: string[];
75
- }
76
- /**
77
- * State machine state for the drop hook
78
- */
79
- interface DropState {
80
- value: DropStateValue;
81
- files: ProcessedFile[];
82
- sourceName: string;
83
- status: DropStatus | null;
84
- }
85
-
86
- interface DropOptions {
87
- /** Ship SDK instance (required for validation) */
88
- ship: Ship;
89
- /** Callback when files are processed and ready */
90
- onFilesReady?: (files: ProcessedFile[]) => void;
91
- /** Callback when validation fails */
92
- onValidationError?: (error: ClientError) => void;
93
- /** Whether to strip common directory prefix from paths (default: true) */
94
- stripPrefix?: boolean;
95
- }
96
- /** Options for getDropzoneProps() */
97
- interface DropzonePropsOptions {
98
- /** Whether clicking the dropzone opens the file picker (default: true) */
99
- clickable?: boolean;
100
- }
101
- interface DropReturn {
102
- /** Current phase of the state machine */
103
- phase: DropStateValue;
104
- /** Whether currently processing files (ZIP extraction, etc.) */
105
- isProcessing: boolean;
106
- /** Whether user is currently dragging over the dropzone */
107
- isDragging: boolean;
108
- /** Whether the dropzone is interactive (idle, dragging, or ready - not processing or error) */
109
- isInteractive: boolean;
110
- /** Whether an error occurred during processing */
111
- hasError: boolean;
112
- /** Flattened access to files */
113
- files: ProcessedFile[];
114
- /** Flattened access to source name */
115
- sourceName: string;
116
- /** Flattened access to status */
117
- status: {
118
- title: string;
119
- details: string;
120
- errors?: string[];
121
- } | null;
122
- /** Get props to spread on dropzone element (handles drag & drop, optionally click) */
123
- getDropzoneProps: (options?: DropzonePropsOptions) => {
124
- onDragOver: (e: React.DragEvent) => void;
125
- onDragLeave: (e: React.DragEvent) => void;
126
- onDrop: (e: React.DragEvent) => void;
127
- onClick?: () => void;
128
- };
129
- /** Get props to spread on hidden file input element */
130
- getInputProps: () => {
131
- ref: React.RefObject<HTMLInputElement | null>;
132
- type: 'file';
133
- style: {
134
- display: string;
135
- };
136
- multiple: boolean;
137
- webkitdirectory: string;
138
- onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
139
- };
140
- /** Programmatically trigger file picker */
141
- open: () => void;
142
- /** Manually process files (for advanced usage) */
143
- processFiles: (files: File[]) => Promise<void>;
144
- /** Reset state and clear all files */
145
- reset: () => void;
146
- /** Get only valid files ready for upload */
147
- validFiles: ProcessedFile[];
148
- /** Get raw File objects ready for Ship SDK upload */
149
- getFilesForUpload: () => File[];
150
- }
151
- /**
152
- * Headless drop hook for file upload workflows
153
- *
154
- * @example
155
- * ```tsx
156
- * const drop = useDrop({ ship });
157
- *
158
- * return (
159
- * <div {...drop.getDropzoneProps()} style={{...}}>
160
- * <input {...drop.getInputProps()} />
161
- * {drop.isDragging ? "📂 Drop" : "📁 Click"}
162
- * </div>
163
- * );
164
- * ```
165
- */
166
- declare function useDrop(options: DropOptions): DropReturn;
1
+ import { P as ProcessedFile } from './useDrop-CRUVkVzW.js';
2
+ export { C as ClientError, D as DropOptions, a as DropReturn, b as DropState, c as DropStateValue, d as DropStatus, e as DropzonePropsOptions, F as FILE_STATUSES, f as FileStatus, g as FileWithPath, u as useDrop } from './useDrop-CRUVkVzW.js';
3
+ import { formatFileSize as formatFileSize$1 } from '@shipstatic/ship';
4
+ import '@shipstatic/types';
167
5
 
168
6
  /**
169
7
  * Unified file processing utilities
@@ -232,4 +70,4 @@ declare function isZipFile(file: File): boolean;
232
70
  */
233
71
  declare function getMimeType(path: string): string;
234
72
 
235
- export { type ClientError, type DropOptions, type DropReturn, type DropState, type DropStateValue, type DropStatus, type DropzonePropsOptions, FILE_STATUSES, type FileStatus, type FileWithPath, type ProcessedFile, type ZipExtractionResult, createProcessedFile, extractZipToFiles, formatFileSize, getMimeType, isZipFile, normalizePath, stripCommonPrefix, traverseFileTree, useDrop };
73
+ export { ProcessedFile, type ZipExtractionResult, createProcessedFile, extractZipToFiles, formatFileSize, getMimeType, isZipFile, normalizePath, stripCommonPrefix, traverseFileTree };
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+
3
+ // src/testing.ts
4
+ function createMockDrop(options = {}) {
5
+ const {
6
+ phase = "idle",
7
+ files = [],
8
+ sourceName = "",
9
+ status = null
10
+ } = options;
11
+ const validFiles = files.filter((f) => f.status === "ready");
12
+ return {
13
+ // State
14
+ phase,
15
+ isProcessing: phase === "processing",
16
+ isDragging: phase === "dragging",
17
+ isInteractive: phase === "idle" || phase === "dragging" || phase === "ready",
18
+ hasError: phase === "error",
19
+ files,
20
+ validFiles,
21
+ sourceName,
22
+ status,
23
+ // Prop getters - return minimal objects for spreading
24
+ getDropzoneProps: (opts) => ({
25
+ onDragOver: () => {
26
+ },
27
+ onDragLeave: () => {
28
+ },
29
+ onDrop: () => {
30
+ },
31
+ ...opts?.clickable !== false && { onClick: () => {
32
+ } }
33
+ }),
34
+ getInputProps: () => ({
35
+ ref: { current: null },
36
+ type: "file",
37
+ style: { display: "none" },
38
+ multiple: true,
39
+ webkitdirectory: "",
40
+ onChange: () => {
41
+ }
42
+ }),
43
+ // Actions - no-op by default, can be spied on
44
+ open: () => {
45
+ },
46
+ processFiles: async () => {
47
+ },
48
+ reset: () => {
49
+ },
50
+ // Helpers
51
+ getFilesForUpload: () => validFiles.map((f) => f.file)
52
+ };
53
+ }
54
+ function createMockDropWithSpies(options = {}) {
55
+ const baseDrop = createMockDrop(options);
56
+ let openCalls = 0;
57
+ let processFilesCalls = [];
58
+ let resetCalls = 0;
59
+ const trackedSpies = {
60
+ open: Object.assign(() => {
61
+ openCalls++;
62
+ }, {
63
+ calls: () => openCalls,
64
+ toHaveBeenCalled: () => openCalls > 0
65
+ }),
66
+ processFiles: Object.assign(async (files) => {
67
+ processFilesCalls.push(files);
68
+ }, {
69
+ calls: () => processFilesCalls,
70
+ toHaveBeenCalled: () => processFilesCalls.length > 0,
71
+ toHaveBeenCalledWith: (files) => processFilesCalls.some((c) => c === files)
72
+ }),
73
+ reset: Object.assign(() => {
74
+ resetCalls++;
75
+ }, {
76
+ calls: () => resetCalls,
77
+ toHaveBeenCalled: () => resetCalls > 0
78
+ }),
79
+ getFilesForUpload: baseDrop.getFilesForUpload
80
+ };
81
+ return {
82
+ drop: {
83
+ ...baseDrop,
84
+ open: trackedSpies.open,
85
+ processFiles: trackedSpies.processFiles,
86
+ reset: trackedSpies.reset,
87
+ getFilesForUpload: trackedSpies.getFilesForUpload
88
+ },
89
+ spies: trackedSpies
90
+ };
91
+ }
92
+ var mockFileIdCounter = 0;
93
+ function createMockProcessedFile(name, options = {}) {
94
+ const {
95
+ path = name,
96
+ content = "test content",
97
+ type = "text/plain",
98
+ status = "ready",
99
+ statusMessage
100
+ } = options;
101
+ const file = new File([content], name, { type });
102
+ return {
103
+ id: `mock-file-${++mockFileIdCounter}`,
104
+ file,
105
+ path,
106
+ name,
107
+ size: file.size,
108
+ type,
109
+ lastModified: Date.now(),
110
+ status,
111
+ statusMessage
112
+ };
113
+ }
114
+ function createMockFile(name, content = "test content", type = "text/plain") {
115
+ return new File([content], name, { type, lastModified: Date.now() });
116
+ }
117
+ function createMockFileWithPath(name, webkitRelativePath, content = "test content", type = "text/plain") {
118
+ const file = createMockFile(name, content, type);
119
+ Object.defineProperty(file, "webkitRelativePath", {
120
+ value: webkitRelativePath,
121
+ writable: false,
122
+ enumerable: true,
123
+ configurable: true
124
+ });
125
+ return file;
126
+ }
127
+ function createMockErrorStatus(title = "Validation Failed", details = "One or more files failed validation", errors = []) {
128
+ return { title, details, errors };
129
+ }
130
+ function createMockProcessingStatus(title = "Processing...", details = "Validating and preparing files.") {
131
+ return { title, details };
132
+ }
133
+ function createMockReadyStatus(fileCount) {
134
+ return {
135
+ title: "Ready",
136
+ details: `${fileCount} file(s) are ready.`
137
+ };
138
+ }
139
+
140
+ exports.createMockDrop = createMockDrop;
141
+ exports.createMockDropWithSpies = createMockDropWithSpies;
142
+ exports.createMockErrorStatus = createMockErrorStatus;
143
+ exports.createMockFile = createMockFile;
144
+ exports.createMockFileWithPath = createMockFileWithPath;
145
+ exports.createMockProcessedFile = createMockProcessedFile;
146
+ exports.createMockProcessingStatus = createMockProcessingStatus;
147
+ exports.createMockReadyStatus = createMockReadyStatus;
148
+ //# sourceMappingURL=testing.cjs.map
149
+ //# sourceMappingURL=testing.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/testing.ts"],"names":[],"mappings":"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","file":"testing.cjs","sourcesContent":["/**\n * Test utilities for @shipstatic/drop\n *\n * Import from '@shipstatic/drop/testing' in your test files:\n *\n * ```typescript\n * import { createMockDrop, createMockFile } from '@shipstatic/drop/testing';\n * ```\n */\n\nimport type { ProcessedFile, DropStatus, DropStateValue } from './types';\nimport type { DropReturn, DropzonePropsOptions } from './hooks/useDrop';\n\n// ============================================================================\n// Mock Drop Hook Return\n// ============================================================================\n\n/**\n * Options for creating a mock drop return value\n */\nexport interface MockDropOptions {\n phase?: DropStateValue;\n files?: ProcessedFile[];\n sourceName?: string;\n status?: DropStatus | null;\n}\n\n/**\n * Creates a mock DropReturn for testing components that receive drop as a prop\n *\n * @example\n * ```tsx\n * import { createMockDrop, createMockProcessedFile } from '@shipstatic/drop/testing';\n *\n * it('renders file count when files are ready', () => {\n * const drop = createMockDrop({\n * phase: 'ready',\n * files: [createMockProcessedFile('index.html')],\n * });\n *\n * render(<DeployDropArea drop={drop} />);\n * expect(screen.getByText('1 files ready')).toBeInTheDocument();\n * });\n * ```\n */\nexport function createMockDrop(options: MockDropOptions = {}): DropReturn {\n const {\n phase = 'idle',\n files = [],\n sourceName = '',\n status = null,\n } = options;\n\n const validFiles = files.filter(f => f.status === 'ready');\n\n return {\n // State\n phase,\n isProcessing: phase === 'processing',\n isDragging: phase === 'dragging',\n isInteractive: phase === 'idle' || phase === 'dragging' || phase === 'ready',\n hasError: phase === 'error',\n files,\n validFiles,\n sourceName,\n status,\n\n // Prop getters - return minimal objects for spreading\n getDropzoneProps: (opts?: DropzonePropsOptions) => ({\n onDragOver: () => {},\n onDragLeave: () => {},\n onDrop: () => {},\n ...(opts?.clickable !== false && { onClick: () => {} }),\n }),\n getInputProps: () => ({\n ref: { current: null },\n type: 'file' as const,\n style: { display: 'none' },\n multiple: true,\n webkitdirectory: '',\n onChange: () => {},\n }),\n\n // Actions - no-op by default, can be spied on\n open: () => {},\n processFiles: async () => {},\n reset: () => {},\n\n // Helpers\n getFilesForUpload: () => validFiles.map(f => f.file),\n };\n}\n\n/**\n * Creates a mock drop with spy functions for testing interactions\n *\n * @example\n * ```tsx\n * import { createMockDropWithSpies } from '@shipstatic/drop/testing';\n *\n * it('calls reset when Clear button is clicked', async () => {\n * const { drop, spies } = createMockDropWithSpies({ phase: 'ready', files: [...] });\n *\n * render(<DeployDropArea drop={drop} />);\n * await userEvent.click(screen.getByText('Clear'));\n *\n * expect(spies.reset).toHaveBeenCalled();\n * });\n * ```\n */\nexport function createMockDropWithSpies(options: MockDropOptions = {}): {\n drop: DropReturn;\n spies: {\n open: () => void;\n processFiles: (files: File[]) => Promise<void>;\n reset: () => void;\n getFilesForUpload: () => File[];\n };\n} {\n const baseDrop = createMockDrop(options);\n\n const spies = {\n open: createNoopSpy(),\n processFiles: createAsyncNoopSpy(),\n reset: createNoopSpy(),\n getFilesForUpload: (() => baseDrop.getFilesForUpload()) as () => File[],\n };\n\n // Track calls manually (works without vitest in runtime)\n let openCalls = 0;\n let processFilesCalls: File[][] = [];\n let resetCalls = 0;\n\n const trackedSpies = {\n open: Object.assign(() => { openCalls++; }, {\n calls: () => openCalls,\n toHaveBeenCalled: () => openCalls > 0,\n }),\n processFiles: Object.assign(async (files: File[]) => { processFilesCalls.push(files); }, {\n calls: () => processFilesCalls,\n toHaveBeenCalled: () => processFilesCalls.length > 0,\n toHaveBeenCalledWith: (files: File[]) => processFilesCalls.some(c => c === files),\n }),\n reset: Object.assign(() => { resetCalls++; }, {\n calls: () => resetCalls,\n toHaveBeenCalled: () => resetCalls > 0,\n }),\n getFilesForUpload: baseDrop.getFilesForUpload,\n };\n\n return {\n drop: {\n ...baseDrop,\n open: trackedSpies.open,\n processFiles: trackedSpies.processFiles,\n reset: trackedSpies.reset,\n getFilesForUpload: trackedSpies.getFilesForUpload,\n },\n spies: trackedSpies,\n };\n}\n\n// ============================================================================\n// Mock File Utilities\n// ============================================================================\n\nlet mockFileIdCounter = 0;\n\n/**\n * Creates a mock ProcessedFile for testing\n */\nexport function createMockProcessedFile(\n name: string,\n options: {\n path?: string;\n content?: string;\n type?: string;\n status?: 'ready' | 'validation_failed' | 'processing_error' | 'empty_file';\n statusMessage?: string;\n } = {}\n): ProcessedFile {\n const {\n path = name,\n content = 'test content',\n type = 'text/plain',\n status = 'ready',\n statusMessage,\n } = options;\n\n const file = new File([content], name, { type });\n\n return {\n id: `mock-file-${++mockFileIdCounter}`,\n file,\n path,\n name,\n size: file.size,\n type,\n lastModified: Date.now(),\n status,\n statusMessage,\n };\n}\n\n/**\n * Creates a mock File object\n */\nexport function createMockFile(\n name: string,\n content: string = 'test content',\n type: string = 'text/plain'\n): File {\n return new File([content], name, { type, lastModified: Date.now() });\n}\n\n/**\n * Creates a mock File object with webkitRelativePath set\n */\nexport function createMockFileWithPath(\n name: string,\n webkitRelativePath: string,\n content: string = 'test content',\n type: string = 'text/plain'\n): File {\n const file = createMockFile(name, content, type);\n Object.defineProperty(file, 'webkitRelativePath', {\n value: webkitRelativePath,\n writable: false,\n enumerable: true,\n configurable: true,\n });\n return file;\n}\n\n// ============================================================================\n// Mock Status Utilities\n// ============================================================================\n\n/**\n * Creates a mock error status\n */\nexport function createMockErrorStatus(\n title: string = 'Validation Failed',\n details: string = 'One or more files failed validation',\n errors: string[] = []\n): DropStatus {\n return { title, details, errors };\n}\n\n/**\n * Creates a mock processing status\n */\nexport function createMockProcessingStatus(\n title: string = 'Processing...',\n details: string = 'Validating and preparing files.'\n): DropStatus {\n return { title, details };\n}\n\n/**\n * Creates a mock ready status\n */\nexport function createMockReadyStatus(fileCount: number): DropStatus {\n return {\n title: 'Ready',\n details: `${fileCount} file(s) are ready.`,\n };\n}\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\nfunction createNoopSpy(): () => void {\n return () => {};\n}\n\nfunction createAsyncNoopSpy(): () => Promise<void> {\n return async () => {};\n}\n"]}
@@ -0,0 +1,100 @@
1
+ import { c as DropStateValue, P as ProcessedFile, d as DropStatus, a as DropReturn } from './useDrop-CRUVkVzW.cjs';
2
+ import '@shipstatic/types';
3
+ import '@shipstatic/ship';
4
+
5
+ /**
6
+ * Test utilities for @shipstatic/drop
7
+ *
8
+ * Import from '@shipstatic/drop/testing' in your test files:
9
+ *
10
+ * ```typescript
11
+ * import { createMockDrop, createMockFile } from '@shipstatic/drop/testing';
12
+ * ```
13
+ */
14
+
15
+ /**
16
+ * Options for creating a mock drop return value
17
+ */
18
+ interface MockDropOptions {
19
+ phase?: DropStateValue;
20
+ files?: ProcessedFile[];
21
+ sourceName?: string;
22
+ status?: DropStatus | null;
23
+ }
24
+ /**
25
+ * Creates a mock DropReturn for testing components that receive drop as a prop
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * import { createMockDrop, createMockProcessedFile } from '@shipstatic/drop/testing';
30
+ *
31
+ * it('renders file count when files are ready', () => {
32
+ * const drop = createMockDrop({
33
+ * phase: 'ready',
34
+ * files: [createMockProcessedFile('index.html')],
35
+ * });
36
+ *
37
+ * render(<DeployDropArea drop={drop} />);
38
+ * expect(screen.getByText('1 files ready')).toBeInTheDocument();
39
+ * });
40
+ * ```
41
+ */
42
+ declare function createMockDrop(options?: MockDropOptions): DropReturn;
43
+ /**
44
+ * Creates a mock drop with spy functions for testing interactions
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * import { createMockDropWithSpies } from '@shipstatic/drop/testing';
49
+ *
50
+ * it('calls reset when Clear button is clicked', async () => {
51
+ * const { drop, spies } = createMockDropWithSpies({ phase: 'ready', files: [...] });
52
+ *
53
+ * render(<DeployDropArea drop={drop} />);
54
+ * await userEvent.click(screen.getByText('Clear'));
55
+ *
56
+ * expect(spies.reset).toHaveBeenCalled();
57
+ * });
58
+ * ```
59
+ */
60
+ declare function createMockDropWithSpies(options?: MockDropOptions): {
61
+ drop: DropReturn;
62
+ spies: {
63
+ open: () => void;
64
+ processFiles: (files: File[]) => Promise<void>;
65
+ reset: () => void;
66
+ getFilesForUpload: () => File[];
67
+ };
68
+ };
69
+ /**
70
+ * Creates a mock ProcessedFile for testing
71
+ */
72
+ declare function createMockProcessedFile(name: string, options?: {
73
+ path?: string;
74
+ content?: string;
75
+ type?: string;
76
+ status?: 'ready' | 'validation_failed' | 'processing_error' | 'empty_file';
77
+ statusMessage?: string;
78
+ }): ProcessedFile;
79
+ /**
80
+ * Creates a mock File object
81
+ */
82
+ declare function createMockFile(name: string, content?: string, type?: string): File;
83
+ /**
84
+ * Creates a mock File object with webkitRelativePath set
85
+ */
86
+ declare function createMockFileWithPath(name: string, webkitRelativePath: string, content?: string, type?: string): File;
87
+ /**
88
+ * Creates a mock error status
89
+ */
90
+ declare function createMockErrorStatus(title?: string, details?: string, errors?: string[]): DropStatus;
91
+ /**
92
+ * Creates a mock processing status
93
+ */
94
+ declare function createMockProcessingStatus(title?: string, details?: string): DropStatus;
95
+ /**
96
+ * Creates a mock ready status
97
+ */
98
+ declare function createMockReadyStatus(fileCount: number): DropStatus;
99
+
100
+ export { type MockDropOptions, createMockDrop, createMockDropWithSpies, createMockErrorStatus, createMockFile, createMockFileWithPath, createMockProcessedFile, createMockProcessingStatus, createMockReadyStatus };
@@ -0,0 +1,100 @@
1
+ import { c as DropStateValue, P as ProcessedFile, d as DropStatus, a as DropReturn } from './useDrop-CRUVkVzW.js';
2
+ import '@shipstatic/types';
3
+ import '@shipstatic/ship';
4
+
5
+ /**
6
+ * Test utilities for @shipstatic/drop
7
+ *
8
+ * Import from '@shipstatic/drop/testing' in your test files:
9
+ *
10
+ * ```typescript
11
+ * import { createMockDrop, createMockFile } from '@shipstatic/drop/testing';
12
+ * ```
13
+ */
14
+
15
+ /**
16
+ * Options for creating a mock drop return value
17
+ */
18
+ interface MockDropOptions {
19
+ phase?: DropStateValue;
20
+ files?: ProcessedFile[];
21
+ sourceName?: string;
22
+ status?: DropStatus | null;
23
+ }
24
+ /**
25
+ * Creates a mock DropReturn for testing components that receive drop as a prop
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * import { createMockDrop, createMockProcessedFile } from '@shipstatic/drop/testing';
30
+ *
31
+ * it('renders file count when files are ready', () => {
32
+ * const drop = createMockDrop({
33
+ * phase: 'ready',
34
+ * files: [createMockProcessedFile('index.html')],
35
+ * });
36
+ *
37
+ * render(<DeployDropArea drop={drop} />);
38
+ * expect(screen.getByText('1 files ready')).toBeInTheDocument();
39
+ * });
40
+ * ```
41
+ */
42
+ declare function createMockDrop(options?: MockDropOptions): DropReturn;
43
+ /**
44
+ * Creates a mock drop with spy functions for testing interactions
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * import { createMockDropWithSpies } from '@shipstatic/drop/testing';
49
+ *
50
+ * it('calls reset when Clear button is clicked', async () => {
51
+ * const { drop, spies } = createMockDropWithSpies({ phase: 'ready', files: [...] });
52
+ *
53
+ * render(<DeployDropArea drop={drop} />);
54
+ * await userEvent.click(screen.getByText('Clear'));
55
+ *
56
+ * expect(spies.reset).toHaveBeenCalled();
57
+ * });
58
+ * ```
59
+ */
60
+ declare function createMockDropWithSpies(options?: MockDropOptions): {
61
+ drop: DropReturn;
62
+ spies: {
63
+ open: () => void;
64
+ processFiles: (files: File[]) => Promise<void>;
65
+ reset: () => void;
66
+ getFilesForUpload: () => File[];
67
+ };
68
+ };
69
+ /**
70
+ * Creates a mock ProcessedFile for testing
71
+ */
72
+ declare function createMockProcessedFile(name: string, options?: {
73
+ path?: string;
74
+ content?: string;
75
+ type?: string;
76
+ status?: 'ready' | 'validation_failed' | 'processing_error' | 'empty_file';
77
+ statusMessage?: string;
78
+ }): ProcessedFile;
79
+ /**
80
+ * Creates a mock File object
81
+ */
82
+ declare function createMockFile(name: string, content?: string, type?: string): File;
83
+ /**
84
+ * Creates a mock File object with webkitRelativePath set
85
+ */
86
+ declare function createMockFileWithPath(name: string, webkitRelativePath: string, content?: string, type?: string): File;
87
+ /**
88
+ * Creates a mock error status
89
+ */
90
+ declare function createMockErrorStatus(title?: string, details?: string, errors?: string[]): DropStatus;
91
+ /**
92
+ * Creates a mock processing status
93
+ */
94
+ declare function createMockProcessingStatus(title?: string, details?: string): DropStatus;
95
+ /**
96
+ * Creates a mock ready status
97
+ */
98
+ declare function createMockReadyStatus(fileCount: number): DropStatus;
99
+
100
+ export { type MockDropOptions, createMockDrop, createMockDropWithSpies, createMockErrorStatus, createMockFile, createMockFileWithPath, createMockProcessedFile, createMockProcessingStatus, createMockReadyStatus };
@@ -0,0 +1,140 @@
1
+ // src/testing.ts
2
+ function createMockDrop(options = {}) {
3
+ const {
4
+ phase = "idle",
5
+ files = [],
6
+ sourceName = "",
7
+ status = null
8
+ } = options;
9
+ const validFiles = files.filter((f) => f.status === "ready");
10
+ return {
11
+ // State
12
+ phase,
13
+ isProcessing: phase === "processing",
14
+ isDragging: phase === "dragging",
15
+ isInteractive: phase === "idle" || phase === "dragging" || phase === "ready",
16
+ hasError: phase === "error",
17
+ files,
18
+ validFiles,
19
+ sourceName,
20
+ status,
21
+ // Prop getters - return minimal objects for spreading
22
+ getDropzoneProps: (opts) => ({
23
+ onDragOver: () => {
24
+ },
25
+ onDragLeave: () => {
26
+ },
27
+ onDrop: () => {
28
+ },
29
+ ...opts?.clickable !== false && { onClick: () => {
30
+ } }
31
+ }),
32
+ getInputProps: () => ({
33
+ ref: { current: null },
34
+ type: "file",
35
+ style: { display: "none" },
36
+ multiple: true,
37
+ webkitdirectory: "",
38
+ onChange: () => {
39
+ }
40
+ }),
41
+ // Actions - no-op by default, can be spied on
42
+ open: () => {
43
+ },
44
+ processFiles: async () => {
45
+ },
46
+ reset: () => {
47
+ },
48
+ // Helpers
49
+ getFilesForUpload: () => validFiles.map((f) => f.file)
50
+ };
51
+ }
52
+ function createMockDropWithSpies(options = {}) {
53
+ const baseDrop = createMockDrop(options);
54
+ let openCalls = 0;
55
+ let processFilesCalls = [];
56
+ let resetCalls = 0;
57
+ const trackedSpies = {
58
+ open: Object.assign(() => {
59
+ openCalls++;
60
+ }, {
61
+ calls: () => openCalls,
62
+ toHaveBeenCalled: () => openCalls > 0
63
+ }),
64
+ processFiles: Object.assign(async (files) => {
65
+ processFilesCalls.push(files);
66
+ }, {
67
+ calls: () => processFilesCalls,
68
+ toHaveBeenCalled: () => processFilesCalls.length > 0,
69
+ toHaveBeenCalledWith: (files) => processFilesCalls.some((c) => c === files)
70
+ }),
71
+ reset: Object.assign(() => {
72
+ resetCalls++;
73
+ }, {
74
+ calls: () => resetCalls,
75
+ toHaveBeenCalled: () => resetCalls > 0
76
+ }),
77
+ getFilesForUpload: baseDrop.getFilesForUpload
78
+ };
79
+ return {
80
+ drop: {
81
+ ...baseDrop,
82
+ open: trackedSpies.open,
83
+ processFiles: trackedSpies.processFiles,
84
+ reset: trackedSpies.reset,
85
+ getFilesForUpload: trackedSpies.getFilesForUpload
86
+ },
87
+ spies: trackedSpies
88
+ };
89
+ }
90
+ var mockFileIdCounter = 0;
91
+ function createMockProcessedFile(name, options = {}) {
92
+ const {
93
+ path = name,
94
+ content = "test content",
95
+ type = "text/plain",
96
+ status = "ready",
97
+ statusMessage
98
+ } = options;
99
+ const file = new File([content], name, { type });
100
+ return {
101
+ id: `mock-file-${++mockFileIdCounter}`,
102
+ file,
103
+ path,
104
+ name,
105
+ size: file.size,
106
+ type,
107
+ lastModified: Date.now(),
108
+ status,
109
+ statusMessage
110
+ };
111
+ }
112
+ function createMockFile(name, content = "test content", type = "text/plain") {
113
+ return new File([content], name, { type, lastModified: Date.now() });
114
+ }
115
+ function createMockFileWithPath(name, webkitRelativePath, content = "test content", type = "text/plain") {
116
+ const file = createMockFile(name, content, type);
117
+ Object.defineProperty(file, "webkitRelativePath", {
118
+ value: webkitRelativePath,
119
+ writable: false,
120
+ enumerable: true,
121
+ configurable: true
122
+ });
123
+ return file;
124
+ }
125
+ function createMockErrorStatus(title = "Validation Failed", details = "One or more files failed validation", errors = []) {
126
+ return { title, details, errors };
127
+ }
128
+ function createMockProcessingStatus(title = "Processing...", details = "Validating and preparing files.") {
129
+ return { title, details };
130
+ }
131
+ function createMockReadyStatus(fileCount) {
132
+ return {
133
+ title: "Ready",
134
+ details: `${fileCount} file(s) are ready.`
135
+ };
136
+ }
137
+
138
+ export { createMockDrop, createMockDropWithSpies, createMockErrorStatus, createMockFile, createMockFileWithPath, createMockProcessedFile, createMockProcessingStatus, createMockReadyStatus };
139
+ //# sourceMappingURL=testing.js.map
140
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/testing.ts"],"names":[],"mappings":"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","file":"testing.js","sourcesContent":["/**\n * Test utilities for @shipstatic/drop\n *\n * Import from '@shipstatic/drop/testing' in your test files:\n *\n * ```typescript\n * import { createMockDrop, createMockFile } from '@shipstatic/drop/testing';\n * ```\n */\n\nimport type { ProcessedFile, DropStatus, DropStateValue } from './types';\nimport type { DropReturn, DropzonePropsOptions } from './hooks/useDrop';\n\n// ============================================================================\n// Mock Drop Hook Return\n// ============================================================================\n\n/**\n * Options for creating a mock drop return value\n */\nexport interface MockDropOptions {\n phase?: DropStateValue;\n files?: ProcessedFile[];\n sourceName?: string;\n status?: DropStatus | null;\n}\n\n/**\n * Creates a mock DropReturn for testing components that receive drop as a prop\n *\n * @example\n * ```tsx\n * import { createMockDrop, createMockProcessedFile } from '@shipstatic/drop/testing';\n *\n * it('renders file count when files are ready', () => {\n * const drop = createMockDrop({\n * phase: 'ready',\n * files: [createMockProcessedFile('index.html')],\n * });\n *\n * render(<DeployDropArea drop={drop} />);\n * expect(screen.getByText('1 files ready')).toBeInTheDocument();\n * });\n * ```\n */\nexport function createMockDrop(options: MockDropOptions = {}): DropReturn {\n const {\n phase = 'idle',\n files = [],\n sourceName = '',\n status = null,\n } = options;\n\n const validFiles = files.filter(f => f.status === 'ready');\n\n return {\n // State\n phase,\n isProcessing: phase === 'processing',\n isDragging: phase === 'dragging',\n isInteractive: phase === 'idle' || phase === 'dragging' || phase === 'ready',\n hasError: phase === 'error',\n files,\n validFiles,\n sourceName,\n status,\n\n // Prop getters - return minimal objects for spreading\n getDropzoneProps: (opts?: DropzonePropsOptions) => ({\n onDragOver: () => {},\n onDragLeave: () => {},\n onDrop: () => {},\n ...(opts?.clickable !== false && { onClick: () => {} }),\n }),\n getInputProps: () => ({\n ref: { current: null },\n type: 'file' as const,\n style: { display: 'none' },\n multiple: true,\n webkitdirectory: '',\n onChange: () => {},\n }),\n\n // Actions - no-op by default, can be spied on\n open: () => {},\n processFiles: async () => {},\n reset: () => {},\n\n // Helpers\n getFilesForUpload: () => validFiles.map(f => f.file),\n };\n}\n\n/**\n * Creates a mock drop with spy functions for testing interactions\n *\n * @example\n * ```tsx\n * import { createMockDropWithSpies } from '@shipstatic/drop/testing';\n *\n * it('calls reset when Clear button is clicked', async () => {\n * const { drop, spies } = createMockDropWithSpies({ phase: 'ready', files: [...] });\n *\n * render(<DeployDropArea drop={drop} />);\n * await userEvent.click(screen.getByText('Clear'));\n *\n * expect(spies.reset).toHaveBeenCalled();\n * });\n * ```\n */\nexport function createMockDropWithSpies(options: MockDropOptions = {}): {\n drop: DropReturn;\n spies: {\n open: () => void;\n processFiles: (files: File[]) => Promise<void>;\n reset: () => void;\n getFilesForUpload: () => File[];\n };\n} {\n const baseDrop = createMockDrop(options);\n\n const spies = {\n open: createNoopSpy(),\n processFiles: createAsyncNoopSpy(),\n reset: createNoopSpy(),\n getFilesForUpload: (() => baseDrop.getFilesForUpload()) as () => File[],\n };\n\n // Track calls manually (works without vitest in runtime)\n let openCalls = 0;\n let processFilesCalls: File[][] = [];\n let resetCalls = 0;\n\n const trackedSpies = {\n open: Object.assign(() => { openCalls++; }, {\n calls: () => openCalls,\n toHaveBeenCalled: () => openCalls > 0,\n }),\n processFiles: Object.assign(async (files: File[]) => { processFilesCalls.push(files); }, {\n calls: () => processFilesCalls,\n toHaveBeenCalled: () => processFilesCalls.length > 0,\n toHaveBeenCalledWith: (files: File[]) => processFilesCalls.some(c => c === files),\n }),\n reset: Object.assign(() => { resetCalls++; }, {\n calls: () => resetCalls,\n toHaveBeenCalled: () => resetCalls > 0,\n }),\n getFilesForUpload: baseDrop.getFilesForUpload,\n };\n\n return {\n drop: {\n ...baseDrop,\n open: trackedSpies.open,\n processFiles: trackedSpies.processFiles,\n reset: trackedSpies.reset,\n getFilesForUpload: trackedSpies.getFilesForUpload,\n },\n spies: trackedSpies,\n };\n}\n\n// ============================================================================\n// Mock File Utilities\n// ============================================================================\n\nlet mockFileIdCounter = 0;\n\n/**\n * Creates a mock ProcessedFile for testing\n */\nexport function createMockProcessedFile(\n name: string,\n options: {\n path?: string;\n content?: string;\n type?: string;\n status?: 'ready' | 'validation_failed' | 'processing_error' | 'empty_file';\n statusMessage?: string;\n } = {}\n): ProcessedFile {\n const {\n path = name,\n content = 'test content',\n type = 'text/plain',\n status = 'ready',\n statusMessage,\n } = options;\n\n const file = new File([content], name, { type });\n\n return {\n id: `mock-file-${++mockFileIdCounter}`,\n file,\n path,\n name,\n size: file.size,\n type,\n lastModified: Date.now(),\n status,\n statusMessage,\n };\n}\n\n/**\n * Creates a mock File object\n */\nexport function createMockFile(\n name: string,\n content: string = 'test content',\n type: string = 'text/plain'\n): File {\n return new File([content], name, { type, lastModified: Date.now() });\n}\n\n/**\n * Creates a mock File object with webkitRelativePath set\n */\nexport function createMockFileWithPath(\n name: string,\n webkitRelativePath: string,\n content: string = 'test content',\n type: string = 'text/plain'\n): File {\n const file = createMockFile(name, content, type);\n Object.defineProperty(file, 'webkitRelativePath', {\n value: webkitRelativePath,\n writable: false,\n enumerable: true,\n configurable: true,\n });\n return file;\n}\n\n// ============================================================================\n// Mock Status Utilities\n// ============================================================================\n\n/**\n * Creates a mock error status\n */\nexport function createMockErrorStatus(\n title: string = 'Validation Failed',\n details: string = 'One or more files failed validation',\n errors: string[] = []\n): DropStatus {\n return { title, details, errors };\n}\n\n/**\n * Creates a mock processing status\n */\nexport function createMockProcessingStatus(\n title: string = 'Processing...',\n details: string = 'Validating and preparing files.'\n): DropStatus {\n return { title, details };\n}\n\n/**\n * Creates a mock ready status\n */\nexport function createMockReadyStatus(fileCount: number): DropStatus {\n return {\n title: 'Ready',\n details: `${fileCount} file(s) are ready.`,\n };\n}\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\nfunction createNoopSpy(): () => void {\n return () => {};\n}\n\nfunction createAsyncNoopSpy(): () => Promise<void> {\n return async () => {};\n}\n"]}
@@ -0,0 +1,168 @@
1
+ import { ValidationError } from '@shipstatic/types';
2
+ import { Ship } from '@shipstatic/ship';
3
+
4
+ /**
5
+ * Core types for @shipstatic/drop
6
+ * Imports types from @shipstatic/types (single source of truth)
7
+ * and defines drop-specific types
8
+ */
9
+
10
+ /**
11
+ * Extended File interface with webkitRelativePath
12
+ * This property is set by browsers for folder uploads and drag-drop
13
+ * https://developer.mozilla.org/en-US/docs/Web/API/File/webkitRelativePath
14
+ */
15
+ interface FileWithPath extends File {
16
+ readonly webkitRelativePath: string;
17
+ }
18
+ declare const FILE_STATUSES: {
19
+ readonly PROCESSING: "processing";
20
+ readonly UPLOADING: "uploading";
21
+ readonly COMPLETE: "complete";
22
+ readonly ERROR: "error";
23
+ readonly PENDING: "pending";
24
+ readonly PROCESSING_ERROR: "processing_error";
25
+ readonly EMPTY_FILE: "empty_file";
26
+ readonly VALIDATION_FAILED: "validation_failed";
27
+ readonly READY: "ready";
28
+ };
29
+ type FileStatus = (typeof FILE_STATUSES)[keyof typeof FILE_STATUSES];
30
+ /**
31
+ * Client-side error structure
32
+ * Matches ValidationError from @shipstatic/ship for consistency
33
+ */
34
+ type ClientError = ValidationError;
35
+ /**
36
+ * Processed file entry ready for upload
37
+ * Contains both the File object and UI-specific metadata
38
+ * Use `file` property to access the underlying File for SDK operations
39
+ */
40
+ interface ProcessedFile {
41
+ /** Unique identifier for React keys and tracking */
42
+ id: string;
43
+ /** The File object - pass this to ship.deployments.create() */
44
+ file: File;
45
+ /** Relative path for deployment (e.g., "images/photo.jpg") */
46
+ path: string;
47
+ /** File size in bytes */
48
+ size: number;
49
+ /** MD5 hash (optional - Ship SDK calculates during deployment if not provided) */
50
+ md5?: string;
51
+ /** Filename without path */
52
+ name: string;
53
+ /** MIME type for UI icons/previews */
54
+ type: string;
55
+ /** Last modified timestamp */
56
+ lastModified: number;
57
+ /** Current processing/upload status */
58
+ status: FileStatus;
59
+ /** Human-readable status message for UI */
60
+ statusMessage?: string;
61
+ /** Upload progress (0-100) - only set during upload */
62
+ progress?: number;
63
+ }
64
+ /**
65
+ * State machine values for the drop hook
66
+ */
67
+ type DropStateValue = 'idle' | 'dragging' | 'processing' | 'ready' | 'error';
68
+ /**
69
+ * Status information with title and details
70
+ */
71
+ interface DropStatus {
72
+ title: string;
73
+ details: string;
74
+ errors?: string[];
75
+ }
76
+ /**
77
+ * State machine state for the drop hook
78
+ */
79
+ interface DropState {
80
+ value: DropStateValue;
81
+ files: ProcessedFile[];
82
+ sourceName: string;
83
+ status: DropStatus | null;
84
+ }
85
+
86
+ interface DropOptions {
87
+ /** Ship SDK instance (required for validation) */
88
+ ship: Ship;
89
+ /** Callback when files are processed and ready */
90
+ onFilesReady?: (files: ProcessedFile[]) => void;
91
+ /** Callback when validation fails */
92
+ onValidationError?: (error: ClientError) => void;
93
+ /** Whether to strip common directory prefix from paths (default: true) */
94
+ stripPrefix?: boolean;
95
+ }
96
+ /** Options for getDropzoneProps() */
97
+ interface DropzonePropsOptions {
98
+ /** Whether clicking the dropzone opens the file picker (default: true) */
99
+ clickable?: boolean;
100
+ }
101
+ interface DropReturn {
102
+ /** Current phase of the state machine */
103
+ phase: DropStateValue;
104
+ /** Whether currently processing files (ZIP extraction, etc.) */
105
+ isProcessing: boolean;
106
+ /** Whether user is currently dragging over the dropzone */
107
+ isDragging: boolean;
108
+ /** Whether the dropzone is interactive (idle, dragging, or ready - not processing or error) */
109
+ isInteractive: boolean;
110
+ /** Whether an error occurred during processing */
111
+ hasError: boolean;
112
+ /** Flattened access to files */
113
+ files: ProcessedFile[];
114
+ /** Flattened access to source name */
115
+ sourceName: string;
116
+ /** Flattened access to status */
117
+ status: {
118
+ title: string;
119
+ details: string;
120
+ errors?: string[];
121
+ } | null;
122
+ /** Get props to spread on dropzone element (handles drag & drop, optionally click) */
123
+ getDropzoneProps: (options?: DropzonePropsOptions) => {
124
+ onDragOver: (e: React.DragEvent) => void;
125
+ onDragLeave: (e: React.DragEvent) => void;
126
+ onDrop: (e: React.DragEvent) => void;
127
+ onClick?: () => void;
128
+ };
129
+ /** Get props to spread on hidden file input element */
130
+ getInputProps: () => {
131
+ ref: React.RefObject<HTMLInputElement | null>;
132
+ type: 'file';
133
+ style: {
134
+ display: string;
135
+ };
136
+ multiple: boolean;
137
+ webkitdirectory: string;
138
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
139
+ };
140
+ /** Programmatically trigger file picker */
141
+ open: () => void;
142
+ /** Manually process files (for advanced usage) */
143
+ processFiles: (files: File[]) => Promise<void>;
144
+ /** Reset state and clear all files */
145
+ reset: () => void;
146
+ /** Get only valid files ready for upload */
147
+ validFiles: ProcessedFile[];
148
+ /** Get raw File objects ready for Ship SDK upload */
149
+ getFilesForUpload: () => File[];
150
+ }
151
+ /**
152
+ * Headless drop hook for file upload workflows
153
+ *
154
+ * @example
155
+ * ```tsx
156
+ * const drop = useDrop({ ship });
157
+ *
158
+ * return (
159
+ * <div {...drop.getDropzoneProps()} style={{...}}>
160
+ * <input {...drop.getInputProps()} />
161
+ * {drop.isDragging ? "📂 Drop" : "📁 Click"}
162
+ * </div>
163
+ * );
164
+ * ```
165
+ */
166
+ declare function useDrop(options: DropOptions): DropReturn;
167
+
168
+ export { type ClientError as C, type DropOptions as D, FILE_STATUSES as F, type ProcessedFile as P, type DropReturn as a, type DropState as b, type DropStateValue as c, type DropStatus as d, type DropzonePropsOptions as e, type FileStatus as f, type FileWithPath as g, useDrop as u };
@@ -0,0 +1,168 @@
1
+ import { ValidationError } from '@shipstatic/types';
2
+ import { Ship } from '@shipstatic/ship';
3
+
4
+ /**
5
+ * Core types for @shipstatic/drop
6
+ * Imports types from @shipstatic/types (single source of truth)
7
+ * and defines drop-specific types
8
+ */
9
+
10
+ /**
11
+ * Extended File interface with webkitRelativePath
12
+ * This property is set by browsers for folder uploads and drag-drop
13
+ * https://developer.mozilla.org/en-US/docs/Web/API/File/webkitRelativePath
14
+ */
15
+ interface FileWithPath extends File {
16
+ readonly webkitRelativePath: string;
17
+ }
18
+ declare const FILE_STATUSES: {
19
+ readonly PROCESSING: "processing";
20
+ readonly UPLOADING: "uploading";
21
+ readonly COMPLETE: "complete";
22
+ readonly ERROR: "error";
23
+ readonly PENDING: "pending";
24
+ readonly PROCESSING_ERROR: "processing_error";
25
+ readonly EMPTY_FILE: "empty_file";
26
+ readonly VALIDATION_FAILED: "validation_failed";
27
+ readonly READY: "ready";
28
+ };
29
+ type FileStatus = (typeof FILE_STATUSES)[keyof typeof FILE_STATUSES];
30
+ /**
31
+ * Client-side error structure
32
+ * Matches ValidationError from @shipstatic/ship for consistency
33
+ */
34
+ type ClientError = ValidationError;
35
+ /**
36
+ * Processed file entry ready for upload
37
+ * Contains both the File object and UI-specific metadata
38
+ * Use `file` property to access the underlying File for SDK operations
39
+ */
40
+ interface ProcessedFile {
41
+ /** Unique identifier for React keys and tracking */
42
+ id: string;
43
+ /** The File object - pass this to ship.deployments.create() */
44
+ file: File;
45
+ /** Relative path for deployment (e.g., "images/photo.jpg") */
46
+ path: string;
47
+ /** File size in bytes */
48
+ size: number;
49
+ /** MD5 hash (optional - Ship SDK calculates during deployment if not provided) */
50
+ md5?: string;
51
+ /** Filename without path */
52
+ name: string;
53
+ /** MIME type for UI icons/previews */
54
+ type: string;
55
+ /** Last modified timestamp */
56
+ lastModified: number;
57
+ /** Current processing/upload status */
58
+ status: FileStatus;
59
+ /** Human-readable status message for UI */
60
+ statusMessage?: string;
61
+ /** Upload progress (0-100) - only set during upload */
62
+ progress?: number;
63
+ }
64
+ /**
65
+ * State machine values for the drop hook
66
+ */
67
+ type DropStateValue = 'idle' | 'dragging' | 'processing' | 'ready' | 'error';
68
+ /**
69
+ * Status information with title and details
70
+ */
71
+ interface DropStatus {
72
+ title: string;
73
+ details: string;
74
+ errors?: string[];
75
+ }
76
+ /**
77
+ * State machine state for the drop hook
78
+ */
79
+ interface DropState {
80
+ value: DropStateValue;
81
+ files: ProcessedFile[];
82
+ sourceName: string;
83
+ status: DropStatus | null;
84
+ }
85
+
86
+ interface DropOptions {
87
+ /** Ship SDK instance (required for validation) */
88
+ ship: Ship;
89
+ /** Callback when files are processed and ready */
90
+ onFilesReady?: (files: ProcessedFile[]) => void;
91
+ /** Callback when validation fails */
92
+ onValidationError?: (error: ClientError) => void;
93
+ /** Whether to strip common directory prefix from paths (default: true) */
94
+ stripPrefix?: boolean;
95
+ }
96
+ /** Options for getDropzoneProps() */
97
+ interface DropzonePropsOptions {
98
+ /** Whether clicking the dropzone opens the file picker (default: true) */
99
+ clickable?: boolean;
100
+ }
101
+ interface DropReturn {
102
+ /** Current phase of the state machine */
103
+ phase: DropStateValue;
104
+ /** Whether currently processing files (ZIP extraction, etc.) */
105
+ isProcessing: boolean;
106
+ /** Whether user is currently dragging over the dropzone */
107
+ isDragging: boolean;
108
+ /** Whether the dropzone is interactive (idle, dragging, or ready - not processing or error) */
109
+ isInteractive: boolean;
110
+ /** Whether an error occurred during processing */
111
+ hasError: boolean;
112
+ /** Flattened access to files */
113
+ files: ProcessedFile[];
114
+ /** Flattened access to source name */
115
+ sourceName: string;
116
+ /** Flattened access to status */
117
+ status: {
118
+ title: string;
119
+ details: string;
120
+ errors?: string[];
121
+ } | null;
122
+ /** Get props to spread on dropzone element (handles drag & drop, optionally click) */
123
+ getDropzoneProps: (options?: DropzonePropsOptions) => {
124
+ onDragOver: (e: React.DragEvent) => void;
125
+ onDragLeave: (e: React.DragEvent) => void;
126
+ onDrop: (e: React.DragEvent) => void;
127
+ onClick?: () => void;
128
+ };
129
+ /** Get props to spread on hidden file input element */
130
+ getInputProps: () => {
131
+ ref: React.RefObject<HTMLInputElement | null>;
132
+ type: 'file';
133
+ style: {
134
+ display: string;
135
+ };
136
+ multiple: boolean;
137
+ webkitdirectory: string;
138
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
139
+ };
140
+ /** Programmatically trigger file picker */
141
+ open: () => void;
142
+ /** Manually process files (for advanced usage) */
143
+ processFiles: (files: File[]) => Promise<void>;
144
+ /** Reset state and clear all files */
145
+ reset: () => void;
146
+ /** Get only valid files ready for upload */
147
+ validFiles: ProcessedFile[];
148
+ /** Get raw File objects ready for Ship SDK upload */
149
+ getFilesForUpload: () => File[];
150
+ }
151
+ /**
152
+ * Headless drop hook for file upload workflows
153
+ *
154
+ * @example
155
+ * ```tsx
156
+ * const drop = useDrop({ ship });
157
+ *
158
+ * return (
159
+ * <div {...drop.getDropzoneProps()} style={{...}}>
160
+ * <input {...drop.getInputProps()} />
161
+ * {drop.isDragging ? "📂 Drop" : "📁 Click"}
162
+ * </div>
163
+ * );
164
+ * ```
165
+ */
166
+ declare function useDrop(options: DropOptions): DropReturn;
167
+
168
+ export { type ClientError as C, type DropOptions as D, FILE_STATUSES as F, type ProcessedFile as P, type DropReturn as a, type DropState as b, type DropStateValue as c, type DropStatus as d, type DropzonePropsOptions as e, type FileStatus as f, type FileWithPath as g, useDrop as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipstatic/drop",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Headless React hook for file dropping, processing, ZIP extraction, and validation - purpose-built for Ship SDK",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -16,6 +16,16 @@
16
16
  "types": "./dist/index.d.cts",
17
17
  "default": "./dist/index.cjs"
18
18
  }
19
+ },
20
+ "./testing": {
21
+ "import": {
22
+ "types": "./dist/testing.d.ts",
23
+ "default": "./dist/testing.js"
24
+ },
25
+ "require": {
26
+ "types": "./dist/testing.d.cts",
27
+ "default": "./dist/testing.cjs"
28
+ }
19
29
  }
20
30
  },
21
31
  "files": [