@expresscsv/sdk 0.1.20 → 0.1.22

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
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@expresscsv/sdk.svg)](https://www.npmjs.com/package/@expresscsv/sdk)
4
4
  ![license](https://img.shields.io/npm/l/@expresscsv/sdk.svg)
5
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.
6
+ A TypeScript SDK for embedding the [ExpressCSV](https://expresscsv.com) CSV importer into any web application. Define a schema, open the importer, and receive validated, typed data in chunks.
7
7
 
8
8
  If you want to define schemas in shared or backend code without any frontend dependencies, use [`@expresscsv/schemas`](https://www.npmjs.com/package/@expresscsv/schemas) for the schema definition itself, then pass that schema into `@expresscsv/sdk`.
9
9
 
@@ -35,12 +35,12 @@ const schema = x.row({
35
35
 
36
36
  const importer = new CSVImporter({
37
37
  schema,
38
- publishableKey: "your-publishable-key",
38
+ getSessionToken: async () => fetchSessionToken(),
39
39
  importIdentifier: "user-import",
40
40
  title: "Import Users",
41
41
  });
42
42
 
43
- // Open the widget and process data in chunks
43
+ // Open the importer and process data in chunks
44
44
  importer.open({
45
45
  onData: (chunk, next) => {
46
46
  console.log(`Chunk ${chunk.currentChunkIndex + 1}/${chunk.totalChunks}`);
@@ -59,7 +59,7 @@ importer.open({
59
59
 
60
60
  If your schema is assembled dynamically at runtime, still use `x.row(...)`. Just note that dynamic schema assembly can widen TypeScript inference, so use it intentionally when you need runtime-driven columns.
61
61
 
62
- 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.
62
+ 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
63
 
64
64
  ## Webhook Delivery
65
65
 
@@ -105,7 +105,6 @@ interface WebhookPayload {
105
105
  metadata?: Record<string, unknown>;
106
106
  /** Delivery context added by ExpressCSV */
107
107
  delivery: {
108
- publishableKey: string;
109
108
  environmentName: string;
110
109
  environmentType: string;
111
110
  teamSlug: string;
@@ -134,7 +133,6 @@ Example payload:
134
133
  "userId": "user-123"
135
134
  },
136
135
  "delivery": {
137
- "publishableKey": "pk_live_abc123",
138
136
  "environmentName": "Production",
139
137
  "environmentType": "production",
140
138
  "teamSlug": "my-team",
@@ -241,17 +239,17 @@ importer.open({
241
239
 
242
240
  ## Preloading
243
241
 
244
- By default, the SDK preloads the widget in a hidden iframe for instant display when `open()` is called. This provides the best user experience.
242
+ 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.
245
243
 
246
244
  ```typescript
247
245
  // Preload is enabled by default
248
246
  const importer = new CSVImporter({
249
247
  schema,
250
- publishableKey: "your-publishable-key",
248
+ getSessionToken: async () => fetchSessionToken(),
251
249
  importIdentifier: "user-import",
252
250
  });
253
251
 
254
- // Later, the widget appears instantly
252
+ // Later, the importer appears instantly
255
253
  importer.open({ onData: (chunk, next) => { /* ... */ next(); } });
256
254
  ```
257
255
 
@@ -260,7 +258,7 @@ To disable preloading (there will be a brief loading screen instead):
260
258
  ```typescript
261
259
  const importer = new CSVImporter({
262
260
  schema,
263
- publishableKey: "your-publishable-key",
261
+ getSessionToken: async () => fetchSessionToken(),
264
262
  importIdentifier: "user-import",
265
263
  preload: false,
266
264
  });
@@ -284,7 +282,7 @@ const candidateSchema = x.row({
284
282
 
285
283
  const importer = new CSVImporter({
286
284
  schema: candidateSchema,
287
- publishableKey: "your-publishable-key",
285
+ getSessionToken: async () => fetchSessionToken(),
288
286
  importIdentifier: "candidate-import",
289
287
  templateDownload: {
290
288
  source: "generate",
@@ -302,7 +300,7 @@ const importer = new CSVImporter({
302
300
 
303
301
  ## Theming and Styling
304
302
 
305
- Customize the widget's appearance with the `theme`, `colorMode`, `customCSS`, and `fonts` options.
303
+ Customize the importer's appearance with the `theme`, `colorMode`, `customCSS`, and `fonts` options.
306
304
 
307
305
  ### Theme
308
306
 
@@ -340,7 +338,7 @@ const dualTheme: ECSVTheme = {
340
338
 
341
339
  const importer = new CSVImporter({
342
340
  schema,
343
- publishableKey: "your-publishable-key",
341
+ getSessionToken: async () => fetchSessionToken(),
344
342
  importIdentifier: "user-import",
345
343
  theme,
346
344
  });
@@ -384,7 +382,7 @@ Control light/dark mode with `colorMode`:
384
382
  ```typescript
385
383
  const importer = new CSVImporter({
386
384
  schema,
387
- publishableKey: "your-publishable-key",
385
+ getSessionToken: async () => fetchSessionToken(),
388
386
  importIdentifier: "user-import",
389
387
  colorMode: "system", // 'light' | 'dark' | 'system'
390
388
  });
@@ -397,7 +395,7 @@ Inject custom CSS for fine-grained styling overrides.
397
395
  ```typescript
398
396
  const importer = new CSVImporter({
399
397
  schema,
400
- publishableKey: "your-publishable-key",
398
+ getSessionToken: async () => fetchSessionToken(),
401
399
  importIdentifier: "user-import",
402
400
  customCSS: `
403
401
  .ecsv [data-step="upload"] {
@@ -417,7 +415,7 @@ Load custom fonts via the `fonts` option:
417
415
  ```typescript
418
416
  const importer = new CSVImporter({
419
417
  schema,
420
- publishableKey: "your-publishable-key",
418
+ getSessionToken: async () => fetchSessionToken(),
421
419
  importIdentifier: "user-import",
422
420
  fonts: {
423
421
  title: { source: "google", name: "Space Grotesk", weights: [400, 600, 700] },
@@ -434,7 +432,7 @@ const importer = new CSVImporter({
434
432
 
435
433
  The `x` schema builder provides a type-safe, fluent API for defining your CSV structure.
436
434
 
437
- 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 widget/runtime dependencies.
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.
438
436
 
439
437
  ### Field Types
440
438
 
@@ -539,7 +537,7 @@ All field types support:
539
537
 
540
538
  | Modifier | Description |
541
539
  |---|---|
542
- | `.label(text)` | User-facing label shown in the widget |
540
+ | `.label(text)` | User-facing label shown in the importer |
543
541
  | `.description(text)` | Help text for the field |
544
542
  | `.example(text)` | Example value shown as placeholder |
545
543
  | `.optional()` | Makes the field optional (default is required) |
@@ -590,7 +588,7 @@ x.string().refine(
590
588
  )
591
589
  ```
592
590
 
593
- **Object-returning validator** — inline `valid`, `message`, and an optional `suggestedFix` the widget can offer the user:
591
+ **Object-returning validator** — inline `valid`, `message`, and an optional `suggestedFix` the importer can offer the user:
594
592
 
595
593
  ```typescript
596
594
  x.string().refine((value) => ({
@@ -657,7 +655,7 @@ x.string().refineBatch((values) => {
657
655
 
658
656
  ### `suggestedFix`
659
657
 
660
- Both `.refine()` and `.refineBatch()` support returning a `suggestedFix` object that the widget offers as a one-click fix for the user:
658
+ Both `.refine()` and `.refineBatch()` support returning a `suggestedFix` object that the importer offers as a one-click fix for the user:
661
659
 
662
660
  ```typescript
663
661
  interface SuggestedFix {
@@ -680,14 +678,14 @@ new CSVImporter(options: SDKOptions)
680
678
  | Option | Type | Required | Default | Description |
681
679
  |---|---|---|---|---|
682
680
  | `schema` | Schema | Yes | - | Schema definition created with `x.row()` |
683
- | `publishableKey` | `string` | Yes | - | Your publishable key from the [dashboard](https://expresscsv.com) |
681
+ | `getSessionToken` | `() => Promise<string>` | Yes | - | Async callback that asks your backend for a short-lived importer session token |
684
682
  | `importIdentifier` | `string` | Yes | - | Unique identifier for this import type |
685
- | `title` | `string` | No | - | Title shown in the widget header |
686
- | `preload` | `boolean` | No | `true` | Preload widget for instant display |
683
+ | `title` | `string` | No | - | Title shown in the importer header |
684
+ | `preload` | `boolean` | No | `true` | Preload the importer for instant display |
687
685
  | `debug` | `boolean` | No | `false` | Enable debug logging |
688
686
  | `theme` | `ECSVTheme` | No | - | Custom theme configuration |
689
687
  | `colorMode` | `ColorModePref` | No | - | Light/dark mode (`'light'`, `'dark'`, or `'system'`) |
690
- | `customCSS` | `string` | No | - | Custom CSS to inject into the widget |
688
+ | `customCSS` | `string` | No | - | Custom CSS to inject into the importer |
691
689
  | `fonts` | `Record<string, ECSVFontSource>` | No | - | Custom font sources |
692
690
  | `stepDisplay` | `'progressBar' \| 'segmented' \| 'numbered'` | No | `'progressBar'` | Step indicator style |
693
691
  | `previewSchemaBeforeUpload` | `boolean` | No | `true` | Show schema preview before upload |
@@ -697,7 +695,7 @@ new CSVImporter(options: SDKOptions)
697
695
 
698
696
  #### `open(options)`
699
697
 
700
- Opens the widget and begins the import flow. At least one of `onData` or `webhook` must be provided.
698
+ Opens the importer and begins the import flow. At least one of `onData` or `webhook` must be provided.
701
699
 
702
700
  ```typescript
703
701
  importer.open(options: OpenOptions): void
@@ -711,15 +709,15 @@ importer.open(options: OpenOptions): void
711
709
  | `onComplete` | `() => void` | No | Called when all chunks have been processed |
712
710
  | `onCancel` | `() => void` | No | Called when the user cancels the import |
713
711
  | `onError` | `(error: Error) => void` | No | Called when an error occurs |
714
- | `onWidgetOpen` | `() => void` | No | Called when the widget opens |
715
- | `onWidgetClose` | `(reason: string) => void` | No | Called when the widget closes |
712
+ | `onImporterOpen` | `() => void` | No | Called when the importer opens |
713
+ | `onImporterClose` | `(reason: string) => void` | No | Called when the importer closes |
716
714
  | `onStepChange` | `(stepId, previousStepId?) => void` | No | Called when the wizard step changes |
717
715
 
718
716
  \* At least one of `onData` or `webhook` is required.
719
717
 
720
718
  #### `close(reason?)`
721
719
 
722
- Closes the widget and cleans up resources.
720
+ Closes the importer and cleans up resources.
723
721
 
724
722
  ```typescript
725
723
  await importer.close(reason?: 'user_close' | 'cancel' | 'complete' | 'error'): Promise<void>
@@ -731,18 +729,18 @@ await importer.close(reason?: 'user_close' | 'cancel' | 'complete' | 'error'): P
731
729
 
732
730
  | Method | Returns | Description |
733
731
  |---|---|---|
734
- | `getState()` | `WidgetState` | Current widget state |
735
- | `getIsReady()` | `boolean` | Whether the widget is ready or open |
736
- | `getIsOpen()` | `boolean` | Whether the widget is currently open |
732
+ | `getState()` | `ImporterState` | Current importer lifecycle state |
733
+ | `getIsReady()` | `boolean` | Whether the importer is ready or open |
734
+ | `getIsOpen()` | `boolean` | Whether the importer is currently open |
737
735
  | `getConnectionStatus()` | `boolean` | Whether the iframe connection is active |
738
- | `getCanRestart()` | `boolean` | Whether the widget can be restarted |
736
+ | `getCanRestart()` | `boolean` | Whether the importer can be restarted |
739
737
  | `getLastError()` | `Error \| null` | Last error, if any |
740
738
  | `getStatus()` | `object` | Comprehensive status snapshot |
741
739
  | `getVersion()` | `string` | SDK version |
742
740
 
743
741
  #### `restart(newOptions?)`
744
742
 
745
- Restarts the widget, optionally with updated options. Returns `Promise<void>`.
743
+ Restarts the importer, optionally with updated options. Returns `Promise<void>`.
746
744
 
747
745
  ### `RecordsChunk<T>`
748
746
 
@@ -788,7 +786,7 @@ type Row = Infer<typeof schema>;
788
786
 
789
787
  const importer = new CSVImporter({
790
788
  schema,
791
- publishableKey: "your-publishable-key",
789
+ getSessionToken: async () => fetchSessionToken(),
792
790
  importIdentifier: "user-import",
793
791
  });
794
792
 
package/dist/index.d.cts CHANGED
@@ -1244,12 +1244,16 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1244
1244
  private debug;
1245
1245
  private importIdentifier;
1246
1246
  private sessionId;
1247
+ private currentSessionToken;
1248
+ private currentSessionTokenExpiresAt;
1249
+ private sessionTokenRefreshTimer;
1250
+ private sessionTokenRefreshPromise;
1247
1251
  private _destroyTimer;
1248
1252
  private _beforeUnloadHandler;
1249
- private widgetUrl;
1253
+ private importerUrl;
1250
1254
  private appUrl;
1251
- private widgetState;
1252
- private widgetMode;
1255
+ private importerState;
1256
+ private importerMode;
1253
1257
  private canRestart;
1254
1258
  private lastError;
1255
1259
  private openOptions;
@@ -1257,6 +1261,7 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1257
1261
  private initCompletePromise;
1258
1262
  private resolveInitComplete;
1259
1263
  private resolvedTemplateDownload;
1264
+ private importerStartupGeneration;
1260
1265
  constructor(options: SDKOptions<TSchema>);
1261
1266
  /**
1262
1267
  * Enhanced state management
@@ -1264,10 +1269,24 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1264
1269
  private setState;
1265
1270
  private updateDerivedState;
1266
1271
  private handleError;
1272
+ private sendImportProgress;
1267
1273
  private waitForEvent;
1274
+ private fetchImporterSessionToken;
1275
+ private updateSessionToken;
1276
+ private clearSessionTokenRefreshTimer;
1277
+ private scheduleSessionTokenRefresh;
1278
+ private ensureActiveSessionToken;
1279
+ private refreshSessionBoundToken;
1280
+ private beginImporterStartup;
1281
+ private cancelImporterStartup;
1282
+ private assertActiveImporterStartup;
1283
+ private withTimeout;
1284
+ private sendSessionDetailsToImporter;
1285
+ private sendStartupErrorToImporter;
1286
+ private bootstrapImporterSession;
1268
1287
  /**
1269
1288
  * Open the import flow with chunk-based data processing.
1270
- * Automatically opens the widget if not already open.
1289
+ * Automatically opens the importer if not already open.
1271
1290
  *
1272
1291
  * @param options Configuration including onData callback for processing chunks
1273
1292
  */
@@ -1282,11 +1301,6 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1282
1301
  * Chunks data using SDK-determined chunk size (independent of customer's chunkSize)
1283
1302
  */
1284
1303
  private deliverToWebhook;
1285
- /**
1286
- * Poll the delivery status endpoint until the delivery reaches a terminal state
1287
- * ('success' or 'failed'), or until the timeout is exceeded.
1288
- */
1289
- private pollDeliveryStatus;
1290
1304
  /**
1291
1305
  * Send a single chunk to backend webhook API with retry logic
1292
1306
  */
@@ -1312,7 +1326,7 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1312
1326
  private removeBeforeUnloadListener;
1313
1327
  private waitForIframeLoad;
1314
1328
  private setupConnectionAndInit;
1315
- openWidget(options?: {
1329
+ openImporter(options?: {
1316
1330
  reset?: boolean;
1317
1331
  }): Promise<void>;
1318
1332
  /**
@@ -1323,20 +1337,20 @@ export declare class CSVImporter<TSchema extends ExType<unknown, ExBaseDef, unkn
1323
1337
  private createAndAppendIframe;
1324
1338
  private destroy;
1325
1339
  close(reason?: 'user_close' | 'cancel' | 'complete' | 'error'): Promise<void>;
1326
- private resetWidget;
1340
+ private resetImporter;
1327
1341
  restart(newOptions?: Partial<SDKOptions<TSchema>>): Promise<void>;
1328
- private handleWidgetClosed;
1342
+ private handleImporterClosed;
1329
1343
  private hideContainer;
1330
1344
  getConnectionStatus(): boolean;
1331
- getState(): WidgetState;
1332
- getMode(): WidgetMode;
1345
+ getState(): ImporterState;
1346
+ getMode(): ImporterMode;
1333
1347
  getIsReady(): boolean;
1334
1348
  getIsOpen(): boolean;
1335
1349
  getCanRestart(): boolean;
1336
1350
  getLastError(): Error | null;
1337
1351
  getStatus(): {
1338
- state: WidgetState;
1339
- mode: WidgetMode;
1352
+ state: ImporterState;
1353
+ mode: ImporterMode;
1340
1354
  isReady: boolean;
1341
1355
  isOpen: boolean;
1342
1356
  canRestart: boolean;
@@ -2866,7 +2880,7 @@ declare interface ExpressCSVLocale {
2866
2880
  nextDisabledReviewValidating: string;
2867
2881
  nextDisabledReviewInvalid: string;
2868
2882
  };
2869
- widget: {
2883
+ importer: {
2870
2884
  title: string;
2871
2885
  loading: string;
2872
2886
  closeConfirmTitle: string;
@@ -2875,7 +2889,6 @@ declare interface ExpressCSVLocale {
2875
2889
  closeConfirmContinue: string;
2876
2890
  errorTitle: string;
2877
2891
  startOver: string;
2878
- invalidPublishableKey: string;
2879
2892
  };
2880
2893
  sessionRecovery: {
2881
2894
  message: string;
@@ -2995,6 +3008,7 @@ declare interface ExpressCSVLocale {
2995
3008
  };
2996
3009
  review: {
2997
3010
  completing: string;
3011
+ processing: string;
2998
3012
  loading: string;
2999
3013
  title: string;
3000
3014
  subtitle: string;
@@ -3587,6 +3601,29 @@ export declare class ImportCancelledError extends Error {
3587
3601
  constructor(message?: string);
3588
3602
  }
3589
3603
 
3604
+ /**
3605
+ * Importer preload mode
3606
+ */
3607
+ export declare enum ImporterMode {
3608
+ NORMAL = "normal",
3609
+ PRELOAD = "preload"
3610
+ }
3611
+
3612
+ /**
3613
+ * Importer lifecycle state
3614
+ */
3615
+ export declare enum ImporterState {
3616
+ UNINITIALIZED = "uninitialized",
3617
+ INITIALIZING = "initializing",
3618
+ READY = "ready",
3619
+ OPENING = "opening",
3620
+ OPEN = "open",
3621
+ CLOSING = "closing",
3622
+ RESETTING = "resetting",
3623
+ ERROR = "error",
3624
+ DESTROYED = "destroyed"
3625
+ }
3626
+
3590
3627
  export declare type Infer<T extends ExType<unknown, ExBaseDef, unknown>> = T extends ExType<infer Output, ExBaseDef, unknown> ? Output : never;
3591
3628
 
3592
3629
  export declare type InferCSVImporter<TSchema extends ExType<unknown, ExBaseDef, unknown>> = CSVImporter<TSchema>;
@@ -3611,12 +3648,12 @@ export declare type OpenOptions<T> = RequireAtLeastOne<DeliveryOptionsBase<T>, '
3611
3648
  onCancel?: () => void;
3612
3649
  /** Called when an error occurs */
3613
3650
  onError?: (error: Error) => void;
3614
- /** Called when the widget opens */
3615
- onWidgetOpen?: () => void;
3651
+ /** Called when the importer opens */
3652
+ onImporterOpen?: () => void;
3653
+ /** Called when the importer closes */
3654
+ onImporterClose?: (reason: 'user_close' | 'cancel' | 'complete' | 'error') => void;
3616
3655
  /** Called when the step changes in the wizard */
3617
3656
  onStepChange?: (stepId: ExpressCSVStep, previousStepId?: ExpressCSVStep) => void;
3618
- /** Called when the widget closes */
3619
- onWidgetClose?: (reason: 'user_close' | 'cancel' | 'complete' | 'error') => void;
3620
3657
  };
3621
3658
 
3622
3659
  declare type PhoneNumberFormat = 'international' | 'national' | 'both';
@@ -3765,7 +3802,7 @@ declare type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Excl
3765
3802
 
3766
3803
  export declare interface SDKOptions<TSchema extends ExType<unknown, ExBaseDef, unknown>> {
3767
3804
  schema: TSchema;
3768
- publishableKey: string;
3805
+ getSessionToken: () => Promise<string>;
3769
3806
  importIdentifier: string;
3770
3807
  title?: string;
3771
3808
  debug?: boolean;
@@ -3858,7 +3895,7 @@ export declare type TemplateDownloadOptions<TSchema extends ExType<unknown, ExBa
3858
3895
 
3859
3896
  /**
3860
3897
  * A branded string type for locale entries that require interpolation via `{variable}` syntax.
3861
- * Widget code cannot render a `TemplateString` directly in JSX — it must go through the `t()` function.
3898
+ * Importer code cannot render a `TemplateString` directly in JSX — it must go through the `t()` function.
3862
3899
  * The generic `TVars` encodes the expected variable names so `t()` enforces the correct vars argument.
3863
3900
  */
3864
3901
  declare type TemplateString<TVars extends string = string> = string & {
@@ -3910,34 +3947,11 @@ export declare interface WebhookConfig {
3910
3947
  * Whether to wait for the delivery service to confirm the webhook was
3911
3948
  * successfully received before considering the import complete.
3912
3949
  * When false, the import completes as soon as all chunks are queued.
3913
- * Default: true
3950
+ * Default: false
3914
3951
  */
3915
3952
  awaitWebhookArrival?: boolean;
3916
3953
  }
3917
3954
 
3918
- /**
3919
- * Widget mode enumeration
3920
- */
3921
- export declare enum WidgetMode {
3922
- NORMAL = "normal",
3923
- PRELOAD = "preload"
3924
- }
3925
-
3926
- /**
3927
- * Widget state enumeration for consistent state management
3928
- */
3929
- export declare enum WidgetState {
3930
- UNINITIALIZED = "uninitialized",
3931
- INITIALIZING = "initializing",
3932
- READY = "ready",
3933
- OPENING = "opening",
3934
- OPEN = "open",
3935
- CLOSING = "closing",
3936
- RESETTING = "resetting",
3937
- ERROR = "error",
3938
- DESTROYED = "destroyed"
3939
- }
3940
-
3941
3955
  export declare const x: {
3942
3956
  string: typeof ExString.create;
3943
3957
  number: typeof ExNumber.create;