@objectstack/rest 13.0.0 → 14.5.0

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/dist/index.d.cts CHANGED
@@ -251,6 +251,10 @@ declare class RestServer {
251
251
  private i18nServiceProvider?;
252
252
  private analyticsServiceProvider?;
253
253
  private settingsServiceProvider?;
254
+ /** [ADR-0090] `security` service resolver — used by the
255
+ * /security/suggested-bindings (D5/D9) and /security/explain (D6)
256
+ * routes (plugin-security). */
257
+ private securityServiceProvider?;
254
258
  /** Sync probe: is a kernel service registered? Single-env path for nav
255
259
  * capability gates (ADR-0057 D10) — resolveExecCtx sets no kernel in
256
260
  * single-kernel deployments, so this prevents the gate failing open. */
@@ -262,7 +266,7 @@ declare class RestServer {
262
266
  * the durable source of truth a restarted/other node reads.
263
267
  */
264
268
  private readonly cancelledImportJobs;
265
- constructor(server: IHttpServer, protocol: ObjectStackProtocol, config?: RestServerConfig, kernelManager?: RestKernelManager, envRegistry?: RestEnvRegistry, defaultEnvironmentIdProvider?: () => string | undefined, authServiceProvider?: (environmentId?: string) => Promise<any | undefined>, objectQLProvider?: (environmentId?: string) => Promise<any | undefined>, emailServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingServiceProvider?: (environmentId?: string) => Promise<any | undefined>, reportsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, approvalsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingRulesServiceProvider?: (environmentId?: string) => Promise<any | undefined>, i18nServiceProvider?: (environmentId?: string) => Promise<any | undefined>, analyticsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, settingsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, serviceExistsProvider?: (name: string) => boolean);
269
+ constructor(server: IHttpServer, protocol: ObjectStackProtocol, config?: RestServerConfig, kernelManager?: RestKernelManager, envRegistry?: RestEnvRegistry, defaultEnvironmentIdProvider?: () => string | undefined, authServiceProvider?: (environmentId?: string) => Promise<any | undefined>, objectQLProvider?: (environmentId?: string) => Promise<any | undefined>, emailServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingServiceProvider?: (environmentId?: string) => Promise<any | undefined>, reportsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, approvalsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingRulesServiceProvider?: (environmentId?: string) => Promise<any | undefined>, i18nServiceProvider?: (environmentId?: string) => Promise<any | undefined>, analyticsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, settingsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, serviceExistsProvider?: (name: string) => boolean, securityServiceProvider?: (environmentId?: string) => Promise<any | undefined>);
266
270
  /**
267
271
  * Resolve the protocol for a given request. When `environmentId` is present
268
272
  * and a KernelManager is wired, fetch the per-project kernel's
@@ -350,6 +354,24 @@ declare class RestServer {
350
354
  * to the protocol layer (the SecurityPlugin treats undefined as anon).
351
355
  */
352
356
  private resolveExecCtx;
357
+ /**
358
+ * [ADR-0046 §6.7] The audience-evaluation view of the caller for book/doc
359
+ * gating. `permissionSets` resolves through the security service's
360
+ * `resolvePermissionSetNames` — the SAME resolution as data-plane
361
+ * enforcement (positions expanded, additive baseline), so the docs gate
362
+ * can never drift from it. `permissionSets` stays undefined when the
363
+ * service is absent or resolution fails; `audienceAllows` then DENIES
364
+ * permission-set-gated audiences (fail closed, ADR-0049). Resolution is
365
+ * skipped unless `needPermissionSets` — callers pass true only when a
366
+ * `{ permissionSet }` audience is actually in play.
367
+ */
368
+ private resolveAudienceCaller;
369
+ /** Whether any of these books carries a `{ permissionSet }` audience. */
370
+ private static anyPermissionSetAudience;
371
+ /** Coerce a getMetaItems result (array | {items}) into an array. */
372
+ private static metaItemsArray;
373
+ /** Fetch every book of the environment, shaped for the audience resolver. */
374
+ private fetchAudienceBooks;
353
375
  /** Heavy path behind `resolveExecCtx` — resolve identity + RBAC/RLS + localization. */
354
376
  private computeExecCtx;
355
377
  /**
@@ -486,9 +508,26 @@ declare class RestServer {
486
508
  private _openApiSpecCache;
487
509
  private loadOpenApiSpec;
488
510
  /**
489
- * Register metadata endpoints
511
+ * Register the metadata routes behind the SAME `requireAuth` gate the
512
+ * `/data` routes use.
513
+ *
514
+ * `registerMetadataEndpoints` builds ~17 `/meta/*` routes but — unlike the
515
+ * `/data` handlers — never calls {@link enforceAuth}: its handlers assumed
516
+ * the `requireAuth` gate rejected anonymous callers "upstream", yet nothing
517
+ * upstream covers `/meta`, so on a `requireAuth` deployment an anonymous
518
+ * caller could read object / field schemas. On a tenant-less runtime host
519
+ * those are SYSTEM-object schemas and the host is publicly reachable — a
520
+ * real leak.
521
+ *
522
+ * Rather than add the gate to every handler (and have the next new route
523
+ * forget it — the exact failure mode that caused this), wrap the route
524
+ * registrar for the duration of registration so every meta route, present
525
+ * and future, inherits it. The check is a no-op when `requireAuth` is off
526
+ * (demo / single-tenant), so the previously-public metadata surface there
527
+ * is unchanged; an authenticated user passes exactly as on `/data`.
490
528
  */
491
529
  private registerMetadataEndpoints;
530
+ private registerMetadataEndpointsInner;
492
531
  /**
493
532
  * Register UI endpoints
494
533
  */
@@ -593,6 +632,26 @@ declare class RestServer {
593
632
  * `@objectstack/service-analytics` fails cleanly.
594
633
  */
595
634
  private registerAnalyticsEndpoints;
635
+ /**
636
+ * [ADR-0090 D6] Access-explanation endpoint — the REST face of the
637
+ * explain engine (framework#2696).
638
+ *
639
+ * GET {basePath}/security/explain?object=…&operation=…&userId=…
640
+ * POST {basePath}/security/explain body: { object, operation, userId? }
641
+ *
642
+ * Delegates to the security service's `explain(request, callerContext)`
643
+ * (`SecurityPlugin.explainAccessForCaller`) — the same code paths the
644
+ * enforcement middleware runs, so the report is explained by
645
+ * construction. Caller authorization lives in the SERVICE, not here:
646
+ * explaining ANOTHER user requires `manage_users` or a delegated
647
+ * `adminScope` covering that user (D12); the service's
648
+ * `PermissionDeniedError` maps to 403. The route itself only insists on
649
+ * an authenticated caller (an access report is sensitive even about
650
+ * oneself, and the anonymous `guest` posture is not this endpoint's
651
+ * business) and returns 501 when no security service exposing `explain`
652
+ * is mounted (a deployment without `@objectstack/plugin-security`).
653
+ */
654
+ private registerSecurityExplainEndpoints;
596
655
  private registerSharingEndpoints;
597
656
  /**
598
657
  * Register sharing-rule endpoints (M10.17). Mirrors the existing
@@ -607,6 +666,25 @@ declare class RestServer {
607
666
  * Returns 501 when no sharing-rule service is configured.
608
667
  */
609
668
  private registerSharingRuleEndpoints;
669
+ /**
670
+ * Register the security admin endpoints (ADR-0090 D5/D9) — suggested
671
+ * audience bindings. A package permission set declaring `isDefault: true`
672
+ * is an install-time SUGGESTION to bind it to the `everyone` position;
673
+ * these routes surface pending suggestions and let a tenant admin resolve
674
+ * them. The `security` service (plugin-security) does the real gating:
675
+ * tenant-admin pre-check on all three, and confirm writes the binding
676
+ * with the caller's execution context so the audience-anchor and
677
+ * delegated-admin gates enforce it — never auto-bound, never system.
678
+ *
679
+ * GET {basePath}/security/suggested-bindings?status=&packageId=
680
+ * POST {basePath}/security/suggested-bindings/:id/confirm
681
+ * POST {basePath}/security/suggested-bindings/:id/dismiss
682
+ *
683
+ * Routes return 501 when the `security` service is not registered
684
+ * (deployment without plugin-security). Typed service errors carry their
685
+ * HTTP status (403 permission / 404 not found / 409 state).
686
+ */
687
+ private registerSecurityEndpoints;
610
688
  /**
611
689
  * Register saved-report + scheduled-digest endpoints (M11.C16).
612
690
  *
@@ -682,4 +760,332 @@ interface RestApiPluginConfig {
682
760
  */
683
761
  declare function createRestApiPlugin(config?: RestApiPluginConfig): Plugin;
684
762
 
685
- export { type RestApiPluginConfig, RestServer, type RouteEntry, RouteGroupBuilder, RouteManager, createRestApiPlugin };
763
+ /**
764
+ * Type-aware value formatting for the streaming data export route
765
+ * (`GET /data/:object/export`).
766
+ *
767
+ * The raw rows returned by `findData` carry *storage* values: lookup / user
768
+ * fields hold ids (or, when `$expand`-ed, nested records), select fields hold
769
+ * option codes, booleans hold true/false, dates hold ISO strings. None of those
770
+ * read well in a spreadsheet. These helpers turn each value into a human
771
+ * readable cell using the object's field metadata.
772
+ *
773
+ * Contract: when no field metadata is available (schema lookup failed or carried
774
+ * no fields) every helper is a pass-through, so the export stays byte-for-byte
775
+ * identical to the un-formatted behaviour.
776
+ */
777
+ interface ExportFieldMeta {
778
+ name: string;
779
+ type?: string;
780
+ label?: string;
781
+ options?: Array<{
782
+ label?: string;
783
+ value?: unknown;
784
+ color?: string;
785
+ }>;
786
+ /** Target object for lookup / master_detail / user fields. */
787
+ reference?: string;
788
+ /** Field on the referenced record to show as its label. */
789
+ displayField?: string;
790
+ /** Field is required — a value (or default) must exist on insert. */
791
+ required?: boolean;
792
+ /** Engine-owned column the client never supplies (never required of import). */
793
+ system?: boolean;
794
+ /** Read-only column the client never supplies (never required of import). */
795
+ readonly?: boolean;
796
+ /** Field declares a `defaultValue` the engine applies on insert (satisfies required). */
797
+ hasDefault?: boolean;
798
+ }
799
+ /**
800
+ * Build a field-name → metadata map from an object schema (best-effort).
801
+ *
802
+ * Accepts both shapes `fields` appears in across the stack: the runtime
803
+ * `ObjectSchema.fields` is a `Record<fieldName, FieldDefinition>` object map
804
+ * (the form served by the engine registry / `getMetaItem`), while some callers
805
+ * and fixtures hand back a plain `FieldDefinition[]` array. A field's name is
806
+ * taken from its own `name`, falling back to the map key.
807
+ */
808
+ declare function buildFieldMetaMap(schema: unknown): Map<string, ExportFieldMeta>;
809
+
810
+ /**
811
+ * Bulk-import request parsing — extracted verbatim from rest-server.ts
812
+ * (#2766 V2) so consumers outside the generic `/data/:object/import` routes
813
+ * (the identity import endpoint in plugin-auth) can accept byte-identical
814
+ * payloads (rows[]/csv/xlsxBase64, mapping in either shape, writeMode +
815
+ * matchFields) without re-implementing the parsers.
816
+ */
817
+
818
+ /**
819
+ * Detect the `getMetaItem` response envelope (`{ type, name, item, lock, … }`)
820
+ * whose translatable metadata document is nested at `.item`. The cached read
821
+ * path and `getMetaItems` element shape hand back the already-unwrapped
822
+ * document instead, so translation helpers must distinguish the two: an
823
+ * envelope carries a nested `item` object alongside its own `type`/`name`,
824
+ * which a bare metadata document never does.
825
+ */
826
+ declare function isMetaEnvelope(value: any): boolean;
827
+ /**
828
+ * Minimal RFC-4180-style CSV parser used by the bulk-import endpoint
829
+ * (M10.9). Handles quoted fields (including embedded quotes via "" and
830
+ * embedded commas/newlines) and both CRLF and LF line endings.
831
+ *
832
+ * The first non-empty line is treated as the header row. Header names
833
+ * can be re-mapped to canonical field names via the optional `mapping`
834
+ * argument (e.g. `{ "First Name": "first_name" }`); unmapped headers
835
+ * pass through unchanged. Empty cells become empty strings.
836
+ *
837
+ * Kept dependency-free so REST stays runtime-portable (Hono / Express
838
+ * adapters both consume this without pulling a CSV lib transitively).
839
+ */
840
+ declare function parseCsvToRows(csv: string, mapping?: Record<string, string>): Array<Record<string, any>>;
841
+ /**
842
+ * Parse an .xlsx workbook (raw bytes) into row objects, mirroring
843
+ * {@link parseCsvToRows}: first non-empty row is the header, each subsequent row
844
+ * becomes `{ header→cell }` with the optional `mapping` renaming columns. Reads
845
+ * the named/indexed `sheet` when given, else the first worksheet. Dynamically
846
+ * imports ExcelJS (already a dependency of the export path) so CSV/JSON imports
847
+ * don't pay for it.
848
+ */
849
+ declare function parseXlsxToRows(buffer: Buffer | ArrayBuffer, mapping?: Record<string, string>, sheet?: string | number): Promise<Array<Record<string, any>>>;
850
+ /** Everything the import runner needs, parsed & validated from a request body. */
851
+ interface PreparedImport {
852
+ rows: Array<Record<string, any>>;
853
+ metaMap: Map<string, ExportFieldMeta>;
854
+ writeMode: 'insert' | 'update' | 'upsert';
855
+ matchFields: string[];
856
+ dryRun: boolean;
857
+ runAutomations: boolean;
858
+ trimWhitespace: boolean;
859
+ nullValues?: string[];
860
+ createMissingOptions: boolean;
861
+ skipBlankMatchKey: boolean;
862
+ }
863
+ type PrepareImportResult = {
864
+ ok: true;
865
+ prepared: PreparedImport;
866
+ } | {
867
+ ok: false;
868
+ status: number;
869
+ code: string;
870
+ error: string;
871
+ };
872
+ /**
873
+ * Parse & validate a bulk-import request body into a {@link PreparedImport}.
874
+ *
875
+ * Shared by the synchronous `POST /data/:object/import` route and the async
876
+ * import-job create route so both accept byte-identical payloads (writeMode +
877
+ * matchFields, mapping in either shape, rows[]/csv/xlsxBase64) and resolve the
878
+ * same field metadata. The only knob that differs is `maxRows` (5k sync vs
879
+ * 50k async). Returns a discriminated result; the caller maps `!ok` to an HTTP
880
+ * error using the returned status/code/error.
881
+ */
882
+ declare function prepareImportRequest(body: any, opts: {
883
+ p: any;
884
+ objectName: string;
885
+ environmentId?: string;
886
+ maxRows: number;
887
+ }): Promise<PrepareImportResult>;
888
+
889
+ /**
890
+ * import-runner — the shared row-processing core for bulk import.
891
+ *
892
+ * Both the synchronous `POST /data/:object/import` route and the asynchronous
893
+ * import-job worker feed rows through {@link runImport}. Extracting the loop
894
+ * keeps the two paths byte-for-byte identical in coercion, upsert matching, and
895
+ * per-row reporting — the async worker only adds progress persistence and
896
+ * cancellation on top.
897
+ *
898
+ * Rows resolved to a CREATE are batched through `p.createManyData` (the
899
+ * engine's array-form `insert()` — one round-trip per batch, with transient
900
+ * retry and per-row degradation on a logical/validation failure) instead of
901
+ * one `p.createData` call per row — see framework#2678. A protocol that
902
+ * doesn't implement `createManyData` falls back to the original per-row
903
+ * `createData` path unchanged.
904
+ */
905
+ type ImportAction = 'created' | 'updated' | 'skipped' | 'failed';
906
+ interface ImportRowResult {
907
+ row: number;
908
+ ok: boolean;
909
+ action: ImportAction;
910
+ id?: string;
911
+ field?: string;
912
+ error?: string;
913
+ code?: string;
914
+ }
915
+ /** Running tallies handed to {@link RunImportOptions.onProgress}. */
916
+ interface ImportProgress {
917
+ processed: number;
918
+ total: number;
919
+ created: number;
920
+ updated: number;
921
+ skipped: number;
922
+ errors: number;
923
+ }
924
+ /**
925
+ * Records exactly what a non-dry-run import changed, so the job can be undone:
926
+ * created records are deleted, and updated records have the touched fields
927
+ * restored to their pre-import values. Only the fields the import wrote are
928
+ * captured (keyed to `before`), keeping the log precise and bounded.
929
+ */
930
+ interface ImportUndoLog {
931
+ /** Ids of records this import created (delete to undo). */
932
+ created: string[];
933
+ /** Per updated record: the touched fields' values *before* the import. */
934
+ updated: Array<{
935
+ id: string;
936
+ before: Record<string, any>;
937
+ }>;
938
+ }
939
+ interface ImportRunSummary extends ImportProgress {
940
+ ok: number;
941
+ results: ImportRowResult[];
942
+ cancelled: boolean;
943
+ /** Present only when `captureUndo` was set — the reversal instructions. */
944
+ undoLog?: ImportUndoLog;
945
+ }
946
+ /** Minimal protocol surface the runner needs (find / create / update). */
947
+ interface ImportProtocolLike {
948
+ findData(args: any): Promise<any>;
949
+ createData(args: any): Promise<any>;
950
+ updateData(args: any): Promise<any>;
951
+ /**
952
+ * Optional bulk-create primitive. When present, `runImport` batches
953
+ * CREATE-resolved rows through it instead of one `createData` call per
954
+ * row — see framework#2678. Must resolve to `{ records: any[] }` with one
955
+ * record per input row, in the same order.
956
+ */
957
+ createManyData?(args: {
958
+ object: string;
959
+ records: any[];
960
+ context?: any;
961
+ environmentId?: string;
962
+ }): Promise<{
963
+ records: any[];
964
+ }>;
965
+ }
966
+ interface RunImportOptions {
967
+ /** Protocol/engine to read & write through. */
968
+ p: ImportProtocolLike;
969
+ objectName: string;
970
+ environmentId?: string;
971
+ /** Exec context threaded onto reads and (with automation toggle) writes. */
972
+ context?: any;
973
+ /** Already-mapped rows (source columns renamed to target fields). */
974
+ rows: Array<Record<string, any>>;
975
+ /** Field metadata for value coercion (name→id lookups, select codes, …). */
976
+ metaMap: Map<string, ExportFieldMeta>;
977
+ writeMode: 'insert' | 'update' | 'upsert';
978
+ matchFields: string[];
979
+ dryRun: boolean;
980
+ runAutomations: boolean;
981
+ trimWhitespace: boolean;
982
+ nullValues?: string[];
983
+ createMissingOptions: boolean;
984
+ skipBlankMatchKey: boolean;
985
+ /**
986
+ * Progress callback, invoked every {@link RunImportOptions.progressEvery}
987
+ * processed rows and once at the end. May be async; the runner awaits it so a
988
+ * DB write of progress completes before the next chunk.
989
+ */
990
+ onProgress?: (p: ImportProgress) => void | Promise<void>;
991
+ /**
992
+ * Rows between onProgress calls (default 200). Also the flush boundary for
993
+ * buffered creates — a batch never grows past this before being written,
994
+ * so progress numbers stay accurate at every reported checkpoint.
995
+ */
996
+ progressEvery?: number;
997
+ /**
998
+ * Cooperative cancellation. Checked at each progress boundary; when it returns
999
+ * truthy the runner stops and returns `cancelled: true` with partial results.
1000
+ */
1001
+ shouldCancel?: () => boolean | Promise<boolean>;
1002
+ /**
1003
+ * When true (and not a dry run), accumulate an {@link ImportUndoLog} so the
1004
+ * import can be reverted later. Callers gate this on row count to bound the
1005
+ * stored snapshot size.
1006
+ */
1007
+ captureUndo?: boolean;
1008
+ }
1009
+ declare function runImport(opts: RunImportOptions): Promise<ImportRunSummary>;
1010
+
1011
+ /**
1012
+ * Type-aware value *coercion* for the bulk-import route
1013
+ * (`POST /data/:object/import`).
1014
+ *
1015
+ * This is the inverse of `export-format.ts`. A spreadsheet / CSV cell arrives as
1016
+ * a raw string (or, for JSON payloads, an arbitrary primitive); the storage
1017
+ * layer, on the other hand, expects *storage* values — booleans as real
1018
+ * booleans, numbers as numbers, dates as ISO strings, select fields as their
1019
+ * option **code** (not the human label), and lookup / user fields as the
1020
+ * referenced record **id** (not its name). The engine deliberately does not
1021
+ * coerce for storage (see `record-validator.ts`, which coerces only to *check*
1022
+ * a value and then discards the coerced form), so import has to do it here.
1023
+ *
1024
+ * The accepted storage shapes below are dictated by what
1025
+ * `validateFieldValue` in `packages/objectql` will accept:
1026
+ * - number / currency / percent / rating / slider → a finite `number`
1027
+ * - boolean / toggle → a real `boolean`
1028
+ * - date / datetime → an ISO-8601 string
1029
+ * - time → `HH:MM` / `HH:MM:SS`
1030
+ * - select / radio → an option *value*
1031
+ * - multiselect / checkboxes / tags → an array of option values
1032
+ * - lookup / master_detail / user / reference → a record id (resolved async)
1033
+ *
1034
+ * Contract: when a field carries no usable metadata the value passes through
1035
+ * untouched, so an import stays byte-identical to the pre-coercion behaviour.
1036
+ */
1037
+
1038
+ /**
1039
+ * Structured outcome of a reference lookup. `id` set → a single record matched.
1040
+ * `ambiguous` → the display value matched more than one record, so linking any
1041
+ * one of them would be a guess the importer refuses to make. `matchedField`
1042
+ * names the field the match came from (for diagnostics). An empty object means
1043
+ * nothing matched. A bare `string | undefined` is still accepted from legacy
1044
+ * resolvers and normalised to this shape.
1045
+ */
1046
+ interface RefMatch {
1047
+ id?: string;
1048
+ ambiguous?: boolean;
1049
+ matchedField?: string;
1050
+ }
1051
+ /**
1052
+ * Resolve a reference field's display value (a name / email / id typed by the
1053
+ * user) to the referenced record's id. Return `undefined` / `{}` when nothing
1054
+ * matches (caller surfaces "not found"), a bare id string / `{ id }` on a unique
1055
+ * hit, or `{ ambiguous: true }` when several records share the value. Legacy
1056
+ * resolvers that return `string | undefined` keep working. Implementations are
1057
+ * expected to cache — the same name shows up on many rows.
1058
+ */
1059
+ type RefResolver = (referenceObject: string, displayValue: string, meta: ExportFieldMeta) => Promise<string | undefined | RefMatch>;
1060
+ interface CoerceContext {
1061
+ /** Trim leading/trailing whitespace from string-ish cells (default true). */
1062
+ trimWhitespace?: boolean;
1063
+ /** Extra strings (besides `''`) treated as null, e.g. `['N/A', 'null']`. */
1064
+ nullValues?: string[];
1065
+ /**
1066
+ * When a select/multiselect cell matches no known option, keep the raw value
1067
+ * instead of failing. Note: the engine still validates option membership, so
1068
+ * this only helps when the option is (or will be) present in the schema.
1069
+ */
1070
+ createMissingOptions?: boolean;
1071
+ /** Async reference resolver (name/email/id → record id). Optional. */
1072
+ resolveRef?: RefResolver;
1073
+ }
1074
+ /** A per-field coercion failure, shaped like the engine's validation errors. */
1075
+ interface FieldCoerceError {
1076
+ field: string;
1077
+ code: string;
1078
+ message: string;
1079
+ }
1080
+ /**
1081
+ * Coerce a whole raw row into a storage-ready record. Unknown columns (no
1082
+ * matching field metadata) pass through untouched so ad-hoc / schemaless
1083
+ * objects still import. Collects every field error rather than stopping at the
1084
+ * first, so a UI can show all problems in a row at once.
1085
+ */
1086
+ declare function coerceRow(rawRow: Record<string, unknown>, metaMap: Map<string, ExportFieldMeta>, ctx: CoerceContext): Promise<{
1087
+ data: Record<string, unknown>;
1088
+ errors: FieldCoerceError[];
1089
+ }>;
1090
+
1091
+ export { type CoerceContext, type ExportFieldMeta, type ImportAction, type ImportProgress, type ImportProtocolLike, type ImportRowResult, type ImportRunSummary, type ImportUndoLog, type PrepareImportResult, type PreparedImport, type RefResolver, type RestApiPluginConfig, RestServer, type RouteEntry, RouteGroupBuilder, RouteManager, type RunImportOptions, buildFieldMetaMap, coerceRow, createRestApiPlugin, isMetaEnvelope, parseCsvToRows, parseXlsxToRows, prepareImportRequest, runImport };