@centrali-io/centrali-sdk 5.5.0 → 6.0.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.
Files changed (49) hide show
  1. package/README.md +164 -14
  2. package/dist/index.d.ts +1807 -878
  3. package/dist/index.js +9153 -4076
  4. package/index.ts +61 -7152
  5. package/package.json +10 -3
  6. package/query-types.ts +83 -2
  7. package/scripts/smoke-types.ts +145 -5
  8. package/src/client.ts +1507 -0
  9. package/src/internal/auth.ts +35 -0
  10. package/src/internal/deprecation.ts +11 -0
  11. package/src/internal/error.ts +90 -0
  12. package/src/internal/paths.ts +456 -0
  13. package/src/internal/queryGuard.ts +21 -0
  14. package/src/managers/allowedDomains.ts +90 -0
  15. package/src/managers/anomalyInsights.ts +215 -0
  16. package/src/managers/auditLog.ts +105 -0
  17. package/src/managers/collections.ts +197 -0
  18. package/src/managers/files.ts +182 -0
  19. package/src/managers/functionRuns.ts +229 -0
  20. package/src/managers/functions.ts +171 -0
  21. package/src/managers/orchestrationRuns.ts +122 -0
  22. package/src/managers/orchestrations.ts +297 -0
  23. package/src/managers/query.ts +199 -0
  24. package/src/managers/records.ts +186 -0
  25. package/src/managers/smartQueries.ts +374 -0
  26. package/src/managers/structures.ts +205 -0
  27. package/src/managers/triggers.ts +349 -0
  28. package/src/managers/validation.ts +303 -0
  29. package/src/managers/webhookSubscriptions.ts +206 -0
  30. package/src/realtime/manager.ts +292 -0
  31. package/src/types/allowedDomains.ts +29 -0
  32. package/src/types/auth.ts +83 -0
  33. package/src/types/common.ts +57 -0
  34. package/src/types/compute.ts +145 -0
  35. package/src/types/insights.ts +113 -0
  36. package/src/types/orchestrations.ts +460 -0
  37. package/src/types/realtime.ts +403 -0
  38. package/src/types/records.ts +261 -0
  39. package/src/types/search.ts +44 -0
  40. package/src/types/smartQueries.ts +303 -0
  41. package/src/types/structures.ts +203 -0
  42. package/src/types/triggers.ts +122 -0
  43. package/src/types/validation.ts +167 -0
  44. package/src/types/webhooks.ts +114 -0
  45. package/src/urls.ts +33 -0
  46. package/dist/query-types.d.ts +0 -187
  47. package/dist/query-types.js +0 -137
  48. package/dist/scripts/smoke-types.d.ts +0 -12
  49. package/dist/scripts/smoke-types.js +0 -102
@@ -0,0 +1,35 @@
1
+ import axios from 'axios';
2
+ import { getApiUrl, getAuthUrl } from '../urls';
3
+
4
+ // Helper to encode form data
5
+ export function encodeFormData(data: Record<string, string>): string {
6
+ return new URLSearchParams(data).toString();
7
+ }
8
+
9
+ /**
10
+ * Retrieve an access token using the Client Credentials flow.
11
+ */
12
+ export async function fetchClientToken(
13
+ clientId: string,
14
+ clientSecret: string,
15
+ baseUrl: string
16
+ ): Promise<string> {
17
+ const authUrl = getAuthUrl(baseUrl);
18
+ const apiUrl = getApiUrl(baseUrl);
19
+ const tokenEndpoint = `${authUrl}/token`;
20
+ const form = encodeFormData({
21
+ grant_type: 'client_credentials',
22
+ client_id: clientId,
23
+ client_secret: clientSecret,
24
+ resource: `${apiUrl}/data`
25
+ });
26
+ const resp = await axios.post(
27
+ tokenEndpoint,
28
+ form,
29
+ { headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
30
+ proxy: false
31
+ }
32
+ );
33
+
34
+ return resp.data.access_token;
35
+ }
@@ -0,0 +1,11 @@
1
+ // =====================================================
2
+ // Deprecation Utility
3
+ // =====================================================
4
+
5
+ const _deprecationWarnings = new Set<string>();
6
+ export function emitDeprecationWarning(message: string): void {
7
+ if (!_deprecationWarnings.has(message)) {
8
+ _deprecationWarnings.add(message);
9
+ console.warn(`[DEPRECATION] ${message}`);
10
+ }
11
+ }
@@ -0,0 +1,90 @@
1
+ // =====================================================
2
+ // Error Types
3
+ // =====================================================
4
+
5
+ import { AxiosError } from 'axios';
6
+
7
+ /**
8
+ * Error thrown by the Centrali SDK when an HTTP request fails.
9
+ * Wraps the underlying HTTP error to avoid leaking transport details.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { CentraliSDK, CentraliError } from '@centrali-io/centrali-sdk';
14
+ *
15
+ * try {
16
+ * await client.records.create(structureId, data);
17
+ * } catch (err) {
18
+ * if (err instanceof CentraliError) {
19
+ * console.log(err.status); // 400
20
+ * console.log(err.code); // 'VALIDATION_ERROR'
21
+ * console.log(err.message); // 'structureSlug is required'
22
+ * }
23
+ * }
24
+ * ```
25
+ */
26
+ export class CentraliError extends Error {
27
+ /** HTTP status code (e.g. 400, 401, 404, 500). Undefined for network errors. */
28
+ readonly status: number | undefined;
29
+ /** HTTP status text (e.g. "Bad Request"). Undefined for network errors. */
30
+ readonly statusText: string | undefined;
31
+ /** Machine-readable error code in SCREAMING_SNAKE_CASE (e.g. 'VALIDATION_ERROR', 'NOT_FOUND'). Undefined for network errors. */
32
+ readonly code: string | undefined;
33
+ /** Field-level validation errors. Present when code is 'VALIDATION_ERROR' and the server included per-field details. */
34
+ readonly fieldErrors: { field: string; message: string }[] | undefined;
35
+ /** Parsed response body from the server. Undefined if no response was received. */
36
+ readonly body: unknown;
37
+
38
+ constructor(
39
+ message: string,
40
+ status?: number,
41
+ statusText?: string,
42
+ body?: unknown,
43
+ code?: string,
44
+ fieldErrors?: { field: string; message: string }[]
45
+ ) {
46
+ super(message);
47
+ this.name = 'CentraliError';
48
+ this.status = status;
49
+ this.statusText = statusText;
50
+ this.body = body;
51
+ this.code = code;
52
+ this.fieldErrors = fieldErrors;
53
+
54
+ // Restore prototype chain (required when extending built-ins in TypeScript)
55
+ Object.setPrototypeOf(this, CentraliError.prototype);
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Type guard to check if a value is a CentraliError.
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * } catch (err) {
65
+ * if (isCentraliError(err)) {
66
+ * console.log(err.status);
67
+ * }
68
+ * }
69
+ * ```
70
+ */
71
+ export function isCentraliError(err: unknown): err is CentraliError {
72
+ return err instanceof CentraliError;
73
+ }
74
+
75
+ export function toCentraliError(err: unknown): CentraliError {
76
+ if (err instanceof CentraliError) return err;
77
+ if (err instanceof AxiosError) {
78
+ const status = err.response?.status;
79
+ const statusText = err.response?.statusText;
80
+ const body = err.response?.data as any;
81
+ const message = body?.message ?? err.message;
82
+ const code = typeof body?.error === 'string' ? body.error : undefined;
83
+ const fieldErrors = Array.isArray(body?.fieldErrors) ? body.fieldErrors : undefined;
84
+ return new CentraliError(message, status, statusText, body, code, fieldErrors);
85
+ }
86
+ if (err instanceof Error) {
87
+ return new CentraliError(err.message);
88
+ }
89
+ return new CentraliError(String(err));
90
+ }
@@ -0,0 +1,456 @@
1
+ /**
2
+ * Generate Records API URL PATH.
3
+ */
4
+
5
+ export function getRecordApiPath(workspaceId: string,recordSlug: string, id?: string): string {
6
+ return id ? `data/workspace/${workspaceId}/api/v1/records/slug/${recordSlug}/${id}` : `data/workspace/${workspaceId}/api/v1/records/slug/${recordSlug}`;
7
+ }
8
+
9
+
10
+ /**
11
+ * Generate File Upload API URL PATH.
12
+ * @param workspaceId
13
+ */
14
+ export function getFileUploadApiPath(workspaceId: string): string {
15
+ return `storage/ws/${workspaceId}/api/v1/files`;
16
+ }
17
+
18
+ /**
19
+ * Generate canonical Files query API URL PATH (CEN-1218 / Phase 3).
20
+ * Backs `POST /files/query`, `/files/query/test`, and the
21
+ * `GET /files` URL adapter. Lives on the storage service alongside
22
+ * the upload + render endpoints.
23
+ */
24
+ export function getFilesCanonicalApiPath(workspaceId: string, suffix?: 'query' | 'query/test'): string {
25
+ const basePath = `storage/ws/${workspaceId}/api/v1/files`;
26
+ return suffix ? `${basePath}/${suffix}` : basePath;
27
+ }
28
+
29
+ /**
30
+ * Generate Function Triggers base API URL PATH.
31
+ */
32
+ export function getFunctionTriggersApiPath(workspaceId: string, triggerId?: string): string {
33
+ const basePath = `data/workspace/${workspaceId}/api/v1/function-triggers`;
34
+ return triggerId ? `${basePath}/${triggerId}` : basePath;
35
+ }
36
+
37
+ /**
38
+ * Generate Function Trigger execute API URL PATH.
39
+ */
40
+ export function getFunctionTriggerExecuteApiPath(workspaceId: string, triggerId: string): string {
41
+ return `data/workspace/${workspaceId}/api/v1/function-triggers/${triggerId}/execute`;
42
+ }
43
+
44
+ /**
45
+ * Generate Function Trigger pause API URL PATH.
46
+ */
47
+ export function getFunctionTriggerPauseApiPath(workspaceId: string, triggerId: string): string {
48
+ return `data/workspace/${workspaceId}/api/v1/function-triggers/${triggerId}/pause`;
49
+ }
50
+
51
+ /**
52
+ * Generate Function Trigger resume API URL PATH.
53
+ */
54
+ export function getFunctionTriggerResumeApiPath(workspaceId: string, triggerId: string): string {
55
+ return `data/workspace/${workspaceId}/api/v1/function-triggers/${triggerId}/resume`;
56
+ }
57
+
58
+ /**
59
+ * Generate Endpoint trigger invocation API URL PATH.
60
+ */
61
+ export function getEndpointApiPath(workspaceId: string, path: string): string {
62
+ return `data/workspace/${workspaceId}/api/v1/endpoints/${path}`;
63
+ }
64
+
65
+ /**
66
+ * Generate Saved Queries base API URL PATH for workspace-level operations.
67
+ *
68
+ * Phase 4 (CEN-1198) of the query foundation moved the canonical mount from
69
+ * `/smart-queries` to `/saved-queries`. The data service dual-mounts both for
70
+ * the deprecation window; this helper emits the canonical path.
71
+ */
72
+ export function getSmartQueriesApiPath(workspaceId: string): string {
73
+ return `data/workspace/${workspaceId}/api/v1/saved-queries`;
74
+ }
75
+
76
+ /**
77
+ * Generate Saved Queries API URL PATH for structure-level operations.
78
+ */
79
+ export function getSmartQueriesStructureApiPath(workspaceId: string, structureSlug: string, queryId?: string): string {
80
+ const basePath = `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}`;
81
+ return queryId ? `${basePath}/${queryId}` : basePath;
82
+ }
83
+
84
+ /**
85
+ * Generate Saved Query by name API URL PATH.
86
+ */
87
+ export function getSmartQueryByNameApiPath(workspaceId: string, structureSlug: string, name: string): string {
88
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}/name/${encodeURIComponent(name)}`;
89
+ }
90
+
91
+ /**
92
+ * Generate Saved Query execute API URL PATH.
93
+ */
94
+ export function getSmartQueryExecuteApiPath(workspaceId: string, structureSlug: string, queryId: string): string {
95
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}/execute/${queryId}`;
96
+ }
97
+
98
+ /**
99
+ * Phase 4 canonical saved-query path helpers (no `slug` segment). These hit
100
+ * the canonical write/execute endpoints that accept canonical `query` bodies
101
+ * and `variables` typed declarations.
102
+ */
103
+ export function getSavedQueryCanonicalCollectionPath(workspaceId: string): string {
104
+ return `data/workspace/${workspaceId}/api/v1/saved-queries`;
105
+ }
106
+ export function getSavedQueryCanonicalByIdPath(workspaceId: string, queryId: string): string {
107
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/${queryId}`;
108
+ }
109
+ export function getSavedQueryCanonicalExecutePath(workspaceId: string, queryId: string): string {
110
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/${queryId}/execute`;
111
+ }
112
+ export function getSavedQueryCanonicalTestPath(workspaceId: string): string {
113
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/test`;
114
+ }
115
+
116
+ /**
117
+ * Generate Search API URL PATH.
118
+ */
119
+ export function getSearchApiPath(workspaceId: string): string {
120
+ return `search/workspace/${workspaceId}/api/v1/records/search`;
121
+ }
122
+
123
+ /**
124
+ * Generate Anomaly Insights base API URL PATH.
125
+ */
126
+ export function getAnomalyInsightsApiPath(workspaceId: string, insightId?: string): string {
127
+ const basePath = `data/workspace/${workspaceId}/api/v1/ai/insights`;
128
+ return insightId ? `${basePath}/${insightId}` : basePath;
129
+ }
130
+
131
+ /**
132
+ * Generate Anomaly Insights summary API URL PATH.
133
+ */
134
+ export function getAnomalyInsightsSummaryApiPath(workspaceId: string): string {
135
+ return `data/workspace/${workspaceId}/api/v1/ai/insights/summary`;
136
+ }
137
+
138
+ /**
139
+ * Generate Anomaly Insights acknowledge API URL PATH.
140
+ */
141
+ export function getAnomalyInsightAcknowledgeApiPath(workspaceId: string, insightId: string): string {
142
+ return `data/workspace/${workspaceId}/api/v1/ai/insights/${insightId}/acknowledge`;
143
+ }
144
+
145
+ /**
146
+ * Generate Anomaly Insights dismiss API URL PATH.
147
+ */
148
+ export function getAnomalyInsightDismissApiPath(workspaceId: string, insightId: string): string {
149
+ return `data/workspace/${workspaceId}/api/v1/ai/insights/${insightId}/dismiss`;
150
+ }
151
+
152
+ /**
153
+ * Generate Anomaly Insights bulk acknowledge API URL PATH.
154
+ */
155
+ export function getAnomalyInsightsBulkAcknowledgeApiPath(workspaceId: string): string {
156
+ return `data/workspace/${workspaceId}/api/v1/ai/insights/bulk-acknowledge`;
157
+ }
158
+
159
+ /**
160
+ * Generate Anomaly analysis trigger API URL PATH.
161
+ */
162
+ export function getAnomalyAnalysisTriggerApiPath(workspaceId: string): string {
163
+ return `data/workspace/${workspaceId}/api/v1/ai/anomalies/analyze`;
164
+ }
165
+
166
+ /**
167
+ * Generate structure-scoped anomaly insights API URL PATH.
168
+ */
169
+ export function getStructureInsightsApiPath(workspaceId: string, structureSlug: string): string {
170
+ return `data/workspace/${workspaceId}/api/v1/structures/${structureSlug}/insights`;
171
+ }
172
+
173
+ // =====================================================
174
+ // Structure API Path Helpers (Configuration-as-Code)
175
+ // =====================================================
176
+
177
+ /**
178
+ * Generate Structures base API URL PATH.
179
+ */
180
+ export function getStructuresApiPath(workspaceId: string, structureId?: string): string {
181
+ const basePath = `data/workspace/${workspaceId}/api/v1/structures`;
182
+ return structureId ? `${basePath}/${structureId}` : basePath;
183
+ }
184
+
185
+ /**
186
+ * Generate Structure by slug API URL PATH.
187
+ */
188
+ export function getStructureBySlugApiPath(workspaceId: string, recordSlug: string): string {
189
+ return `data/workspace/${workspaceId}/api/v1/structures/slug/${recordSlug}`;
190
+ }
191
+
192
+ /**
193
+ * Generate Structure validate API URL PATH.
194
+ */
195
+ export function getStructureValidateApiPath(workspaceId: string): string {
196
+ return `data/workspace/${workspaceId}/api/v1/structures/validate`;
197
+ }
198
+
199
+ // =====================================================
200
+ // Collection API Path Helpers (Configuration-as-Code)
201
+ // =====================================================
202
+
203
+ /**
204
+ * Generate collection-scoped anomaly insights API URL PATH.
205
+ */
206
+ export function getCollectionInsightsApiPath(workspaceId: string, collectionSlug: string): string {
207
+ return `data/workspace/${workspaceId}/api/v1/collections/${collectionSlug}/insights`;
208
+ }
209
+
210
+ /**
211
+ * Generate Collections base API URL PATH.
212
+ */
213
+ export function getCollectionsApiPath(workspaceId: string, collectionId?: string): string {
214
+ const basePath = `data/workspace/${workspaceId}/api/v1/collections`;
215
+ return collectionId ? `${basePath}/${collectionId}` : basePath;
216
+ }
217
+
218
+ /**
219
+ * Generate Collection by slug API URL PATH.
220
+ */
221
+ export function getCollectionBySlugApiPath(workspaceId: string, recordSlug: string): string {
222
+ return `data/workspace/${workspaceId}/api/v1/collections/slug/${recordSlug}`;
223
+ }
224
+
225
+ /**
226
+ * Generate Collection validate API URL PATH.
227
+ */
228
+ export function getCollectionValidateApiPath(workspaceId: string): string {
229
+ return `data/workspace/${workspaceId}/api/v1/collections/validate`;
230
+ }
231
+
232
+ // =====================================================
233
+ // Compute Function API Path Helpers (Configuration-as-Code)
234
+ // =====================================================
235
+
236
+ /**
237
+ * Generate Compute Functions base API URL PATH.
238
+ */
239
+ export function getComputeFunctionsApiPath(workspaceId: string, functionId?: string): string {
240
+ const basePath = `data/workspace/${workspaceId}/api/v1/compute-functions`;
241
+ return functionId ? `${basePath}/${functionId}` : basePath;
242
+ }
243
+
244
+ /**
245
+ * Generate Compute Function test execution API URL PATH.
246
+ */
247
+ export function getComputeFunctionTestApiPath(workspaceId: string): string {
248
+ return `data/workspace/${workspaceId}/api/v1/compute-functions/test`;
249
+ }
250
+
251
+ // =====================================================
252
+ // Function Runs API Path Helpers
253
+ // =====================================================
254
+
255
+ /**
256
+ * Generate Function Runs base API URL PATH.
257
+ */
258
+ export function getFunctionRunsApiPath(workspaceId: string, runId?: string): string {
259
+ const basePath = `data/workspace/${workspaceId}/api/v1/function-runs`;
260
+ return runId ? `${basePath}/${runId}` : basePath;
261
+ }
262
+
263
+ /**
264
+ * Generate Function Runs by trigger API URL PATH.
265
+ */
266
+ export function getFunctionRunsByTriggerApiPath(workspaceId: string, triggerId: string): string {
267
+ return `data/workspace/${workspaceId}/api/v1/function-runs/trigger/${triggerId}`;
268
+ }
269
+
270
+ /**
271
+ * Generate Function Runs by function API URL PATH.
272
+ */
273
+ export function getFunctionRunsByFunctionApiPath(workspaceId: string, functionId: string): string {
274
+ return `data/workspace/${workspaceId}/api/v1/function-runs/function/${functionId}`;
275
+ }
276
+
277
+ // =====================================================
278
+ // Compute Job Status API Path Helper
279
+ // =====================================================
280
+
281
+ /**
282
+ * Generate Compute Job Status API URL PATH.
283
+ */
284
+ export function getComputeJobStatusApiPath(workspaceId: string, jobId: string): string {
285
+ return `data/workspace/${workspaceId}/api/v1/jobs/compute/${jobId}`;
286
+ }
287
+
288
+ // =====================================================
289
+ // Smart Query Test API Path Helper (Configuration-as-Code)
290
+ // =====================================================
291
+
292
+ /**
293
+ * Generate Saved Query test execution API URL PATH.
294
+ */
295
+ export function getSmartQueryTestApiPath(workspaceId: string, structureSlug: string): string {
296
+ return `data/workspace/${workspaceId}/api/v1/saved-queries/slug/${structureSlug}/test`;
297
+ }
298
+
299
+ // =====================================================
300
+ // Validation API Path Helpers
301
+ // =====================================================
302
+
303
+ /**
304
+ * Generate Validation suggestions base API URL PATH.
305
+ */
306
+ export function getValidationSuggestionsApiPath(workspaceId: string, suggestionId?: string): string {
307
+ const basePath = `data/workspace/${workspaceId}/api/v1/ai/validation/suggestions`;
308
+ return suggestionId ? `${basePath}/${suggestionId}` : basePath;
309
+ }
310
+
311
+ /**
312
+ * Generate Validation suggestion accept API URL PATH.
313
+ */
314
+ export function getValidationSuggestionAcceptApiPath(workspaceId: string, suggestionId: string): string {
315
+ return `data/workspace/${workspaceId}/api/v1/ai/validation/suggestions/${suggestionId}/accept`;
316
+ }
317
+
318
+ /**
319
+ * Generate Validation suggestion reject API URL PATH.
320
+ */
321
+ export function getValidationSuggestionRejectApiPath(workspaceId: string, suggestionId: string): string {
322
+ return `data/workspace/${workspaceId}/api/v1/ai/validation/suggestions/${suggestionId}/reject`;
323
+ }
324
+
325
+ /**
326
+ * Generate Validation bulk accept API URL PATH.
327
+ */
328
+ export function getValidationBulkAcceptApiPath(workspaceId: string): string {
329
+ return `data/workspace/${workspaceId}/api/v1/ai/validation/suggestions/bulk-accept`;
330
+ }
331
+
332
+ /**
333
+ * Generate Validation bulk reject API URL PATH.
334
+ */
335
+ export function getValidationBulkRejectApiPath(workspaceId: string): string {
336
+ return `data/workspace/${workspaceId}/api/v1/ai/validation/suggestions/bulk-reject`;
337
+ }
338
+
339
+ /**
340
+ * Generate Validation summary API URL PATH.
341
+ */
342
+ export function getValidationSummaryApiPath(workspaceId: string): string {
343
+ return `data/workspace/${workspaceId}/api/v1/ai/validation/summary`;
344
+ }
345
+
346
+ /**
347
+ * Generate Validation record suggestions API URL PATH.
348
+ */
349
+ export function getValidationRecordSuggestionsApiPath(workspaceId: string, recordId: string): string {
350
+ return `data/workspace/${workspaceId}/api/v1/ai/validation/records/${recordId}/suggestions`;
351
+ }
352
+
353
+ /**
354
+ * Generate Validation structure pending count API URL PATH.
355
+ */
356
+ export function getValidationPendingCountApiPath(workspaceId: string, structureSlug: string): string {
357
+ return `data/workspace/${workspaceId}/api/v1/ai/validation/structures/${structureSlug}/pending-count`;
358
+ }
359
+
360
+ /**
361
+ * Generate Validation batch scan API URL PATH (AI Service).
362
+ * Note: This routes to the AI service, not the Data service.
363
+ */
364
+ export function getValidationScanApiPath(workspaceId: string, batchId?: string): string {
365
+ const basePath = `ai/workspace/${workspaceId}/api/v1/validate/scan`;
366
+ return batchId ? `${basePath}/${batchId}` : basePath;
367
+ }
368
+
369
+ // =====================================================
370
+ // Orchestration API Paths
371
+ // =====================================================
372
+
373
+ /**
374
+ * Generate Orchestrations API URL PATH.
375
+ * Routes to the orchestration service.
376
+ */
377
+ export function getOrchestrationsApiPath(workspaceId: string, orchestrationId?: string): string {
378
+ const basePath = `orchestration/ws/${workspaceId}/api/v1/orchestrations`;
379
+ return orchestrationId ? `${basePath}/${orchestrationId}` : basePath;
380
+ }
381
+
382
+ /**
383
+ * Generate Orchestration Runs API URL PATH.
384
+ */
385
+ export function getOrchestrationRunsApiPath(workspaceId: string, orchestrationId: string, runId?: string): string {
386
+ const basePath = `orchestration/ws/${workspaceId}/api/v1/orchestrations/${orchestrationId}/runs`;
387
+ return runId ? `${basePath}/${runId}` : basePath;
388
+ }
389
+
390
+ /**
391
+ * Generate Orchestration Run Steps API URL PATH.
392
+ */
393
+ export function getOrchestrationRunStepsApiPath(workspaceId: string, orchestrationId: string, runId: string): string {
394
+ return `orchestration/ws/${workspaceId}/api/v1/orchestrations/${orchestrationId}/runs/${runId}/steps`;
395
+ }
396
+
397
+ /**
398
+ * Generate canonical Orchestration Runs query API URL PATH (CEN-1217).
399
+ * Backs `POST /orchestration-runs/query`, `/query/test`, and the
400
+ * `GET /orchestration-runs` URL adapter.
401
+ */
402
+ export function getOrchestrationRunsCanonicalApiPath(workspaceId: string, suffix?: 'query' | 'query/test'): string {
403
+ const basePath = `orchestration/ws/${workspaceId}/api/v1/orchestration-runs`;
404
+ return suffix ? `${basePath}/${suffix}` : basePath;
405
+ }
406
+
407
+ /**
408
+ * Generate Allowed Domains API URL PATH.
409
+ */
410
+ export function getAllowedDomainsApiPath(workspaceId: string, domainId?: string): string {
411
+ const basePath = `data/workspace/${workspaceId}/api/v1/settings/allowed-domains`;
412
+ return domainId ? `${basePath}/${domainId}` : basePath;
413
+ }
414
+
415
+ // =====================================================
416
+ // Webhook Subscription API Paths
417
+ // =====================================================
418
+
419
+ /**
420
+ * Generate Webhook Subscriptions API URL PATH.
421
+ */
422
+ export function getWebhookSubscriptionsApiPath(workspaceId: string, subscriptionId?: string): string {
423
+ const basePath = `data/workspace/${workspaceId}/api/v1/webhook-subscriptions`;
424
+ return subscriptionId ? `${basePath}/${subscriptionId}` : basePath;
425
+ }
426
+
427
+ /**
428
+ * Generate rotate-secret API URL PATH for a webhook subscription.
429
+ */
430
+ export function getWebhookSubscriptionRotateSecretApiPath(workspaceId: string, subscriptionId: string): string {
431
+ return `data/workspace/${workspaceId}/api/v1/webhook-subscriptions/${subscriptionId}/rotate-secret`;
432
+ }
433
+
434
+ /**
435
+ * Generate deliveries API URL PATH scoped to a webhook subscription.
436
+ */
437
+ export function getWebhookSubscriptionDeliveriesApiPath(workspaceId: string, subscriptionId: string, deliveryId?: string): string {
438
+ const basePath = `data/workspace/${workspaceId}/api/v1/webhook-subscriptions/${subscriptionId}/deliveries`;
439
+ return deliveryId ? `${basePath}/${deliveryId}` : basePath;
440
+ }
441
+
442
+ /**
443
+ * Generate retry API URL PATH for a webhook delivery.
444
+ * Retry is workspace-scoped (not nested under a subscription) — only the delivery ID is needed.
445
+ */
446
+ export function getWebhookDeliveryRetryApiPath(workspaceId: string, deliveryId: string): string {
447
+ return `data/workspace/${workspaceId}/api/v1/webhook-subscriptions/deliveries/${deliveryId}/retry`;
448
+ }
449
+
450
+ /**
451
+ * Generate cancel API URL PATH for a webhook delivery retry.
452
+ * Cancel is workspace-scoped — only the delivery ID is needed.
453
+ */
454
+ export function getWebhookDeliveryCancelApiPath(workspaceId: string, deliveryId: string): string {
455
+ return `data/workspace/${workspaceId}/api/v1/webhook-subscriptions/deliveries/${deliveryId}/cancel`;
456
+ }
@@ -0,0 +1,21 @@
1
+ import type { QueryDefinition } from '../../query-types';
2
+
3
+ /**
4
+ * Discriminator for the dual-shape `client.queryRecords(...)` overload.
5
+ *
6
+ * Returns `true` if the argument is a canonical `QueryDefinition`. The
7
+ * marker is **the presence of `resource`** — that field is required by the
8
+ * canonical contract (`docs/superpowers/specs/2026-04-25-query-foundation-contract.md` §3)
9
+ * and isn't a legal key in the legacy `QueryRecordOptions` URL-param shape.
10
+ *
11
+ * Callers who want to omit `resource` (relying on the first argument to fill
12
+ * it in) should use `client.records.query(resource, definition)` directly,
13
+ * which has its own backfill behavior.
14
+ */
15
+ export function isCanonicalQueryDefinition(arg: any): arg is QueryDefinition {
16
+ return (
17
+ arg !== null &&
18
+ typeof arg === 'object' &&
19
+ typeof arg.resource === 'string'
20
+ );
21
+ }