@centrali-io/centrali-sdk 5.4.0 → 5.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.js CHANGED
@@ -38,6 +38,9 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  return result;
39
39
  };
40
40
  })();
41
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
42
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
43
+ };
41
44
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
42
45
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
43
46
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -51,7 +54,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
51
54
  return (mod && mod.__esModule) ? mod : { "default": mod };
52
55
  };
53
56
  Object.defineProperty(exports, "__esModule", { value: true });
54
- exports.CentraliSDK = exports.WebhookSubscriptionsManager = exports.FunctionRunsManager = exports.ComputeFunctionsManager = exports.CollectionsManager = exports.StructuresManager = exports.AllowedDomainsManager = exports.ValidationManager = exports.AnomalyInsightsManager = exports.SmartQueriesManager = exports.TriggersManager = exports.OrchestrationsManager = exports.RealtimeManager = exports.RecordEvents = exports.CentraliError = void 0;
57
+ exports.CentraliSDK = exports.WebhookSubscriptionsManager = exports.FunctionRunsManager = exports.ComputeFunctionsManager = exports.CollectionsManager = exports.StructuresManager = exports.AllowedDomainsManager = exports.ValidationManager = exports.AnomalyInsightsManager = exports.SmartQueriesManager = exports.RecordsManager = exports.TriggersManager = exports.OrchestrationsManager = exports.RealtimeManager = exports.RecordEvents = exports.CentraliError = void 0;
55
58
  exports.isCentraliError = isCentraliError;
56
59
  exports.getApiUrl = getApiUrl;
57
60
  exports.getAuthUrl = getAuthUrl;
@@ -199,6 +202,48 @@ function encodeFormData(data) {
199
202
  return new URLSearchParams(data).toString();
200
203
  }
201
204
  // =====================================================
205
+ // Canonical query types (CEN-1194 — Phase 1 query foundation)
206
+ // =====================================================
207
+ // Re-exports the canonical `QueryDefinition` surface from @centrali/query.
208
+ // `query-types.ts` is generated from `services/backend/shared/query/src/types.ts`
209
+ // by `scripts/sync-query-types.mjs` (runs on prebuild). Do not edit it by hand.
210
+ //
211
+ // Use these for new code:
212
+ // - `client.records.query(resource, definition)` — POST /records/query
213
+ // - `client.records.list(resource, urlOpts?)` — GET adapter
214
+ // - `client.records.search(resource, text, opts?)` — sugar over `{ text }`
215
+ // - `client.records.test(resource, definition)` — authoring dry-run
216
+ // - `client.queryRecords<T>(resource, definition)` — top-level convenience
217
+ //
218
+ // Saved queries: `client.savedQueries.*` is the canonical namespace, routing
219
+ // through the `/saved-queries/*` HTTP endpoints (Phase 4 — CEN-1198).
220
+ // `client.smartQueries.*` is preserved as a deprecated alias during the
221
+ // deprecation window.
222
+ //
223
+ // The legacy `FilterOperators` / `QueryRecordOptions` block below is preserved
224
+ // for the GET adapter (`client.records.list()` and the legacy
225
+ // `client.queryRecords(slug, urlOpts)` form). Migration guidance for the
226
+ // canonical operator vocabulary lives in
227
+ // `docs/maintainers/overview/query-foundation.md`.
228
+ __exportStar(require("./query-types"), exports);
229
+ /**
230
+ * Discriminator for the dual-shape `client.queryRecords(...)` overload.
231
+ *
232
+ * Returns `true` if the argument is a canonical `QueryDefinition`. The
233
+ * marker is **the presence of `resource`** — that field is required by the
234
+ * canonical contract (`docs/superpowers/specs/2026-04-25-query-foundation-contract.md` §3)
235
+ * and isn't a legal key in the legacy `QueryRecordOptions` URL-param shape.
236
+ *
237
+ * Callers who want to omit `resource` (relying on the first argument to fill
238
+ * it in) should use `client.records.query(resource, definition)` directly,
239
+ * which has its own backfill behavior.
240
+ */
241
+ function isCanonicalQueryDefinition(arg) {
242
+ return (arg !== null &&
243
+ typeof arg === 'object' &&
244
+ typeof arg.resource === 'string');
245
+ }
246
+ // =====================================================
202
247
  // Webhook Subscription Types
203
248
  // =====================================================
204
249
  /**
@@ -554,29 +599,33 @@ function getEndpointApiPath(workspaceId, path) {
554
599
  return `data/workspace/${workspaceId}/api/v1/endpoints/${path}`;
555
600
  }
556
601
  /**
557
- * Generate Smart Queries base API URL PATH for workspace-level operations.
602
+ * Generate Saved Queries base API URL PATH for workspace-level operations.
603
+ *
604
+ * Phase 4 (CEN-1198) of the query foundation moved the canonical mount from
605
+ * `/smart-queries` to `/saved-queries`. The data service dual-mounts both for
606
+ * the deprecation window; this helper emits the canonical path.
558
607
  */
559
608
  function getSmartQueriesApiPath(workspaceId) {
560
- return `data/workspace/${workspaceId}/api/v1/smart-queries`;
609
+ return `data/workspace/${workspaceId}/api/v1/saved-queries`;
561
610
  }
562
611
  /**
563
- * Generate Smart Queries API URL PATH for structure-level operations.
612
+ * Generate Saved Queries API URL PATH for structure-level operations.
564
613
  */
565
614
  function getSmartQueriesStructureApiPath(workspaceId, structureSlug, queryId) {
566
- const basePath = `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}`;
615
+ const basePath = `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}`;
567
616
  return queryId ? `${basePath}/${queryId}` : basePath;
568
617
  }
569
618
  /**
570
- * Generate Smart Query by name API URL PATH.
619
+ * Generate Saved Query by name API URL PATH.
571
620
  */
572
621
  function getSmartQueryByNameApiPath(workspaceId, structureSlug, name) {
573
- return `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}/name/${encodeURIComponent(name)}`;
622
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}/name/${encodeURIComponent(name)}`;
574
623
  }
575
624
  /**
576
- * Generate Smart Query execute API URL PATH.
625
+ * Generate Saved Query execute API URL PATH.
577
626
  */
578
627
  function getSmartQueryExecuteApiPath(workspaceId, structureSlug, queryId) {
579
- return `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}/execute/${queryId}`;
628
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}/execute/${queryId}`;
580
629
  }
581
630
  /**
582
631
  * Generate Search API URL PATH.
@@ -728,10 +777,10 @@ function getComputeJobStatusApiPath(workspaceId, jobId) {
728
777
  // Smart Query Test API Path Helper (Configuration-as-Code)
729
778
  // =====================================================
730
779
  /**
731
- * Generate Smart Query test execution API URL PATH.
780
+ * Generate Saved Query test execution API URL PATH.
732
781
  */
733
782
  function getSmartQueryTestApiPath(workspaceId, structureSlug) {
734
- return `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}/test`;
783
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}/test`;
735
784
  }
736
785
  // =====================================================
737
786
  // Validation API Path Helpers
@@ -1454,24 +1503,164 @@ class TriggersManager {
1454
1503
  }
1455
1504
  exports.TriggersManager = TriggersManager;
1456
1505
  // =====================================================
1506
+ // Records Manager (canonical query surface — CEN-1194)
1507
+ // =====================================================
1508
+ /**
1509
+ * RecordsManager exposes the canonical query surface for records.
1510
+ *
1511
+ * All three methods compile to a single canonical `QueryDefinition` server-side
1512
+ * and return the canonical `{ data, meta }` envelope (`QueryResult<T>`).
1513
+ *
1514
+ * - {@link RecordsManager.query | query} — full POST `/records/query`. Use for
1515
+ * nested boolean trees, `select`, `text`, `include`.
1516
+ * - {@link RecordsManager.list | list} — GET adapter for simple URL-param
1517
+ * queries. Bookmarkable, cacheable, but cannot express nested `or`/`not`.
1518
+ * - {@link RecordsManager.search | search} — sugar over `query({ text: ... })`.
1519
+ *
1520
+ * Access via `client.records`.
1521
+ *
1522
+ * @example
1523
+ * ```ts
1524
+ * // Canonical query — boolean tree, select, paging
1525
+ * const open = await client.records.query<Order>('orders', {
1526
+ * resource: 'orders',
1527
+ * where: {
1528
+ * and: [
1529
+ * { 'data.status': { eq: 'open' } },
1530
+ * { or: [
1531
+ * { 'data.amount': { gte: 100 } },
1532
+ * { 'data.priority': { eq: 'high' } }
1533
+ * ]}
1534
+ * ]
1535
+ * },
1536
+ * sort: [{ field: 'createdAt', direction: 'desc' }],
1537
+ * page: { limit: 50 },
1538
+ * select: { fields: ['id', 'data.status', 'data.amount'] }
1539
+ * });
1540
+ * console.log(open.data, open.meta.hasMore);
1541
+ *
1542
+ * // GET adapter — simple cases
1543
+ * const recent = await client.records.list<Order>('orders', {
1544
+ * 'data.status': 'paid',
1545
+ * sort: '-createdAt',
1546
+ * pageSize: 25,
1547
+ * });
1548
+ *
1549
+ * // Text search — sugar over { text }
1550
+ * const matches = await client.records.search<Order>('orders', 'urgent shipping');
1551
+ * ```
1552
+ */
1553
+ class RecordsManager {
1554
+ constructor(workspaceId, requestFn) {
1555
+ this.workspaceId = workspaceId;
1556
+ this.requestFn = requestFn;
1557
+ }
1558
+ /**
1559
+ * Run a canonical query against `POST /records/query`.
1560
+ *
1561
+ * The body **is** a `QueryDefinition`. Returns the canonical `{ data, meta }`
1562
+ * envelope. If `definition.resource` is omitted, `resource` is filled in
1563
+ * from the first argument so the wire shape always matches the contract.
1564
+ *
1565
+ * @example
1566
+ * const open = await client.records.query<Order>('orders', {
1567
+ * resource: 'orders',
1568
+ * where: { 'data.status': { eq: 'open' } },
1569
+ * page: { limit: 100 }
1570
+ * });
1571
+ */
1572
+ query(resource, definition) {
1573
+ return __awaiter(this, void 0, void 0, function* () {
1574
+ var _a;
1575
+ const path = `data/workspace/${this.workspaceId}/api/v1/records/query`;
1576
+ const body = Object.assign(Object.assign({}, definition), { resource: (_a = definition.resource) !== null && _a !== void 0 ? _a : resource });
1577
+ const resp = yield this.requestFn('POST', path, body);
1578
+ // POST /records/query returns canonical `{ data, meta }`. The SDK's
1579
+ // `request()` normalizer leaves objects with a `data` key untouched, so
1580
+ // the runtime shape already matches `QueryResult<T>` — we only need to
1581
+ // re-type the response. `meta` is server-guaranteed for this route.
1582
+ return resp;
1583
+ });
1584
+ }
1585
+ /**
1586
+ * Authoring-time dry-run of a canonical query against `POST /records/query/test`.
1587
+ *
1588
+ * Same input shape as {@link RecordsManager.query | query}. Returns
1589
+ * `422 unreadable_field` on fields the caller can't see (instead of the
1590
+ * runtime privacy-preserving empty-result behavior). Use from query
1591
+ * builders to surface precise errors to the author.
1592
+ */
1593
+ test(resource, definition) {
1594
+ return __awaiter(this, void 0, void 0, function* () {
1595
+ var _a;
1596
+ const path = `data/workspace/${this.workspaceId}/api/v1/records/query/test`;
1597
+ const body = Object.assign(Object.assign({}, definition), { resource: (_a = definition.resource) !== null && _a !== void 0 ? _a : resource });
1598
+ const resp = yield this.requestFn('POST', path, body);
1599
+ return resp;
1600
+ });
1601
+ }
1602
+ /**
1603
+ * GET adapter for simple, URL-encodable queries (`?status=paid&sort=-createdAt`).
1604
+ *
1605
+ * Server-side this routes through the same canonical engine as
1606
+ * {@link RecordsManager.query | query} (per CEN-1181 WS3) — the URL
1607
+ * params compile into a `QueryDefinition` before execution. Use this when
1608
+ * a flat AND of field conditions is enough; reach for `query()` when you
1609
+ * need nested booleans, `select`, `text`, or `include`.
1610
+ *
1611
+ * Returns the records-list `ApiResponse<T[]>` envelope unchanged so
1612
+ * callers that read `meta.page` / `meta.pageSize` keep working during
1613
+ * the deprecation window.
1614
+ */
1615
+ list(resource, urlOpts) {
1616
+ const path = getRecordApiPath(this.workspaceId, resource);
1617
+ return this.requestFn('GET', path, null, urlOpts);
1618
+ }
1619
+ /**
1620
+ * Full-text search sugar — equivalent to `query(resource, { text: ... })`.
1621
+ *
1622
+ * Routes through `RecordsSearchExecutor` (Meilisearch) when only `text` is
1623
+ * provided, or through the hybrid Meili-first path when `where` is also
1624
+ * set. Result envelope is identical across pure-filter, pure-text, and
1625
+ * hybrid (`{ data, meta }`).
1626
+ */
1627
+ search(resource, text, opts) {
1628
+ return __awaiter(this, void 0, void 0, function* () {
1629
+ return this.query(resource, {
1630
+ resource,
1631
+ text: { query: text, fields: opts === null || opts === void 0 ? void 0 : opts.fields, typoTolerance: opts === null || opts === void 0 ? void 0 : opts.typoTolerance },
1632
+ where: opts === null || opts === void 0 ? void 0 : opts.where,
1633
+ sort: opts === null || opts === void 0 ? void 0 : opts.sort,
1634
+ page: opts === null || opts === void 0 ? void 0 : opts.page,
1635
+ select: opts === null || opts === void 0 ? void 0 : opts.select,
1636
+ });
1637
+ });
1638
+ }
1639
+ }
1640
+ exports.RecordsManager = RecordsManager;
1641
+ // =====================================================
1457
1642
  // Smart Queries Manager
1458
1643
  // =====================================================
1459
1644
  /**
1460
- * SmartQueriesManager provides methods for listing and executing smart queries.
1461
- * Smart queries are reusable, parameterized queries defined in the console
1462
- * that can be executed programmatically via the SDK.
1463
- * Access via `client.smartQueries`.
1645
+ * SmartQueriesManager reusable, parameterized saved queries. Access via
1646
+ * `client.savedQueries` (canonical) or `client.smartQueries` (deprecated alias).
1647
+ *
1648
+ * Phase 4 of the query foundation (CEN-1198) shipped the canonical
1649
+ * `/saved-queries/*` HTTP routes; this manager now routes through them. The
1650
+ * data service dual-mounts the deprecated `/smart-queries/*` alias for the
1651
+ * deprecation window. New code that doesn't need saved queries should still
1652
+ * prefer `client.records.query()` for ad-hoc queries.
1464
1653
  *
1465
1654
  * Usage:
1466
1655
  * ```ts
1467
- * // List smart queries for a structure
1468
- * const queries = await client.smartQueries.list('employee');
1656
+ * // List saved queries for a structure
1657
+ * const queries = await client.savedQueries.list('employee');
1469
1658
  *
1470
- * // Execute a smart query by ID
1471
- * const results = await client.smartQueries.execute('employee', 'query-uuid');
1659
+ * // Execute a saved query by ID
1660
+ * const results = await client.savedQueries.execute('employee', 'query-uuid');
1472
1661
  *
1473
- * // Get a smart query by name
1474
- * const query = await client.smartQueries.getByName('employee', 'Active Employees');
1662
+ * // Get a saved query by name
1663
+ * const query = await client.savedQueries.getByName('employee', 'Active Employees');
1475
1664
  * ```
1476
1665
  */
1477
1666
  class SmartQueriesManager {
@@ -2921,7 +3110,9 @@ class CentraliSDK {
2921
3110
  this.token = null;
2922
3111
  this._realtime = null;
2923
3112
  this._triggers = null;
3113
+ this._records = null;
2924
3114
  this._smartQueries = null;
3115
+ this._queryRecordsLegacyWarned = false;
2925
3116
  this._anomalyInsights = null;
2926
3117
  this._validation = null;
2927
3118
  this._orchestrations = null;
@@ -3099,30 +3290,86 @@ class CentraliSDK {
3099
3290
  return this._triggers;
3100
3291
  }
3101
3292
  /**
3102
- * Smart Queries namespace for listing and executing smart queries.
3293
+ * Records namespace canonical query surface for records (CEN-1194).
3294
+ *
3295
+ * Three methods, one engine, one envelope:
3296
+ * - `records.query(resource, definition)` — POST `/records/query` with a
3297
+ * full `QueryDefinition` (boolean trees, `select`, `text`, `include`).
3298
+ * - `records.list(resource, urlOpts?)` — GET adapter for simple URL-param
3299
+ * queries. Bookmarkable; cannot express nested booleans.
3300
+ * - `records.search(resource, text, opts?)` — sugar over `query({ text })`.
3301
+ *
3302
+ * @example
3303
+ * ```ts
3304
+ * // Boolean tree with sort + projection
3305
+ * const orders = await client.records.query<Order>('orders', {
3306
+ * resource: 'orders',
3307
+ * where: {
3308
+ * and: [
3309
+ * { 'data.status': { eq: 'paid' } },
3310
+ * { 'data.amount': { gte: 100 } }
3311
+ * ]
3312
+ * },
3313
+ * sort: [{ field: 'createdAt', direction: 'desc' }],
3314
+ * page: { limit: 50 },
3315
+ * select: { fields: ['id', 'data.amount', 'data.customer'] }
3316
+ * });
3317
+ *
3318
+ * // Simple GET
3319
+ * const recent = await client.records.list<Order>('orders', {
3320
+ * 'data.status': 'paid',
3321
+ * sort: '-createdAt',
3322
+ * });
3323
+ *
3324
+ * // Text search
3325
+ * const matches = await client.records.search<Order>('orders', 'urgent');
3326
+ * ```
3327
+ */
3328
+ get records() {
3329
+ if (!this._records) {
3330
+ this._records = new RecordsManager(this.options.workspaceId, this.request.bind(this));
3331
+ }
3332
+ return this._records;
3333
+ }
3334
+ /**
3335
+ * Saved Queries namespace — list, execute, create, update, and delete
3336
+ * saved (formerly "smart") queries. Routes through the canonical
3337
+ * `/saved-queries/*` endpoints (Phase 4 of the query foundation,
3338
+ * CEN-1198). The data service dual-mounts the deprecated `/smart-queries`
3339
+ * alias for the deprecation window.
3103
3340
  *
3104
3341
  * Usage:
3105
3342
  * ```ts
3106
- * // List all smart queries in workspace
3107
- * const allQueries = await client.smartQueries.listAll();
3343
+ * // List all saved queries in workspace
3344
+ * const allQueries = await client.savedQueries.listAll();
3108
3345
  *
3109
- * // List smart queries for a structure
3110
- * const queries = await client.smartQueries.list('employee');
3346
+ * // List saved queries for a structure
3347
+ * const queries = await client.savedQueries.list('employee');
3111
3348
  *
3112
- * // Get a smart query by name
3113
- * const query = await client.smartQueries.getByName('employee', 'Active Employees');
3349
+ * // Get a saved query by name
3350
+ * const query = await client.savedQueries.getByName('employee', 'Active Employees');
3114
3351
  *
3115
- * // Execute a smart query
3116
- * const results = await client.smartQueries.execute('employee', query.data.id);
3352
+ * // Execute a saved query
3353
+ * const results = await client.savedQueries.execute('employee', query.data.id);
3117
3354
  * console.log('Found:', results.data.length, 'records');
3118
3355
  * ```
3119
3356
  */
3120
- get smartQueries() {
3357
+ get savedQueries() {
3121
3358
  if (!this._smartQueries) {
3122
3359
  this._smartQueries = new SmartQueriesManager(this.options.workspaceId, this.request.bind(this));
3123
3360
  }
3124
3361
  return this._smartQueries;
3125
3362
  }
3363
+ /**
3364
+ * @deprecated Use `client.savedQueries` instead. The "smart queries"
3365
+ * surface was renamed to "saved queries" in Phase 4 of the query
3366
+ * foundation (CEN-1198). This getter is a deprecated alias and will be
3367
+ * removed in a future major SDK release.
3368
+ */
3369
+ get smartQueries() {
3370
+ emitDeprecationWarning('client.smartQueries is deprecated. Use client.savedQueries instead.');
3371
+ return this.savedQueries;
3372
+ }
3126
3373
  /**
3127
3374
  * Anomaly Insights namespace for querying and managing AI-generated insights.
3128
3375
  *
@@ -3459,55 +3706,17 @@ class CentraliSDK {
3459
3706
  const path = getRecordApiPath(this.options.workspaceId, recordSlug, id);
3460
3707
  return this.request('GET', path, null, options);
3461
3708
  }
3462
- /**
3463
- * Query records with filters, pagination, sorting, and reference expansion.
3464
- *
3465
- * IMPORTANT: Filters are passed at the TOP LEVEL, not nested under 'filter'.
3466
- * Use 'data.' prefix for custom fields and bracket notation for operators.
3467
- *
3468
- * @param recordSlug - The structure's record slug
3469
- * @param queryParams - Query parameters (filters at top level, plus sort, pagination, expand)
3470
- *
3471
- * @example
3472
- * // Simple equality filter
3473
- * const activeProducts = await centrali.queryRecords('Product', {
3474
- * 'data.status': 'active',
3475
- * sort: '-createdAt',
3476
- * page: 1,
3477
- * pageSize: 10
3478
- * });
3479
- *
3480
- * // Filter with operators (bracket notation)
3481
- * const products = await centrali.queryRecords('Product', {
3482
- * 'data.inStock': true,
3483
- * 'data.price[lte]': 100,
3484
- * sort: '-createdAt',
3485
- * pageSize: 10
3486
- * });
3487
- *
3488
- * // Multiple values with 'in' operator (comma-separated string)
3489
- * const orders = await centrali.queryRecords('Order', {
3490
- * 'data.status[in]': 'pending,processing',
3491
- * expand: 'customer,items'
3492
- * });
3493
- * // Access expanded data: orders.data[0].data._expanded.customer
3494
- *
3495
- * // Range filters
3496
- * const customers = await centrali.queryRecords('Customer', {
3497
- * 'data.age[gte]': 18,
3498
- * 'data.age[lte]': 65,
3499
- * 'data.verified': true
3500
- * });
3501
- *
3502
- * // Filter with 'ne' (not equal)
3503
- * const availableItems = await centrali.queryRecords('Product', {
3504
- * 'data.status[ne]': 'discontinued',
3505
- * pageSize: 100
3506
- * });
3507
- */
3508
- queryRecords(recordSlug, queryParams) {
3509
- const path = getRecordApiPath(this.options.workspaceId, recordSlug);
3510
- return this.request('GET', path, null, queryParams);
3709
+ queryRecords(resourceOrSlug, arg2) {
3710
+ if (isCanonicalQueryDefinition(arg2)) {
3711
+ return this.records.query(resourceOrSlug, arg2);
3712
+ }
3713
+ if (!this._queryRecordsLegacyWarned) {
3714
+ this._queryRecordsLegacyWarned = true;
3715
+ // eslint-disable-next-line no-console
3716
+ console.warn('[centrali-sdk] `client.queryRecords(slug, urlOpts)` (legacy URL-param form) is deprecated pass a canonical `QueryDefinition` (`{ resource, where, sort, page, … }`) for `POST /records/query`, or use `client.records.list(resource, urlOpts)` for the GET adapter explicitly.');
3717
+ }
3718
+ const path = getRecordApiPath(this.options.workspaceId, resourceOrSlug);
3719
+ return this.request('GET', path, null, arg2);
3511
3720
  }
3512
3721
  /** Get records by Ids. */
3513
3722
  getRecordsByIds(recordSlug, ids) {
@@ -0,0 +1,187 @@
1
+ export type QueryDefinition = {
2
+ /**
3
+ * Logical resource being queried.
4
+ *
5
+ * - For records: the collection (a.k.a. structure) slug — e.g. `"orders"`,
6
+ * `"customers"`. The records executor treats this as the collection slug.
7
+ * - For other queryable resources: the resource type identifier — e.g.
8
+ * `"audit-log"`, `"function-runs"`, `"files"`, `"webhook-deliveries"`.
9
+ *
10
+ * Named `resource` (not `collection`) because "collection" is overloaded in
11
+ * Centrali — it's the renamed records-bucket entity, so `collection: "audit-log"`
12
+ * would read as a category error. `resource` is REST-canonical and reads
13
+ * truthfully across every executor.
14
+ */
15
+ resource: string;
16
+ where?: WhereExpression;
17
+ text?: TextSearchClause;
18
+ sort?: SortClause[];
19
+ page?: PageClause;
20
+ select?: SelectClause;
21
+ include?: IncludeClause[];
22
+ };
23
+ export type SavedQueryDefinition = {
24
+ name: string;
25
+ description?: string;
26
+ query: QueryDefinition;
27
+ variables?: Record<string, QueryVariableDefinition>;
28
+ };
29
+ export type WhereExpression = FieldConditionMap | {
30
+ and: WhereExpression[];
31
+ } | {
32
+ or: WhereExpression[];
33
+ } | {
34
+ not: WhereExpression;
35
+ };
36
+ export type FieldConditionMap = {
37
+ [field: string]: FieldCondition;
38
+ };
39
+ /**
40
+ * Exactly-one helper: for a record of operator → value-type, produces a union
41
+ * where each variant has exactly one of the keys present and all the others
42
+ * forbidden via `?: never`. This enforces the contract's "exactly one operator
43
+ * per FieldCondition" rule at the type level — `{ eq: 1, ne: 2 }` fails to
44
+ * type-check.
45
+ */
46
+ type ExactlyOneOperator<T> = {
47
+ [K in keyof T]: {
48
+ [P in K]: T[P];
49
+ } & {
50
+ [P in Exclude<keyof T, K>]?: never;
51
+ };
52
+ }[keyof T];
53
+ type FieldOperatorMap = {
54
+ eq: ScalarValue;
55
+ ne: ScalarValue;
56
+ gt: number | string;
57
+ gte: number | string;
58
+ lt: number | string;
59
+ lte: number | string;
60
+ in: ScalarValue[];
61
+ nin: ScalarValue[];
62
+ contains: string;
63
+ startsWith: string;
64
+ endsWith: string;
65
+ hasAny: ScalarValue[];
66
+ hasAll: ScalarValue[];
67
+ exists: boolean;
68
+ };
69
+ export type FieldCondition = ExactlyOneOperator<FieldOperatorMap>;
70
+ export type ScalarValue = string | number | boolean | null;
71
+ export type CanonicalOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'startsWith' | 'endsWith' | 'hasAny' | 'hasAll' | 'exists';
72
+ export declare const CANONICAL_OPERATORS: readonly CanonicalOperator[];
73
+ /** Argument shape an operator expects in a `FieldCondition`. */
74
+ export type OperatorValueShape =
75
+ /** Single scalar input (string/number/boolean/null). */
76
+ 'scalar'
77
+ /** Array of scalars — UI typically renders a chip / comma input. */
78
+ | 'array'
79
+ /** Boolean toggle — `exists`. */
80
+ | 'boolean';
81
+ /**
82
+ * Logical type families an operator applies to. The visual builder narrows
83
+ * the dropdown to the operators valid for the selected field's type.
84
+ *
85
+ * `any` means the operator is type-agnostic (`eq`, `ne`, `exists`).
86
+ */
87
+ export type OperatorTypeApplicability = 'string' | 'number' | 'boolean' | 'datetime' | 'array' | 'any';
88
+ export type OperatorMeta = {
89
+ /** Canonical bare operator name. */
90
+ operator: CanonicalOperator;
91
+ /** Human-readable label for dropdowns. */
92
+ label: string;
93
+ /** Short, symbol-style label suitable for compact number/datetime UIs. */
94
+ shortLabel?: string;
95
+ /** Argument shape — drives the value input affordance. */
96
+ valueShape: OperatorValueShape;
97
+ /** Field types this operator applies to. */
98
+ applicableTypes: OperatorTypeApplicability[];
99
+ };
100
+ export declare const OPERATOR_METADATA: Readonly<Record<CanonicalOperator, OperatorMeta>>;
101
+ /**
102
+ * Operators applicable to a given field-type. Used by builder UIs to narrow
103
+ * the dropdown when the user selects a field; falls back to `any`-applicable
104
+ * operators when the type is unknown.
105
+ */
106
+ export declare function operatorsForFieldType(type: OperatorTypeApplicability | string): readonly OperatorMeta[];
107
+ export type TextSearchClause = {
108
+ query: string;
109
+ fields?: string[];
110
+ typoTolerance?: boolean;
111
+ };
112
+ export type SortClause = {
113
+ field: string;
114
+ direction: 'asc' | 'desc';
115
+ };
116
+ /**
117
+ * Two pagination modes. Per contract §6 they are mutually exclusive — `cursor`
118
+ * is forbidden in offset mode and `offset` is forbidden in cursor mode so a
119
+ * caller cannot construct an ambiguous request.
120
+ */
121
+ export type PageClause = {
122
+ limit: number;
123
+ offset?: number;
124
+ cursor?: never;
125
+ } | {
126
+ limit: number;
127
+ cursor?: string;
128
+ offset?: never;
129
+ };
130
+ /**
131
+ * Records Phase 1 page defaults (contract §6).
132
+ * Same default and cap for GET adapter and POST query body.
133
+ */
134
+ export declare const RECORDS_PAGE_DEFAULT_LIMIT = 50;
135
+ export declare const RECORDS_PAGE_MAX_LIMIT = 500;
136
+ export type SelectClause = {
137
+ fields: string[];
138
+ };
139
+ /**
140
+ * Phase 1 reserves the shape but rejects execution with `unsupported_clause`.
141
+ */
142
+ export type IncludeClause = {
143
+ relation: string;
144
+ };
145
+ export type VariableType = 'string' | 'number' | 'boolean' | 'datetime' | 'id' | {
146
+ array: VariableType;
147
+ } | {
148
+ reference: string;
149
+ };
150
+ export type QueryVariableDefinition = {
151
+ type: VariableType;
152
+ required?: boolean;
153
+ default?: ScalarValue;
154
+ description?: string;
155
+ };
156
+ export type QueryExecutionMode = 'filter' | 'search' | 'hybrid';
157
+ export type QueryResultMeta = {
158
+ total?: number;
159
+ limit: number;
160
+ offset?: number;
161
+ cursor?: string;
162
+ nextCursor?: string;
163
+ hasMore?: boolean;
164
+ processingTimeMs?: number;
165
+ mode?: QueryExecutionMode;
166
+ };
167
+ export type QueryResult<T> = {
168
+ data: T[];
169
+ meta: QueryResultMeta;
170
+ };
171
+ export type QueryErrorCode = 'unsupported_clause' | 'unsupported_operator' | 'unsupported_legacy_operator' | 'unreadable_field' | 'invalid_query' | 'legacy_write_unsupported';
172
+ export type QueryError = {
173
+ code: QueryErrorCode;
174
+ message: string;
175
+ /** Dotted path inside the QueryDefinition where the error was detected, e.g. "where.data.status.eq". */
176
+ path?: string;
177
+ /** For `unsupported_clause` / `unsupported_operator`, the offending name. */
178
+ clause?: string;
179
+ };
180
+ export type ValidationResult<T> = {
181
+ ok: true;
182
+ value: T;
183
+ } | {
184
+ ok: false;
185
+ errors: QueryError[];
186
+ };
187
+ export {};