@centrali-io/centrali-sdk 4.4.1 → 4.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -5
- package/dist/index.js +185 -3
- package/index.ts +270 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,20 +40,36 @@ console.log('Found:', results.data.totalHits, 'results');
|
|
|
40
40
|
|
|
41
41
|
## Authentication
|
|
42
42
|
|
|
43
|
-
The SDK supports
|
|
43
|
+
The SDK supports three authentication methods. See the [full authentication guide](../docs/sdk/authentication.md) for details.
|
|
44
44
|
|
|
45
|
-
###
|
|
45
|
+
### Publishable Key (Frontend Apps)
|
|
46
|
+
|
|
47
|
+
Safe to use in browser code. Scoped to specific resources.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
const centrali = new CentraliSDK({
|
|
51
|
+
baseUrl: 'https://centrali.io',
|
|
52
|
+
workspaceId: 'your-workspace',
|
|
53
|
+
publishableKey: 'pk_live_your_key_here'
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### External Token / BYOT (Clerk, Auth0, etc.)
|
|
58
|
+
|
|
59
|
+
Dynamic token callback for apps with their own auth.
|
|
46
60
|
|
|
47
61
|
```typescript
|
|
48
62
|
const centrali = new CentraliSDK({
|
|
49
63
|
baseUrl: 'https://centrali.io',
|
|
50
64
|
workspaceId: 'your-workspace',
|
|
51
|
-
|
|
65
|
+
getToken: async () => await clerk.session.getToken()
|
|
52
66
|
});
|
|
53
67
|
```
|
|
54
68
|
|
|
55
69
|
### Service Account (Server-to-Server)
|
|
56
70
|
|
|
71
|
+
Never use in browser code. Client secret must stay on the server.
|
|
72
|
+
|
|
57
73
|
```typescript
|
|
58
74
|
const centrali = new CentraliSDK({
|
|
59
75
|
baseUrl: 'https://centrali.io',
|
|
@@ -61,8 +77,6 @@ const centrali = new CentraliSDK({
|
|
|
61
77
|
clientId: process.env.CENTRALI_CLIENT_ID,
|
|
62
78
|
clientSecret: process.env.CENTRALI_CLIENT_SECRET
|
|
63
79
|
});
|
|
64
|
-
|
|
65
|
-
// The SDK automatically fetches and manages tokens
|
|
66
80
|
```
|
|
67
81
|
|
|
68
82
|
## Features
|
|
@@ -79,6 +93,8 @@ const centrali = new CentraliSDK({
|
|
|
79
93
|
- ✅ **Data Validation** - AI-powered typo detection, format validation, duplicate detection
|
|
80
94
|
- ✅ **Anomaly Insights** - AI-powered anomaly detection and data quality insights
|
|
81
95
|
- ✅ **Orchestrations** - Multi-step workflows with compute functions, decision logic, and delays
|
|
96
|
+
- ✅ **Publishable keys** - Scoped, browser-safe keys for frontend apps
|
|
97
|
+
- ✅ **External auth (BYOT)** - Dynamic token callback for Clerk, Auth0, etc.
|
|
82
98
|
- ✅ **Service accounts** - Automatic token refresh for server-to-server auth
|
|
83
99
|
|
|
84
100
|
## Core Operations
|
package/dist/index.js
CHANGED
|
@@ -51,7 +51,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
51
51
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
52
52
|
};
|
|
53
53
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
-
exports.CentraliSDK = exports.ComputeFunctionsManager = exports.CollectionsManager = exports.StructuresManager = exports.AllowedDomainsManager = exports.ValidationManager = exports.AnomalyInsightsManager = exports.SmartQueriesManager = exports.TriggersManager = exports.OrchestrationsManager = exports.RealtimeManager = exports.CentraliError = void 0;
|
|
54
|
+
exports.CentraliSDK = exports.FunctionRunsManager = exports.ComputeFunctionsManager = exports.CollectionsManager = exports.StructuresManager = exports.AllowedDomainsManager = exports.ValidationManager = exports.AnomalyInsightsManager = exports.SmartQueriesManager = exports.TriggersManager = exports.OrchestrationsManager = exports.RealtimeManager = exports.CentraliError = void 0;
|
|
55
55
|
exports.isCentraliError = isCentraliError;
|
|
56
56
|
exports.getApiUrl = getApiUrl;
|
|
57
57
|
exports.getAuthUrl = getAuthUrl;
|
|
@@ -84,6 +84,9 @@ exports.getCollectionBySlugApiPath = getCollectionBySlugApiPath;
|
|
|
84
84
|
exports.getCollectionValidateApiPath = getCollectionValidateApiPath;
|
|
85
85
|
exports.getComputeFunctionsApiPath = getComputeFunctionsApiPath;
|
|
86
86
|
exports.getComputeFunctionTestApiPath = getComputeFunctionTestApiPath;
|
|
87
|
+
exports.getFunctionRunsApiPath = getFunctionRunsApiPath;
|
|
88
|
+
exports.getFunctionRunsByTriggerApiPath = getFunctionRunsByTriggerApiPath;
|
|
89
|
+
exports.getFunctionRunsByFunctionApiPath = getFunctionRunsByFunctionApiPath;
|
|
87
90
|
exports.getSmartQueryTestApiPath = getSmartQueryTestApiPath;
|
|
88
91
|
exports.getValidationSuggestionsApiPath = getValidationSuggestionsApiPath;
|
|
89
92
|
exports.getValidationSuggestionAcceptApiPath = getValidationSuggestionAcceptApiPath;
|
|
@@ -656,6 +659,28 @@ function getComputeFunctionTestApiPath(workspaceId) {
|
|
|
656
659
|
return `data/workspace/${workspaceId}/api/v1/compute-functions/test`;
|
|
657
660
|
}
|
|
658
661
|
// =====================================================
|
|
662
|
+
// Function Runs API Path Helpers
|
|
663
|
+
// =====================================================
|
|
664
|
+
/**
|
|
665
|
+
* Generate Function Runs base API URL PATH.
|
|
666
|
+
*/
|
|
667
|
+
function getFunctionRunsApiPath(workspaceId, runId) {
|
|
668
|
+
const basePath = `data/workspace/${workspaceId}/api/v1/function-runs`;
|
|
669
|
+
return runId ? `${basePath}/${runId}` : basePath;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Generate Function Runs by trigger API URL PATH.
|
|
673
|
+
*/
|
|
674
|
+
function getFunctionRunsByTriggerApiPath(workspaceId, triggerId) {
|
|
675
|
+
return `data/workspace/${workspaceId}/api/v1/function-runs/trigger/${triggerId}`;
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Generate Function Runs by function API URL PATH.
|
|
679
|
+
*/
|
|
680
|
+
function getFunctionRunsByFunctionApiPath(workspaceId, functionId) {
|
|
681
|
+
return `data/workspace/${workspaceId}/api/v1/function-runs/function/${functionId}`;
|
|
682
|
+
}
|
|
683
|
+
// =====================================================
|
|
659
684
|
// Smart Query Test API Path Helper (Configuration-as-Code)
|
|
660
685
|
// =====================================================
|
|
661
686
|
/**
|
|
@@ -2505,6 +2530,97 @@ class ComputeFunctionsManager {
|
|
|
2505
2530
|
}
|
|
2506
2531
|
}
|
|
2507
2532
|
exports.ComputeFunctionsManager = ComputeFunctionsManager;
|
|
2533
|
+
/**
|
|
2534
|
+
* Manager for querying function execution runs.
|
|
2535
|
+
*
|
|
2536
|
+
* Provides read access to function run history — useful for checking
|
|
2537
|
+
* whether jobs completed, inspecting outputs, and monitoring trigger activity.
|
|
2538
|
+
*
|
|
2539
|
+
* Access via `client.runs`.
|
|
2540
|
+
*/
|
|
2541
|
+
class FunctionRunsManager {
|
|
2542
|
+
constructor(workspaceId, requestFn) {
|
|
2543
|
+
this.workspaceId = workspaceId;
|
|
2544
|
+
this.requestFn = requestFn;
|
|
2545
|
+
}
|
|
2546
|
+
/**
|
|
2547
|
+
* Get a function run by ID.
|
|
2548
|
+
*
|
|
2549
|
+
* @param runId - The function run UUID
|
|
2550
|
+
* @returns The function run details
|
|
2551
|
+
*
|
|
2552
|
+
* @example
|
|
2553
|
+
* ```ts
|
|
2554
|
+
* const run = await client.runs.get('run-uuid');
|
|
2555
|
+
* console.log('Status:', run.data.status);
|
|
2556
|
+
* console.log('Duration:', run.data.endedAt ? Date.parse(run.data.endedAt) - Date.parse(run.data.startedAt) : 'still running');
|
|
2557
|
+
* ```
|
|
2558
|
+
*/
|
|
2559
|
+
get(runId) {
|
|
2560
|
+
const path = getFunctionRunsApiPath(this.workspaceId, runId);
|
|
2561
|
+
return this.requestFn('GET', path);
|
|
2562
|
+
}
|
|
2563
|
+
/**
|
|
2564
|
+
* List runs for a specific trigger.
|
|
2565
|
+
*
|
|
2566
|
+
* @param triggerId - The trigger UUID
|
|
2567
|
+
* @param options - Optional pagination and status filter
|
|
2568
|
+
* @returns Paginated list of function runs
|
|
2569
|
+
*
|
|
2570
|
+
* @example
|
|
2571
|
+
* ```ts
|
|
2572
|
+
* // List recent runs for a trigger
|
|
2573
|
+
* const runs = await client.runs.listByTrigger('trigger-uuid');
|
|
2574
|
+
*
|
|
2575
|
+
* // Filter to only failed runs
|
|
2576
|
+
* const failed = await client.runs.listByTrigger('trigger-uuid', {
|
|
2577
|
+
* status: 'failure'
|
|
2578
|
+
* });
|
|
2579
|
+
* ```
|
|
2580
|
+
*/
|
|
2581
|
+
listByTrigger(triggerId, options) {
|
|
2582
|
+
const path = getFunctionRunsByTriggerApiPath(this.workspaceId, triggerId);
|
|
2583
|
+
const queryParams = {};
|
|
2584
|
+
if (options === null || options === void 0 ? void 0 : options.page)
|
|
2585
|
+
queryParams.page = options.page;
|
|
2586
|
+
if (options === null || options === void 0 ? void 0 : options.limit)
|
|
2587
|
+
queryParams.limit = options.limit;
|
|
2588
|
+
if (options === null || options === void 0 ? void 0 : options.status)
|
|
2589
|
+
queryParams.status = options.status;
|
|
2590
|
+
return this.requestFn('GET', path, null, queryParams);
|
|
2591
|
+
}
|
|
2592
|
+
/**
|
|
2593
|
+
* List runs for a specific compute function.
|
|
2594
|
+
*
|
|
2595
|
+
* @param functionId - The compute function UUID
|
|
2596
|
+
* @param options - Optional pagination and status filter
|
|
2597
|
+
* @returns Paginated list of function runs
|
|
2598
|
+
*
|
|
2599
|
+
* @example
|
|
2600
|
+
* ```ts
|
|
2601
|
+
* // List recent runs for a function
|
|
2602
|
+
* const runs = await client.runs.listByFunction('function-uuid');
|
|
2603
|
+
*
|
|
2604
|
+
* // Only completed runs, page 2
|
|
2605
|
+
* const completed = await client.runs.listByFunction('function-uuid', {
|
|
2606
|
+
* status: 'completed',
|
|
2607
|
+
* page: 2
|
|
2608
|
+
* });
|
|
2609
|
+
* ```
|
|
2610
|
+
*/
|
|
2611
|
+
listByFunction(functionId, options) {
|
|
2612
|
+
const path = getFunctionRunsByFunctionApiPath(this.workspaceId, functionId);
|
|
2613
|
+
const queryParams = {};
|
|
2614
|
+
if (options === null || options === void 0 ? void 0 : options.page)
|
|
2615
|
+
queryParams.page = options.page;
|
|
2616
|
+
if (options === null || options === void 0 ? void 0 : options.limit)
|
|
2617
|
+
queryParams.limit = options.limit;
|
|
2618
|
+
if (options === null || options === void 0 ? void 0 : options.status)
|
|
2619
|
+
queryParams.status = options.status;
|
|
2620
|
+
return this.requestFn('GET', path, null, queryParams);
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
exports.FunctionRunsManager = FunctionRunsManager;
|
|
2508
2624
|
/**
|
|
2509
2625
|
* Main Centrali SDK client.
|
|
2510
2626
|
*/
|
|
@@ -2521,14 +2637,41 @@ class CentraliSDK {
|
|
|
2521
2637
|
this._structures = null;
|
|
2522
2638
|
this._collections = null;
|
|
2523
2639
|
this._functions = null;
|
|
2640
|
+
this._runs = null;
|
|
2524
2641
|
this.isRefreshingToken = false;
|
|
2525
2642
|
this.tokenRefreshPromise = null;
|
|
2526
2643
|
this.options = options;
|
|
2527
2644
|
this.token = options.token || null;
|
|
2645
|
+
// Validate mutually exclusive auth options
|
|
2646
|
+
const authPaths = [
|
|
2647
|
+
options.publishableKey ? 'publishableKey' : null,
|
|
2648
|
+
options.getToken ? 'getToken' : null,
|
|
2649
|
+
(options.clientId || options.clientSecret) ? 'clientId/clientSecret' : null,
|
|
2650
|
+
].filter(Boolean);
|
|
2651
|
+
if (options.publishableKey && authPaths.length > 1) {
|
|
2652
|
+
throw new Error(`Cannot use publishableKey with ${authPaths.filter(p => p !== 'publishableKey').join(', ')}. Use one auth method.`);
|
|
2653
|
+
}
|
|
2654
|
+
if (options.getToken && (options.clientId || options.clientSecret)) {
|
|
2655
|
+
throw new Error('Cannot use getToken with clientId/clientSecret. Use one auth method.');
|
|
2656
|
+
}
|
|
2528
2657
|
const apiUrl = getApiUrl(options.baseUrl);
|
|
2529
2658
|
this.axios = axios_1.default.create(Object.assign({ baseURL: apiUrl, paramsSerializer: (params) => qs_1.default.stringify(params, { arrayFormat: "repeat" }), proxy: false }, options.axiosConfig));
|
|
2530
2659
|
// Attach async interceptor to fetch token on first request if needed
|
|
2531
2660
|
this.axios.interceptors.request.use((config) => __awaiter(this, void 0, void 0, function* () {
|
|
2661
|
+
// Auth path 1: Publishable key — send as x-api-key header, no token logic
|
|
2662
|
+
if (this.options.publishableKey) {
|
|
2663
|
+
config.headers['x-api-key'] = this.options.publishableKey;
|
|
2664
|
+
return config;
|
|
2665
|
+
}
|
|
2666
|
+
// Auth path 2: Dynamic token callback (getToken) — call on each request
|
|
2667
|
+
if (this.options.getToken) {
|
|
2668
|
+
this.token = yield this.options.getToken();
|
|
2669
|
+
if (this.token) {
|
|
2670
|
+
config.headers.Authorization = `Bearer ${this.token}`;
|
|
2671
|
+
}
|
|
2672
|
+
return config;
|
|
2673
|
+
}
|
|
2674
|
+
// Auth path 3: Client credentials — fetch token on first request
|
|
2532
2675
|
if (!this.token && this.options.clientId && this.options.clientSecret) {
|
|
2533
2676
|
this.token = yield fetchClientToken(this.options.clientId, this.options.clientSecret, this.options.baseUrl);
|
|
2534
2677
|
}
|
|
@@ -2541,10 +2684,26 @@ class CentraliSDK {
|
|
|
2541
2684
|
this.axios.interceptors.response.use((response) => response, (error) => __awaiter(this, void 0, void 0, function* () {
|
|
2542
2685
|
var _a, _b;
|
|
2543
2686
|
const originalRequest = error.config;
|
|
2544
|
-
//
|
|
2687
|
+
// Publishable keys: no retry — scope errors are permanent
|
|
2688
|
+
if (this.options.publishableKey) {
|
|
2689
|
+
return Promise.reject(error);
|
|
2690
|
+
}
|
|
2545
2691
|
const isAuthError = ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 401 || ((_b = error.response) === null || _b === void 0 ? void 0 : _b.status) === 403;
|
|
2546
|
-
const hasClientCredentials = this.options.clientId && this.options.clientSecret;
|
|
2547
2692
|
const hasNotRetried = !originalRequest._hasRetried;
|
|
2693
|
+
// getToken path: retry once with a fresh token on 401
|
|
2694
|
+
if (isAuthError && this.options.getToken && hasNotRetried) {
|
|
2695
|
+
originalRequest._hasRetried = true;
|
|
2696
|
+
try {
|
|
2697
|
+
this.token = yield this.options.getToken();
|
|
2698
|
+
originalRequest.headers.Authorization = `Bearer ${this.token}`;
|
|
2699
|
+
return this.axios(originalRequest);
|
|
2700
|
+
}
|
|
2701
|
+
catch (refreshError) {
|
|
2702
|
+
return Promise.reject(error);
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
// Client credentials path: refresh token and retry on 401/403
|
|
2706
|
+
const hasClientCredentials = this.options.clientId && this.options.clientSecret;
|
|
2548
2707
|
if (isAuthError && hasClientCredentials && hasNotRetried) {
|
|
2549
2708
|
// Mark request as retried to prevent infinite loops
|
|
2550
2709
|
originalRequest._hasRetried = true;
|
|
@@ -2871,6 +3030,29 @@ class CentraliSDK {
|
|
|
2871
3030
|
}
|
|
2872
3031
|
return this._functions;
|
|
2873
3032
|
}
|
|
3033
|
+
/**
|
|
3034
|
+
* Runs namespace for querying execution history.
|
|
3035
|
+
*
|
|
3036
|
+
* Usage:
|
|
3037
|
+
* ```ts
|
|
3038
|
+
* // Get a specific run
|
|
3039
|
+
* const run = await client.runs.get('run-id');
|
|
3040
|
+
*
|
|
3041
|
+
* // List runs for a trigger
|
|
3042
|
+
* const runs = await client.runs.listByTrigger('trigger-id');
|
|
3043
|
+
*
|
|
3044
|
+
* // List failed runs for a compute definition
|
|
3045
|
+
* const failed = await client.runs.listByFunction('fn-id', {
|
|
3046
|
+
* status: 'failure'
|
|
3047
|
+
* });
|
|
3048
|
+
* ```
|
|
3049
|
+
*/
|
|
3050
|
+
get runs() {
|
|
3051
|
+
if (!this._runs) {
|
|
3052
|
+
this._runs = new FunctionRunsManager(this.options.workspaceId, this.request.bind(this));
|
|
3053
|
+
}
|
|
3054
|
+
return this._runs;
|
|
3055
|
+
}
|
|
2874
3056
|
/**
|
|
2875
3057
|
* Manually set or update the bearer token for subsequent requests.
|
|
2876
3058
|
*/
|
package/index.ts
CHANGED
|
@@ -542,11 +542,22 @@ export interface CentraliSDKOptions {
|
|
|
542
542
|
/** Base URL of Centrali (e.g. https://centrali.io). The SDK automatically uses api.centrali.io for API calls. */
|
|
543
543
|
baseUrl: string;
|
|
544
544
|
workspaceId: string;
|
|
545
|
+
|
|
546
|
+
// Auth path 1: Publishable key (frontend apps — safe to expose in browser code)
|
|
547
|
+
/** Publishable key for frontend access. Sent as x-api-key header. No token refresh needed. */
|
|
548
|
+
publishableKey?: string;
|
|
549
|
+
|
|
550
|
+
// Auth path 2: Bearer token (existing) + dynamic token callback (new)
|
|
545
551
|
/** Optional initial bearer token for authentication */
|
|
546
552
|
token?: string;
|
|
553
|
+
/** Optional callback to dynamically fetch a fresh token before each request (e.g., for Clerk, Auth0) */
|
|
554
|
+
getToken?: () => Promise<string>;
|
|
555
|
+
|
|
556
|
+
// Auth path 3: Service account (server-side only — never use in browser)
|
|
547
557
|
/** Optional OAuth2 client credentials */
|
|
548
558
|
clientId?: string;
|
|
549
559
|
clientSecret?: string;
|
|
560
|
+
|
|
550
561
|
/** Optional custom axios config */
|
|
551
562
|
axiosConfig?: AxiosRequestConfig;
|
|
552
563
|
}
|
|
@@ -1769,6 +1780,66 @@ export interface TestComputeFunctionResult {
|
|
|
1769
1780
|
error?: string;
|
|
1770
1781
|
}
|
|
1771
1782
|
|
|
1783
|
+
// =====================================================
|
|
1784
|
+
// Function Run Types
|
|
1785
|
+
// =====================================================
|
|
1786
|
+
|
|
1787
|
+
/**
|
|
1788
|
+
* Execution source for a function run.
|
|
1789
|
+
*/
|
|
1790
|
+
export type FunctionRunExecutionSource = 'trigger' | 'rerun' | 'manual' | 'scheduled' | 'http-trigger' | 'orchestration' | 'endpoint';
|
|
1791
|
+
|
|
1792
|
+
/**
|
|
1793
|
+
* Status of a function run.
|
|
1794
|
+
*/
|
|
1795
|
+
export type FunctionRunStatus = 'pending' | 'running' | 'completed' | 'failure' | 'timeout';
|
|
1796
|
+
|
|
1797
|
+
/**
|
|
1798
|
+
* A function run record representing a single execution of a compute function.
|
|
1799
|
+
*/
|
|
1800
|
+
export interface FunctionRun {
|
|
1801
|
+
id: string;
|
|
1802
|
+
createdBy: string;
|
|
1803
|
+
functionId: string;
|
|
1804
|
+
workspaceSlug: string;
|
|
1805
|
+
jobId: string;
|
|
1806
|
+
status: FunctionRunStatus;
|
|
1807
|
+
runData: any | null;
|
|
1808
|
+
startedAt: string;
|
|
1809
|
+
endedAt?: string | null;
|
|
1810
|
+
createdAt?: string;
|
|
1811
|
+
updatedAt?: string;
|
|
1812
|
+
executionId: string;
|
|
1813
|
+
triggerId?: string | null;
|
|
1814
|
+
triggerType?: string | null;
|
|
1815
|
+
orchestrationRunId?: string | null;
|
|
1816
|
+
orchestrationStepId?: string | null;
|
|
1817
|
+
originalRunId?: string | null;
|
|
1818
|
+
isRerun: boolean;
|
|
1819
|
+
rerunCount: number;
|
|
1820
|
+
rerunBy?: string | null;
|
|
1821
|
+
rerunReason?: string | null;
|
|
1822
|
+
functionCodeHash?: string | null;
|
|
1823
|
+
functionVersion?: string | null;
|
|
1824
|
+
executionSource: FunctionRunExecutionSource;
|
|
1825
|
+
memoryUsageBytes?: number | null;
|
|
1826
|
+
cpuUsageSeconds?: number | null;
|
|
1827
|
+
errorCode?: string | null;
|
|
1828
|
+
errorMessage?: string | null;
|
|
1829
|
+
dataStrippedAt?: string | null;
|
|
1830
|
+
archiveStorageAddress?: string | null;
|
|
1831
|
+
archivedAt?: string | null;
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
/**
|
|
1835
|
+
* Options for listing function runs by trigger or function.
|
|
1836
|
+
*/
|
|
1837
|
+
export interface ListFunctionRunsOptions {
|
|
1838
|
+
page?: number;
|
|
1839
|
+
limit?: number;
|
|
1840
|
+
status?: FunctionRunStatus;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1772
1843
|
// =====================================================
|
|
1773
1844
|
// Trigger CRUD Types (Configuration-as-Code)
|
|
1774
1845
|
// =====================================================
|
|
@@ -2723,6 +2794,32 @@ export function getComputeFunctionTestApiPath(workspaceId: string): string {
|
|
|
2723
2794
|
return `data/workspace/${workspaceId}/api/v1/compute-functions/test`;
|
|
2724
2795
|
}
|
|
2725
2796
|
|
|
2797
|
+
// =====================================================
|
|
2798
|
+
// Function Runs API Path Helpers
|
|
2799
|
+
// =====================================================
|
|
2800
|
+
|
|
2801
|
+
/**
|
|
2802
|
+
* Generate Function Runs base API URL PATH.
|
|
2803
|
+
*/
|
|
2804
|
+
export function getFunctionRunsApiPath(workspaceId: string, runId?: string): string {
|
|
2805
|
+
const basePath = `data/workspace/${workspaceId}/api/v1/function-runs`;
|
|
2806
|
+
return runId ? `${basePath}/${runId}` : basePath;
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
/**
|
|
2810
|
+
* Generate Function Runs by trigger API URL PATH.
|
|
2811
|
+
*/
|
|
2812
|
+
export function getFunctionRunsByTriggerApiPath(workspaceId: string, triggerId: string): string {
|
|
2813
|
+
return `data/workspace/${workspaceId}/api/v1/function-runs/trigger/${triggerId}`;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
/**
|
|
2817
|
+
* Generate Function Runs by function API URL PATH.
|
|
2818
|
+
*/
|
|
2819
|
+
export function getFunctionRunsByFunctionApiPath(workspaceId: string, functionId: string): string {
|
|
2820
|
+
return `data/workspace/${workspaceId}/api/v1/function-runs/function/${functionId}`;
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2726
2823
|
// =====================================================
|
|
2727
2824
|
// Smart Query Test API Path Helper (Configuration-as-Code)
|
|
2728
2825
|
// =====================================================
|
|
@@ -4750,6 +4847,100 @@ export class ComputeFunctionsManager {
|
|
|
4750
4847
|
}
|
|
4751
4848
|
}
|
|
4752
4849
|
|
|
4850
|
+
/**
|
|
4851
|
+
* Manager for querying function execution runs.
|
|
4852
|
+
*
|
|
4853
|
+
* Provides read access to function run history — useful for checking
|
|
4854
|
+
* whether jobs completed, inspecting outputs, and monitoring trigger activity.
|
|
4855
|
+
*
|
|
4856
|
+
* Access via `client.runs`.
|
|
4857
|
+
*/
|
|
4858
|
+
export class FunctionRunsManager {
|
|
4859
|
+
private requestFn: <T>(method: Method, path: string, data?: any, queryParams?: Record<string, any>) => Promise<ApiResponse<T>>;
|
|
4860
|
+
private workspaceId: string;
|
|
4861
|
+
|
|
4862
|
+
constructor(
|
|
4863
|
+
workspaceId: string,
|
|
4864
|
+
requestFn: <T>(method: Method, path: string, data?: any, queryParams?: Record<string, any>) => Promise<ApiResponse<T>>
|
|
4865
|
+
) {
|
|
4866
|
+
this.workspaceId = workspaceId;
|
|
4867
|
+
this.requestFn = requestFn;
|
|
4868
|
+
}
|
|
4869
|
+
|
|
4870
|
+
/**
|
|
4871
|
+
* Get a function run by ID.
|
|
4872
|
+
*
|
|
4873
|
+
* @param runId - The function run UUID
|
|
4874
|
+
* @returns The function run details
|
|
4875
|
+
*
|
|
4876
|
+
* @example
|
|
4877
|
+
* ```ts
|
|
4878
|
+
* const run = await client.runs.get('run-uuid');
|
|
4879
|
+
* console.log('Status:', run.data.status);
|
|
4880
|
+
* console.log('Duration:', run.data.endedAt ? Date.parse(run.data.endedAt) - Date.parse(run.data.startedAt) : 'still running');
|
|
4881
|
+
* ```
|
|
4882
|
+
*/
|
|
4883
|
+
public get(runId: string): Promise<ApiResponse<FunctionRun>> {
|
|
4884
|
+
const path = getFunctionRunsApiPath(this.workspaceId, runId);
|
|
4885
|
+
return this.requestFn<FunctionRun>('GET', path);
|
|
4886
|
+
}
|
|
4887
|
+
|
|
4888
|
+
/**
|
|
4889
|
+
* List runs for a specific trigger.
|
|
4890
|
+
*
|
|
4891
|
+
* @param triggerId - The trigger UUID
|
|
4892
|
+
* @param options - Optional pagination and status filter
|
|
4893
|
+
* @returns Paginated list of function runs
|
|
4894
|
+
*
|
|
4895
|
+
* @example
|
|
4896
|
+
* ```ts
|
|
4897
|
+
* // List recent runs for a trigger
|
|
4898
|
+
* const runs = await client.runs.listByTrigger('trigger-uuid');
|
|
4899
|
+
*
|
|
4900
|
+
* // Filter to only failed runs
|
|
4901
|
+
* const failed = await client.runs.listByTrigger('trigger-uuid', {
|
|
4902
|
+
* status: 'failure'
|
|
4903
|
+
* });
|
|
4904
|
+
* ```
|
|
4905
|
+
*/
|
|
4906
|
+
public listByTrigger(triggerId: string, options?: ListFunctionRunsOptions): Promise<ApiResponse<PaginatedResponse<FunctionRun>>> {
|
|
4907
|
+
const path = getFunctionRunsByTriggerApiPath(this.workspaceId, triggerId);
|
|
4908
|
+
const queryParams: Record<string, any> = {};
|
|
4909
|
+
if (options?.page) queryParams.page = options.page;
|
|
4910
|
+
if (options?.limit) queryParams.limit = options.limit;
|
|
4911
|
+
if (options?.status) queryParams.status = options.status;
|
|
4912
|
+
return this.requestFn<PaginatedResponse<FunctionRun>>('GET', path, null, queryParams);
|
|
4913
|
+
}
|
|
4914
|
+
|
|
4915
|
+
/**
|
|
4916
|
+
* List runs for a specific compute function.
|
|
4917
|
+
*
|
|
4918
|
+
* @param functionId - The compute function UUID
|
|
4919
|
+
* @param options - Optional pagination and status filter
|
|
4920
|
+
* @returns Paginated list of function runs
|
|
4921
|
+
*
|
|
4922
|
+
* @example
|
|
4923
|
+
* ```ts
|
|
4924
|
+
* // List recent runs for a function
|
|
4925
|
+
* const runs = await client.runs.listByFunction('function-uuid');
|
|
4926
|
+
*
|
|
4927
|
+
* // Only completed runs, page 2
|
|
4928
|
+
* const completed = await client.runs.listByFunction('function-uuid', {
|
|
4929
|
+
* status: 'completed',
|
|
4930
|
+
* page: 2
|
|
4931
|
+
* });
|
|
4932
|
+
* ```
|
|
4933
|
+
*/
|
|
4934
|
+
public listByFunction(functionId: string, options?: ListFunctionRunsOptions): Promise<ApiResponse<PaginatedResponse<FunctionRun>>> {
|
|
4935
|
+
const path = getFunctionRunsByFunctionApiPath(this.workspaceId, functionId);
|
|
4936
|
+
const queryParams: Record<string, any> = {};
|
|
4937
|
+
if (options?.page) queryParams.page = options.page;
|
|
4938
|
+
if (options?.limit) queryParams.limit = options.limit;
|
|
4939
|
+
if (options?.status) queryParams.status = options.status;
|
|
4940
|
+
return this.requestFn<PaginatedResponse<FunctionRun>>('GET', path, null, queryParams);
|
|
4941
|
+
}
|
|
4942
|
+
}
|
|
4943
|
+
|
|
4753
4944
|
/**
|
|
4754
4945
|
* Main Centrali SDK client.
|
|
4755
4946
|
*/
|
|
@@ -4767,12 +4958,28 @@ export class CentraliSDK {
|
|
|
4767
4958
|
private _structures: StructuresManager | null = null;
|
|
4768
4959
|
private _collections: CollectionsManager | null = null;
|
|
4769
4960
|
private _functions: ComputeFunctionsManager | null = null;
|
|
4961
|
+
private _runs: FunctionRunsManager | null = null;
|
|
4770
4962
|
private isRefreshingToken: boolean = false;
|
|
4771
4963
|
private tokenRefreshPromise: Promise<string> | null = null;
|
|
4772
4964
|
|
|
4773
4965
|
constructor(options: CentraliSDKOptions) {
|
|
4774
4966
|
this.options = options;
|
|
4775
4967
|
this.token = options.token || null;
|
|
4968
|
+
|
|
4969
|
+
// Validate mutually exclusive auth options
|
|
4970
|
+
const authPaths = [
|
|
4971
|
+
options.publishableKey ? 'publishableKey' : null,
|
|
4972
|
+
options.getToken ? 'getToken' : null,
|
|
4973
|
+
(options.clientId || options.clientSecret) ? 'clientId/clientSecret' : null,
|
|
4974
|
+
].filter(Boolean);
|
|
4975
|
+
|
|
4976
|
+
if (options.publishableKey && authPaths.length > 1) {
|
|
4977
|
+
throw new Error(`Cannot use publishableKey with ${authPaths.filter(p => p !== 'publishableKey').join(', ')}. Use one auth method.`);
|
|
4978
|
+
}
|
|
4979
|
+
if (options.getToken && (options.clientId || options.clientSecret)) {
|
|
4980
|
+
throw new Error('Cannot use getToken with clientId/clientSecret. Use one auth method.');
|
|
4981
|
+
}
|
|
4982
|
+
|
|
4776
4983
|
const apiUrl = getApiUrl(options.baseUrl);
|
|
4777
4984
|
this.axios = axios.create({
|
|
4778
4985
|
baseURL: apiUrl,
|
|
@@ -4785,6 +4992,22 @@ export class CentraliSDK {
|
|
|
4785
4992
|
// Attach async interceptor to fetch token on first request if needed
|
|
4786
4993
|
this.axios.interceptors.request.use(
|
|
4787
4994
|
async (config) => {
|
|
4995
|
+
// Auth path 1: Publishable key — send as x-api-key header, no token logic
|
|
4996
|
+
if (this.options.publishableKey) {
|
|
4997
|
+
config.headers['x-api-key'] = this.options.publishableKey;
|
|
4998
|
+
return config;
|
|
4999
|
+
}
|
|
5000
|
+
|
|
5001
|
+
// Auth path 2: Dynamic token callback (getToken) — call on each request
|
|
5002
|
+
if (this.options.getToken) {
|
|
5003
|
+
this.token = await this.options.getToken();
|
|
5004
|
+
if (this.token) {
|
|
5005
|
+
config.headers.Authorization = `Bearer ${this.token}`;
|
|
5006
|
+
}
|
|
5007
|
+
return config;
|
|
5008
|
+
}
|
|
5009
|
+
|
|
5010
|
+
// Auth path 3: Client credentials — fetch token on first request
|
|
4788
5011
|
if (!this.token && this.options.clientId && this.options.clientSecret) {
|
|
4789
5012
|
this.token = await fetchClientToken(
|
|
4790
5013
|
this.options.clientId,
|
|
@@ -4806,11 +5029,29 @@ export class CentraliSDK {
|
|
|
4806
5029
|
async (error) => {
|
|
4807
5030
|
const originalRequest = error.config;
|
|
4808
5031
|
|
|
4809
|
-
//
|
|
5032
|
+
// Publishable keys: no retry — scope errors are permanent
|
|
5033
|
+
if (this.options.publishableKey) {
|
|
5034
|
+
return Promise.reject(error);
|
|
5035
|
+
}
|
|
5036
|
+
|
|
4810
5037
|
const isAuthError = error.response?.status === 401 || error.response?.status === 403;
|
|
4811
|
-
const hasClientCredentials = this.options.clientId && this.options.clientSecret;
|
|
4812
5038
|
const hasNotRetried = !originalRequest._hasRetried;
|
|
4813
5039
|
|
|
5040
|
+
// getToken path: retry once with a fresh token on 401
|
|
5041
|
+
if (isAuthError && this.options.getToken && hasNotRetried) {
|
|
5042
|
+
originalRequest._hasRetried = true;
|
|
5043
|
+
try {
|
|
5044
|
+
this.token = await this.options.getToken();
|
|
5045
|
+
originalRequest.headers.Authorization = `Bearer ${this.token}`;
|
|
5046
|
+
return this.axios(originalRequest);
|
|
5047
|
+
} catch (refreshError) {
|
|
5048
|
+
return Promise.reject(error);
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
|
|
5052
|
+
// Client credentials path: refresh token and retry on 401/403
|
|
5053
|
+
const hasClientCredentials = this.options.clientId && this.options.clientSecret;
|
|
5054
|
+
|
|
4814
5055
|
if (isAuthError && hasClientCredentials && hasNotRetried) {
|
|
4815
5056
|
// Mark request as retried to prevent infinite loops
|
|
4816
5057
|
originalRequest._hasRetried = true;
|
|
@@ -5188,6 +5429,33 @@ export class CentraliSDK {
|
|
|
5188
5429
|
return this._functions;
|
|
5189
5430
|
}
|
|
5190
5431
|
|
|
5432
|
+
/**
|
|
5433
|
+
* Runs namespace for querying execution history.
|
|
5434
|
+
*
|
|
5435
|
+
* Usage:
|
|
5436
|
+
* ```ts
|
|
5437
|
+
* // Get a specific run
|
|
5438
|
+
* const run = await client.runs.get('run-id');
|
|
5439
|
+
*
|
|
5440
|
+
* // List runs for a trigger
|
|
5441
|
+
* const runs = await client.runs.listByTrigger('trigger-id');
|
|
5442
|
+
*
|
|
5443
|
+
* // List failed runs for a compute definition
|
|
5444
|
+
* const failed = await client.runs.listByFunction('fn-id', {
|
|
5445
|
+
* status: 'failure'
|
|
5446
|
+
* });
|
|
5447
|
+
* ```
|
|
5448
|
+
*/
|
|
5449
|
+
public get runs(): FunctionRunsManager {
|
|
5450
|
+
if (!this._runs) {
|
|
5451
|
+
this._runs = new FunctionRunsManager(
|
|
5452
|
+
this.options.workspaceId,
|
|
5453
|
+
this.request.bind(this)
|
|
5454
|
+
);
|
|
5455
|
+
}
|
|
5456
|
+
return this._runs;
|
|
5457
|
+
}
|
|
5458
|
+
|
|
5191
5459
|
/**
|
|
5192
5460
|
* Manually set or update the bearer token for subsequent requests.
|
|
5193
5461
|
*/
|