@expresscsv/react 0.1.16 → 0.1.18

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
@@ -5,6 +5,8 @@
5
5
 
6
6
  React hook for embedding the [ExpressCSV](https://expresscsv.com) CSV import widget. Wraps [`@expresscsv/sdk`](https://www.npmjs.com/package/@expresscsv/sdk) with automatic lifecycle management and reactive state.
7
7
 
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/react`.
9
+
8
10
  ## Installation
9
11
 
10
12
  ```bash
@@ -312,6 +314,8 @@ interface WebhookPayload {
312
314
  }
313
315
  ```
314
316
 
317
+ When you already have a schema, prefer `InferWebhookPayload<typeof schema>` from `@expresscsv/schemas` instead of rewriting this object shape by hand.
318
+
315
319
  Example payload:
316
320
 
317
321
  ```json
@@ -350,6 +354,25 @@ Your endpoint should return a **2xx** status code to acknowledge each chunk. Non
350
354
 
351
355
  Chunks are delivered **serially** — the next chunk is only sent after the previous one succeeds.
352
356
 
357
+ If your schema lives in shared or backend code via `@expresscsv/schemas`, you can reuse the built-in webhook payload helper in your webhook handler:
358
+
359
+ ```typescript
360
+ import express from "express";
361
+ import type { InferWebhookPayload } from "@expresscsv/schemas";
362
+ import { candidateSchema } from "../shared/candidate-schema";
363
+
364
+ const app = express();
365
+ app.use(express.json());
366
+
367
+ app.post("/webhooks/csv-import", async (req, res) => {
368
+ const payload = req.body as InferWebhookPayload<typeof candidateSchema>;
369
+
370
+ await importCandidates(payload.records);
371
+
372
+ res.status(200).json({ ok: true });
373
+ });
374
+ ```
375
+
353
376
  Below is a minimal Express.js example:
354
377
 
355
378
  ```typescript
@@ -566,6 +589,8 @@ interface WebhookConfig {
566
589
 
567
590
  The `x` schema builder provides a type-safe, fluent API for defining your CSV structure.
568
591
 
592
+ For apps that share schemas with backend code or webhook receivers, prefer defining the schema in `@expresscsv/schemas` and importing it into your React app. That keeps schema authoring free of React and widget dependencies, while also giving your backend access to `InferWebhookPayload<typeof schema>`.
593
+
569
594
  #### Field Types
570
595
 
571
596
  ```tsx
@@ -604,6 +629,63 @@ const schema = x.row({
604
629
  });
605
630
  ```
606
631
 
632
+ #### Runtime-Built Schemas
633
+
634
+ For a fixed schema, keep it directly in `x.row(...)`:
635
+
636
+ ```tsx
637
+ const schema = x.row({
638
+ email: x.string().email().label("Email"),
639
+ lifecycleStage: x
640
+ .select([
641
+ { label: "Lead", value: "lead" },
642
+ { label: "Customer", value: "customer" },
643
+ { label: "Churned", value: "churned" },
644
+ ])
645
+ .label("Lifecycle Stage"),
646
+ accountOwner: x.string().label("Account Owner").optional(),
647
+ contractValue: x.number().currency("USD").label("Contract Value").optional(),
648
+ });
649
+ ```
650
+
651
+ `x.dynamicRow(...)` lets you build the row at runtime, which is useful when the schema depends on account data, user preferences, or enabled custom fields.
652
+
653
+ ```tsx
654
+ import { x } from "@expresscsv/react";
655
+
656
+ function buildCustomerSchema(options: {
657
+ collectCrmId: boolean;
658
+ collectHealthScore: boolean;
659
+ collectSegment: boolean;
660
+ }) {
661
+ return x.dynamicRow({
662
+ email: x.string().email().label("Email"),
663
+ companyName: x.string().label("Company Name"),
664
+ ...(options.collectCrmId ? { crmId: x.string().label("CRM ID") } : {}),
665
+ ...(options.collectHealthScore
666
+ ? { healthScore: x.number().label("Health Score") }
667
+ : {}),
668
+ ...(options.collectSegment
669
+ ? {
670
+ segment: x
671
+ .select([
672
+ { label: "SMB", value: "smb" },
673
+ { label: "Mid-Market", value: "mid-market" },
674
+ { label: "Enterprise", value: "enterprise" },
675
+ ])
676
+ .label("Segment"),
677
+ }
678
+ : {}),
679
+ });
680
+ }
681
+
682
+ const schema = buildCustomerSchema({
683
+ collectCrmId: true,
684
+ collectHealthScore: true,
685
+ collectSegment: false,
686
+ });
687
+ ```
688
+
607
689
  #### Common Modifiers
608
690
 
609
691
  All field types support:
package/dist/index.d.cts CHANGED
@@ -3018,6 +3018,13 @@ declare class ExRow<T extends ExRowShape> extends ExType<{
3018
3018
  * @returns A new ExRow instance
3019
3019
  */
3020
3020
  static create<T extends ExRowShape>(shape: T): ExRow<T>;
3021
+ /**
3022
+ * Factory method for runtime-built row shapes.
3023
+ *
3024
+ * This intentionally widens the returned type to ExRow<ExRowShape> so callers
3025
+ * can assemble fields conditionally without relying on exact key inference.
3026
+ */
3027
+ static createDynamic(shape: ExRowShape): ExRow<ExRowShape>;
3021
3028
  /**
3022
3029
  * Specifies columns that can be null/optional with conditional logic
3023
3030
  *
@@ -3824,6 +3831,7 @@ export declare const x: {
3824
3831
  time: typeof ExTime.create;
3825
3832
  datetime: typeof ExDatetime.create;
3826
3833
  row: typeof ExRow.create;
3834
+ dynamicRow: typeof ExRow.createDynamic;
3827
3835
  select: typeof ExSelect.create;
3828
3836
  multiselect: typeof ExMultiselect.create;
3829
3837
  infer: <T extends ExType<unknown, ExBaseDef, unknown>>(_schema: T) => Infer<T>;
package/dist/index.d.mts CHANGED
@@ -3018,6 +3018,13 @@ declare class ExRow<T extends ExRowShape> extends ExType<{
3018
3018
  * @returns A new ExRow instance
3019
3019
  */
3020
3020
  static create<T extends ExRowShape>(shape: T): ExRow<T>;
3021
+ /**
3022
+ * Factory method for runtime-built row shapes.
3023
+ *
3024
+ * This intentionally widens the returned type to ExRow<ExRowShape> so callers
3025
+ * can assemble fields conditionally without relying on exact key inference.
3026
+ */
3027
+ static createDynamic(shape: ExRowShape): ExRow<ExRowShape>;
3021
3028
  /**
3022
3029
  * Specifies columns that can be null/optional with conditional logic
3023
3030
  *
@@ -3824,6 +3831,7 @@ export declare const x: {
3824
3831
  time: typeof ExTime.create;
3825
3832
  datetime: typeof ExDatetime.create;
3826
3833
  row: typeof ExRow.create;
3834
+ dynamicRow: typeof ExRow.createDynamic;
3827
3835
  select: typeof ExSelect.create;
3828
3836
  multiselect: typeof ExMultiselect.create;
3829
3837
  infer: <T extends ExType<unknown, ExBaseDef, unknown>>(_schema: T) => Infer<T>;
package/dist/index.d.ts CHANGED
@@ -3018,6 +3018,13 @@ declare class ExRow<T extends ExRowShape> extends ExType<{
3018
3018
  * @returns A new ExRow instance
3019
3019
  */
3020
3020
  static create<T extends ExRowShape>(shape: T): ExRow<T>;
3021
+ /**
3022
+ * Factory method for runtime-built row shapes.
3023
+ *
3024
+ * This intentionally widens the returned type to ExRow<ExRowShape> so callers
3025
+ * can assemble fields conditionally without relying on exact key inference.
3026
+ */
3027
+ static createDynamic(shape: ExRowShape): ExRow<ExRowShape>;
3021
3028
  /**
3022
3029
  * Specifies columns that can be null/optional with conditional logic
3023
3030
  *
@@ -3824,6 +3831,7 @@ export declare const x: {
3824
3831
  time: typeof ExTime.create;
3825
3832
  datetime: typeof ExDatetime.create;
3826
3833
  row: typeof ExRow.create;
3834
+ dynamicRow: typeof ExRow.createDynamic;
3827
3835
  select: typeof ExSelect.create;
3828
3836
  multiselect: typeof ExMultiselect.create;
3829
3837
  infer: <T extends ExType<unknown, ExBaseDef, unknown>>(_schema: T) => Infer<T>;