@object-ui/data-objectstack 16.1.0 → 17.1.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.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ObjectStackClient } from '@objectstack/client';
2
- import { DataSource, QueryParams, QueryResult, MutationEvent, BatchTransactionOperation, ImportRequestOptions, ImportRecordsResult, CreateImportJobResult, ImportJobProgressInfo, ImportJobResultsInfo, ListImportJobsOptions, ImportJobSummaryInfo, ImportJobUndoResult, ExportDownloadRequest, FileUploadResult } from '@object-ui/types';
2
+ import { DataSource, QueryParams, QueryResult, GlobalSearchResult, DataSourceMutationEvent, BatchTransactionOperation, ImportRequestOptions, ImportRecordsResult, CreateImportJobResult, ImportJobProgressInfo, ImportJobResultsInfo, ListImportJobsOptions, ImportJobSummaryInfo, ImportJobUndoResult, ExportDownloadRequest, FileUploadResult } from '@object-ui/types';
3
3
  export { FileUploadResult } from '@object-ui/types';
4
4
 
5
5
  /**
@@ -1174,6 +1174,64 @@ declare function calculateAutoLayout(items: Array<{
1174
1174
  y: number;
1175
1175
  }>;
1176
1176
 
1177
+ /**
1178
+ * Map human-readable filter operator names produced by SDUI view configs
1179
+ * (e.g. `lead.view.ts`) to the canonical operator symbols expected by the
1180
+ * ObjectStack server's filter AST. Unknown operators fall through unchanged
1181
+ * so existing AST-style entries keep working.
1182
+ *
1183
+ * Every VALUE here must be a member of the spec's `VALID_AST_OPERATORS`
1184
+ * (`@objectstack/spec/data`) — that set gates `isFilterAST()`, and a filter it
1185
+ * rejects is not converted, not validated, and then silently DROPPED by
1186
+ * driver-sql (objectstack#3948). Pinned by `filter-operator-ast-parity.test.ts`.
1187
+ *
1188
+ * Exported for that test. @internal
1189
+ */
1190
+ declare const FILTER_OPERATOR_ALIASES: Record<string, string>;
1191
+ /**
1192
+ * A filter entry this adapter cannot translate into an AST tuple.
1193
+ *
1194
+ * Thrown rather than skipped. Dropping one entry out of an `and` WIDENS the
1195
+ * result set, and dropping the last one emits no `filter=` at all — every row,
1196
+ * no error, from a query that asked for a subset. That is the same silent
1197
+ * over-fetch the server-side drivers stopped doing in objectstack#3948, and
1198
+ * skipping it here just moves it one layer up.
1199
+ *
1200
+ * Carries the code and status the data API uses for its own version of this
1201
+ * refusal (objectstack#4121) so a failed list renders "this view's filter is
1202
+ * malformed" rather than "check your connection" (#3066).
1203
+ */
1204
+ declare class MalformedFilterError extends Error {
1205
+ readonly code = "INVALID_FILTER";
1206
+ readonly httpStatus = 400;
1207
+ readonly entry: unknown;
1208
+ readonly index: number;
1209
+ constructor(entry: unknown, index: number);
1210
+ }
1211
+ /** Detect the malformed-filter refusal, whether raised here or by the server. */
1212
+ declare function isMalformedFilterError(error: unknown): boolean;
1213
+ /**
1214
+ * Serialize a `$orderby` to the server's `sort` shorthand
1215
+ * (`field,-other_field`), for every shape `QueryParams['$orderby']` declares.
1216
+ *
1217
+ * The type declares four — `string`, `string[]`, `SortNode[]`,
1218
+ * `Record<field, direction>` — and the two `find()` routes each open-coded a
1219
+ * fold that handled three of them. The missing one was the bare string, and it
1220
+ * did not degrade quietly: `Object.entries('name asc')` enumerates a string's
1221
+ * character indices, so the request went out as `sort=0,1,2,3,4,5,6,7`. Against
1222
+ * a server that rejects an unreadable sort rather than ignoring it
1223
+ * (objectstack#4226), that is a `400 INVALID_SORT` and an empty list — so a
1224
+ * standalone `ObjectGrid` with a `sort` in its metadata, which is exactly the
1225
+ * shape it builds (`ObjectGrid.tsx`: `` `${field} ${order}` ``), failed to load
1226
+ * at all.
1227
+ *
1228
+ * One serializer for both routes, for the reason the filter path already has
1229
+ * one: two copies of a fold can only agree by inspection, and these two did not.
1230
+ *
1231
+ * Returns `undefined` when nothing is sortable, so callers skip the parameter
1232
+ * entirely rather than sending an empty one.
1233
+ */
1234
+ declare function serializeOrderBy(orderby: QueryParams['$orderby']): string | undefined;
1177
1235
  /**
1178
1236
  * Fetch the server `discovery` document once per (baseUrl) and reuse the
1179
1237
  * resulting Promise. Used by `ObjectStackAdapter.connect()` (and any caller
@@ -1212,6 +1270,73 @@ declare function readTransactionalBatchCapability(discovery: unknown): boolean |
1212
1270
  * tripping on the property-name mismatch.
1213
1271
  */
1214
1272
  declare function is404Error(error: unknown): boolean;
1273
+ /**
1274
+ * Thrown when the deployment has no analytics capability installed
1275
+ * (framework#3891 / #4019).
1276
+ *
1277
+ * The framework retired its degraded in-kernel analytics fallback — it dropped
1278
+ * the caller's RLS/tenant scope and ignored the contract filter, so it answered
1279
+ * 200 with over-broad numbers. `@objectstack/service-analytics` is now the
1280
+ * domain's only implementation, and a deployment without it answers:
1281
+ *
1282
+ * - `POST /api/v1/analytics/query` → **404** (the routes aren't even mounted);
1283
+ * - `POST /api/v1/analytics/dataset/query` → **501 NOT_IMPLEMENTED**.
1284
+ *
1285
+ * Neither is a bug to report as a stack trace: it is a deployment that hasn't
1286
+ * installed the capability. This error carries a message a UI can show as-is.
1287
+ */
1288
+ declare class AnalyticsNotInstalledError extends Error {
1289
+ readonly code = "ANALYTICS_NOT_INSTALLED";
1290
+ /** The surface that was unavailable, for the message a host renders. */
1291
+ readonly surface: string;
1292
+ constructor(surface: string, detail?: string);
1293
+ }
1294
+ /** True when `error` is an {@link AnalyticsNotInstalledError} (or its wire twin). */
1295
+ declare function isAnalyticsNotInstalledError(error: unknown): boolean;
1296
+ /**
1297
+ * Thrown when the server REJECTED the analytics query body (HTTP 400 —
1298
+ * `VALIDATION_FAILED` since framework#4010 validates `/analytics/query` at the
1299
+ * entry against the canonical bare `AnalyticsQuery` shape).
1300
+ *
1301
+ * Distinct from {@link AnalyticsNotInstalledError} on purpose: this one is a
1302
+ * defect in what WE sent, so it must never be answered with the client-side
1303
+ * fallback. Numbers produced by a different code path would look plausible and
1304
+ * bury the contract violation — the misdirection framework#3878 documented.
1305
+ */
1306
+ declare class AnalyticsQueryRejectedError extends Error {
1307
+ readonly code = "ANALYTICS_QUERY_REJECTED";
1308
+ /** The server's own error code (e.g. `VALIDATION_FAILED`), when it sent one. */
1309
+ readonly serverCode?: string;
1310
+ constructor(detail?: string, serverCode?: string);
1311
+ }
1312
+ /**
1313
+ * Classify a FAILED analytics call so the caller knows whether to degrade or
1314
+ * to surface the failure.
1315
+ *
1316
+ * `@objectstack/client`'s fetch wrapper throws on a non-2xx, decorating the
1317
+ * error with the semantic `code` string and the numeric `httpStatus` (the
1318
+ * ADR-0112 / framework#3842 shape this repo already reads elsewhere). Two
1319
+ * outcomes must NOT be conflated:
1320
+ *
1321
+ * - **`not-installed`** — the deployment has no analytics service. Since
1322
+ * framework#3891 retired the degraded in-kernel shim, that is a 404 (the
1323
+ * routes aren't mounted at all, framework#4019) or a 501 `NOT_IMPLEMENTED`
1324
+ * (REST's dataset route with no service behind it). Degrading to a
1325
+ * client-side aggregate over a scoped `find()` is CORRECT here.
1326
+ * - **`rejected`** — the server refused OUR body (400 `VALIDATION_FAILED`;
1327
+ * framework#4010 validates `/analytics/query` at the entry). Degrading
1328
+ * would answer our own contract violation with plausible numbers from a
1329
+ * different code path and bury it — the misdirection framework#3878
1330
+ * documented. It must be surfaced.
1331
+ *
1332
+ * Anything else (5xx, network) is `unknown`: degrade, but silently — it is a
1333
+ * transient failure, not a deployment that is missing a capability.
1334
+ */
1335
+ declare function classifyAnalyticsFailure(error: unknown): {
1336
+ kind: 'not-installed' | 'rejected' | 'unknown';
1337
+ code?: string;
1338
+ message?: string;
1339
+ };
1215
1340
  /**
1216
1341
  * Thrown by `update()` / `delete()` when the server returns
1217
1342
  * `409 CONCURRENT_UPDATE` — i.e. the record was modified by someone else
@@ -1248,10 +1373,15 @@ declare class ConcurrentUpdateError extends Error {
1248
1373
  */
1249
1374
  declare function isConcurrentUpdateError(error: unknown): error is ConcurrentUpdateError;
1250
1375
  /**
1251
- * Convert any error thrown by the upstream client into a typed
1252
- * `ConcurrentUpdateError` when it represents a 409 CONCURRENT_UPDATE.
1253
- * Returns the original error untouched otherwise. Callers can simply
1254
- * `throw normaliseClientError(err)` from their catch blocks.
1376
+ * Convert any error thrown by the upstream client into a typed error when we
1377
+ * recognise its shape. Returns the original error untouched otherwise, so
1378
+ * callers can simply `throw normaliseClientError(err)` from their catch blocks.
1379
+ *
1380
+ * Two shapes are recognised:
1381
+ * - `409` + `CONCURRENT_UPDATE` → {@link ConcurrentUpdateError};
1382
+ * - `400` + `VALIDATION_FAILED` → {@link ValidationError}, carrying the
1383
+ * server's per-field entries so a form can mark the offending inputs
1384
+ * instead of showing one undirected toast.
1255
1385
  */
1256
1386
  declare function normaliseClientError(error: unknown): unknown;
1257
1387
  /**
@@ -1284,6 +1414,33 @@ type ConnectionStateListener = (event: ConnectionStateEvent) => void;
1284
1414
  * Event listener type for batch operation progress
1285
1415
  */
1286
1416
  type BatchProgressListener = (event: BatchProgressEvent) => void;
1417
+ /**
1418
+ * One server-reported write-strip: caller-supplied fields the backend LEGALLY
1419
+ * removed from a write before persisting (a non-system caller cannot seed a
1420
+ * `readonly` field, a `readonlyWhen` predicate locked it, etc.). Mirrors the
1421
+ * framework `DroppedFieldsEvent` (spec `DroppedFieldsEventSchema`) structurally
1422
+ * so we don't pin a client type version — `reason` is kept as a widened string
1423
+ * for forward-compatibility with reasons added server-side.
1424
+ */
1425
+ interface DroppedFieldsEvent {
1426
+ object: string;
1427
+ fields: string[];
1428
+ reason: string;
1429
+ }
1430
+ /**
1431
+ * Emitted after a create/update whose response carried `droppedFields`
1432
+ * (framework #3431/#3455). The write SUCCEEDED — this is a warning that some
1433
+ * supplied fields never landed, so the UI can tell the user rather than let it
1434
+ * pass silently. Subscribe via {@link ObjectStackAdapter.onWriteWarning}.
1435
+ */
1436
+ interface WriteWarningEvent {
1437
+ operation: 'create' | 'update';
1438
+ resource: string;
1439
+ id?: string | number;
1440
+ droppedFields: DroppedFieldsEvent[];
1441
+ }
1442
+ /** Event listener type for write-warning (dropped-fields) events. */
1443
+ type WriteWarningListener = (event: WriteWarningEvent) => void;
1287
1444
 
1288
1445
  /**
1289
1446
  * ObjectStack Data Source Adapter
@@ -1328,12 +1485,15 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
1328
1485
  private reconnectAttempts;
1329
1486
  private baseUrl;
1330
1487
  private token?;
1488
+ /** One "analytics capability is missing" console line per adapter, not per widget. */
1489
+ private analyticsCapabilityWarned;
1331
1490
  private fetchImpl;
1332
1491
  private inflightFinds;
1333
1492
  private missingResources;
1334
1493
  private batchUnsupported;
1335
1494
  private atomicBatchCapability;
1336
1495
  private mutationListeners;
1496
+ private writeWarningListeners;
1337
1497
  constructor(config: {
1338
1498
  baseUrl: string;
1339
1499
  token?: string;
@@ -1384,6 +1544,26 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
1384
1544
  * Converts OData-style params to ObjectStack query options.
1385
1545
  */
1386
1546
  find(resource: string, params?: QueryParams): Promise<QueryResult<T>>;
1547
+ /**
1548
+ * Full-text search across every searchable object in a single round-trip.
1549
+ *
1550
+ * Hits `GET /api/v1/search?q=`, the platform's global search endpoint served
1551
+ * by the registered search service (the pinyin full-text plugin) and backed
1552
+ * by `metadata-protocol`'s `searchAll`. Unlike `find(resource, { $search })`
1553
+ * — a per-object metadata-driven search (ADR-0061) — this consults the search
1554
+ * index and ranks hits across objects, so it surfaces records the per-object
1555
+ * fanout misses. Global affordances (⌘K command palette, search page) prefer
1556
+ * this path (framework #3371).
1557
+ *
1558
+ * Returns `{ query, hits }`. A backend without the search plugin installed
1559
+ * answers `404`; we treat that as "no global search here" and return an empty
1560
+ * hit set so callers can fall back to a per-object fanout rather than surface
1561
+ * an error.
1562
+ */
1563
+ searchAll(query: string, options?: {
1564
+ limit?: number;
1565
+ objects?: string[];
1566
+ }): Promise<GlobalSearchResult>;
1387
1567
  /**
1388
1568
  * Find a single record by ID.
1389
1569
  */
@@ -1402,7 +1582,38 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
1402
1582
  * mutation (e.g. inline-edit "Save All", which writes through `update` and
1403
1583
  * must repaint the list without a manual reload).
1404
1584
  */
1405
- onMutation(callback: (event: MutationEvent<T>) => void): () => void;
1585
+ onMutation(callback: (event: DataSourceMutationEvent<T>) => void): () => void;
1586
+ /**
1587
+ * Notify all write-warning subscribers. Isolated like {@link emitMutation}: a
1588
+ * throwing listener must not break the write or starve the others.
1589
+ */
1590
+ private emitWriteWarning;
1591
+ /**
1592
+ * Read `droppedFields` off a create/update response (framework #3431/#3455)
1593
+ * and, when present, notify write-warning subscribers. Tolerant of a client
1594
+ * whose response type predates `droppedFields`: the field is read structurally
1595
+ * and validated, so an older client (or a backend that never drops) is a no-op.
1596
+ */
1597
+ private notifyDroppedFields;
1598
+ /**
1599
+ * Same, for the cross-object transactional batch (framework #3794). Its
1600
+ * response hangs the events off a top-level `droppedFields` list, each tagged
1601
+ * with the `index` of the operation it came from — `results` entries are bare
1602
+ * record echoes with nowhere to hang a per-row list.
1603
+ *
1604
+ * This is the path that matters most for the warning: `batchTransaction` is
1605
+ * how the console's record form saves a master-detail record, so a
1606
+ * `readonlyWhen`-locked field edited in that form was stripped server-side
1607
+ * while the UI reported a plain success. The operation kind is taken from the
1608
+ * originating op so the toast doesn't call an update a create.
1609
+ */
1610
+ private notifyBatchDroppedFields;
1611
+ /**
1612
+ * Subscribe to write-warning events (a create/update dropped caller-supplied
1613
+ * fields — #3431/#3455). Returns an unsubscribe function. The app shell uses
1614
+ * this to toast the user; the write itself already succeeded.
1615
+ */
1616
+ onWriteWarning(callback: WriteWarningListener): () => void;
1406
1617
  create(resource: string, data: Partial<T>): Promise<T>;
1407
1618
  /**
1408
1619
  * Update an existing record.
@@ -1498,7 +1709,7 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
1498
1709
  /** Mark the endpoint unsupported (warn once) and serve via emulation. */
1499
1710
  private fallbackToEmulation;
1500
1711
  /**
1501
- * Emit one MutationEvent per committed operation so the invalidation bus
1712
+ * Emit one DataSourceMutationEvent per committed operation so the invalidation bus
1502
1713
  * (#2269) sees writes that went through /batch exactly like single
1503
1714
  * create/update/delete calls — master-detail ModalForm saves otherwise leave
1504
1715
  * related lists and count badges stale (#2582). `results` is index-aligned
@@ -1776,6 +1987,23 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
1776
1987
  * is not available.
1777
1988
  */
1778
1989
  aggregate(resource: string, params: any): Promise<any[]>;
1990
+ /**
1991
+ * Client-side aggregation over a server-scoped `find()` — the fallback used
1992
+ * whenever the analytics endpoint cannot answer (capability absent, network
1993
+ * failure, or a result whose measure came back missing).
1994
+ *
1995
+ * Forwarding `params.filter` is load-bearing: without it the fallback
1996
+ * aggregates the whole table while the caller believes it applied a filter,
1997
+ * which is the "KPI silently sums everything" failure this adapter has
1998
+ * guarded against since the widget filter was threaded through.
1999
+ */
2000
+ private aggregateViaFind;
2001
+ /**
2002
+ * Say "the analytics capability isn't installed" ONCE per adapter, not once
2003
+ * per widget: a dashboard fans out one aggregate() per KPI, and N identical
2004
+ * console lines read like N different failures.
2005
+ */
2006
+ private warnAnalyticsCapabilityOnce;
1779
2007
  /**
1780
2008
  * Run a semantic-layer `dataset` (ADR-0021) and return chart-ready rows.
1781
2009
  *
@@ -1836,6 +2064,13 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
1836
2064
  }>;
1837
2065
  }>;
1838
2066
  /** Client-side aggregation fallback */
2067
+ /**
2068
+ * The result column an object-bound `aggregate` projects its value under
2069
+ * (framework#3701, `chartAggregateValueKey` in `@objectstack/spec/ui`): the
2070
+ * raw `field` name — no `sum_`-style decoration, unlike a dataset measure —
2071
+ * or the literal `count` when a count names no field.
2072
+ */
2073
+ private aggregateValueKey;
1839
2074
  private aggregateClientSide;
1840
2075
  /**
1841
2076
  * Get multiple metadata items from ObjectStack.
@@ -1938,4 +2173,4 @@ declare function createObjectStackAdapter<T = unknown>(config: {
1938
2173
  reconnectDelay?: number;
1939
2174
  }): DataSource<T>;
1940
2175
 
1941
- export { type AuditEventType, type AuditLogConfig, type AuditLogEntry, AuthenticationError, type BatchProgressEvent, type BatchProgressListener, BulkOperationError, type CSPConfig, type CacheStats, type CloudDeploymentConfig, type CloudHostingConfig, type CloudMarketplaceEntry, CloudOperations, ConcurrentUpdateError, ConnectionError, type ConnectionState, type ConnectionStateEvent, type ConnectionStateListener, type ContractValidationError, type ContractValidationResult, type DataMaskingConfig, type DataMaskingRule, type EmailIntegrationConfig, type IntegrationConfig, IntegrationManager, type IntegrationProvider, type IntegrationTrigger, type MetadataAuditEntry, type MetadataAuditResponse, MetadataClient, type MetadataClientConfig, type MetadataDeleteOptions, type MetadataDiagnostics, type MetadataDiagnosticsEntry, type MetadataDiagnosticsOptions, type MetadataDiagnosticsSummary, type MetadataDraftHeader, type MetadataError, type MetadataGetOptions, type MetadataHistoryOptions, type MetadataLayered, type MetadataListOptions, MetadataNotFoundError, type MetadataReference, type MetadataSaveOptions, type MetadataValidationIssue, ObjectStackAdapter, ObjectStackError, type ObjectStackUserStateAdapterOptions, type PluginAPIContract, type PluginContract, type PluginExport, SecurityManager, type SecurityPolicy, type SlackIntegrationConfig, type StudioCanvasConfig, type StudioColorPalette, type StudioPropertyEditor, type StudioShadowPreset, type StudioThemeBuilderConfig, type StudioTypographyPreset, type UserDataAdapter, ValidationError, type WebhookIntegrationConfig, calculateAutoLayout, clearSharedDiscoveryCache, createDefaultCanvasConfig, createErrorFromResponse, createObjectStackAdapter, createObjectStackUserStateAdapter, generateContractManifest, getSharedDiscovery, is404Error, isConcurrentUpdateError, isErrorType, isObjectStackError, normaliseClientError, readTransactionalBatchCapability, snapToGrid, validatePluginContract };
2176
+ export { AnalyticsNotInstalledError, AnalyticsQueryRejectedError, type AuditEventType, type AuditLogConfig, type AuditLogEntry, AuthenticationError, type BatchProgressEvent, type BatchProgressListener, BulkOperationError, type CSPConfig, type CacheStats, type CloudDeploymentConfig, type CloudHostingConfig, type CloudMarketplaceEntry, CloudOperations, ConcurrentUpdateError, ConnectionError, type ConnectionState, type ConnectionStateEvent, type ConnectionStateListener, type ContractValidationError, type ContractValidationResult, type DataMaskingConfig, type DataMaskingRule, type DroppedFieldsEvent, type EmailIntegrationConfig, FILTER_OPERATOR_ALIASES, type IntegrationConfig, IntegrationManager, type IntegrationProvider, type IntegrationTrigger, MalformedFilterError, type MetadataAuditEntry, type MetadataAuditResponse, MetadataClient, type MetadataClientConfig, type MetadataDeleteOptions, type MetadataDiagnostics, type MetadataDiagnosticsEntry, type MetadataDiagnosticsOptions, type MetadataDiagnosticsSummary, type MetadataDraftHeader, type MetadataError, type MetadataGetOptions, type MetadataHistoryOptions, type MetadataLayered, type MetadataListOptions, MetadataNotFoundError, type MetadataReference, type MetadataSaveOptions, type MetadataValidationIssue, ObjectStackAdapter, ObjectStackError, type ObjectStackUserStateAdapterOptions, type PluginAPIContract, type PluginContract, type PluginExport, SecurityManager, type SecurityPolicy, type SlackIntegrationConfig, type StudioCanvasConfig, type StudioColorPalette, type StudioPropertyEditor, type StudioShadowPreset, type StudioThemeBuilderConfig, type StudioTypographyPreset, type UserDataAdapter, ValidationError, type WebhookIntegrationConfig, type WriteWarningEvent, type WriteWarningListener, calculateAutoLayout, classifyAnalyticsFailure, clearSharedDiscoveryCache, createDefaultCanvasConfig, createErrorFromResponse, createObjectStackAdapter, createObjectStackUserStateAdapter, generateContractManifest, getSharedDiscovery, is404Error, isAnalyticsNotInstalledError, isConcurrentUpdateError, isErrorType, isMalformedFilterError, isObjectStackError, normaliseClientError, readTransactionalBatchCapability, serializeOrderBy, snapToGrid, validatePluginContract };