@centrali-io/centrali-sdk 2.2.1 → 2.2.3

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.
Files changed (4) hide show
  1. package/README.md +76 -12
  2. package/dist/index.js +333 -4
  3. package/index.ts +552 -3
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -31,13 +31,9 @@ const product = await centrali.createRecord('Product', {
31
31
 
32
32
  console.log('Created product:', product.data);
33
33
 
34
- // Invoke a compute function
35
- const result = await centrali.invokeFunction('calculate-discount', {
36
- productId: product.id,
37
- couponCode: 'SAVE20'
38
- });
39
-
40
- console.log('Discount result:', result.data);
34
+ // Search for records
35
+ const results = await centrali.search('Awesome');
36
+ console.log('Found:', results.data.totalHits, 'results');
41
37
  ```
42
38
 
43
39
  ## Authentication
@@ -73,6 +69,8 @@ const centrali = new CentraliSDK({
73
69
  - ✅ **Automatic authentication** and token management
74
70
  - ✅ **Records management** - Create, read, update, delete records
75
71
  - ✅ **Query operations** - Powerful data querying with filters and sorting
72
+ - ✅ **Smart Queries** - Execute reusable, predefined queries
73
+ - ✅ **Full-text search** - Search records across structures with Meilisearch
76
74
  - ✅ **Realtime events** - Subscribe to record changes via SSE
77
75
  - ✅ **Compute functions** - Execute serverless functions
78
76
  - ✅ **File uploads** - Upload files to Centrali storage
@@ -97,9 +95,20 @@ await centrali.updateRecord('StructureName', 'record-id', {
97
95
  field1: 'new value'
98
96
  });
99
97
 
100
- // Delete a record
98
+ // Delete a record (soft delete - can be restored)
101
99
  await centrali.deleteRecord('StructureName', 'record-id');
102
100
 
101
+ // Hard delete a record (permanent - cannot be restored)
102
+ await centrali.deleteRecord('StructureName', 'record-id', { hard: true });
103
+
104
+ // Restore a soft-deleted record
105
+ await centrali.restoreRecord('StructureName', 'record-id');
106
+
107
+ // Query archived (soft-deleted) records
108
+ const archived = await centrali.queryRecords('StructureName', {
109
+ includeArchived: true
110
+ });
111
+
103
112
  // Query records
104
113
  const products = await centrali.queryRecords('Product', {
105
114
  filter: 'inStock = true AND price < 100',
@@ -108,6 +117,59 @@ const products = await centrali.queryRecords('Product', {
108
117
  });
109
118
  ```
110
119
 
120
+ ### Smart Queries
121
+
122
+ Smart queries are reusable, predefined queries that are created in the Centrali console and can be executed programmatically via the SDK. Pagination (limit/skip) is defined in the query definition itself.
123
+
124
+ ```typescript
125
+ // List all smart queries in the workspace
126
+ const allQueries = await centrali.smartQueries.listAll();
127
+
128
+ // List smart queries for a specific structure
129
+ const employeeQueries = await centrali.smartQueries.list('employee');
130
+
131
+ // Get a smart query by name
132
+ const query = await centrali.smartQueries.getByName('employee', 'Active Employees');
133
+ console.log('Query ID:', query.data.id);
134
+
135
+ // Execute a smart query
136
+ const results = await centrali.smartQueries.execute('employee', query.data.id);
137
+ console.log('Found:', results.data.length, 'employees');
138
+ ```
139
+
140
+ ### Search
141
+
142
+ Perform full-text search across workspace records using Meilisearch:
143
+
144
+ ```typescript
145
+ // Basic search
146
+ const results = await centrali.search('customer email');
147
+ console.log('Found:', results.data.totalHits, 'results');
148
+ results.data.hits.forEach(hit => {
149
+ console.log(hit.id, hit.structureSlug);
150
+ });
151
+
152
+ // Search with structure filter
153
+ const userResults = await centrali.search('john', {
154
+ structures: 'users'
155
+ });
156
+
157
+ // Search multiple structures with limit
158
+ const multiResults = await centrali.search('active', {
159
+ structures: ['users', 'orders'],
160
+ limit: 50
161
+ });
162
+ ```
163
+
164
+ #### Search Response
165
+
166
+ | Field | Type | Description |
167
+ |-------|------|-------------|
168
+ | `hits` | `SearchHit[]` | Array of matching records |
169
+ | `totalHits` | `number` | Estimated total matches |
170
+ | `processingTimeMs` | `number` | Search time in milliseconds |
171
+ | `query` | `string` | The original search query |
172
+
111
173
  ### Realtime Events
112
174
 
113
175
  Subscribe to record changes in real-time using Server-Sent Events (SSE):
@@ -206,13 +268,15 @@ const realtime = new RealtimeManager(
206
268
  );
207
269
  ```
208
270
 
209
- ### Compute Functions
271
+ ### Compute Functions (via Triggers)
210
272
 
211
273
  ```typescript
212
- // Execute a function
213
- const result = await centrali.invokeFunction('myFunction', {
214
- data: 'your-input-data'
274
+ // Invoke an on-demand trigger to execute a compute function
275
+ const job = await centrali.triggers.invoke('trigger-id', {
276
+ payload: { data: 'your-input-data' }
215
277
  });
278
+
279
+ console.log('Job queued:', job.data);
216
280
  ```
217
281
 
218
282
  ### File Uploads
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
18
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.CentraliSDK = exports.TriggersManager = exports.RealtimeManager = void 0;
21
+ exports.CentraliSDK = exports.SmartQueriesManager = exports.TriggersManager = exports.RealtimeManager = void 0;
22
22
  exports.getApiUrl = getApiUrl;
23
23
  exports.getAuthUrl = getAuthUrl;
24
24
  exports.getRealtimeUrl = getRealtimeUrl;
@@ -27,6 +27,13 @@ exports.getRecordApiPath = getRecordApiPath;
27
27
  exports.getFileUploadApiPath = getFileUploadApiPath;
28
28
  exports.getFunctionTriggersApiPath = getFunctionTriggersApiPath;
29
29
  exports.getFunctionTriggerExecuteApiPath = getFunctionTriggerExecuteApiPath;
30
+ exports.getFunctionTriggerPauseApiPath = getFunctionTriggerPauseApiPath;
31
+ exports.getFunctionTriggerResumeApiPath = getFunctionTriggerResumeApiPath;
32
+ exports.getSmartQueriesApiPath = getSmartQueriesApiPath;
33
+ exports.getSmartQueriesStructureApiPath = getSmartQueriesStructureApiPath;
34
+ exports.getSmartQueryByNameApiPath = getSmartQueryByNameApiPath;
35
+ exports.getSmartQueryExecuteApiPath = getSmartQueryExecuteApiPath;
36
+ exports.getSearchApiPath = getSearchApiPath;
30
37
  const axios_1 = __importDefault(require("axios"));
31
38
  const qs_1 = __importDefault(require("qs"));
32
39
  const eventsource_1 = require("eventsource");
@@ -332,6 +339,49 @@ function getFunctionTriggersApiPath(workspaceId, triggerId) {
332
339
  function getFunctionTriggerExecuteApiPath(workspaceId, triggerId) {
333
340
  return `data/workspace/${workspaceId}/api/v1/function-triggers/${triggerId}/execute`;
334
341
  }
342
+ /**
343
+ * Generate Function Trigger pause API URL PATH.
344
+ */
345
+ function getFunctionTriggerPauseApiPath(workspaceId, triggerId) {
346
+ return `data/workspace/${workspaceId}/api/v1/function-triggers/${triggerId}/pause`;
347
+ }
348
+ /**
349
+ * Generate Function Trigger resume API URL PATH.
350
+ */
351
+ function getFunctionTriggerResumeApiPath(workspaceId, triggerId) {
352
+ return `data/workspace/${workspaceId}/api/v1/function-triggers/${triggerId}/resume`;
353
+ }
354
+ /**
355
+ * Generate Smart Queries base API URL PATH for workspace-level operations.
356
+ */
357
+ function getSmartQueriesApiPath(workspaceId) {
358
+ return `data/workspace/${workspaceId}/api/v1/smart-queries`;
359
+ }
360
+ /**
361
+ * Generate Smart Queries API URL PATH for structure-level operations.
362
+ */
363
+ function getSmartQueriesStructureApiPath(workspaceId, structureSlug, queryId) {
364
+ const basePath = `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}`;
365
+ return queryId ? `${basePath}/${queryId}` : basePath;
366
+ }
367
+ /**
368
+ * Generate Smart Query by name API URL PATH.
369
+ */
370
+ function getSmartQueryByNameApiPath(workspaceId, structureSlug, name) {
371
+ return `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}/name/${encodeURIComponent(name)}`;
372
+ }
373
+ /**
374
+ * Generate Smart Query execute API URL PATH.
375
+ */
376
+ function getSmartQueryExecuteApiPath(workspaceId, structureSlug, queryId) {
377
+ return `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}/execute/${queryId}`;
378
+ }
379
+ /**
380
+ * Generate Search API URL PATH.
381
+ */
382
+ function getSearchApiPath(workspaceId) {
383
+ return `search/workspace/${workspaceId}/api/v1/records/search`;
384
+ }
335
385
  // =====================================================
336
386
  // Triggers Manager
337
387
  // =====================================================
@@ -442,8 +492,177 @@ class TriggersManager {
442
492
  const params = Object.assign(Object.assign({}, queryParams), { executionType: 'on-demand' });
443
493
  return this.requestFn('GET', path, null, params);
444
494
  }
495
+ /**
496
+ * Pause a function trigger.
497
+ *
498
+ * Pauses an event-driven or scheduled trigger to temporarily disable it from firing.
499
+ * When paused, the trigger will not execute until resumed.
500
+ *
501
+ * For scheduled triggers, this also pauses the underlying scheduler job.
502
+ *
503
+ * @param triggerId - The ID of the trigger to pause
504
+ * @returns The updated trigger with enabled = false
505
+ *
506
+ * @example
507
+ * ```ts
508
+ * // Pause a trigger
509
+ * const result = await client.triggers.pauseTrigger('trigger-id');
510
+ * console.log('Trigger paused:', result.data.enabled === false);
511
+ * ```
512
+ */
513
+ pauseTrigger(triggerId) {
514
+ const path = getFunctionTriggerPauseApiPath(this.workspaceId, triggerId);
515
+ return this.requestFn('PATCH', path, {});
516
+ }
517
+ /**
518
+ * Resume a paused function trigger.
519
+ *
520
+ * Resumes a paused event-driven or scheduled trigger to re-enable it.
521
+ * Once resumed, the trigger will start firing again on matching events or schedules.
522
+ *
523
+ * For scheduled triggers, this also resumes the underlying scheduler job.
524
+ *
525
+ * @param triggerId - The ID of the trigger to resume
526
+ * @returns The updated trigger with enabled = true
527
+ *
528
+ * @example
529
+ * ```ts
530
+ * // Resume a paused trigger
531
+ * const result = await client.triggers.resumeTrigger('trigger-id');
532
+ * console.log('Trigger resumed:', result.data.enabled === true);
533
+ * ```
534
+ */
535
+ resumeTrigger(triggerId) {
536
+ const path = getFunctionTriggerResumeApiPath(this.workspaceId, triggerId);
537
+ return this.requestFn('PATCH', path, {});
538
+ }
445
539
  }
446
540
  exports.TriggersManager = TriggersManager;
541
+ // =====================================================
542
+ // Smart Queries Manager
543
+ // =====================================================
544
+ /**
545
+ * SmartQueriesManager provides methods for listing and executing smart queries.
546
+ * Smart queries are reusable, parameterized queries defined in the console
547
+ * that can be executed programmatically via the SDK.
548
+ * Access via `client.smartQueries`.
549
+ *
550
+ * Usage:
551
+ * ```ts
552
+ * // List smart queries for a structure
553
+ * const queries = await client.smartQueries.list('employee');
554
+ *
555
+ * // Execute a smart query by ID
556
+ * const results = await client.smartQueries.execute('employee', 'query-uuid');
557
+ *
558
+ * // Get a smart query by name
559
+ * const query = await client.smartQueries.getByName('employee', 'Active Employees');
560
+ * ```
561
+ */
562
+ class SmartQueriesManager {
563
+ constructor(workspaceId, requestFn) {
564
+ this.workspaceId = workspaceId;
565
+ this.requestFn = requestFn;
566
+ }
567
+ /**
568
+ * List all smart queries in the workspace.
569
+ *
570
+ * @param options - Optional list parameters (pagination, search, etc.)
571
+ * @returns List of smart queries with pagination metadata
572
+ *
573
+ * @example
574
+ * ```ts
575
+ * // List all smart queries
576
+ * const queries = await client.smartQueries.listAll();
577
+ *
578
+ * // With pagination
579
+ * const queries = await client.smartQueries.listAll({ page: 1, limit: 20 });
580
+ * ```
581
+ */
582
+ listAll(options) {
583
+ const path = getSmartQueriesApiPath(this.workspaceId);
584
+ return this.requestFn('GET', path, null, options);
585
+ }
586
+ /**
587
+ * List smart queries for a specific structure.
588
+ *
589
+ * @param structureSlug - The structure's record slug (e.g., "employee")
590
+ * @param options - Optional list parameters (pagination, search, etc.)
591
+ * @returns List of smart queries for the structure
592
+ *
593
+ * @example
594
+ * ```ts
595
+ * // List queries for employee structure
596
+ * const queries = await client.smartQueries.list('employee');
597
+ *
598
+ * // With pagination and search
599
+ * const queries = await client.smartQueries.list('employee', {
600
+ * page: 1,
601
+ * limit: 10,
602
+ * search: 'active'
603
+ * });
604
+ * ```
605
+ */
606
+ list(structureSlug, options) {
607
+ const path = getSmartQueriesStructureApiPath(this.workspaceId, structureSlug);
608
+ return this.requestFn('GET', path, null, options);
609
+ }
610
+ /**
611
+ * Get a smart query by ID.
612
+ *
613
+ * @param structureSlug - The structure's record slug
614
+ * @param queryId - The smart query UUID
615
+ * @returns The smart query details
616
+ *
617
+ * @example
618
+ * ```ts
619
+ * const query = await client.smartQueries.get('employee', 'query-uuid');
620
+ * console.log('Query name:', query.data.name);
621
+ * ```
622
+ */
623
+ get(structureSlug, queryId) {
624
+ const path = getSmartQueriesStructureApiPath(this.workspaceId, structureSlug, queryId);
625
+ return this.requestFn('GET', path);
626
+ }
627
+ /**
628
+ * Get a smart query by name.
629
+ *
630
+ * @param structureSlug - The structure's record slug
631
+ * @param name - The smart query name
632
+ * @returns The smart query details
633
+ *
634
+ * @example
635
+ * ```ts
636
+ * const query = await client.smartQueries.getByName('employee', 'Active Employees');
637
+ * console.log('Query ID:', query.data.id);
638
+ * ```
639
+ */
640
+ getByName(structureSlug, name) {
641
+ const path = getSmartQueryByNameApiPath(this.workspaceId, structureSlug, name);
642
+ return this.requestFn('GET', path);
643
+ }
644
+ /**
645
+ * Execute a smart query and return results.
646
+ *
647
+ * Note: Pagination (limit/skip) is defined in the query definition itself,
648
+ * not at execution time.
649
+ *
650
+ * @param structureSlug - The structure's record slug
651
+ * @param queryId - The smart query UUID
652
+ * @returns Query results
653
+ *
654
+ * @example
655
+ * ```ts
656
+ * const results = await client.smartQueries.execute('employee', 'query-uuid');
657
+ * console.log('Found:', results.data.length, 'records');
658
+ * ```
659
+ */
660
+ execute(structureSlug, queryId) {
661
+ const path = getSmartQueryExecuteApiPath(this.workspaceId, structureSlug, queryId);
662
+ return this.requestFn('GET', path);
663
+ }
664
+ }
665
+ exports.SmartQueriesManager = SmartQueriesManager;
447
666
  /**
448
667
  * Main Centrali SDK client.
449
668
  */
@@ -452,6 +671,7 @@ class CentraliSDK {
452
671
  this.token = null;
453
672
  this._realtime = null;
454
673
  this._triggers = null;
674
+ this._smartQueries = null;
455
675
  this.options = options;
456
676
  this.token = options.token || null;
457
677
  const apiUrl = getApiUrl(options.baseUrl);
@@ -537,6 +757,31 @@ class CentraliSDK {
537
757
  }
538
758
  return this._triggers;
539
759
  }
760
+ /**
761
+ * Smart Queries namespace for listing and executing smart queries.
762
+ *
763
+ * Usage:
764
+ * ```ts
765
+ * // List all smart queries in workspace
766
+ * const allQueries = await client.smartQueries.listAll();
767
+ *
768
+ * // List smart queries for a structure
769
+ * const queries = await client.smartQueries.list('employee');
770
+ *
771
+ * // Get a smart query by name
772
+ * const query = await client.smartQueries.getByName('employee', 'Active Employees');
773
+ *
774
+ * // Execute a smart query
775
+ * const results = await client.smartQueries.execute('employee', query.data.id);
776
+ * console.log('Found:', results.data.length, 'records');
777
+ * ```
778
+ */
779
+ get smartQueries() {
780
+ if (!this._smartQueries) {
781
+ this._smartQueries = new SmartQueriesManager(this.options.workspaceId, this.request.bind(this));
782
+ }
783
+ return this._smartQueries;
784
+ }
540
785
  /**
541
786
  * Manually set or update the bearer token for subsequent requests.
542
787
  */
@@ -570,6 +815,10 @@ class CentraliSDK {
570
815
  if (Array.isArray(resp.data)) {
571
816
  return { data: resp.data };
572
817
  }
818
+ // Handle { result } responses (smart queries and some other endpoints)
819
+ if ('result' in resp.data && !('data' in resp.data)) {
820
+ return { data: resp.data.result };
821
+ }
573
822
  // Handle objects that don't have a data property (legacy endpoints)
574
823
  if (!('data' in resp.data)) {
575
824
  return { data: resp.data };
@@ -607,10 +856,16 @@ class CentraliSDK {
607
856
  const path = getRecordApiPath(this.options.workspaceId, recordSlug, id);
608
857
  return this.request('PUT', path, Object.assign({}, updates));
609
858
  }
610
- /** Delete a record by ID. */
611
- deleteRecord(recordSlug, id) {
859
+ /** Delete a record by ID (soft delete by default, can be restored). */
860
+ deleteRecord(recordSlug, id, options) {
612
861
  const path = getRecordApiPath(this.options.workspaceId, recordSlug, id);
613
- return this.request('DELETE', path);
862
+ const queryParams = (options === null || options === void 0 ? void 0 : options.hard) ? { hard: 'true' } : undefined;
863
+ return this.request('DELETE', path, null, queryParams);
864
+ }
865
+ /** Restore a soft-deleted record by ID. */
866
+ restoreRecord(recordSlug, id) {
867
+ const path = getRecordApiPath(this.options.workspaceId, recordSlug, id) + '/restore';
868
+ return this.request('POST', path);
614
869
  }
615
870
  // ------------------ Storage API Methods ------------------
616
871
  /** Upload a file to the storage service. */
@@ -630,6 +885,47 @@ class CentraliSDK {
630
885
  });
631
886
  });
632
887
  }
888
+ // ------------------ Search API Methods ------------------
889
+ /**
890
+ * Search records across the workspace using full-text search.
891
+ *
892
+ * @param query - The search query string
893
+ * @param options - Optional search parameters
894
+ * @returns Search results with hits and metadata
895
+ *
896
+ * @example
897
+ * ```ts
898
+ * // Basic search
899
+ * const results = await client.search('customer email');
900
+ * console.log('Found:', results.data.totalHits, 'results');
901
+ * results.data.hits.forEach(hit => console.log(hit.id, hit.structureSlug));
902
+ *
903
+ * // Search with structure filter
904
+ * const userResults = await client.search('john', { structures: 'users' });
905
+ *
906
+ * // Search multiple structures with limit
907
+ * const results = await client.search('active', {
908
+ * structures: ['users', 'orders'],
909
+ * limit: 50
910
+ * });
911
+ * ```
912
+ */
913
+ search(query, options) {
914
+ return __awaiter(this, void 0, void 0, function* () {
915
+ const path = getSearchApiPath(this.options.workspaceId);
916
+ const queryParams = { q: query };
917
+ if (options === null || options === void 0 ? void 0 : options.structures) {
918
+ const structures = Array.isArray(options.structures)
919
+ ? options.structures.join(',')
920
+ : options.structures;
921
+ queryParams.structures = structures;
922
+ }
923
+ if (options === null || options === void 0 ? void 0 : options.limit) {
924
+ queryParams.limit = String(options.limit);
925
+ }
926
+ return this.request('GET', path, null, queryParams);
927
+ });
928
+ }
633
929
  }
634
930
  exports.CentraliSDK = CentraliSDK;
635
931
  /**
@@ -690,5 +986,38 @@ exports.CentraliSDK = CentraliSDK;
690
986
  * // List all triggers:
691
987
  * const triggers = await client.triggers.list();
692
988
  * triggers.data.forEach(t => console.log(t.name));
989
+ *
990
+ * // ---- Smart Queries ----
991
+ *
992
+ * // List all smart queries in the workspace:
993
+ * const allQueries = await client.smartQueries.listAll();
994
+ *
995
+ * // List smart queries for a specific structure:
996
+ * const employeeQueries = await client.smartQueries.list('employee');
997
+ * employeeQueries.data.forEach(q => console.log(q.name));
998
+ *
999
+ * // Get a smart query by name:
1000
+ * const activeQuery = await client.smartQueries.getByName('employee', 'Active Employees');
1001
+ * console.log('Query ID:', activeQuery.data.id);
1002
+ *
1003
+ * // Execute a smart query:
1004
+ * const results = await client.smartQueries.execute('employee', activeQuery.data.id);
1005
+ * console.log('Found:', results.data.length, 'employees');
1006
+ *
1007
+ * // ---- Search ----
1008
+ *
1009
+ * // Basic full-text search:
1010
+ * const searchResults = await client.search('customer email');
1011
+ * console.log('Found:', searchResults.data.totalHits, 'results');
1012
+ * searchResults.data.hits.forEach(hit => console.log(hit.id, hit.structureSlug));
1013
+ *
1014
+ * // Search with structure filter:
1015
+ * const userResults = await client.search('john', { structures: 'users' });
1016
+ *
1017
+ * // Search multiple structures with limit:
1018
+ * const multiResults = await client.search('active', {
1019
+ * structures: ['users', 'orders'],
1020
+ * limit: 50
1021
+ * });
693
1022
  *```
694
1023
  */
package/index.ts CHANGED
@@ -186,14 +186,201 @@ export interface InvokeTriggerOptions {
186
186
  payload?: Record<string, any>;
187
187
  }
188
188
 
189
+ /**
190
+ * Options for deleting a record.
191
+ */
192
+ export interface DeleteRecordOptions {
193
+ /** Perform a hard delete (permanent). Default: false (soft delete) */
194
+ hard?: boolean;
195
+ }
196
+
189
197
  /**
190
198
  * Response from invoking a trigger.
191
199
  * Currently the API returns the queued job ID as a string.
192
200
  */
193
201
  export type TriggerInvokeResponse = string;
194
202
 
203
+ // =====================================================
204
+ // Smart Query Types
205
+ // =====================================================
206
+
207
+ /**
208
+ * Smart query definition stored in the database.
209
+ */
210
+ export interface SmartQuery {
211
+ /** Unique identifier (UUID) */
212
+ id: string;
213
+ /** Workspace slug where the query belongs */
214
+ workspaceSlug: string;
215
+ /** Structure's record slug (e.g., "employee") */
216
+ recordSlug: string;
217
+ /** Human-readable name (unique within workspace) */
218
+ name: string;
219
+ /** Optional description */
220
+ description?: string;
221
+ /** Query definition object */
222
+ queryDefinition: SmartQueryDefinition;
223
+ /** Status (active, archived) */
224
+ status: string;
225
+ /** User ID who created the query */
226
+ createdBy: string;
227
+ /** User ID who last updated the query */
228
+ updatedBy: string;
229
+ /** ISO timestamp of creation */
230
+ createdAt: string;
231
+ /** ISO timestamp of last update */
232
+ updatedAt: string;
233
+ }
234
+
235
+ /**
236
+ * Query definition format for smart queries.
237
+ * Supports filtering, selection, joins, sorting, and pagination.
238
+ */
239
+ export interface SmartQueryDefinition {
240
+ /** Fields to select from the structure */
241
+ select?: string[];
242
+ /** Filter conditions */
243
+ where?: SmartQueryWhereClause;
244
+ /** Join to another structure */
245
+ join?: SmartQueryJoin;
246
+ /** Sorting specification */
247
+ sort?: SmartQuerySort[];
248
+ /** Maximum number of results */
249
+ limit?: number;
250
+ /** Number of results to skip */
251
+ skip?: number;
252
+ }
253
+
254
+ /**
255
+ * Where clause for smart query filtering.
256
+ * Supports comparison operators and logical operators.
257
+ */
258
+ export interface SmartQueryWhereClause {
259
+ /** Field-level conditions */
260
+ [field: string]: SmartQueryFieldCondition | SmartQueryWhereClause[] | undefined;
261
+ /** Logical AND of multiple conditions */
262
+ $and?: SmartQueryWhereClause[];
263
+ /** Logical OR of multiple conditions */
264
+ $or?: SmartQueryWhereClause[];
265
+ }
266
+
267
+ /**
268
+ * Field-level condition operators for smart query filtering.
269
+ */
270
+ export interface SmartQueryFieldCondition {
271
+ /** Equality */
272
+ $eq?: any;
273
+ /** Not equal */
274
+ $ne?: any;
275
+ /** Greater than */
276
+ $gt?: number;
277
+ /** Greater than or equal */
278
+ $gte?: number;
279
+ /** Less than */
280
+ $lt?: number;
281
+ /** Less than or equal */
282
+ $lte?: number;
283
+ /** String starts with */
284
+ $startsWith?: string;
285
+ /** String ends with */
286
+ $endsWith?: string;
287
+ /** String contains */
288
+ $contains?: string;
289
+ /** Regex pattern match */
290
+ $regex?: string;
291
+ /** Value in array */
292
+ $in?: any[];
293
+ /** Value not in array */
294
+ $nin?: any[];
295
+ /** Field exists check */
296
+ $exists?: boolean;
297
+ /** JSONB type check */
298
+ $type?: string;
299
+ }
300
+
301
+ /**
302
+ * Join definition for smart queries.
303
+ */
304
+ export interface SmartQueryJoin {
305
+ /** Target structure slug to join */
306
+ foreignSlug: string;
307
+ /** Field in the main structure */
308
+ localField: string;
309
+ /** Field in the foreign structure */
310
+ foreignField: string;
311
+ /** Fields to select from the joined structure */
312
+ select?: string[];
313
+ }
314
+
315
+ /**
316
+ * Sort specification for smart queries.
317
+ */
318
+ export interface SmartQuerySort {
319
+ /** Field to sort by */
320
+ field: string;
321
+ /** Sort direction */
322
+ direction: 'asc' | 'desc';
323
+ }
195
324
 
196
325
 
326
+ /**
327
+ * Options for listing smart queries.
328
+ */
329
+ export interface ListSmartQueryOptions {
330
+ /** Page number (default: 1) */
331
+ page?: number;
332
+ /** Results per page (default: 20) */
333
+ limit?: number;
334
+ /** Search term */
335
+ search?: string;
336
+ /** Sort field */
337
+ sort?: string;
338
+ /** Sort direction */
339
+ sortDirection?: 'asc' | 'desc';
340
+ }
341
+
342
+ // =====================================================
343
+ // Search Types
344
+ // =====================================================
345
+
346
+ /**
347
+ * Options for searching records.
348
+ */
349
+ export interface SearchOptions {
350
+ /** Filter by structure slug(s). Can be a single slug or array of slugs */
351
+ structures?: string | string[];
352
+ /** Maximum number of results to return (default: 20, max: 100) */
353
+ limit?: number;
354
+ }
355
+
356
+ /**
357
+ * A single search result hit.
358
+ */
359
+ export interface SearchHit {
360
+ /** The record ID */
361
+ id: string;
362
+ /** The record slug (structure type) */
363
+ recordSlug: string;
364
+ /** The structure slug */
365
+ structureSlug: string;
366
+ /** The indexed record data */
367
+ [key: string]: unknown;
368
+ }
369
+
370
+ /**
371
+ * Response from a search query.
372
+ */
373
+ export interface SearchResponse {
374
+ /** Array of matching records */
375
+ hits: SearchHit[];
376
+ /** Estimated total number of matching records */
377
+ totalHits: number;
378
+ /** Time taken to process the search in milliseconds */
379
+ processingTimeMs: number;
380
+ /** The original search query */
381
+ query: string;
382
+ }
383
+
197
384
  /**
198
385
  * Generate the API URL from the base URL by adding the 'api.' subdomain.
199
386
  * E.g., https://centrali.io -> https://api.centrali.io
@@ -532,6 +719,56 @@ export function getFunctionTriggerExecuteApiPath(workspaceId: string, triggerId:
532
719
  return `data/workspace/${workspaceId}/api/v1/function-triggers/${triggerId}/execute`;
533
720
  }
534
721
 
722
+ /**
723
+ * Generate Function Trigger pause API URL PATH.
724
+ */
725
+ export function getFunctionTriggerPauseApiPath(workspaceId: string, triggerId: string): string {
726
+ return `data/workspace/${workspaceId}/api/v1/function-triggers/${triggerId}/pause`;
727
+ }
728
+
729
+ /**
730
+ * Generate Function Trigger resume API URL PATH.
731
+ */
732
+ export function getFunctionTriggerResumeApiPath(workspaceId: string, triggerId: string): string {
733
+ return `data/workspace/${workspaceId}/api/v1/function-triggers/${triggerId}/resume`;
734
+ }
735
+
736
+ /**
737
+ * Generate Smart Queries base API URL PATH for workspace-level operations.
738
+ */
739
+ export function getSmartQueriesApiPath(workspaceId: string): string {
740
+ return `data/workspace/${workspaceId}/api/v1/smart-queries`;
741
+ }
742
+
743
+ /**
744
+ * Generate Smart Queries API URL PATH for structure-level operations.
745
+ */
746
+ export function getSmartQueriesStructureApiPath(workspaceId: string, structureSlug: string, queryId?: string): string {
747
+ const basePath = `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}`;
748
+ return queryId ? `${basePath}/${queryId}` : basePath;
749
+ }
750
+
751
+ /**
752
+ * Generate Smart Query by name API URL PATH.
753
+ */
754
+ export function getSmartQueryByNameApiPath(workspaceId: string, structureSlug: string, name: string): string {
755
+ return `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}/name/${encodeURIComponent(name)}`;
756
+ }
757
+
758
+ /**
759
+ * Generate Smart Query execute API URL PATH.
760
+ */
761
+ export function getSmartQueryExecuteApiPath(workspaceId: string, structureSlug: string, queryId: string): string {
762
+ return `data/workspace/${workspaceId}/api/v1/smart-queries/slug/${structureSlug}/execute/${queryId}`;
763
+ }
764
+
765
+ /**
766
+ * Generate Search API URL PATH.
767
+ */
768
+ export function getSearchApiPath(workspaceId: string): string {
769
+ return `search/workspace/${workspaceId}/api/v1/records/search`;
770
+ }
771
+
535
772
  // =====================================================
536
773
  // Triggers Manager
537
774
  // =====================================================
@@ -657,6 +894,192 @@ export class TriggersManager {
657
894
  };
658
895
  return this.requestFn<FunctionTrigger[]>('GET', path, null, params);
659
896
  }
897
+
898
+ /**
899
+ * Pause a function trigger.
900
+ *
901
+ * Pauses an event-driven or scheduled trigger to temporarily disable it from firing.
902
+ * When paused, the trigger will not execute until resumed.
903
+ *
904
+ * For scheduled triggers, this also pauses the underlying scheduler job.
905
+ *
906
+ * @param triggerId - The ID of the trigger to pause
907
+ * @returns The updated trigger with enabled = false
908
+ *
909
+ * @example
910
+ * ```ts
911
+ * // Pause a trigger
912
+ * const result = await client.triggers.pauseTrigger('trigger-id');
913
+ * console.log('Trigger paused:', result.data.enabled === false);
914
+ * ```
915
+ */
916
+ public pauseTrigger(triggerId: string): Promise<ApiResponse<FunctionTrigger>> {
917
+ const path = getFunctionTriggerPauseApiPath(this.workspaceId, triggerId);
918
+ return this.requestFn<FunctionTrigger>('PATCH', path, {});
919
+ }
920
+
921
+ /**
922
+ * Resume a paused function trigger.
923
+ *
924
+ * Resumes a paused event-driven or scheduled trigger to re-enable it.
925
+ * Once resumed, the trigger will start firing again on matching events or schedules.
926
+ *
927
+ * For scheduled triggers, this also resumes the underlying scheduler job.
928
+ *
929
+ * @param triggerId - The ID of the trigger to resume
930
+ * @returns The updated trigger with enabled = true
931
+ *
932
+ * @example
933
+ * ```ts
934
+ * // Resume a paused trigger
935
+ * const result = await client.triggers.resumeTrigger('trigger-id');
936
+ * console.log('Trigger resumed:', result.data.enabled === true);
937
+ * ```
938
+ */
939
+ public resumeTrigger(triggerId: string): Promise<ApiResponse<FunctionTrigger>> {
940
+ const path = getFunctionTriggerResumeApiPath(this.workspaceId, triggerId);
941
+ return this.requestFn<FunctionTrigger>('PATCH', path, {});
942
+ }
943
+ }
944
+
945
+ // =====================================================
946
+ // Smart Queries Manager
947
+ // =====================================================
948
+
949
+ /**
950
+ * SmartQueriesManager provides methods for listing and executing smart queries.
951
+ * Smart queries are reusable, parameterized queries defined in the console
952
+ * that can be executed programmatically via the SDK.
953
+ * Access via `client.smartQueries`.
954
+ *
955
+ * Usage:
956
+ * ```ts
957
+ * // List smart queries for a structure
958
+ * const queries = await client.smartQueries.list('employee');
959
+ *
960
+ * // Execute a smart query by ID
961
+ * const results = await client.smartQueries.execute('employee', 'query-uuid');
962
+ *
963
+ * // Get a smart query by name
964
+ * const query = await client.smartQueries.getByName('employee', 'Active Employees');
965
+ * ```
966
+ */
967
+ export class SmartQueriesManager {
968
+ private requestFn: <T>(method: Method, path: string, data?: any, queryParams?: Record<string, any>) => Promise<ApiResponse<T>>;
969
+ private workspaceId: string;
970
+
971
+ constructor(
972
+ workspaceId: string,
973
+ requestFn: <T>(method: Method, path: string, data?: any, queryParams?: Record<string, any>) => Promise<ApiResponse<T>>
974
+ ) {
975
+ this.workspaceId = workspaceId;
976
+ this.requestFn = requestFn;
977
+ }
978
+
979
+ /**
980
+ * List all smart queries in the workspace.
981
+ *
982
+ * @param options - Optional list parameters (pagination, search, etc.)
983
+ * @returns List of smart queries with pagination metadata
984
+ *
985
+ * @example
986
+ * ```ts
987
+ * // List all smart queries
988
+ * const queries = await client.smartQueries.listAll();
989
+ *
990
+ * // With pagination
991
+ * const queries = await client.smartQueries.listAll({ page: 1, limit: 20 });
992
+ * ```
993
+ */
994
+ public listAll(options?: ListSmartQueryOptions): Promise<ApiResponse<SmartQuery[]>> {
995
+ const path = getSmartQueriesApiPath(this.workspaceId);
996
+ return this.requestFn<SmartQuery[]>('GET', path, null, options);
997
+ }
998
+
999
+ /**
1000
+ * List smart queries for a specific structure.
1001
+ *
1002
+ * @param structureSlug - The structure's record slug (e.g., "employee")
1003
+ * @param options - Optional list parameters (pagination, search, etc.)
1004
+ * @returns List of smart queries for the structure
1005
+ *
1006
+ * @example
1007
+ * ```ts
1008
+ * // List queries for employee structure
1009
+ * const queries = await client.smartQueries.list('employee');
1010
+ *
1011
+ * // With pagination and search
1012
+ * const queries = await client.smartQueries.list('employee', {
1013
+ * page: 1,
1014
+ * limit: 10,
1015
+ * search: 'active'
1016
+ * });
1017
+ * ```
1018
+ */
1019
+ public list(structureSlug: string, options?: ListSmartQueryOptions): Promise<ApiResponse<SmartQuery[]>> {
1020
+ const path = getSmartQueriesStructureApiPath(this.workspaceId, structureSlug);
1021
+ return this.requestFn<SmartQuery[]>('GET', path, null, options);
1022
+ }
1023
+
1024
+ /**
1025
+ * Get a smart query by ID.
1026
+ *
1027
+ * @param structureSlug - The structure's record slug
1028
+ * @param queryId - The smart query UUID
1029
+ * @returns The smart query details
1030
+ *
1031
+ * @example
1032
+ * ```ts
1033
+ * const query = await client.smartQueries.get('employee', 'query-uuid');
1034
+ * console.log('Query name:', query.data.name);
1035
+ * ```
1036
+ */
1037
+ public get(structureSlug: string, queryId: string): Promise<ApiResponse<SmartQuery>> {
1038
+ const path = getSmartQueriesStructureApiPath(this.workspaceId, structureSlug, queryId);
1039
+ return this.requestFn<SmartQuery>('GET', path);
1040
+ }
1041
+
1042
+ /**
1043
+ * Get a smart query by name.
1044
+ *
1045
+ * @param structureSlug - The structure's record slug
1046
+ * @param name - The smart query name
1047
+ * @returns The smart query details
1048
+ *
1049
+ * @example
1050
+ * ```ts
1051
+ * const query = await client.smartQueries.getByName('employee', 'Active Employees');
1052
+ * console.log('Query ID:', query.data.id);
1053
+ * ```
1054
+ */
1055
+ public getByName(structureSlug: string, name: string): Promise<ApiResponse<SmartQuery>> {
1056
+ const path = getSmartQueryByNameApiPath(this.workspaceId, structureSlug, name);
1057
+ return this.requestFn<SmartQuery>('GET', path);
1058
+ }
1059
+
1060
+ /**
1061
+ * Execute a smart query and return results.
1062
+ *
1063
+ * Note: Pagination (limit/skip) is defined in the query definition itself,
1064
+ * not at execution time.
1065
+ *
1066
+ * @param structureSlug - The structure's record slug
1067
+ * @param queryId - The smart query UUID
1068
+ * @returns Query results
1069
+ *
1070
+ * @example
1071
+ * ```ts
1072
+ * const results = await client.smartQueries.execute('employee', 'query-uuid');
1073
+ * console.log('Found:', results.data.length, 'records');
1074
+ * ```
1075
+ */
1076
+ public execute<T = any>(
1077
+ structureSlug: string,
1078
+ queryId: string
1079
+ ): Promise<ApiResponse<T[]>> {
1080
+ const path = getSmartQueryExecuteApiPath(this.workspaceId, structureSlug, queryId);
1081
+ return this.requestFn<T[]>('GET', path);
1082
+ }
660
1083
  }
661
1084
 
662
1085
  /**
@@ -668,6 +1091,7 @@ export class CentraliSDK {
668
1091
  private options: CentraliSDKOptions;
669
1092
  private _realtime: RealtimeManager | null = null;
670
1093
  private _triggers: TriggersManager | null = null;
1094
+ private _smartQueries: SmartQueriesManager | null = null;
671
1095
 
672
1096
  constructor(options: CentraliSDKOptions) {
673
1097
  this.options = options;
@@ -782,6 +1206,35 @@ export class CentraliSDK {
782
1206
  return this._triggers;
783
1207
  }
784
1208
 
1209
+ /**
1210
+ * Smart Queries namespace for listing and executing smart queries.
1211
+ *
1212
+ * Usage:
1213
+ * ```ts
1214
+ * // List all smart queries in workspace
1215
+ * const allQueries = await client.smartQueries.listAll();
1216
+ *
1217
+ * // List smart queries for a structure
1218
+ * const queries = await client.smartQueries.list('employee');
1219
+ *
1220
+ * // Get a smart query by name
1221
+ * const query = await client.smartQueries.getByName('employee', 'Active Employees');
1222
+ *
1223
+ * // Execute a smart query
1224
+ * const results = await client.smartQueries.execute('employee', query.data.id);
1225
+ * console.log('Found:', results.data.length, 'records');
1226
+ * ```
1227
+ */
1228
+ public get smartQueries(): SmartQueriesManager {
1229
+ if (!this._smartQueries) {
1230
+ this._smartQueries = new SmartQueriesManager(
1231
+ this.options.workspaceId,
1232
+ this.request.bind(this)
1233
+ );
1234
+ }
1235
+ return this._smartQueries;
1236
+ }
1237
+
785
1238
  /**
786
1239
  * Manually set or update the bearer token for subsequent requests.
787
1240
  */
@@ -831,6 +1284,10 @@ export class CentraliSDK {
831
1284
  if (Array.isArray(resp.data)) {
832
1285
  return { data: resp.data as T };
833
1286
  }
1287
+ // Handle { result } responses (smart queries and some other endpoints)
1288
+ if ('result' in resp.data && !('data' in resp.data)) {
1289
+ return { data: (resp.data as any).result as T };
1290
+ }
834
1291
  // Handle objects that don't have a data property (legacy endpoints)
835
1292
  if (!('data' in resp.data)) {
836
1293
  return { data: resp.data as T };
@@ -894,13 +1351,24 @@ export class CentraliSDK {
894
1351
  return this.request('PUT', path, { ...updates });
895
1352
  }
896
1353
 
897
- /** Delete a record by ID. */
1354
+ /** Delete a record by ID (soft delete by default, can be restored). */
898
1355
  public deleteRecord(
899
1356
  recordSlug: string,
900
- id: string
1357
+ id: string,
1358
+ options?: DeleteRecordOptions
901
1359
  ): Promise<ApiResponse<null>> {
902
1360
  const path = getRecordApiPath(this.options.workspaceId, recordSlug, id);
903
- return this.request('DELETE', path);
1361
+ const queryParams = options?.hard ? { hard: 'true' } : undefined;
1362
+ return this.request('DELETE', path, null, queryParams);
1363
+ }
1364
+
1365
+ /** Restore a soft-deleted record by ID. */
1366
+ public restoreRecord(
1367
+ recordSlug: string,
1368
+ id: string
1369
+ ): Promise<ApiResponse<null>> {
1370
+ const path = getRecordApiPath(this.options.workspaceId, recordSlug, id) + '/restore';
1371
+ return this.request('POST', path);
904
1372
  }
905
1373
 
906
1374
 
@@ -929,6 +1397,54 @@ export class CentraliSDK {
929
1397
  });
930
1398
  }
931
1399
 
1400
+ // ------------------ Search API Methods ------------------
1401
+
1402
+ /**
1403
+ * Search records across the workspace using full-text search.
1404
+ *
1405
+ * @param query - The search query string
1406
+ * @param options - Optional search parameters
1407
+ * @returns Search results with hits and metadata
1408
+ *
1409
+ * @example
1410
+ * ```ts
1411
+ * // Basic search
1412
+ * const results = await client.search('customer email');
1413
+ * console.log('Found:', results.data.totalHits, 'results');
1414
+ * results.data.hits.forEach(hit => console.log(hit.id, hit.structureSlug));
1415
+ *
1416
+ * // Search with structure filter
1417
+ * const userResults = await client.search('john', { structures: 'users' });
1418
+ *
1419
+ * // Search multiple structures with limit
1420
+ * const results = await client.search('active', {
1421
+ * structures: ['users', 'orders'],
1422
+ * limit: 50
1423
+ * });
1424
+ * ```
1425
+ */
1426
+ public async search(
1427
+ query: string,
1428
+ options?: SearchOptions
1429
+ ): Promise<ApiResponse<SearchResponse>> {
1430
+ const path = getSearchApiPath(this.options.workspaceId);
1431
+
1432
+ const queryParams: Record<string, string> = { q: query };
1433
+
1434
+ if (options?.structures) {
1435
+ const structures = Array.isArray(options.structures)
1436
+ ? options.structures.join(',')
1437
+ : options.structures;
1438
+ queryParams.structures = structures;
1439
+ }
1440
+
1441
+ if (options?.limit) {
1442
+ queryParams.limit = String(options.limit);
1443
+ }
1444
+
1445
+ return this.request<SearchResponse>('GET', path, null, queryParams);
1446
+ }
1447
+
932
1448
  }
933
1449
 
934
1450
  /**
@@ -989,5 +1505,38 @@ export class CentraliSDK {
989
1505
  * // List all triggers:
990
1506
  * const triggers = await client.triggers.list();
991
1507
  * triggers.data.forEach(t => console.log(t.name));
1508
+ *
1509
+ * // ---- Smart Queries ----
1510
+ *
1511
+ * // List all smart queries in the workspace:
1512
+ * const allQueries = await client.smartQueries.listAll();
1513
+ *
1514
+ * // List smart queries for a specific structure:
1515
+ * const employeeQueries = await client.smartQueries.list('employee');
1516
+ * employeeQueries.data.forEach(q => console.log(q.name));
1517
+ *
1518
+ * // Get a smart query by name:
1519
+ * const activeQuery = await client.smartQueries.getByName('employee', 'Active Employees');
1520
+ * console.log('Query ID:', activeQuery.data.id);
1521
+ *
1522
+ * // Execute a smart query:
1523
+ * const results = await client.smartQueries.execute('employee', activeQuery.data.id);
1524
+ * console.log('Found:', results.data.length, 'employees');
1525
+ *
1526
+ * // ---- Search ----
1527
+ *
1528
+ * // Basic full-text search:
1529
+ * const searchResults = await client.search('customer email');
1530
+ * console.log('Found:', searchResults.data.totalHits, 'results');
1531
+ * searchResults.data.hits.forEach(hit => console.log(hit.id, hit.structureSlug));
1532
+ *
1533
+ * // Search with structure filter:
1534
+ * const userResults = await client.search('john', { structures: 'users' });
1535
+ *
1536
+ * // Search multiple structures with limit:
1537
+ * const multiResults = await client.search('active', {
1538
+ * structures: ['users', 'orders'],
1539
+ * limit: 50
1540
+ * });
992
1541
  *```
993
1542
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centrali-io/centrali-sdk",
3
- "version": "2.2.1",
3
+ "version": "2.2.3",
4
4
  "description": "Centrali Node SDK",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",