@expresscsv/sdk 0.1.22 → 0.1.23

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 CHANGED
@@ -44,15 +44,17 @@ const importer = new CSVImporter({
44
44
  importer.open({
45
45
  onData: (chunk, next) => {
46
46
  console.log(`Chunk ${chunk.currentChunkIndex + 1}/${chunk.totalChunks}`);
47
+ console.log("Session:", chunk.sessionId);
48
+ console.log("Idempotency key:", chunk.chunkIdempotencyKey);
47
49
  console.log("Records:", chunk.records);
48
50
  // Process your validated, typed records here
49
51
  next();
50
52
  },
51
- onComplete: () => {
52
- console.log("All chunks processed successfully");
53
+ onComplete: ({ sessionId }) => {
54
+ console.log("All chunks processed successfully for", sessionId);
53
55
  },
54
- onError: (error) => {
55
- console.error("Import failed:", error);
56
+ onError: (error, { sessionId }) => {
57
+ console.error("Import failed for", sessionId, error);
56
58
  },
57
59
  });
58
60
  ```
@@ -61,182 +63,52 @@ If your schema is assembled dynamically at runtime, still use `x.row(...)`. Just
61
63
 
62
64
  Have your backend keep the ExpressCSV secret key, call the ExpressCSV session-creation endpoint, and return a short-lived importer session token to the browser via `getSessionToken`. The SDK opens the importer immediately, then completes the initial session bootstrap in the background.
63
65
 
64
- ## Webhook Delivery
66
+ ## Delivery
65
67
 
66
- Deliver imported data directly to your backend via webhook instead of (or in addition to) a local callback:
67
-
68
- ```typescript
69
- importer.open({
70
- webhook: {
71
- url: "https://api.example.com/webhooks/csv-import",
72
- method: "POST",
73
- headers: {
74
- Authorization: "Bearer your-api-token",
75
- },
76
- metadata: {
77
- source: "web-app",
78
- userId: "user-123",
79
- },
80
- },
81
- onComplete: () => {
82
- console.log("Webhook delivery initiated");
83
- },
84
- onError: (error) => {
85
- console.error("Delivery error:", error);
86
- },
87
- });
88
- ```
89
-
90
- ### Webhook Payload Structure
91
-
92
- Each chunk is delivered as a JSON `POST` (or whichever method you configured) to your endpoint. The request body has this shape:
93
-
94
- ```typescript
95
- interface WebhookPayload {
96
- /** Imported records for this chunk, matching your schema */
97
- records: Record<string, unknown>[];
98
- /** 0-based index of the current chunk */
99
- chunkIndex: number;
100
- /** Total number of chunks in this delivery */
101
- totalChunks: number;
102
- /** Total number of records across all chunks */
103
- totalRecords: number;
104
- /** Present only if you passed `metadata` in `WebhookConfig` */
105
- metadata?: Record<string, unknown>;
106
- /** Delivery context added by ExpressCSV */
107
- delivery: {
108
- environmentName: string;
109
- environmentType: string;
110
- teamSlug: string;
111
- importIdentifier: string;
112
- deliveryId: string;
113
- timestamp: string; // ISO 8601
114
- };
115
- }
116
- ```
117
-
118
- When you already have a schema, prefer `InferWebhookPayload<typeof schema>` from `@expresscsv/schemas` instead of rewriting this object shape by hand.
119
-
120
- Example payload:
121
-
122
- ```json
123
- {
124
- "records": [
125
- { "name": "Alice Johnson", "email": "alice@example.com", "age": 32 },
126
- { "name": "Bob Smith", "email": "bob@example.com", "age": 45 }
127
- ],
128
- "chunkIndex": 0,
129
- "totalChunks": 3,
130
- "totalRecords": 2500,
131
- "metadata": {
132
- "source": "web-app",
133
- "userId": "user-123"
134
- },
135
- "delivery": {
136
- "environmentName": "Production",
137
- "environmentType": "production",
138
- "teamSlug": "my-team",
139
- "importIdentifier": "user-import",
140
- "deliveryId": "del_abc123",
141
- "timestamp": "2026-03-02T14:30:00.000Z"
142
- }
143
- }
144
- ```
145
-
146
- The request includes a `Content-Type: application/json` header plus any custom `headers` you specified in `WebhookConfig`.
147
-
148
- ### Handling Webhooks on Your Server
149
-
150
- Your endpoint should return a **2xx** status code to acknowledge each chunk. Non-2xx responses trigger the following retry behaviour:
151
-
152
- - **5xx** and **429** responses are retried automatically (up to 5 attempts per chunk).
153
- - **4xx** responses (except 429) are treated as permanent failures and are **not** retried.
154
-
155
- Chunks are delivered **serially** — the next chunk is only sent after the previous one succeeds.
156
-
157
- If your schema is defined in shared or backend code with `@expresscsv/schemas`, you can reuse the built-in webhook payload helper when handling deliveries:
158
-
159
- ```typescript
160
- import express from "express";
161
- import type { InferWebhookPayload } from "@expresscsv/schemas";
162
- import { employeeSchema } from "../shared/employee-schema";
163
-
164
- const app = express();
165
- app.use(express.json());
166
-
167
- app.post("/webhooks/csv-import", async (req, res) => {
168
- const payload = req.body as InferWebhookPayload<typeof employeeSchema>;
169
-
170
- await db.insertMany("users", payload.records);
171
-
172
- res.status(200).json({ ok: true });
173
- });
174
- ```
175
-
176
- Below is a minimal Express.js example:
177
-
178
- ```typescript
179
- import express from "express";
180
-
181
- const app = express();
182
- app.use(express.json());
183
-
184
- app.post("/webhooks/csv-import", async (req, res) => {
185
- const token = req.headers.authorization;
186
- if (token !== "Bearer your-api-token") {
187
- return res.status(401).json({ error: "Unauthorized" });
188
- }
189
-
190
- const { records, chunkIndex, totalChunks, totalRecords, metadata, delivery } =
191
- req.body;
192
-
193
- try {
194
- // Process the records — e.g. insert into your database
195
- await db.insertMany("users", records);
196
-
197
- console.log(
198
- `Chunk ${chunkIndex + 1}/${totalChunks} processed ` +
199
- `(${records.length} records, delivery ${delivery.deliveryId})`
200
- );
201
-
202
- res.status(200).json({ success: true });
203
- } catch (error) {
204
- // Return 500 so ExpressCSV retries this chunk
205
- console.error("Failed to process chunk:", error);
206
- res.status(500).json({ error: "Internal server error" });
207
- }
208
- });
209
-
210
- app.listen(3000);
211
- ```
212
-
213
- **Tips:**
214
-
215
- - Use `delivery.deliveryId` and `chunkIndex` to **deduplicate** retried chunks (the same chunk may be delivered more than once on retry).
216
- - Use `chunkIndex` and `totalChunks` to track progress and know when the full import is complete (`chunkIndex === totalChunks - 1` for the last chunk).
217
- - Store `metadata` alongside imported records if you need to correlate the import with a specific user or action in your app.
218
-
219
- ### Combined Local Callback and Webhook
220
-
221
- You can use both `onData` and `webhook` simultaneously:
68
+ ExpressCSV delivers imported data through `onData`. Your app receives validated chunks in the browser and can forward them to your backend using whatever request shape fits your stack.
222
69
 
223
70
  ```typescript
224
71
  importer.open({
225
72
  chunkSize: 500,
226
73
  onData: async (chunk, next) => {
227
- await saveToLocalDatabase(chunk.records);
74
+ const response = await fetch("/api/import-users/chunks", {
75
+ method: "POST",
76
+ headers: {
77
+ "Content-Type": "application/json",
78
+ },
79
+ body: JSON.stringify({
80
+ sessionId: chunk.sessionId,
81
+ chunkIdempotencyKey: chunk.chunkIdempotencyKey,
82
+ records: chunk.records,
83
+ currentChunkIndex: chunk.currentChunkIndex,
84
+ totalChunks: chunk.totalChunks,
85
+ totalRecords: chunk.totalRecords,
86
+ }),
87
+ });
88
+
89
+ if (!response.ok) {
90
+ throw new Error("Backend rejected this import chunk");
91
+ }
92
+
228
93
  next();
229
94
  },
230
- webhook: {
231
- url: "https://api.example.com/webhooks/csv-import",
232
- headers: { Authorization: "Bearer your-api-token" },
233
- },
234
- onComplete: () => {
235
- console.log("Local processing and webhook delivery complete");
95
+ onComplete: ({ sessionId }) => {
96
+ console.log("Import complete for", sessionId);
236
97
  },
237
98
  });
238
99
  ```
239
100
 
101
+ Each delivered chunk includes:
102
+
103
+ - `records`
104
+ - `sessionId`
105
+ - `chunkIdempotencyKey`
106
+ - `currentChunkIndex`
107
+ - `totalChunks`
108
+ - `totalRecords`
109
+
110
+ Use `sessionId` and `chunkIdempotencyKey` to stage writes safely and deduplicate retries from your own app logic.
111
+
240
112
  ## Preloading
241
113
 
242
114
  By default, the SDK preloads the importer in a hidden iframe for instant display when `open()` is called. This provides the best user experience while the initial session bootstrap continues in the background.
@@ -432,7 +304,7 @@ const importer = new CSVImporter({
432
304
 
433
305
  The `x` schema builder provides a type-safe, fluent API for defining your CSV structure.
434
306
 
435
- For apps that share schema definitions with backend code or webhook handlers, prefer defining the schema in `@expresscsv/schemas` and importing it into your frontend. That keeps schema authoring free of importer/runtime dependencies.
307
+ For apps that share schema definitions with backend code, prefer defining the schema in `@expresscsv/schemas` and importing it into your frontend. That keeps schema authoring free of importer/runtime dependencies.
436
308
 
437
309
  ### Field Types
438
310
 
@@ -689,13 +561,16 @@ new CSVImporter(options: SDKOptions)
689
561
  | `fonts` | `Record<string, ECSVFontSource>` | No | - | Custom font sources |
690
562
  | `stepDisplay` | `'progressBar' \| 'segmented' \| 'numbered'` | No | `'progressBar'` | Step indicator style |
691
563
  | `previewSchemaBeforeUpload` | `boolean` | No | `true` | Show schema preview before upload |
564
+ | `aiColumnMatching` | `boolean` | No | `true` | Enable AI-assisted column matching |
565
+ | `aiTransform` | `boolean` | No | `true` | Enable AI-assisted transform generation |
692
566
  | `templateDownload` | `TemplateDownloadOptions<TSchema>` | No | - | Template download configuration with optional schema-typed example rows |
693
- | `saveSession` | `boolean` | No | - | Persist session state |
567
+ | `storage` | `{ type: "local" } \| { type: "custom", ... }` | No | - | Enable Recovered Sessions with the built-in local backend or a custom adapter implementing `get`, `set`, and `remove` |
694
568
  | `locale` | `DeepPartial<ExpressCSVLocaleInput>` | No | - | Localization overrides |
569
+ | `disableStatusStep` | `boolean` | No | - | Skip the success/error status screen |
695
570
 
696
571
  #### `open(options)`
697
572
 
698
- Opens the importer and begins the import flow. At least one of `onData` or `webhook` must be provided.
573
+ Opens the importer and begins the import flow.
699
574
 
700
575
  ```typescript
701
576
  importer.open(options: OpenOptions): void
@@ -703,18 +578,15 @@ importer.open(options: OpenOptions): void
703
578
 
704
579
  | Option | Type | Required | Description |
705
580
  |---|---|---|---|
706
- | `onData` | `(chunk: RecordsChunk<T>, next: () => void) => void` | * | Callback for each chunk of records. Call `next()` to continue. |
707
- | `webhook` | `WebhookConfig` | * | Webhook endpoint for server-side delivery |
581
+ | `onData` | `(chunk: RecordsChunk<T>, next: () => void) => void` | Yes | Callback for each delivered chunk of records. Call `next()` to continue. |
708
582
  | `chunkSize` | `number` | No | Records per chunk (default: 1000) |
709
- | `onComplete` | `() => void` | No | Called when all chunks have been processed |
710
- | `onCancel` | `() => void` | No | Called when the user cancels the import |
711
- | `onError` | `(error: Error) => void` | No | Called when an error occurs |
583
+ | `onComplete` | `(context: { sessionId: string }) => void` | No | Called when all chunks have been processed |
584
+ | `onCancel` | `(context: { sessionId: string }) => void` | No | Called when the user cancels the import |
585
+ | `onError` | `(error: Error, context: { sessionId: string }) => void` | No | Called when an error occurs |
712
586
  | `onImporterOpen` | `() => void` | No | Called when the importer opens |
713
- | `onImporterClose` | `(reason: string) => void` | No | Called when the importer closes |
587
+ | `onImporterClose` | `(reason: 'user_close' \| 'cancel' \| 'complete' \| 'error') => void` | No | Called when the importer closes |
714
588
  | `onStepChange` | `(stepId, previousStepId?) => void` | No | Called when the wizard step changes |
715
589
 
716
- \* At least one of `onData` or `webhook` is required.
717
-
718
590
  #### `close(reason?)`
719
591
 
720
592
  Closes the importer and cleans up resources.
@@ -736,7 +608,6 @@ await importer.close(reason?: 'user_close' | 'cancel' | 'complete' | 'error'): P
736
608
  | `getCanRestart()` | `boolean` | Whether the importer can be restarted |
737
609
  | `getLastError()` | `Error \| null` | Last error, if any |
738
610
  | `getStatus()` | `object` | Comprehensive status snapshot |
739
- | `getVersion()` | `string` | SDK version |
740
611
 
741
612
  #### `restart(newOptions?)`
742
613
 
@@ -752,19 +623,8 @@ interface RecordsChunk<T> {
752
623
  totalChunks: number;
753
624
  currentChunkIndex: number;
754
625
  totalRecords: number;
755
- }
756
- ```
757
-
758
- ### `WebhookConfig`
759
-
760
- ```typescript
761
- interface WebhookConfig {
762
- url: string;
763
- headers?: Record<string, string>;
764
- method?: "POST" | "PUT" | "PATCH";
765
- timeout?: number;
766
- retries?: number;
767
- metadata?: Record<string, unknown>;
626
+ sessionId: string;
627
+ chunkIdempotencyKey: string;
768
628
  }
769
629
  ```
770
630
 
package/dist/index.d.cts CHANGED
@@ -1256,6 +1256,8 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1256
1256
  private importerMode;
1257
1257
  private canRestart;
1258
1258
  private lastError;
1259
+ private stateChangeListeners;
1260
+ private lastTerminalSessionState;
1259
1261
  private openOptions;
1260
1262
  private cachedSchemaJson;
1261
1263
  private initCompletePromise;
@@ -1263,11 +1265,17 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1263
1265
  private resolvedTemplateDownload;
1264
1266
  private importerStartupGeneration;
1265
1267
  constructor(options: SDKOptions<TSchema>);
1268
+ private getCustomStorageRpcHandler;
1266
1269
  /**
1267
1270
  * Enhanced state management
1268
1271
  */
1269
1272
  private setState;
1270
1273
  private updateDerivedState;
1274
+ private createSessionId;
1275
+ private getOrCreateSessionId;
1276
+ private getImportSessionContext;
1277
+ private getChunkIdempotencyKey;
1278
+ private createRecordsChunk;
1271
1279
  private handleError;
1272
1280
  private sendImportProgress;
1273
1281
  private waitForEvent;
@@ -1297,16 +1305,7 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1297
1305
  */
1298
1306
  private createImportSession;
1299
1307
  /**
1300
- * Deliver results to webhook endpoint via backend API
1301
- * Chunks data using SDK-determined chunk size (independent of customer's chunkSize)
1302
- */
1303
- private deliverToWebhook;
1304
- /**
1305
- * Send a single chunk to backend webhook API with retry logic
1306
- */
1307
- private sendChunkToBackend;
1308
- /**
1309
- * Process results in chunks, calling onData and/or webhook for each chunk with backpressure control
1308
+ * Process results in chunks, calling onData with backpressure control
1310
1309
  */
1311
1310
  private processResultsInChunks;
1312
1311
  /**
@@ -1342,6 +1341,7 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1342
1341
  private handleImporterClosed;
1343
1342
  private hideContainer;
1344
1343
  getConnectionStatus(): boolean;
1344
+ subscribeToStateChanges(listener: StateChangeListener): () => void;
1345
1345
  getState(): ImporterState;
1346
1346
  getMode(): ImporterMode;
1347
1347
  getIsReady(): boolean;
@@ -1358,7 +1358,6 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1358
1358
  lastError: Error | null;
1359
1359
  connectionStatus: boolean;
1360
1360
  };
1361
- getVersion(): string;
1362
1361
  }
1363
1362
 
1364
1363
  /**
@@ -2004,6 +2003,13 @@ declare const CurrencyCodes: readonly [{
2004
2003
  readonly numeric: "932";
2005
2004
  }];
2006
2005
 
2006
+ export declare type CustomStorageOptions = {
2007
+ type: 'custom';
2008
+ get: (key: StorageKey) => Promise<StoredSession | null>;
2009
+ set: (key: StorageKey, value: StoredSession) => Promise<void>;
2010
+ remove: (key: StorageKey) => Promise<void>;
2011
+ };
2012
+
2007
2013
  declare interface DatetimeOptions {
2008
2014
  message?: string;
2009
2015
  }
@@ -2012,19 +2018,9 @@ export declare type DeepPartial<T> = {
2012
2018
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
2013
2019
  };
2014
2020
 
2015
- /**
2016
- * Delivery options - requires at least one of onData or webhook
2017
- */
2018
- export declare type DeliveryOptions<T> = RequireAtLeastOne<DeliveryOptionsBase<T>, 'onData' | 'webhook'>;
2019
-
2020
- /**
2021
- * Base delivery options - at least one must be provided
2022
- */
2023
- declare interface DeliveryOptionsBase<T> {
2024
- /** Local callback for processing chunks */
2025
- onData?: (chunk: RecordsChunk<T>, next: () => void) => void | Promise<void>;
2026
- /** Webhook configuration for remote delivery */
2027
- webhook?: WebhookConfig;
2021
+ export declare interface DeliveryOptions<T> {
2022
+ /** Callback for processing delivered chunks */
2023
+ onData: (chunk: RecordsChunk<T>, next: () => void) => void | Promise<void>;
2028
2024
  }
2029
2025
 
2030
2026
  /**
@@ -3624,12 +3620,21 @@ export declare enum ImporterState {
3624
3620
  DESTROYED = "destroyed"
3625
3621
  }
3626
3622
 
3623
+ export declare interface ImportSessionContext {
3624
+ /** Generated session ID for the current import run */
3625
+ sessionId: string;
3626
+ }
3627
+
3627
3628
  export declare type Infer<T extends ExType<unknown, ExBaseDef, unknown>> = T extends ExType<infer Output, ExBaseDef, unknown> ? Output : never;
3628
3629
 
3629
3630
  export declare type InferCSVImporter<TSchema extends ExType<unknown, ExBaseDef, unknown>> = CSVImporter<TSchema>;
3630
3631
 
3631
3632
  declare type IPAddressVersion = 'v4' | 'v6' | 'all';
3632
3633
 
3634
+ export declare type LocalStorageOptions = {
3635
+ type: 'local';
3636
+ };
3637
+
3633
3638
  declare interface MultiselectOptions {
3634
3639
  enforceCaseSensitiveMatch?: boolean;
3635
3640
  message?: string;
@@ -3637,17 +3642,17 @@ declare interface MultiselectOptions {
3637
3642
 
3638
3643
  /**
3639
3644
  * Options for the open() method
3640
- * Requires at least one of onData or webhook for delivery
3645
+ * Requires an onData callback for delivery
3641
3646
  */
3642
- export declare type OpenOptions<T> = RequireAtLeastOne<DeliveryOptionsBase<T>, 'onData' | 'webhook'> & {
3647
+ export declare type OpenOptions<T> = DeliveryOptions<T> & {
3643
3648
  /** Number of records per chunk (default: 1000) */
3644
3649
  chunkSize?: number;
3645
3650
  /** Called when all chunks have been processed */
3646
- onComplete?: () => void;
3651
+ onComplete?: (context: ImportSessionContext) => void;
3647
3652
  /** Called when the user cancels the import */
3648
- onCancel?: () => void;
3653
+ onCancel?: (context: ImportSessionContext) => void;
3649
3654
  /** Called when an error occurs */
3650
- onError?: (error: Error) => void;
3655
+ onError?: (error: Error, context: ImportSessionContext) => void;
3651
3656
  /** Called when the importer opens */
3652
3657
  onImporterOpen?: () => void;
3653
3658
  /** Called when the importer closes */
@@ -3656,6 +3661,17 @@ export declare type OpenOptions<T> = RequireAtLeastOne<DeliveryOptionsBase<T>, '
3656
3661
  onStepChange?: (stepId: ExpressCSVStep, previousStepId?: ExpressCSVStep) => void;
3657
3662
  };
3658
3663
 
3664
+ declare interface PersistedImportSessionData {
3665
+ data: PersistedImportSessionPayload;
3666
+ }
3667
+
3668
+ declare type PersistedImportSessionKey = string;
3669
+
3670
+ declare type PersistedImportSessionPayload = {
3671
+ rowData: Record<string, unknown>[];
3672
+ columnKeys: string[];
3673
+ };
3674
+
3659
3675
  declare type PhoneNumberFormat = 'international' | 'national' | 'both';
3660
3676
 
3661
3677
  declare type PhoneNumberOutput = 'e164' | 'formatted' | 'digits';
@@ -3762,6 +3778,10 @@ export declare interface RecordsChunk<T> {
3762
3778
  currentChunkIndex: number;
3763
3779
  /** Total number of records across all chunks */
3764
3780
  totalRecords: number;
3781
+ /** Generated session ID for the current import run */
3782
+ sessionId: string;
3783
+ /** Stable per-chunk idempotency key */
3784
+ chunkIdempotencyKey: string;
3765
3785
  }
3766
3786
 
3767
3787
  declare type RefineBatchResultItem = {
@@ -3793,13 +3813,6 @@ declare type RefineResultItem = {
3793
3813
  };
3794
3814
  };
3795
3815
 
3796
- /**
3797
- * Type helper that requires at least one of the specified keys to be present
3798
- */
3799
- declare type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> & {
3800
- [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>;
3801
- }[Keys];
3802
-
3803
3816
  export declare interface SDKOptions<TSchema extends ExType<unknown, ExBaseDef, unknown>> {
3804
3817
  schema: TSchema;
3805
3818
  getSessionToken: () => Promise<string>;
@@ -3813,8 +3826,10 @@ export declare interface SDKOptions<TSchema extends ExType<unknown, ExBaseDef, u
3813
3826
  fonts?: Record<string, ECSVFontSource>;
3814
3827
  stepDisplay?: 'progressBar' | 'segmented' | 'numbered';
3815
3828
  previewSchemaBeforeUpload?: boolean;
3829
+ aiColumnMatching?: boolean;
3830
+ aiTransform?: boolean;
3816
3831
  templateDownload?: TemplateDownloadOptions<TSchema>;
3817
- saveSession?: boolean;
3832
+ storage?: StorageOptions;
3818
3833
  locale?: DeepPartial<ExpressCSVLocaleInput>;
3819
3834
  disableStatusStep?: boolean;
3820
3835
  }
@@ -3831,6 +3846,16 @@ declare interface SelectOptions {
3831
3846
  message?: string;
3832
3847
  }
3833
3848
 
3849
+ declare type StateChangeListener = (state: ImporterState) => void;
3850
+
3851
+ export declare type StorageKey = PersistedImportSessionKey;
3852
+
3853
+ export declare type StorageOptions = LocalStorageOptions | CustomStorageOptions;
3854
+
3855
+ export declare type StoredSession = PersistedImportSessionData;
3856
+
3857
+ export declare type StoredSessionData = PersistedImportSessionPayload;
3858
+
3834
3859
  declare type StripBrand<T> = {
3835
3860
  [K in keyof T]: T[K] extends TemplateString<string> ? string : T[K] extends object ? StripBrand<T[K]> : T[K];
3836
3861
  };
@@ -3927,31 +3952,6 @@ declare interface ValidatorCheck {
3927
3952
  message?: string;
3928
3953
  }
3929
3954
 
3930
- /**
3931
- * Webhook configuration for remote delivery of results
3932
- */
3933
- export declare interface WebhookConfig {
3934
- /** The URL to send webhook requests to */
3935
- url: string;
3936
- /** Optional HTTP headers to include in the request */
3937
- headers?: Record<string, string>;
3938
- /** HTTP method to use (default: 'POST') */
3939
- method?: 'POST' | 'PUT' | 'PATCH';
3940
- /** Request timeout in milliseconds (default: 30000) */
3941
- timeout?: number;
3942
- /** Number of retry attempts on failure (default: 0) */
3943
- retries?: number;
3944
- /** Arbitrary developer-provided metadata */
3945
- metadata?: Record<string, unknown>;
3946
- /**
3947
- * Whether to wait for the delivery service to confirm the webhook was
3948
- * successfully received before considering the import complete.
3949
- * When false, the import completes as soon as all chunks are queued.
3950
- * Default: false
3951
- */
3952
- awaitWebhookArrival?: boolean;
3953
- }
3954
-
3955
3955
  export declare const x: {
3956
3956
  string: typeof ExString.create;
3957
3957
  number: typeof ExNumber.create;