@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 +21 -0
- package/README.md +401 -114
- package/dist/index.d.mts +2 -11
- package/package.json +1 -1
- package/dist/.dts/errors.d.ts +0 -6
- package/dist/.dts/index.d.ts +0 -207
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
|
-
#
|
|
1
|
+
# @expresscsv/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@expresscsv/sdk)
|
|
4
|
+

|
|
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
|
-
|
|
21
|
+
> **Looking for React?** Use [`@expresscsv/react`](https://www.npmjs.com/package/@expresscsv/react) for a hook-based integration.
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
## Quick Start
|
|
21
24
|
|
|
22
25
|
```typescript
|
|
23
|
-
import { CSVImporter } from "@expresscsv/sdk";
|
|
26
|
+
import { CSVImporter, x } from "@expresscsv/sdk";
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
31
|
-
|
|
34
|
+
const importer = new CSVImporter({
|
|
35
|
+
schema,
|
|
36
|
+
publishableKey: "your-publishable-key",
|
|
37
|
+
importIdentifier: "user-import",
|
|
38
|
+
title: "Import Users",
|
|
39
|
+
});
|
|
32
40
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
111
|
+
```typescript
|
|
112
|
+
// Preload is enabled by default
|
|
61
113
|
const importer = new CSVImporter({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
//
|
|
79
|
-
importer.
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
85
|
-
|
|
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
|
-
|
|
134
|
+
## Theming and Styling
|
|
135
|
+
|
|
136
|
+
Customize the widget's appearance with the `theme`, `colorMode`, `customCSS`, and `fonts` options.
|
|
89
137
|
|
|
90
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
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
|
-
###
|
|
348
|
+
### `CSVImporter`
|
|
125
349
|
|
|
126
|
-
|
|
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
|
-
|
|
352
|
+
```typescript
|
|
353
|
+
new CSVImporter(options: SDKOptions)
|
|
354
|
+
```
|
|
137
355
|
|
|
138
|
-
|
|
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
|
-
|
|
379
|
+
```typescript
|
|
380
|
+
importer.open(options: OpenOptions): void
|
|
381
|
+
```
|
|
141
382
|
|
|
142
|
-
|
|
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
|
-
|
|
395
|
+
\* At least one of `onData` or `webhook` is required.
|
|
145
396
|
|
|
146
|
-
#### `close()
|
|
397
|
+
#### `close(reason?)`
|
|
147
398
|
|
|
148
399
|
Closes the widget and cleans up resources.
|
|
149
400
|
|
|
150
|
-
|
|
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
|
-
|
|
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
|
-
|
|
420
|
+
#### `restart(newOptions?)`
|
|
159
421
|
|
|
160
|
-
|
|
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
|
-
###
|
|
424
|
+
### `RecordsChunk<T>`
|
|
168
425
|
|
|
169
|
-
The
|
|
426
|
+
The object passed to `onData` callbacks:
|
|
170
427
|
|
|
171
|
-
|
|
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
|
-
|
|
174
|
-
- `x.number()` - Number fields
|
|
175
|
-
- `x.email()` - Email fields
|
|
176
|
-
- `x.select(['option1', 'option2'])` - Dropdown selection fields
|
|
437
|
+
### `WebhookConfig`
|
|
177
438
|
|
|
178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
452
|
+
The SDK is written in TypeScript and provides full type inference from your schema:
|
|
188
453
|
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
194
|
-
|
|
463
|
+
type Row = Infer<typeof schema>;
|
|
464
|
+
// { name: string; age: number; email?: string }
|
|
195
465
|
|
|
196
|
-
|
|
197
|
-
|
|
466
|
+
const importer = new CSVImporter({
|
|
467
|
+
schema,
|
|
468
|
+
publishableKey: "your-publishable-key",
|
|
469
|
+
importIdentifier: "user-import",
|
|
470
|
+
});
|
|
198
471
|
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
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
|
|
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?:
|
|
3749
|
+
colorMode?: ColorModePref;
|
|
3759
3750
|
customCSS?: string;
|
|
3760
3751
|
fonts?: Record<string, ECSVFontSource>;
|
|
3761
3752
|
stepDisplay?: 'progressBar' | 'segmented' | 'numbered';
|
package/package.json
CHANGED
package/dist/.dts/errors.d.ts
DELETED
package/dist/.dts/index.d.ts
DELETED
|
@@ -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 };
|