@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/index.ts CHANGED
@@ -797,10 +797,74 @@ export interface ExpandOptions {
797
797
  */
798
798
  export interface GetRecordOptions extends ExpandOptions {}
799
799
 
800
+ // =====================================================
801
+ // Canonical query types (CEN-1194 — Phase 1 query foundation)
802
+ // =====================================================
803
+ // Re-exports the canonical `QueryDefinition` surface from @centrali/query.
804
+ // `query-types.ts` is generated from `services/backend/shared/query/src/types.ts`
805
+ // by `scripts/sync-query-types.mjs` (runs on prebuild). Do not edit it by hand.
806
+ //
807
+ // Use these for new code:
808
+ // - `client.records.query(resource, definition)` — POST /records/query
809
+ // - `client.records.list(resource, urlOpts?)` — GET adapter
810
+ // - `client.records.search(resource, text, opts?)` — sugar over `{ text }`
811
+ // - `client.records.test(resource, definition)` — authoring dry-run
812
+ // - `client.queryRecords<T>(resource, definition)` — top-level convenience
813
+ //
814
+ // Saved queries: `client.savedQueries.*` is the canonical namespace, routing
815
+ // through the `/saved-queries/*` HTTP endpoints (Phase 4 — CEN-1198).
816
+ // `client.smartQueries.*` is preserved as a deprecated alias during the
817
+ // deprecation window.
818
+ //
819
+ // The legacy `FilterOperators` / `QueryRecordOptions` block below is preserved
820
+ // for the GET adapter (`client.records.list()` and the legacy
821
+ // `client.queryRecords(slug, urlOpts)` form). Migration guidance for the
822
+ // canonical operator vocabulary lives in
823
+ // `docs/maintainers/overview/query-foundation.md`.
824
+ export * from './query-types';
825
+ import type {
826
+ QueryDefinition,
827
+ QueryResult,
828
+ WhereExpression,
829
+ SortClause,
830
+ PageClause,
831
+ SelectClause,
832
+ QueryVariableDefinition,
833
+ ScalarValue,
834
+ FieldConditionMap,
835
+ FieldCondition,
836
+ } from './query-types';
837
+
838
+ /**
839
+ * Discriminator for the dual-shape `client.queryRecords(...)` overload.
840
+ *
841
+ * Returns `true` if the argument is a canonical `QueryDefinition`. The
842
+ * marker is **the presence of `resource`** — that field is required by the
843
+ * canonical contract (`docs/superpowers/specs/2026-04-25-query-foundation-contract.md` §3)
844
+ * and isn't a legal key in the legacy `QueryRecordOptions` URL-param shape.
845
+ *
846
+ * Callers who want to omit `resource` (relying on the first argument to fill
847
+ * it in) should use `client.records.query(resource, definition)` directly,
848
+ * which has its own backfill behavior.
849
+ */
850
+ function isCanonicalQueryDefinition(arg: any): arg is QueryDefinition {
851
+ return (
852
+ arg !== null &&
853
+ typeof arg === 'object' &&
854
+ typeof arg.resource === 'string'
855
+ );
856
+ }
857
+
800
858
  /**
801
- * Filter operators for querying records.
859
+ * Filter operators for querying records via the legacy GET adapter.
860
+ *
802
861
  * Pass filters at the TOP LEVEL of query params (not nested under 'filter').
803
- * Use bracket notation for operators: 'data.field[operator]': value
862
+ * Use bracket notation for operators: `'data.field[operator]': value`
863
+ *
864
+ * @deprecated Since 5.5.0. Prefer canonical `FieldCondition` from
865
+ * `@centrali-io/centrali-sdk` (re-exported above) and use `client.records.query()`
866
+ * with a `QueryDefinition` body. This shape stays for back-compat with
867
+ * `client.queryRecords(slug, options)` / `client.records.list()`.
804
868
  *
805
869
  * @example
806
870
  * // Simple equality - just use the field name
@@ -865,7 +929,7 @@ export interface DateWindowOption {
865
929
  }
866
930
 
867
931
  /**
868
- * Options for querying records.
932
+ * Options for querying records via the GET adapter.
869
933
  *
870
934
  * Supports two filter styles:
871
935
  *
@@ -884,6 +948,14 @@ export interface DateWindowOption {
884
948
  * Both styles are supported and can be mixed. Use `data.` prefix for custom fields.
885
949
  * System fields (`id`, `createdAt`, `updatedAt`, `status`) don't need the prefix.
886
950
  *
951
+ * @deprecated Since 5.5.0. This is the URL-param shape consumed by
952
+ * `client.records.list()` and the legacy `client.queryRecords(slug, opts)`
953
+ * form. New code should use a canonical `QueryDefinition` (`POST /records/query`)
954
+ * via `client.records.query()` or the canonical `client.queryRecords(resource, definition)`
955
+ * overload — same engine, but boolean trees, `select`, `text`, and `include`
956
+ * become first-class. The GET adapter and this type stay supported during
957
+ * the deprecation window per `docs/maintainers/overview/query-foundation.md`.
958
+ *
887
959
  * @example
888
960
  * // Top-level filters with bracket notation
889
961
  * { 'data.status': 'active', 'data.age[gte]': 18, sort: '-createdAt' }
@@ -1430,8 +1502,14 @@ export interface PaginatedResponse<T> {
1430
1502
  }
1431
1503
 
1432
1504
  // =====================================================
1433
- // Smart Query Types
1505
+ // Smart Query Types (saved-query surface for `client.savedQueries`)
1434
1506
  // =====================================================
1507
+ //
1508
+ // These types describe the **stored** saved-query shape and the operator
1509
+ // vocabulary the `/saved-queries/*` routes accept. They back both
1510
+ // `client.savedQueries.*` (canonical) and the deprecated `client.smartQueries`
1511
+ // alias. The SDK still exports the type names with `SmartQuery` prefixes for
1512
+ // back-compat; canonical `SavedQuery*` aliases will be added in a follow-up.
1435
1513
 
1436
1514
  /**
1437
1515
  * Smart query definition stored in the database.
@@ -2882,32 +2960,36 @@ export function getEndpointApiPath(workspaceId: string, path: string): string {
2882
2960
  }
2883
2961
 
2884
2962
  /**
2885
- * Generate Smart Queries base API URL PATH for workspace-level operations.
2963
+ * Generate Saved Queries base API URL PATH for workspace-level operations.
2964
+ *
2965
+ * Phase 4 (CEN-1198) of the query foundation moved the canonical mount from
2966
+ * `/smart-queries` to `/saved-queries`. The data service dual-mounts both for
2967
+ * the deprecation window; this helper emits the canonical path.
2886
2968
  */
2887
2969
  export function getSmartQueriesApiPath(workspaceId: string): string {
2888
- return `data/workspace/${workspaceId}/api/v1/smart-queries`;
2970
+ return `data/workspace/${workspaceId}/api/v1/saved-queries`;
2889
2971
  }
2890
2972
 
2891
2973
  /**
2892
- * Generate Smart Queries API URL PATH for structure-level operations.
2974
+ * Generate Saved Queries API URL PATH for structure-level operations.
2893
2975
  */
2894
2976
  export function getSmartQueriesStructureApiPath(workspaceId: string, structureSlug: string, queryId?: string): string {
2895
- const basePath = `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}`;
2977
+ const basePath = `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}`;
2896
2978
  return queryId ? `${basePath}/${queryId}` : basePath;
2897
2979
  }
2898
2980
 
2899
2981
  /**
2900
- * Generate Smart Query by name API URL PATH.
2982
+ * Generate Saved Query by name API URL PATH.
2901
2983
  */
2902
2984
  export function getSmartQueryByNameApiPath(workspaceId: string, structureSlug: string, name: string): string {
2903
- return `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}/name/${encodeURIComponent(name)}`;
2985
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}/name/${encodeURIComponent(name)}`;
2904
2986
  }
2905
2987
 
2906
2988
  /**
2907
- * Generate Smart Query execute API URL PATH.
2989
+ * Generate Saved Query execute API URL PATH.
2908
2990
  */
2909
2991
  export function getSmartQueryExecuteApiPath(workspaceId: string, structureSlug: string, queryId: string): string {
2910
- return `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}/execute/${queryId}`;
2992
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}/execute/${queryId}`;
2911
2993
  }
2912
2994
 
2913
2995
  /**
@@ -3087,10 +3169,10 @@ export function getComputeJobStatusApiPath(workspaceId: string, jobId: string):
3087
3169
  // =====================================================
3088
3170
 
3089
3171
  /**
3090
- * Generate Smart Query test execution API URL PATH.
3172
+ * Generate Saved Query test execution API URL PATH.
3091
3173
  */
3092
3174
  export function getSmartQueryTestApiPath(workspaceId: string, structureSlug: string): string {
3093
- return `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}/test`;
3175
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}/test`;
3094
3176
  }
3095
3177
 
3096
3178
  // =====================================================
@@ -3877,26 +3959,189 @@ export class TriggersManager {
3877
3959
  }
3878
3960
  }
3879
3961
 
3962
+ // =====================================================
3963
+ // Records Manager (canonical query surface — CEN-1194)
3964
+ // =====================================================
3965
+
3966
+ /**
3967
+ * RecordsManager exposes the canonical query surface for records.
3968
+ *
3969
+ * All three methods compile to a single canonical `QueryDefinition` server-side
3970
+ * and return the canonical `{ data, meta }` envelope (`QueryResult<T>`).
3971
+ *
3972
+ * - {@link RecordsManager.query | query} — full POST `/records/query`. Use for
3973
+ * nested boolean trees, `select`, `text`, `include`.
3974
+ * - {@link RecordsManager.list | list} — GET adapter for simple URL-param
3975
+ * queries. Bookmarkable, cacheable, but cannot express nested `or`/`not`.
3976
+ * - {@link RecordsManager.search | search} — sugar over `query({ text: ... })`.
3977
+ *
3978
+ * Access via `client.records`.
3979
+ *
3980
+ * @example
3981
+ * ```ts
3982
+ * // Canonical query — boolean tree, select, paging
3983
+ * const open = await client.records.query<Order>('orders', {
3984
+ * resource: 'orders',
3985
+ * where: {
3986
+ * and: [
3987
+ * { 'data.status': { eq: 'open' } },
3988
+ * { or: [
3989
+ * { 'data.amount': { gte: 100 } },
3990
+ * { 'data.priority': { eq: 'high' } }
3991
+ * ]}
3992
+ * ]
3993
+ * },
3994
+ * sort: [{ field: 'createdAt', direction: 'desc' }],
3995
+ * page: { limit: 50 },
3996
+ * select: { fields: ['id', 'data.status', 'data.amount'] }
3997
+ * });
3998
+ * console.log(open.data, open.meta.hasMore);
3999
+ *
4000
+ * // GET adapter — simple cases
4001
+ * const recent = await client.records.list<Order>('orders', {
4002
+ * 'data.status': 'paid',
4003
+ * sort: '-createdAt',
4004
+ * pageSize: 25,
4005
+ * });
4006
+ *
4007
+ * // Text search — sugar over { text }
4008
+ * const matches = await client.records.search<Order>('orders', 'urgent shipping');
4009
+ * ```
4010
+ */
4011
+ export class RecordsManager {
4012
+ private requestFn: <T>(method: Method, path: string, data?: any, queryParams?: Record<string, any>) => Promise<ApiResponse<T>>;
4013
+ private workspaceId: string;
4014
+
4015
+ constructor(
4016
+ workspaceId: string,
4017
+ requestFn: <T>(method: Method, path: string, data?: any, queryParams?: Record<string, any>) => Promise<ApiResponse<T>>
4018
+ ) {
4019
+ this.workspaceId = workspaceId;
4020
+ this.requestFn = requestFn;
4021
+ }
4022
+
4023
+ /**
4024
+ * Run a canonical query against `POST /records/query`.
4025
+ *
4026
+ * The body **is** a `QueryDefinition`. Returns the canonical `{ data, meta }`
4027
+ * envelope. If `definition.resource` is omitted, `resource` is filled in
4028
+ * from the first argument so the wire shape always matches the contract.
4029
+ *
4030
+ * @example
4031
+ * const open = await client.records.query<Order>('orders', {
4032
+ * resource: 'orders',
4033
+ * where: { 'data.status': { eq: 'open' } },
4034
+ * page: { limit: 100 }
4035
+ * });
4036
+ */
4037
+ public async query<T = any>(
4038
+ resource: string,
4039
+ definition: Omit<QueryDefinition, 'resource'> & { resource?: string }
4040
+ ): Promise<QueryResult<T>> {
4041
+ const path = `data/workspace/${this.workspaceId}/api/v1/records/query`;
4042
+ const body: QueryDefinition = { ...definition, resource: definition.resource ?? resource };
4043
+ const resp = await this.requestFn<T[]>('POST', path, body);
4044
+ // POST /records/query returns canonical `{ data, meta }`. The SDK's
4045
+ // `request()` normalizer leaves objects with a `data` key untouched, so
4046
+ // the runtime shape already matches `QueryResult<T>` — we only need to
4047
+ // re-type the response. `meta` is server-guaranteed for this route.
4048
+ return resp as unknown as QueryResult<T>;
4049
+ }
4050
+
4051
+ /**
4052
+ * Authoring-time dry-run of a canonical query against `POST /records/query/test`.
4053
+ *
4054
+ * Same input shape as {@link RecordsManager.query | query}. Returns
4055
+ * `422 unreadable_field` on fields the caller can't see (instead of the
4056
+ * runtime privacy-preserving empty-result behavior). Use from query
4057
+ * builders to surface precise errors to the author.
4058
+ */
4059
+ public async test<T = any>(
4060
+ resource: string,
4061
+ definition: Omit<QueryDefinition, 'resource'> & { resource?: string }
4062
+ ): Promise<QueryResult<T>> {
4063
+ const path = `data/workspace/${this.workspaceId}/api/v1/records/query/test`;
4064
+ const body: QueryDefinition = { ...definition, resource: definition.resource ?? resource };
4065
+ const resp = await this.requestFn<T[]>('POST', path, body);
4066
+ return resp as unknown as QueryResult<T>;
4067
+ }
4068
+
4069
+ /**
4070
+ * GET adapter for simple, URL-encodable queries (`?status=paid&sort=-createdAt`).
4071
+ *
4072
+ * Server-side this routes through the same canonical engine as
4073
+ * {@link RecordsManager.query | query} (per CEN-1181 WS3) — the URL
4074
+ * params compile into a `QueryDefinition` before execution. Use this when
4075
+ * a flat AND of field conditions is enough; reach for `query()` when you
4076
+ * need nested booleans, `select`, `text`, or `include`.
4077
+ *
4078
+ * Returns the records-list `ApiResponse<T[]>` envelope unchanged so
4079
+ * callers that read `meta.page` / `meta.pageSize` keep working during
4080
+ * the deprecation window.
4081
+ */
4082
+ public list<T = any>(
4083
+ resource: string,
4084
+ urlOpts?: QueryRecordOptions
4085
+ ): Promise<ApiResponse<T[]>> {
4086
+ const path = getRecordApiPath(this.workspaceId, resource);
4087
+ return this.requestFn<T[]>('GET', path, null, urlOpts);
4088
+ }
4089
+
4090
+ /**
4091
+ * Full-text search sugar — equivalent to `query(resource, { text: ... })`.
4092
+ *
4093
+ * Routes through `RecordsSearchExecutor` (Meilisearch) when only `text` is
4094
+ * provided, or through the hybrid Meili-first path when `where` is also
4095
+ * set. Result envelope is identical across pure-filter, pure-text, and
4096
+ * hybrid (`{ data, meta }`).
4097
+ */
4098
+ public async search<T = any>(
4099
+ resource: string,
4100
+ text: string,
4101
+ opts?: {
4102
+ fields?: string[];
4103
+ typoTolerance?: boolean;
4104
+ where?: WhereExpression;
4105
+ sort?: SortClause[];
4106
+ page?: PageClause;
4107
+ select?: SelectClause;
4108
+ }
4109
+ ): Promise<QueryResult<T>> {
4110
+ return this.query<T>(resource, {
4111
+ resource,
4112
+ text: { query: text, fields: opts?.fields, typoTolerance: opts?.typoTolerance },
4113
+ where: opts?.where,
4114
+ sort: opts?.sort,
4115
+ page: opts?.page,
4116
+ select: opts?.select,
4117
+ });
4118
+ }
4119
+ }
4120
+
3880
4121
  // =====================================================
3881
4122
  // Smart Queries Manager
3882
4123
  // =====================================================
3883
4124
 
3884
4125
  /**
3885
- * SmartQueriesManager provides methods for listing and executing smart queries.
3886
- * Smart queries are reusable, parameterized queries defined in the console
3887
- * that can be executed programmatically via the SDK.
3888
- * Access via `client.smartQueries`.
4126
+ * SmartQueriesManager reusable, parameterized saved queries. Access via
4127
+ * `client.savedQueries` (canonical) or `client.smartQueries` (deprecated alias).
4128
+ *
4129
+ * Phase 4 of the query foundation (CEN-1198) shipped the canonical
4130
+ * `/saved-queries/*` HTTP routes; this manager now routes through them. The
4131
+ * data service dual-mounts the deprecated `/smart-queries/*` alias for the
4132
+ * deprecation window. New code that doesn't need saved queries should still
4133
+ * prefer `client.records.query()` for ad-hoc queries.
3889
4134
  *
3890
4135
  * Usage:
3891
4136
  * ```ts
3892
- * // List smart queries for a structure
3893
- * const queries = await client.smartQueries.list('employee');
4137
+ * // List saved queries for a structure
4138
+ * const queries = await client.savedQueries.list('employee');
3894
4139
  *
3895
- * // Execute a smart query by ID
3896
- * const results = await client.smartQueries.execute('employee', 'query-uuid');
4140
+ * // Execute a saved query by ID
4141
+ * const results = await client.savedQueries.execute('employee', 'query-uuid');
3897
4142
  *
3898
- * // Get a smart query by name
3899
- * const query = await client.smartQueries.getByName('employee', 'Active Employees');
4143
+ * // Get a saved query by name
4144
+ * const query = await client.savedQueries.getByName('employee', 'Active Employees');
3900
4145
  * ```
3901
4146
  */
3902
4147
  export class SmartQueriesManager {
@@ -5513,7 +5758,9 @@ export class CentraliSDK {
5513
5758
  private options: CentraliSDKOptions;
5514
5759
  private _realtime: RealtimeManager | null = null;
5515
5760
  private _triggers: TriggersManager | null = null;
5761
+ private _records: RecordsManager | null = null;
5516
5762
  private _smartQueries: SmartQueriesManager | null = null;
5763
+ private _queryRecordsLegacyWarned: boolean = false;
5517
5764
  private _anomalyInsights: AnomalyInsightsManager | null = null;
5518
5765
  private _validation: ValidationManager | null = null;
5519
5766
  private _orchestrations: OrchestrationsManager | null = null;
@@ -5737,25 +5984,75 @@ export class CentraliSDK {
5737
5984
  }
5738
5985
 
5739
5986
  /**
5740
- * Smart Queries namespace for listing and executing smart queries.
5987
+ * Records namespace canonical query surface for records (CEN-1194).
5988
+ *
5989
+ * Three methods, one engine, one envelope:
5990
+ * - `records.query(resource, definition)` — POST `/records/query` with a
5991
+ * full `QueryDefinition` (boolean trees, `select`, `text`, `include`).
5992
+ * - `records.list(resource, urlOpts?)` — GET adapter for simple URL-param
5993
+ * queries. Bookmarkable; cannot express nested booleans.
5994
+ * - `records.search(resource, text, opts?)` — sugar over `query({ text })`.
5995
+ *
5996
+ * @example
5997
+ * ```ts
5998
+ * // Boolean tree with sort + projection
5999
+ * const orders = await client.records.query<Order>('orders', {
6000
+ * resource: 'orders',
6001
+ * where: {
6002
+ * and: [
6003
+ * { 'data.status': { eq: 'paid' } },
6004
+ * { 'data.amount': { gte: 100 } }
6005
+ * ]
6006
+ * },
6007
+ * sort: [{ field: 'createdAt', direction: 'desc' }],
6008
+ * page: { limit: 50 },
6009
+ * select: { fields: ['id', 'data.amount', 'data.customer'] }
6010
+ * });
6011
+ *
6012
+ * // Simple GET
6013
+ * const recent = await client.records.list<Order>('orders', {
6014
+ * 'data.status': 'paid',
6015
+ * sort: '-createdAt',
6016
+ * });
6017
+ *
6018
+ * // Text search
6019
+ * const matches = await client.records.search<Order>('orders', 'urgent');
6020
+ * ```
6021
+ */
6022
+ public get records(): RecordsManager {
6023
+ if (!this._records) {
6024
+ this._records = new RecordsManager(
6025
+ this.options.workspaceId,
6026
+ this.request.bind(this)
6027
+ );
6028
+ }
6029
+ return this._records;
6030
+ }
6031
+
6032
+ /**
6033
+ * Saved Queries namespace — list, execute, create, update, and delete
6034
+ * saved (formerly "smart") queries. Routes through the canonical
6035
+ * `/saved-queries/*` endpoints (Phase 4 of the query foundation,
6036
+ * CEN-1198). The data service dual-mounts the deprecated `/smart-queries`
6037
+ * alias for the deprecation window.
5741
6038
  *
5742
6039
  * Usage:
5743
6040
  * ```ts
5744
- * // List all smart queries in workspace
5745
- * const allQueries = await client.smartQueries.listAll();
6041
+ * // List all saved queries in workspace
6042
+ * const allQueries = await client.savedQueries.listAll();
5746
6043
  *
5747
- * // List smart queries for a structure
5748
- * const queries = await client.smartQueries.list('employee');
6044
+ * // List saved queries for a structure
6045
+ * const queries = await client.savedQueries.list('employee');
5749
6046
  *
5750
- * // Get a smart query by name
5751
- * const query = await client.smartQueries.getByName('employee', 'Active Employees');
6047
+ * // Get a saved query by name
6048
+ * const query = await client.savedQueries.getByName('employee', 'Active Employees');
5752
6049
  *
5753
- * // Execute a smart query
5754
- * const results = await client.smartQueries.execute('employee', query.data.id);
6050
+ * // Execute a saved query
6051
+ * const results = await client.savedQueries.execute('employee', query.data.id);
5755
6052
  * console.log('Found:', results.data.length, 'records');
5756
6053
  * ```
5757
6054
  */
5758
- public get smartQueries(): SmartQueriesManager {
6055
+ public get savedQueries(): SmartQueriesManager {
5759
6056
  if (!this._smartQueries) {
5760
6057
  this._smartQueries = new SmartQueriesManager(
5761
6058
  this.options.workspaceId,
@@ -5765,6 +6062,17 @@ export class CentraliSDK {
5765
6062
  return this._smartQueries;
5766
6063
  }
5767
6064
 
6065
+ /**
6066
+ * @deprecated Use `client.savedQueries` instead. The "smart queries"
6067
+ * surface was renamed to "saved queries" in Phase 4 of the query
6068
+ * foundation (CEN-1198). This getter is a deprecated alias and will be
6069
+ * removed in a future major SDK release.
6070
+ */
6071
+ public get smartQueries(): SmartQueriesManager {
6072
+ emitDeprecationWarning('client.smartQueries is deprecated. Use client.savedQueries instead.');
6073
+ return this.savedQueries;
6074
+ }
6075
+
5768
6076
  /**
5769
6077
  * Anomaly Insights namespace for querying and managing AI-generated insights.
5770
6078
  *
@@ -6210,12 +6518,54 @@ export class CentraliSDK {
6210
6518
  * pageSize: 100
6211
6519
  * });
6212
6520
  */
6521
+ /**
6522
+ * Canonical query (CEN-1194). When called with a `QueryDefinition` body,
6523
+ * routes to `POST /records/query` and returns canonical `QueryResult<T>`.
6524
+ *
6525
+ * @example
6526
+ * const result = await centrali.queryRecords<Order>('orders', {
6527
+ * resource: 'orders',
6528
+ * where: { 'data.status': { eq: 'paid' } },
6529
+ * page: { limit: 50 }
6530
+ * });
6531
+ * console.log(result.data, result.meta.hasMore);
6532
+ */
6533
+ public queryRecords<T = any>(
6534
+ resource: string,
6535
+ definition: QueryDefinition
6536
+ ): Promise<QueryResult<T>>;
6537
+ /**
6538
+ * Legacy GET-adapter form. Pass `QueryRecordOptions` (URL-param style with
6539
+ * `data.field[op]` keys, `sort: '-createdAt'`, `pageSize`, etc.) and the
6540
+ * call routes to `GET /records/slug/:rs`.
6541
+ *
6542
+ * @deprecated Since 5.5.0. Prefer the canonical overload above (pass a
6543
+ * `QueryDefinition`) or {@link CentraliSDK.records | client.records.list()}
6544
+ * for the GET adapter explicitly. The legacy form keeps working — server-side
6545
+ * it already routes through the canonical engine (CEN-1181 WS3) — but the
6546
+ * client-side type story diverges from the canonical surface. Emits a
6547
+ * one-shot `console.warn` per client.
6548
+ */
6213
6549
  public queryRecords<T = any>(
6214
6550
  recordSlug: string,
6215
6551
  queryParams?: QueryRecordOptions
6216
- ): Promise<ApiResponse<T>> {
6217
- const path = getRecordApiPath(this.options.workspaceId, recordSlug);
6218
- return this.request('GET', path, null, queryParams);
6552
+ ): Promise<ApiResponse<T>>;
6553
+ public queryRecords<T = any>(
6554
+ resourceOrSlug: string,
6555
+ arg2?: QueryDefinition | QueryRecordOptions
6556
+ ): Promise<QueryResult<T>> | Promise<ApiResponse<T>> {
6557
+ if (isCanonicalQueryDefinition(arg2)) {
6558
+ return this.records.query<T>(resourceOrSlug, arg2);
6559
+ }
6560
+ if (!this._queryRecordsLegacyWarned) {
6561
+ this._queryRecordsLegacyWarned = true;
6562
+ // eslint-disable-next-line no-console
6563
+ console.warn(
6564
+ '[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.'
6565
+ );
6566
+ }
6567
+ const path = getRecordApiPath(this.options.workspaceId, resourceOrSlug);
6568
+ return this.request<T>('GET', path, null, arg2);
6219
6569
  }
6220
6570
 
6221
6571
  /** Get records by Ids. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centrali-io/centrali-sdk",
3
- "version": "5.4.0",
3
+ "version": "5.5.0",
4
4
  "description": "Centrali Node SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,6 +10,8 @@
10
10
  "url": "git+https://github.com/blueinit/centrali-sdk.git"
11
11
  },
12
12
  "scripts": {
13
+ "sync:query-types": "node scripts/sync-query-types.mjs",
14
+ "prebuild": "npm run sync:query-types",
13
15
  "build": "tsc --project tsconfig.json",
14
16
  "prepublishOnly": "npm run build"
15
17
  },