@expresscsv/sdk 0.1.4 → 0.1.6

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ExpressCSV
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,9 @@
1
- # ExpressCSV Importer
1
+ # @expresscsv/sdk
2
2
 
3
- A TypeScript SDK for integrating the ExpressCSV widget into your web application.
3
+ [![npm version](https://img.shields.io/npm/v/@expresscsv/sdk.svg)](https://www.npmjs.com/package/@expresscsv/sdk)
4
+ ![license](https://img.shields.io/npm/l/@expresscsv/sdk.svg)
5
+
6
+ A TypeScript SDK for embedding the [ExpressCSV](https://expresscsv.com) CSV import widget into any web application. Define a schema, open the widget, and receive validated, typed data in chunks.
4
7
 
5
8
  ## Installation
6
9
 
@@ -15,191 +18,475 @@ npm install @expresscsv/sdk
15
18
  yarn add @expresscsv/sdk
16
19
  ```
17
20
 
18
- ## Usage
21
+ > **Looking for React?** Use [`@expresscsv/react`](https://www.npmjs.com/package/@expresscsv/react) for a hook-based integration.
19
22
 
20
- ### Basic Usage
23
+ ## Quick Start
21
24
 
22
25
  ```typescript
23
- import { CSVImporter } from "@expresscsv/sdk";
26
+ import { CSVImporter, x } from "@expresscsv/sdk";
24
27
 
25
- // Initialize the importer
26
- const importer = new CSVImporter({
27
- title: "CSV Import",
28
+ const schema = x.row({
29
+ name: x.string().label("Full Name"),
30
+ email: x.string().email().label("Email Address"),
31
+ age: x.number().label("Age").min(18).max(120),
28
32
  });
29
33
 
30
- // Open the widget
31
- await importer.open();
34
+ const importer = new CSVImporter({
35
+ schema,
36
+ publishableKey: "your-publishable-key",
37
+ importIdentifier: "user-import",
38
+ title: "Import Users",
39
+ });
32
40
 
33
- // Send a message to the widget
34
- await importer.sendMessage({
35
- type: "example",
36
- payload: {
37
- /* your data */
41
+ // Open the widget and process data in chunks
42
+ importer.open({
43
+ onData: (chunk, next) => {
44
+ console.log(`Chunk ${chunk.currentChunkIndex + 1}/${chunk.totalChunks}`);
45
+ console.log("Records:", chunk.records);
46
+ // Process your validated, typed records here
47
+ next();
48
+ },
49
+ onComplete: () => {
50
+ console.log("All chunks processed successfully");
51
+ },
52
+ onError: (error) => {
53
+ console.error("Import failed:", error);
38
54
  },
39
55
  });
40
-
41
- // Clean up when done
42
- importer.close();
43
56
  ```
44
57
 
45
- ### Field Schema Validation
58
+ Your `publishableKey` is available from the [ExpressCSV dashboard](https://expresscsv.com). Two key types are available: **production** keys for live usage, and **dev/testing** keys that provide unlimited test imports.
59
+
60
+ ## Webhook Delivery
46
61
 
47
- ExpressCSV SDK provides a Zod-like schema validation system for CSV imports. This allows you to define the expected structure of your CSV data and receive type-safe results.
62
+ Deliver imported data directly to your backend via webhook instead of (or in addition to) a local callback:
48
63
 
49
64
  ```typescript
50
- import { CSVImporter, x } from "@expresscsv/sdk";
65
+ importer.open({
66
+ webhook: {
67
+ url: "https://api.example.com/webhooks/csv-import",
68
+ method: "POST",
69
+ headers: {
70
+ Authorization: "Bearer your-api-token",
71
+ },
72
+ metadata: {
73
+ source: "web-app",
74
+ userId: "user-123",
75
+ },
76
+ },
77
+ onComplete: () => {
78
+ console.log("Webhook delivery initiated");
79
+ },
80
+ onError: (error) => {
81
+ console.error("Delivery error:", error);
82
+ },
83
+ });
84
+ ```
51
85
 
52
- // Define your schema
53
- const schema = x.row({
54
- name: x.string().humanLabel("Full Name"),
55
- email: x.email().humanLabel("Email Address"),
56
- age: x.number().humanLabel("Age").min(18).max(100),
57
- role: x.select(["admin", "user", "guest"]).humanLabel("User Role")
86
+ ### Combined Local Callback and Webhook
87
+
88
+ You can use both `onData` and `webhook` simultaneously:
89
+
90
+ ```typescript
91
+ importer.open({
92
+ chunkSize: 500,
93
+ onData: async (chunk, next) => {
94
+ await saveToLocalDatabase(chunk.records);
95
+ next();
96
+ },
97
+ webhook: {
98
+ url: "https://api.example.com/webhooks/csv-import",
99
+ headers: { Authorization: "Bearer your-api-token" },
100
+ },
101
+ onComplete: () => {
102
+ console.log("Local processing and webhook delivery complete");
103
+ },
58
104
  });
105
+ ```
106
+
107
+ ## Preloading
108
+
109
+ By default, the SDK preloads the widget in a hidden iframe for instant display when `open()` is called. This provides the best user experience.
59
110
 
60
- // Initialize with schema and results handler
111
+ ```typescript
112
+ // Preload is enabled by default
61
113
  const importer = new CSVImporter({
62
- title: "User Import",
63
- fields: schema,
64
- onResults: (data) => {
65
- // data is fully typed based on your schema
66
- console.log(`Imported ${data.length} users`);
67
-
68
- // Access properties with full type safety
69
- data.forEach(user => {
70
- console.log(`Name: ${user.name}, Email: ${user.email}`);
71
- if (user.age) {
72
- console.log(`Age: ${user.age + 1}`); // Numbers are typed correctly
73
- }
74
- });
75
- }
114
+ schema,
115
+ publishableKey: "your-publishable-key",
116
+ importIdentifier: "user-import",
76
117
  });
77
118
 
78
- // Or set the handler separately
79
- importer.onResults = (data) => {
80
- // Process the validated data
81
- processUsers(data);
82
- };
119
+ // Later, the widget appears instantly
120
+ importer.open({ onData: (chunk, next) => { /* ... */ next(); } });
121
+ ```
122
+
123
+ To disable preloading (there will be a brief loading screen instead):
83
124
 
84
- // Open the widget
85
- await importer.open();
125
+ ```typescript
126
+ const importer = new CSVImporter({
127
+ schema,
128
+ publishableKey: "your-publishable-key",
129
+ importIdentifier: "user-import",
130
+ preload: false,
131
+ });
86
132
  ```
87
133
 
88
- ### Preload for Instant Display
134
+ ## Theming and Styling
135
+
136
+ Customize the widget's appearance with the `theme`, `colorMode`, `customCSS`, and `fonts` options.
89
137
 
90
- By default, the SDK preloads the widget in the background for instant display when opened. This provides the best user experience with minimal perceived loading time.
138
+ ### Theme
139
+
140
+ Use the `theme` option to override CSS variables (colors, radius, typography). Pass either a single theme (applies to both light and dark) or a dual-mode theme with separate light/dark values:
91
141
 
92
142
  ```typescript
93
- import { CSVImporter, x } from "@expresscsv/sdk";
143
+ import { CSVImporter, x, type ECSVTheme } from "@expresscsv/sdk";
144
+
145
+ // Single theme (both modes)
146
+ const theme: ECSVTheme = {
147
+ primary: "#4F46E5",
148
+ "primary-foreground": "#ffffff",
149
+ background: "#ffffff",
150
+ foreground: "#0f172a",
151
+ border: "#e5e7eb",
152
+ ring: "#A5B4FC",
153
+ radius: "0.5rem",
154
+ };
94
155
 
95
- const schema = x.row({
96
- name: x.string().humanLabel("Full Name"),
97
- email: x.email().humanLabel("Email Address"),
156
+ // Dual-mode (light and dark)
157
+ const dualTheme: ECSVTheme = {
158
+ modes: {
159
+ light: {
160
+ primary: "#4F46E5",
161
+ background: "#ffffff",
162
+ foreground: "#0f172a",
163
+ },
164
+ dark: {
165
+ primary: "#a5b4fc",
166
+ background: "#09090b",
167
+ foreground: "#fafafa",
168
+ },
169
+ },
170
+ };
171
+
172
+ const importer = new CSVImporter({
173
+ schema,
174
+ publishableKey: "your-publishable-key",
175
+ importIdentifier: "user-import",
176
+ theme,
98
177
  });
178
+ ```
99
179
 
100
- // Preload is enabled by default - widget loads in background
180
+ Theme variables:
181
+
182
+ | Variable | Default |
183
+ |---|---|
184
+ | `radius` | `0.625rem` |
185
+ | `background` | `oklch(1 0 0)` |
186
+ | `foreground` | `oklch(0.145 0 0)` |
187
+ | `card` | `oklch(1 0 0)` |
188
+ | `card-foreground` | `oklch(0.145 0 0)` |
189
+ | `popover` | `oklch(1 0 0)` |
190
+ | `popover-foreground` | `oklch(0.145 0 0)` |
191
+ | `primary` | `oklch(0.205 0 0)` |
192
+ | `primary-foreground` | `oklch(0.985 0 0)` |
193
+ | `secondary` | `oklch(0.97 0 0)` |
194
+ | `secondary-foreground` | `oklch(0.205 0 0)` |
195
+ | `muted` | `oklch(0.97 0 0)` |
196
+ | `muted-foreground` | `oklch(0.556 0 0)` |
197
+ | `accent` | `oklch(0.7 0.2 145)` |
198
+ | `accent-foreground` | `oklch(0.985 0 0)` |
199
+ | `destructive` | `oklch(0.577 0.245 27.325)` |
200
+ | `destructive-foreground` | `oklch(0.985 0 0)` |
201
+ | `success` | `oklch(0.7 0.2 145)` |
202
+ | `success-foreground` | `oklch(0.985 0 0)` |
203
+ | `warning` | `oklch(0.769 0.188 70)` |
204
+ | `warning-foreground` | `oklch(0.985 0 0)` |
205
+ | `border` | `oklch(0.922 0 0)` |
206
+ | `input` | `oklch(0.922 0 0)` |
207
+ | `ring` | `oklch(0.708 0 0)` |
208
+ | `font-title` | `inherit` |
209
+ | `font-body` | `inherit` |
210
+
211
+ ### Color Mode
212
+
213
+ Control light/dark mode with `colorMode`:
214
+
215
+ ```typescript
101
216
  const importer = new CSVImporter({
102
217
  schema,
218
+ publishableKey: "your-publishable-key",
103
219
  importIdentifier: "user-import",
104
- title: "User Import",
220
+ colorMode: "system", // 'light' | 'dark' | 'system'
105
221
  });
222
+ ```
223
+
224
+ ### Custom CSS
225
+
226
+ Inject custom CSS for fine-grained styling overrides.
106
227
 
107
- // Later, when user clicks import button, widget displays instantly
108
- await importer.open();
228
+ ```typescript
229
+ const importer = new CSVImporter({
230
+ schema,
231
+ publishableKey: "your-publishable-key",
232
+ importIdentifier: "user-import",
233
+ customCSS: `
234
+ .ecsv [data-step="upload"] {
235
+ border-radius: 1rem;
236
+ }
237
+ .ecsv button {
238
+ font-weight: 600;
239
+ }
240
+ `,
241
+ });
109
242
  ```
110
243
 
111
- To disable preload (not recommended for most use cases):
244
+ ### Custom Fonts
245
+
246
+ Load custom fonts via the `fonts` option:
112
247
 
113
248
  ```typescript
114
249
  const importer = new CSVImporter({
115
250
  schema,
251
+ publishableKey: "your-publishable-key",
116
252
  importIdentifier: "user-import",
117
- title: "User Import",
118
- preload: false, // Opt-out of preload
253
+ fonts: {
254
+ title: { source: "google", name: "Space Grotesk", weights: [400, 600, 700] },
255
+ body: { source: "custom", url: "https://example.com/font.woff2", format: "woff2" },
256
+ },
257
+ theme: {
258
+ "font-title": "'Space Grotesk', sans-serif",
259
+ "font-body": "'Custom Font', sans-serif",
260
+ },
261
+ });
262
+ ```
263
+
264
+ ## Schema Builder
265
+
266
+ The `x` schema builder provides a type-safe, fluent API for defining your CSV structure.
267
+
268
+ ### Field Types
269
+
270
+ ```typescript
271
+ import { x } from "@expresscsv/sdk";
272
+
273
+ const schema = x.row({
274
+ // Strings with validation
275
+ name: x.string().label("Full Name").min(2).max(100),
276
+ email: x.string().email().label("Email"),
277
+ website: x.string().url().label("Website").optional(),
278
+
279
+ // Numbers with constraints
280
+ age: x.number().label("Age").min(0).max(150).integer(),
281
+ salary: x.number().currency("USD").label("Salary").min(0),
282
+
283
+ // Boolean
284
+ isActive: x.boolean().label("Active"),
285
+
286
+ // Dates and times
287
+ startDate: x.date().label("Start Date"),
288
+ createdAt: x.datetime().label("Created At"),
289
+
290
+ // Single selection (requires { label, value } objects)
291
+ role: x.select([
292
+ { label: "Admin", value: "admin" },
293
+ { label: "Editor", value: "editor" },
294
+ { label: "Viewer", value: "viewer" },
295
+ ]).label("Role"),
296
+
297
+ // Multi-selection
298
+ tags: x.multiselect([
299
+ { label: "Engineering", value: "eng" },
300
+ { label: "Design", value: "design" },
301
+ { label: "Marketing", value: "mkt" },
302
+ ]).label("Tags"),
119
303
  });
120
304
  ```
121
305
 
306
+ ### Common Modifiers
307
+
308
+ All field types support:
309
+
310
+ | Modifier | Description |
311
+ |---|---|
312
+ | `.label(text)` | User-facing label shown in the widget |
313
+ | `.description(text)` | Help text for the field |
314
+ | `.example(text)` | Example value shown as placeholder |
315
+ | `.optional()` | Makes the field optional (default is required) |
316
+ | `.refine(fn)` | Custom validation function |
317
+
318
+ ### String Modifiers
319
+
320
+ | Modifier | Description |
321
+ |---|---|
322
+ | `.email()` | Validates email format |
323
+ | `.url()` | Validates URL format |
324
+ | `.uuid()` | Validates UUID format |
325
+ | `.ip()` | Validates IP address |
326
+ | `.phone()` | Validates phone number |
327
+ | `.regex(pattern)` | Matches a regular expression |
328
+ | `.min(n)` | Minimum string length |
329
+ | `.max(n)` | Maximum string length |
330
+ | `.length(n)` | Exact string length |
331
+ | `.includes(str)` | Must contain substring |
332
+ | `.startsWith(str)` | Must start with prefix |
333
+ | `.endsWith(str)` | Must end with suffix |
334
+
335
+ ### Number Modifiers
336
+
337
+ | Modifier | Description |
338
+ |---|---|
339
+ | `.min(n)` | Minimum value |
340
+ | `.max(n)` | Maximum value |
341
+ | `.integer()` | Must be a whole number |
342
+ | `.multipleOf(n)` | Must be a multiple of n |
343
+ | `.currency(code)` | Formats as currency (e.g. `"USD"`) |
344
+ | `.percentage()` | Formats as percentage |
345
+
122
346
  ## API Reference
123
347
 
124
- ### Constructor Options
348
+ ### `CSVImporter`
125
349
 
126
- | Option | Type | Required | Default | Description |
127
- |------------------|------------------------|----------|---------|--------------------------------------------|
128
- | schema | ExType | Yes | - | Schema definition for CSV field validation |
129
- | importIdentifier | string | Yes | - | Unique identifier for this import |
130
- | title | string | No | - | Title to display in the widget |
131
- | publishableKey | string | Yes | - | Publishable key for webhook authentication |
132
- | debug | boolean | No | false | Enable debug logging |
133
- | developerMode | boolean | No | false | Enable developer mode features |
134
- | preload | boolean | No | true | Preload widget in background for instant display. Set to `false` to disable |
350
+ #### Constructor
135
351
 
136
- ### Methods
352
+ ```typescript
353
+ new CSVImporter(options: SDKOptions)
354
+ ```
137
355
 
138
- #### `open(): Promise<void>`
356
+ | Option | Type | Required | Default | Description |
357
+ |---|---|---|---|---|
358
+ | `schema` | Schema | Yes | - | Schema definition created with `x.row()` |
359
+ | `publishableKey` | `string` | Yes | - | Your publishable key from the [dashboard](https://expresscsv.com) |
360
+ | `importIdentifier` | `string` | Yes | - | Unique identifier for this import type |
361
+ | `title` | `string` | No | - | Title shown in the widget header |
362
+ | `preload` | `boolean` | No | `true` | Preload widget for instant display |
363
+ | `debug` | `boolean` | No | `false` | Enable debug logging |
364
+ | `developerMode` | `boolean` | No | `false` | Enable developer mode features |
365
+ | `theme` | `ECSVTheme` | No | - | Custom theme configuration |
366
+ | `colorMode` | `ColorModePref` | No | - | Light/dark mode (`'light'`, `'dark'`, or `'system'`) |
367
+ | `customCSS` | `string` | No | - | Custom CSS to inject into the widget |
368
+ | `fonts` | `Record<string, ECSVFontSource>` | No | - | Custom font sources |
369
+ | `stepDisplay` | `'progressBar' \| 'segmented' \| 'numbered'` | No | `'progressBar'` | Step indicator style |
370
+ | `previewSchemaBeforeUpload` | `boolean` | No | `true` | Show schema preview before upload |
371
+ | `templateDownload` | `TemplateDownloadConfig` | No | - | Template download configuration |
372
+ | `saveSession` | `boolean` | No | - | Persist session state |
373
+ | `locale` | `DeepPartial<ExpressCSVLocaleInput>` | No | - | Localization overrides |
374
+
375
+ #### `open(options)`
376
+
377
+ Opens the widget and begins the import flow. At least one of `onData` or `webhook` must be provided.
139
378
 
140
- Opens the ExpressCSV widget as a modal and establishes communication using iframe-comm.
379
+ ```typescript
380
+ importer.open(options: OpenOptions): void
381
+ ```
141
382
 
142
- #### `sendMessage(message: Message): Promise<void>`
383
+ | Option | Type | Required | Description |
384
+ |---|---|---|---|
385
+ | `onData` | `(chunk: RecordsChunk<T>, next: () => void) => void` | * | Callback for each chunk of records. Call `next()` to continue. |
386
+ | `webhook` | `WebhookConfig` | * | Webhook endpoint for server-side delivery |
387
+ | `chunkSize` | `number` | No | Records per chunk (default: 1000) |
388
+ | `onComplete` | `() => void` | No | Called when all chunks have been processed |
389
+ | `onCancel` | `() => void` | No | Called when the user cancels the import |
390
+ | `onError` | `(error: Error) => void` | No | Called when an error occurs |
391
+ | `onWidgetOpen` | `() => void` | No | Called when the widget opens |
392
+ | `onWidgetClose` | `(reason: string) => void` | No | Called when the widget closes |
393
+ | `onStepChange` | `(stepId, previousStepId?) => void` | No | Called when the wizard step changes |
143
394
 
144
- Sends a message to the ExpressCSV widget.
395
+ \* At least one of `onData` or `webhook` is required.
145
396
 
146
- #### `close(): void`
397
+ #### `close(reason?)`
147
398
 
148
399
  Closes the widget and cleans up resources.
149
400
 
150
- #### `getConnectionStatus(): boolean`
401
+ ```typescript
402
+ await importer.close(reason?: 'user_close' | 'cancel' | 'complete' | 'error'): Promise<void>
403
+ ```
404
+
405
+ #### Status Methods
151
406
 
152
- Returns whether the widget is currently connected.
153
407
 
154
- #### `getVersion(): string`
155
408
 
156
- Returns the current version of the SDK.
409
+ | Method | Returns | Description |
410
+ |---|---|---|
411
+ | `getState()` | `WidgetState` | Current widget state |
412
+ | `getIsReady()` | `boolean` | Whether the widget is ready or open |
413
+ | `getIsOpen()` | `boolean` | Whether the widget is currently open |
414
+ | `getConnectionStatus()` | `boolean` | Whether the iframe connection is active |
415
+ | `getCanRestart()` | `boolean` | Whether the widget can be restarted |
416
+ | `getLastError()` | `Error \| null` | Last error, if any |
417
+ | `getStatus()` | `object` | Comprehensive status snapshot |
418
+ | `getVersion()` | `string` | SDK version |
157
419
 
158
- ### Event Handlers
420
+ #### `restart(newOptions?)`
159
421
 
160
- | Handler | Type | Description |
161
- |--------------|----------------------|------------------------------------------------|
162
- | onConnected | () => void | Called when connection is established |
163
- | onError | (error: Error) => void | Called when an error occurs |
164
- | onClose | () => void | Called when the widget is closed |
165
- | onResults | (data: T[]) => void | Called with schema-validated CSV data |
422
+ Restarts the widget, optionally with updated options. Returns `Promise<void>`.
166
423
 
167
- ### Schema Builder API
424
+ ### `RecordsChunk<T>`
168
425
 
169
- The SDK exports a Zod-like API for defining field validation schemas:
426
+ The object passed to `onData` callbacks:
170
427
 
171
- #### Field Types
428
+ ```typescript
429
+ interface RecordsChunk<T> {
430
+ records: T[]; // Automatically typed to your schema
431
+ totalChunks: number;
432
+ currentChunkIndex: number;
433
+ totalRecords: number;
434
+ }
435
+ ```
172
436
 
173
- - `x.string()` - String fields
174
- - `x.number()` - Number fields
175
- - `x.email()` - Email fields
176
- - `x.select(['option1', 'option2'])` - Dropdown selection fields
437
+ ### `WebhookConfig`
177
438
 
178
- #### Field Modifiers
439
+ ```typescript
440
+ interface WebhookConfig {
441
+ url: string;
442
+ headers?: Record<string, string>;
443
+ method?: "POST" | "PUT" | "PATCH";
444
+ timeout?: number;
445
+ retries?: number;
446
+ metadata?: Record<string, unknown>;
447
+ }
448
+ ```
179
449
 
180
- - `.humanLabel(string)` - Sets a user-friendly label for the field
181
- - `.required(boolean)` - Marks a field as required
182
- - `.minLength(number)` - Sets minimum length for string fields
183
- - `.maxLength(number)` - Sets maximum length for string fields
184
- - `.min(number)` - Sets minimum value for number fields
185
- - `.max(number)` - Sets maximum value for number fields
450
+ ## TypeScript
186
451
 
187
- ## Development
452
+ The SDK is written in TypeScript and provides full type inference from your schema:
188
453
 
189
- ```bash
190
- # Install dependencies
191
- pnpm install
454
+ ```typescript
455
+ import { CSVImporter, x, type Infer } from "@expresscsv/sdk";
456
+
457
+ const schema = x.row({
458
+ name: x.string(),
459
+ age: x.number(),
460
+ email: x.string().email().optional(),
461
+ });
192
462
 
193
- # Start development mode
194
- pnpm dev
463
+ type Row = Infer<typeof schema>;
464
+ // { name: string; age: number; email?: string }
195
465
 
196
- # Build the package
197
- pnpm build
466
+ const importer = new CSVImporter({
467
+ schema,
468
+ publishableKey: "your-publishable-key",
469
+ importIdentifier: "user-import",
470
+ });
198
471
 
199
- # Run linting
200
- pnpm lint
472
+ importer.open({
473
+ onData: (chunk, next) => {
474
+ // chunk.records is fully typed as Row[]
475
+ for (const row of chunk.records) {
476
+ console.log(row.name); // string
477
+ console.log(row.age); // number
478
+ console.log(row.email); // string | undefined
479
+ }
480
+ next();
481
+ },
482
+ });
201
483
  ```
202
484
 
485
+ ## Resources
486
+
487
+ - [ExpressCSV Dashboard](https://expresscsv.com) -- manage your imports and API keys
488
+ - [`@expresscsv/react`](https://www.npmjs.com/package/@expresscsv/react) -- React hook wrapper
489
+
203
490
  ## License
204
491
 
205
- ISC
492
+ [MIT](./LICENSE)
package/dist/index.d.mts CHANGED
@@ -4,15 +4,6 @@ declare interface BICOptions {
4
4
 
5
5
  declare type BooleanControlType = 'toggle' | 'checkbox' | 'dropdown';
6
6
 
7
- /**
8
- * Color mode configuration
9
- */
10
- export declare interface ColorModeConfig {
11
- default?: ColorModePref;
12
- persist?: boolean;
13
- onChange?: (mode: ColorModePref) => void;
14
- }
15
-
16
7
  /**
17
8
  * Color mode preference
18
9
  */
@@ -1326,7 +1317,7 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1326
1317
  private createAndAppendIframe;
1327
1318
  private destroy;
1328
1319
  close(reason?: 'user_close' | 'cancel' | 'complete' | 'error'): Promise<void>;
1329
- resetWidget(): Promise<void>;
1320
+ private resetWidget;
1330
1321
  restart(newOptions?: Partial<SDKOptions<TSchema>>): Promise<void>;
1331
1322
  private handleWidgetClosed;
1332
1323
  private hideContainer;
@@ -3755,7 +3746,7 @@ export declare interface SDKOptions<TSchema extends ExType<unknown, ExBaseDef, u
3755
3746
  developerMode?: boolean;
3756
3747
  preload?: boolean;
3757
3748
  theme?: ECSVTheme;
3758
- colorMode?: ColorModeConfig;
3749
+ colorMode?: ColorModePref;
3759
3750
  customCSS?: string;
3760
3751
  fonts?: Record<string, ECSVFontSource>;
3761
3752
  stepDisplay?: 'progressBar' | 'segmented' | 'numbered';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expresscsv/sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "SDK for integrating widget",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.mts",
@@ -1,6 +0,0 @@
1
- /**
2
- * Error thrown when the user cancels the import operation
3
- */
4
- export declare class ImportCancelledError extends Error {
5
- constructor(message?: string);
6
- }
@@ -1,207 +0,0 @@
1
- import { type ExRow, type ExRowShape, type ExType, type Infer, x } from '@expresscsv/fields';
2
- import { WidgetMode, WidgetState } from '@expresscsv/iframe-comm';
3
- import type { ExBaseDef } from '@expresscsv/fields';
4
- import type { ColorModeConfig, ECSVFontSource, ECSVTheme } from '@expresscsv/theme';
5
- export { ImportCancelledError } from './errors';
6
- /**
7
- * A chunk of records passed to the onData callback
8
- */
9
- export interface RecordsChunk<T> {
10
- /** The records in this chunk */
11
- records: T[];
12
- /** Total number of chunks */
13
- totalChunks: number;
14
- /** Current chunk index (0-based) */
15
- currentChunkIndex: number;
16
- /** Total number of records across all chunks */
17
- totalRecords: number;
18
- }
19
- /**
20
- * Webhook configuration for remote delivery of results
21
- */
22
- export interface WebhookConfig {
23
- /** The URL to send webhook requests to */
24
- url: string;
25
- /** Optional HTTP headers to include in the request */
26
- headers?: Record<string, string>;
27
- /** HTTP method to use (default: 'POST') */
28
- method?: 'POST' | 'PUT' | 'PATCH';
29
- /** Request timeout in milliseconds (default: 30000) */
30
- timeout?: number;
31
- /** Number of retry attempts on failure (default: 0) */
32
- retries?: number;
33
- /** Arbitrary developer-provided metadata */
34
- metadata?: Record<string, unknown>;
35
- }
36
- /**
37
- * Type helper that requires at least one of the specified keys to be present
38
- */
39
- type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> & {
40
- [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>;
41
- }[Keys];
42
- /**
43
- * Base delivery options - at least one must be provided
44
- */
45
- interface DeliveryOptionsBase<T> {
46
- /** Local callback for processing chunks */
47
- onData?: (chunk: RecordsChunk<T>, next: () => void) => void | Promise<void>;
48
- /** Webhook configuration for remote delivery */
49
- webhook?: WebhookConfig;
50
- }
51
- /**
52
- * Delivery options - requires at least one of onData or webhook
53
- */
54
- export type DeliveryOptions<T> = RequireAtLeastOne<DeliveryOptionsBase<T>, 'onData' | 'webhook'>;
55
- /**
56
- * Options for the open() method
57
- * Requires at least one of onData or webhook for delivery
58
- */
59
- export type OpenOptions<T> = RequireAtLeastOne<DeliveryOptionsBase<T>, 'onData' | 'webhook'> & {
60
- /** Number of records per chunk (default: 1000) */
61
- chunkSize?: number;
62
- /** Called when all chunks have been processed */
63
- onComplete?: () => void;
64
- /** Called when the user cancels the import */
65
- onCancel?: () => void;
66
- /** Called when an error occurs */
67
- onError?: (error: Error) => void;
68
- /** Called when the widget opens */
69
- onWidgetOpen?: () => void;
70
- /** Called when the step changes in the wizard */
71
- onStepChange?: (stepId: ExpressCSVStep, previousStepId?: ExpressCSVStep) => void;
72
- /** Called when the widget closes */
73
- onWidgetClose?: (reason: 'user_close' | 'cancel' | 'complete' | 'error') => void;
74
- };
75
- export type InferCSVImporter<TSchema extends ExType<unknown, ExBaseDef, unknown>> = CSVImporter<TSchema>;
76
- export type { ColorModeConfig, ColorModePref, ECSVFontSource, ECSVTheme, TailwindThemeVars, } from '@expresscsv/theme';
77
- export interface SDKOptions<TSchema extends ExType<unknown, ExBaseDef, unknown>> {
78
- schema: TSchema;
79
- publishableKey: string;
80
- importIdentifier: string;
81
- title?: string;
82
- debug?: boolean;
83
- developerMode?: boolean;
84
- preload?: boolean;
85
- theme?: ECSVTheme;
86
- colorMode?: ColorModeConfig;
87
- customCSS?: string;
88
- fonts?: Record<string, ECSVFontSource>;
89
- stepDisplay?: 'progressBar' | 'segmented' | 'numbered';
90
- previewSchemaBeforeUpload?: boolean;
91
- templateDownload?: TemplateDownloadConfig;
92
- saveSession?: boolean;
93
- locale?: DeepPartial<ExpressCSVLocaleInput>;
94
- }
95
- declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unknown> = ExRow<ExRowShape>> {
96
- private options;
97
- private iframe;
98
- private container;
99
- private connection;
100
- private connectionState;
101
- private debug;
102
- private developerMode;
103
- private importIdentifier;
104
- private sessionId;
105
- private _destroyTimer;
106
- private _beforeUnloadHandler;
107
- private widgetUrl;
108
- private widgetState;
109
- private widgetMode;
110
- private canRestart;
111
- private lastError;
112
- private openOptions;
113
- private cachedSchemaJson;
114
- private initCompletePromise;
115
- private resolveInitComplete;
116
- constructor(options: SDKOptions<TSchema>);
117
- /**
118
- * Enhanced state management
119
- */
120
- private setState;
121
- private updateDerivedState;
122
- private handleError;
123
- private waitForEvent;
124
- /**
125
- * Open the import flow with chunk-based data processing.
126
- * Automatically opens the widget if not already open.
127
- *
128
- * @param options Configuration including onData callback for processing chunks
129
- */
130
- open(options: OpenOptions<Infer<TSchema>>): void;
131
- /**
132
- * Create import session on backend with configuration
133
- * Idempotent: safe to call multiple times with same sessionId
134
- */
135
- private createImportSession;
136
- /**
137
- * Deliver results to webhook endpoint via backend API
138
- * Chunks data using SDK-determined chunk size (independent of customer's chunkSize)
139
- */
140
- private deliverToWebhook;
141
- /**
142
- * Send a single chunk to backend webhook API with retry logic
143
- */
144
- private sendChunkToBackend;
145
- /**
146
- * Process results in chunks, calling onData and/or webhook for each chunk with backpressure control
147
- */
148
- private processResultsInChunks;
149
- /**
150
- * Initialize the iframe and connection.
151
- * @param hidden Whether to create the iframe hidden (for preload) or visible (for normal open)
152
- */
153
- private initializeIframe;
154
- private log;
155
- private error;
156
- /**
157
- * Add beforeunload event listener to warn users about losing progress
158
- */
159
- private addBeforeUnloadListener;
160
- /**
161
- * Remove beforeunload event listener
162
- */
163
- private removeBeforeUnloadListener;
164
- private waitForIframeLoad;
165
- private setupConnectionAndInit;
166
- openWidget(options?: {
167
- reset?: boolean;
168
- }): Promise<void>;
169
- /**
170
- * Makes a hidden container visible for instant display of preloaded iframe
171
- */
172
- private makeContainerVisible;
173
- private setupMessageHandlers;
174
- private createAndAppendIframe;
175
- private destroy;
176
- close(reason?: 'user_close' | 'cancel' | 'complete' | 'error'): Promise<void>;
177
- resetWidget(): Promise<void>;
178
- restart(newOptions?: Partial<SDKOptions<TSchema>>): Promise<void>;
179
- private handleWidgetClosed;
180
- private hideContainer;
181
- getConnectionStatus(): boolean;
182
- getState(): WidgetState;
183
- getMode(): WidgetMode;
184
- getIsReady(): boolean;
185
- getIsOpen(): boolean;
186
- getCanRestart(): boolean;
187
- getLastError(): Error | null;
188
- getStatus(): {
189
- state: WidgetState;
190
- mode: WidgetMode;
191
- isReady: boolean;
192
- isOpen: boolean;
193
- canRestart: boolean;
194
- hasError: boolean;
195
- lastError: Error | null;
196
- connectionStatus: boolean;
197
- };
198
- getVersion(): string;
199
- }
200
- export { x };
201
- export type { Infer, ExType } from '@expresscsv/fields';
202
- export type { ExBaseDef } from '@expresscsv/fields';
203
- export type { ExpressCSVStep } from '@expresscsv/core';
204
- export type { ExpressCSVLocaleInput, DeepPartial } from '@expresscsv/core';
205
- export { WidgetState, WidgetMode } from '@expresscsv/iframe-comm';
206
- import type { DeepPartial, ExpressCSVLocaleInput, ExpressCSVStep, TemplateDownloadConfig } from '@expresscsv/core';
207
- export { CSVImporter };