@expresscsv/react 0.1.14 → 0.1.16
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/README.md +111 -1
- package/dist/index.d.cts +14 -0
- package/dist/index.d.mts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ npm install @expresscsv/react
|
|
|
18
18
|
yarn add @expresscsv/react
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
> **Peer dependency:** React
|
|
21
|
+
> **Peer dependency:** React 16.8+
|
|
22
22
|
|
|
23
23
|
## Quick Start
|
|
24
24
|
|
|
@@ -283,6 +283,116 @@ function App() {
|
|
|
283
283
|
}
|
|
284
284
|
```
|
|
285
285
|
|
|
286
|
+
### Webhook Payload Structure
|
|
287
|
+
|
|
288
|
+
Each chunk is delivered as a JSON `POST` (or whichever method you configured) to your endpoint. The request body has this shape:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
interface WebhookPayload {
|
|
292
|
+
/** Imported records for this chunk, matching your schema */
|
|
293
|
+
records: Record<string, unknown>[];
|
|
294
|
+
/** 0-based index of the current chunk */
|
|
295
|
+
chunkIndex: number;
|
|
296
|
+
/** Total number of chunks in this delivery */
|
|
297
|
+
totalChunks: number;
|
|
298
|
+
/** Total number of records across all chunks */
|
|
299
|
+
totalRecords: number;
|
|
300
|
+
/** Present only if you passed `metadata` in `WebhookConfig` */
|
|
301
|
+
metadata?: Record<string, unknown>;
|
|
302
|
+
/** Delivery context added by ExpressCSV */
|
|
303
|
+
delivery: {
|
|
304
|
+
publishableKey: string;
|
|
305
|
+
environmentName: string;
|
|
306
|
+
environmentType: string;
|
|
307
|
+
teamSlug: string;
|
|
308
|
+
importIdentifier: string;
|
|
309
|
+
deliveryId: string;
|
|
310
|
+
timestamp: string; // ISO 8601
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Example payload:
|
|
316
|
+
|
|
317
|
+
```json
|
|
318
|
+
{
|
|
319
|
+
"records": [
|
|
320
|
+
{ "name": "Alice Johnson", "email": "alice@example.com" },
|
|
321
|
+
{ "name": "Bob Smith", "email": "bob@example.com" }
|
|
322
|
+
],
|
|
323
|
+
"chunkIndex": 0,
|
|
324
|
+
"totalChunks": 3,
|
|
325
|
+
"totalRecords": 2500,
|
|
326
|
+
"metadata": {
|
|
327
|
+
"source": "react-app",
|
|
328
|
+
"userId": "user-123"
|
|
329
|
+
},
|
|
330
|
+
"delivery": {
|
|
331
|
+
"publishableKey": "pk_live_abc123",
|
|
332
|
+
"environmentName": "Production",
|
|
333
|
+
"environmentType": "production",
|
|
334
|
+
"teamSlug": "my-team",
|
|
335
|
+
"importIdentifier": "user-import",
|
|
336
|
+
"deliveryId": "del_abc123",
|
|
337
|
+
"timestamp": "2026-03-02T14:30:00.000Z"
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
The request includes a `Content-Type: application/json` header plus any custom `headers` you specified in `WebhookConfig`.
|
|
343
|
+
|
|
344
|
+
### Handling Webhooks on Your Server
|
|
345
|
+
|
|
346
|
+
Your endpoint should return a **2xx** status code to acknowledge each chunk. Non-2xx responses trigger the following retry behaviour:
|
|
347
|
+
|
|
348
|
+
- **5xx** and **429** responses are retried automatically (up to 5 attempts per chunk).
|
|
349
|
+
- **4xx** responses (except 429) are treated as permanent failures and are **not** retried.
|
|
350
|
+
|
|
351
|
+
Chunks are delivered **serially** — the next chunk is only sent after the previous one succeeds.
|
|
352
|
+
|
|
353
|
+
Below is a minimal Express.js example:
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
import express from "express";
|
|
357
|
+
|
|
358
|
+
const app = express();
|
|
359
|
+
app.use(express.json());
|
|
360
|
+
|
|
361
|
+
app.post("/webhooks/csv-import", async (req, res) => {
|
|
362
|
+
const token = req.headers.authorization;
|
|
363
|
+
if (token !== "Bearer your-api-token") {
|
|
364
|
+
return res.status(401).json({ error: "Unauthorized" });
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const { records, chunkIndex, totalChunks, totalRecords, metadata, delivery } =
|
|
368
|
+
req.body;
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
// Process the records — e.g. insert into your database
|
|
372
|
+
await db.insertMany("users", records);
|
|
373
|
+
|
|
374
|
+
console.log(
|
|
375
|
+
`Chunk ${chunkIndex + 1}/${totalChunks} processed ` +
|
|
376
|
+
`(${records.length} records, delivery ${delivery.deliveryId})`
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
res.status(200).json({ success: true });
|
|
380
|
+
} catch (error) {
|
|
381
|
+
// Return 500 so ExpressCSV retries this chunk
|
|
382
|
+
console.error("Failed to process chunk:", error);
|
|
383
|
+
res.status(500).json({ error: "Internal server error" });
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
app.listen(3000);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Tips:**
|
|
391
|
+
|
|
392
|
+
- Use `delivery.deliveryId` and `chunkIndex` to **deduplicate** retried chunks (the same chunk may be delivered more than once on retry).
|
|
393
|
+
- Use `chunkIndex` and `totalChunks` to track progress and know when the full import is complete (`chunkIndex === totalChunks - 1` for the last chunk).
|
|
394
|
+
- Store `metadata` alongside imported records if you need to correlate the import with a specific user or action in your app.
|
|
395
|
+
|
|
286
396
|
### Combined Local Callback and Webhook
|
|
287
397
|
|
|
288
398
|
```tsx
|
package/dist/index.d.cts
CHANGED
|
@@ -2746,6 +2746,13 @@ declare interface ExpressCSVLocale {
|
|
|
2746
2746
|
matched: string;
|
|
2747
2747
|
unmatched: string;
|
|
2748
2748
|
custom: string;
|
|
2749
|
+
nextDisabledUpload: string;
|
|
2750
|
+
nextDisabledSelectSheet: string;
|
|
2751
|
+
nextDisabledSelectHeader: string;
|
|
2752
|
+
nextDisabledMatchColumns: string;
|
|
2753
|
+
nextDisabledMatchOptions: string;
|
|
2754
|
+
nextDisabledReviewValidating: string;
|
|
2755
|
+
nextDisabledReviewInvalid: string;
|
|
2749
2756
|
};
|
|
2750
2757
|
widget: {
|
|
2751
2758
|
title: string;
|
|
@@ -3777,6 +3784,13 @@ export declare interface WebhookConfig {
|
|
|
3777
3784
|
retries?: number;
|
|
3778
3785
|
/** Arbitrary developer-provided metadata */
|
|
3779
3786
|
metadata?: Record<string, unknown>;
|
|
3787
|
+
/**
|
|
3788
|
+
* Whether to wait for the delivery service to confirm the webhook was
|
|
3789
|
+
* successfully received before considering the import complete.
|
|
3790
|
+
* When false, the import completes as soon as all chunks are queued.
|
|
3791
|
+
* Default: true
|
|
3792
|
+
*/
|
|
3793
|
+
awaitWebhookArrival?: boolean;
|
|
3780
3794
|
}
|
|
3781
3795
|
|
|
3782
3796
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -2746,6 +2746,13 @@ declare interface ExpressCSVLocale {
|
|
|
2746
2746
|
matched: string;
|
|
2747
2747
|
unmatched: string;
|
|
2748
2748
|
custom: string;
|
|
2749
|
+
nextDisabledUpload: string;
|
|
2750
|
+
nextDisabledSelectSheet: string;
|
|
2751
|
+
nextDisabledSelectHeader: string;
|
|
2752
|
+
nextDisabledMatchColumns: string;
|
|
2753
|
+
nextDisabledMatchOptions: string;
|
|
2754
|
+
nextDisabledReviewValidating: string;
|
|
2755
|
+
nextDisabledReviewInvalid: string;
|
|
2749
2756
|
};
|
|
2750
2757
|
widget: {
|
|
2751
2758
|
title: string;
|
|
@@ -3777,6 +3784,13 @@ export declare interface WebhookConfig {
|
|
|
3777
3784
|
retries?: number;
|
|
3778
3785
|
/** Arbitrary developer-provided metadata */
|
|
3779
3786
|
metadata?: Record<string, unknown>;
|
|
3787
|
+
/**
|
|
3788
|
+
* Whether to wait for the delivery service to confirm the webhook was
|
|
3789
|
+
* successfully received before considering the import complete.
|
|
3790
|
+
* When false, the import completes as soon as all chunks are queued.
|
|
3791
|
+
* Default: true
|
|
3792
|
+
*/
|
|
3793
|
+
awaitWebhookArrival?: boolean;
|
|
3780
3794
|
}
|
|
3781
3795
|
|
|
3782
3796
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -2746,6 +2746,13 @@ declare interface ExpressCSVLocale {
|
|
|
2746
2746
|
matched: string;
|
|
2747
2747
|
unmatched: string;
|
|
2748
2748
|
custom: string;
|
|
2749
|
+
nextDisabledUpload: string;
|
|
2750
|
+
nextDisabledSelectSheet: string;
|
|
2751
|
+
nextDisabledSelectHeader: string;
|
|
2752
|
+
nextDisabledMatchColumns: string;
|
|
2753
|
+
nextDisabledMatchOptions: string;
|
|
2754
|
+
nextDisabledReviewValidating: string;
|
|
2755
|
+
nextDisabledReviewInvalid: string;
|
|
2749
2756
|
};
|
|
2750
2757
|
widget: {
|
|
2751
2758
|
title: string;
|
|
@@ -3777,6 +3784,13 @@ export declare interface WebhookConfig {
|
|
|
3777
3784
|
retries?: number;
|
|
3778
3785
|
/** Arbitrary developer-provided metadata */
|
|
3779
3786
|
metadata?: Record<string, unknown>;
|
|
3787
|
+
/**
|
|
3788
|
+
* Whether to wait for the delivery service to confirm the webhook was
|
|
3789
|
+
* successfully received before considering the import complete.
|
|
3790
|
+
* When false, the import completes as soon as all chunks are queued.
|
|
3791
|
+
* Default: true
|
|
3792
|
+
*/
|
|
3793
|
+
awaitWebhookArrival?: boolean;
|
|
3780
3794
|
}
|
|
3781
3795
|
|
|
3782
3796
|
/**
|