@centrali-io/centrali-sdk 5.3.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/README.md +89 -14
- package/dist/index.d.ts +234 -28
- package/dist/index.js +289 -80
- package/dist/query-types.d.ts +187 -0
- package/dist/query-types.js +137 -0
- package/dist/scripts/smoke-types.d.ts +12 -0
- package/dist/scripts/smoke-types.js +102 -0
- package/index.ts +387 -37
- package/package.json +3 -1
- package/query-types.ts +394 -0
- package/scripts/smoke-types.ts +128 -0
- package/scripts/sync-query-types.mjs +40 -0
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
|
|
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/
|
|
609
|
+
return `data/workspace/${workspaceId}/api/v1/saved-queries`;
|
|
561
610
|
}
|
|
562
611
|
/**
|
|
563
|
-
* Generate
|
|
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/
|
|
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
|
|
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/
|
|
622
|
+
return `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}/name/${encodeURIComponent(name)}`;
|
|
574
623
|
}
|
|
575
624
|
/**
|
|
576
|
-
* Generate
|
|
625
|
+
* Generate Saved Query execute API URL PATH.
|
|
577
626
|
*/
|
|
578
627
|
function getSmartQueryExecuteApiPath(workspaceId, structureSlug, queryId) {
|
|
579
|
-
return `data/workspace/${workspaceId}/api/v1/
|
|
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
|
|
780
|
+
* Generate Saved Query test execution API URL PATH.
|
|
732
781
|
*/
|
|
733
782
|
function getSmartQueryTestApiPath(workspaceId, structureSlug) {
|
|
734
|
-
return `data/workspace/${workspaceId}/api/v1/
|
|
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
|
|
1461
|
-
*
|
|
1462
|
-
*
|
|
1463
|
-
*
|
|
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
|
|
1468
|
-
* const queries = await client.
|
|
1656
|
+
* // List saved queries for a structure
|
|
1657
|
+
* const queries = await client.savedQueries.list('employee');
|
|
1469
1658
|
*
|
|
1470
|
-
* // Execute a
|
|
1471
|
-
* const results = await client.
|
|
1659
|
+
* // Execute a saved query by ID
|
|
1660
|
+
* const results = await client.savedQueries.execute('employee', 'query-uuid');
|
|
1472
1661
|
*
|
|
1473
|
-
* // Get a
|
|
1474
|
-
* const query = await client.
|
|
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
|
-
*
|
|
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
|
|
3107
|
-
* const allQueries = await client.
|
|
3343
|
+
* // List all saved queries in workspace
|
|
3344
|
+
* const allQueries = await client.savedQueries.listAll();
|
|
3108
3345
|
*
|
|
3109
|
-
* // List
|
|
3110
|
-
* const queries = await client.
|
|
3346
|
+
* // List saved queries for a structure
|
|
3347
|
+
* const queries = await client.savedQueries.list('employee');
|
|
3111
3348
|
*
|
|
3112
|
-
* // Get a
|
|
3113
|
-
* const query = await client.
|
|
3349
|
+
* // Get a saved query by name
|
|
3350
|
+
* const query = await client.savedQueries.getByName('employee', 'Active Employees');
|
|
3114
3351
|
*
|
|
3115
|
-
* // Execute a
|
|
3116
|
-
* const results = await client.
|
|
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
|
|
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
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
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 {};
|