@tollara/service-sdk 0.0.1

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.
@@ -0,0 +1,372 @@
1
+ /** Canonical Tollara HTTP header names (gateway-signed requests and signed responses). */
2
+ declare const TollaraHeaders: {
3
+ readonly SIGNATURE: "X-Tollara-Signature";
4
+ readonly TIMESTAMP: "X-Tollara-Timestamp";
5
+ readonly USER_ID: "X-Tollara-User-ID";
6
+ readonly PLAN: "X-Tollara-Plan";
7
+ readonly ROLES: "X-Tollara-Roles";
8
+ readonly QUOTA_REMAINING: "X-Tollara-Quota-Remaining";
9
+ readonly SUBSCRIPTION_ACTIVE: "X-Tollara-Subscription-Active";
10
+ readonly BILLING_MODEL: "X-Tollara-Billing-Model";
11
+ readonly MEASUREMENT_TYPE: "X-Tollara-Measurement-Type";
12
+ readonly UNIT_LABEL: "X-Tollara-Unit-Label";
13
+ /** Gateway HMAC user-context schema: `2` = v2 suffix (leading `2`, no quota segment). */
14
+ readonly SIGNING_VERSION: "X-Tollara-Signing-Version";
15
+ };
16
+ type TollaraHeaderName = (typeof TollaraHeaders)[keyof typeof TollaraHeaders];
17
+
18
+ /**
19
+ * HMAC-SHA256 with UTF-8 key/message and Base64 output (per docs/hmac-spec.md).
20
+ */
21
+ declare function calculateHmac(data: string, key: string): string;
22
+ /**
23
+ * Outbound signing: canonical string = bodyString + timestamp.
24
+ */
25
+ declare function calculateHmacWithTimestamp(bodyString: string, timestamp: string | number, key: string): string;
26
+ /**
27
+ * Constant-time string comparison to avoid timing attacks.
28
+ */
29
+ declare function constantTimeEquals(a: string, b: string): boolean;
30
+ /**
31
+ * Validate that signature matches HMAC(payloadString, key).
32
+ */
33
+ declare function validateHmacSignature(signature: string, payloadString: string, key: string): boolean;
34
+
35
+ interface UserContext {
36
+ userId: string | null;
37
+ plan: string | null;
38
+ roles: string[];
39
+ quotaRemaining: number | null;
40
+ subscriptionActive: boolean;
41
+ billingModelType: string | null;
42
+ measurementType: string | null;
43
+ unitLabel: string | null;
44
+ }
45
+ interface VerifySignatureInput {
46
+ signature: string;
47
+ timestamp: string;
48
+ payload: string | object | null;
49
+ userId: string | null;
50
+ plan: string | null;
51
+ roles: string[];
52
+ quotaRemaining: number | string | null;
53
+ /** Must match X-Tollara-Subscription-Active for verification. */
54
+ subscriptionActive: boolean;
55
+ billingModelType?: string | null;
56
+ measurementType?: string | null;
57
+ unitLabel?: string | null;
58
+ /** When `2`, uses HMAC user-context v2 (see TollaraHeaders.SIGNING_VERSION). */
59
+ signingVersion?: string | null;
60
+ }
61
+ /** User fields that participate in inbound HMAC userContextString (docs/hmac-spec.md). */
62
+ interface SignedUserContext {
63
+ userId: string | null;
64
+ plan: string | null;
65
+ roles: string[];
66
+ quotaRemaining: number | string | null;
67
+ subscriptionActive: boolean;
68
+ billingModelType?: string | null;
69
+ measurementType?: string | null;
70
+ unitLabel?: string | null;
71
+ }
72
+ interface InboundHmacRequest {
73
+ signature: string;
74
+ timestamp: string;
75
+ payload: string | object | null;
76
+ signedUserContext: SignedUserContext;
77
+ signingVersion?: string | null;
78
+ }
79
+ /** Loose header map (e.g. Node/Express lowercases keys). */
80
+ type HeaderBag = Record<string, string | string[] | undefined | null>;
81
+ /**
82
+ * Gateway inbound HMAC suffix: userId + plan + rolesCsv + quota + subscriptionActive + billing + measurement + unit.
83
+ */
84
+ declare function buildGatewayUserContextString(userId: string | null, plan: string | null, roles: string[], quotaRemaining: number | string | null, subscriptionActive: boolean, billingModelType: string | null, measurementType: string | null, unitLabel: string | null): string;
85
+ /** Gateway HMAC user-context v2: leading `2`, then userId, plan, roles, subscription, billing fields (no quota). */
86
+ declare function buildGatewayUserContextStringV2(userId: string | null, plan: string | null, roles: string[], subscriptionActive: boolean, billingModelType: string | null, measurementType: string | null, unitLabel: string | null): string;
87
+ /**
88
+ * Verifies HMAC on an inbound gateway request.
89
+ * Canonical string: payload + timestamp + userContextString (see docs/hmac-spec.md).
90
+ */
91
+ declare function verifySignature(serviceSecret: string, input: VerifySignatureInput): boolean;
92
+ /**
93
+ * Verifies inbound HMAC using {@link InboundHmacRequest} (preferred typed entry point).
94
+ */
95
+ declare function verifyInboundHmac(serviceSecret: string, request: InboundHmacRequest): boolean;
96
+ /**
97
+ * Verifies inbound HMAC from a case-insensitive header bag and payload.
98
+ */
99
+ declare function verifySignatureFromHeaders(serviceSecret: string, headers: HeaderBag, payload: string | object | null): boolean;
100
+ /**
101
+ * Verifies inbound HMAC; if valid returns user context, else `null` (do not trust headers).
102
+ * Parses `X-Tollara-*` headers into {@link UserContext} (case-insensitive header names).
103
+ */
104
+ declare function verifySignatureFromHeadersAndGetUserContext(serviceSecret: string, headers: HeaderBag, payload: string | object | null): UserContext | null;
105
+ declare function getUserContext(headers: HeaderBag): UserContext;
106
+
107
+ interface ServiceKeyValidationResult {
108
+ userId: string | null;
109
+ serviceId: string | null;
110
+ /** Core service-key row id when present in the validate JSON body. */
111
+ serviceKeyId: string | null;
112
+ plan: string | null;
113
+ roles: string[];
114
+ quotaRemaining: number | null;
115
+ subscriptionActive: boolean;
116
+ billingModelType: string | null;
117
+ measurementType: string | null;
118
+ unitLabel: string | null;
119
+ }
120
+ interface UsageEstimateResult {
121
+ sufficientCredits: boolean;
122
+ wouldExceedCap: boolean;
123
+ wouldAllow: boolean;
124
+ estimatedCost: number | null;
125
+ remainingCredits: number | null;
126
+ remainingSpendingCap: number | null;
127
+ billingModelType: string | null;
128
+ measurementType: string | null;
129
+ unitLabel: string | null;
130
+ breakdown: Record<string, unknown> | null;
131
+ estimateSchemaVersion: number;
132
+ timestamp: number;
133
+ httpStatus: number;
134
+ }
135
+ /**
136
+ * Validates a service key via the Tollara API and verifies response HMAC.
137
+ * Uses `baseUrl` (default production API origin) + `/api/v1/service-keys/validate`.
138
+ * Optional in-memory cache with 60s TTL via {@link createValidationCache}.
139
+ */
140
+ declare function validateServiceKey(params: {
141
+ /** API origin; defaults to `https://api.tollara.ai`. */
142
+ baseUrl?: string | null;
143
+ serviceKey: string;
144
+ serviceId: string | null;
145
+ serviceSecret: string;
146
+ fetch?: typeof globalThis.fetch;
147
+ }): Promise<ServiceKeyValidationResult | null>;
148
+ /**
149
+ * Usage pre-flight for a service key (Core). Same trust model as {@link validateServiceKey}: JSON body, response HMAC.
150
+ * Verifies signatures on 200 / 403 / 429 when `X-Tollara-Signature` and `X-Tollara-Timestamp` are present.
151
+ */
152
+ declare function estimateUsage(params: {
153
+ baseUrl?: string | null;
154
+ serviceKey: string;
155
+ serviceId: string | null;
156
+ serviceSecret: string;
157
+ estimatedUnits: number;
158
+ fetch?: typeof globalThis.fetch;
159
+ }): Promise<UsageEstimateResult | null>;
160
+ /**
161
+ * Core JWT usage estimate (`POST …/billing/usage/estimate`). Not HMAC-signed (spec §2.2).
162
+ */
163
+ declare function estimateUsageWithJwt(params: {
164
+ baseUrl?: string | null;
165
+ corePathPrefix?: string | null;
166
+ bearerToken: string;
167
+ userId: string;
168
+ serviceId: string;
169
+ estimatedUnits: number;
170
+ fetch?: typeof globalThis.fetch;
171
+ }): Promise<UsageEstimateResult | null>;
172
+ /**
173
+ * Simple cache for validateServiceKey (optional).
174
+ */
175
+ declare function createValidationCache(): {
176
+ get(serviceKey: string): ServiceKeyValidationResult | null;
177
+ set(serviceKey: string, result: ServiceKeyValidationResult): void;
178
+ clear(): void;
179
+ };
180
+
181
+ /**
182
+ * Completion status for usage service async completion (docs/sdk-api-spec.md §3.3).
183
+ */
184
+ declare enum CompletionStatus {
185
+ Completed = "COMPLETED",
186
+ Failed = "FAILED"
187
+ }
188
+
189
+ /** Default Tollara API origin (production). */
190
+ declare const DEFAULT_API_URL = "https://api.tollara.ai";
191
+ /** Path segments joined to `baseUrl` for each service (not part of the public URL API). */
192
+ declare const DEFAULT_CORE_PATH_PREFIX = "/api/v1";
193
+ declare const DEFAULT_GATEWAY_PATH_PREFIX = "/api";
194
+ declare const DEFAULT_USAGE_PATH_PREFIX = "/api/usage";
195
+
196
+ /** Builds `{baseUrl}/api/usage/report` using the default usage path. */
197
+ declare function buildUsageReportUrl(baseUrl: string): string;
198
+ type ReportProgressParams = {
199
+ progressUrl: string;
200
+ requestId: string;
201
+ stage: string;
202
+ percentageComplete: number;
203
+ /** Omit or leave unset when there is no error (avoids passing null). */
204
+ errorMessage?: string | null;
205
+ serviceSecret: string;
206
+ fetch?: typeof globalThis.fetch;
207
+ };
208
+ /**
209
+ * POST to progressUrl with signed body (optional errorMessage).
210
+ */
211
+ declare function reportProgress(params: ReportProgressParams): Promise<boolean>;
212
+ type ReportCompletionParams = {
213
+ callbackUrl: string;
214
+ requestId: string;
215
+ status: CompletionStatus;
216
+ result?: string | null;
217
+ resultUrl?: string | null;
218
+ contentType?: string | null;
219
+ units?: number | null;
220
+ serviceSecret: string;
221
+ fetch?: typeof globalThis.fetch;
222
+ };
223
+ /**
224
+ * POST completion with status and optional units (defaults to 0).
225
+ */
226
+ declare function reportCompletion(params: Pick<ReportCompletionParams, 'callbackUrl' | 'requestId' | 'status' | 'serviceSecret' | 'fetch'> & {
227
+ units?: number | null;
228
+ }): Promise<boolean>;
229
+ /**
230
+ * POST completion with inline result text.
231
+ */
232
+ declare function reportCompletionWithResult(params: Pick<ReportCompletionParams, 'callbackUrl' | 'requestId' | 'status' | 'result' | 'units' | 'serviceSecret' | 'fetch'>): Promise<boolean>;
233
+ /**
234
+ * POST to callbackUrl with signed body (all optional fields).
235
+ */
236
+ declare function reportCompletionFull(params: ReportCompletionParams): Promise<boolean>;
237
+ interface UsageReportResponse {
238
+ status?: string;
239
+ warning?: string;
240
+ isOverLimit?: boolean;
241
+ remainingRequestsPerPeriod?: number;
242
+ remainingTimeUnitsPerPeriod?: number;
243
+ remainingSpendingCap?: number;
244
+ overageRate?: number;
245
+ }
246
+ /**
247
+ * POST usage report with signed body (`{baseUrl}/api/usage/report`).
248
+ */
249
+ declare function reportUsage(params: {
250
+ /** API origin; defaults to `https://api.tollara.ai`. */
251
+ baseUrl?: string | null;
252
+ userId: string;
253
+ serviceId: string;
254
+ unitsUsed: number;
255
+ timestamp?: number | Date | null;
256
+ serviceSecret: string;
257
+ fetch?: typeof globalThis.fetch;
258
+ }): Promise<UsageReportResponse>;
259
+
260
+ /**
261
+ * Caller-side gateway polling for async jobs.
262
+ */
263
+ interface GatewayPollResult {
264
+ ok: boolean;
265
+ status: number;
266
+ body: string;
267
+ }
268
+ /**
269
+ * GET .../requests/{requestId}/status with Bearer service key.
270
+ */
271
+ declare function getRequestStatus(params: {
272
+ /** API origin; defaults to `https://api.tollara.ai`. */
273
+ baseUrl?: string | null;
274
+ requestId: string;
275
+ serviceKey: string;
276
+ fetch?: typeof globalThis.fetch;
277
+ }): Promise<GatewayPollResult>;
278
+ /**
279
+ * GET .../requests/{requestId}/result with Bearer service key.
280
+ */
281
+ declare function getRequestResult(params: {
282
+ /** API origin; defaults to `https://api.tollara.ai`. */
283
+ baseUrl?: string | null;
284
+ requestId: string;
285
+ serviceKey: string;
286
+ fetch?: typeof globalThis.fetch;
287
+ }): Promise<GatewayPollResult>;
288
+
289
+ type GatewayHttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
290
+ type GatewayInvokeAsyncEnvelope = {
291
+ requestId: string;
292
+ callbackUrl: string;
293
+ progressUrl: string;
294
+ };
295
+ type GatewayInvokeResult = {
296
+ statusCode: number;
297
+ body: string;
298
+ asyncEnvelope?: GatewayInvokeAsyncEnvelope;
299
+ };
300
+ /**
301
+ * Gateway service invoke (sync or async). See `docs-sdk/MAIN-SDK-API-SPEC.md` §1.1–1.2.
302
+ */
303
+ declare function invokeService(params: {
304
+ baseUrl?: string | null;
305
+ gatewayPathPrefix?: string | null;
306
+ method: GatewayHttpMethod;
307
+ serviceId: string;
308
+ endpointId: string;
309
+ serviceKey: string;
310
+ body?: string | null;
311
+ async?: boolean;
312
+ fetch?: typeof globalThis.fetch;
313
+ }): Promise<GatewayInvokeResult | null>;
314
+
315
+ declare const ENV_API_URL = "TOLLARA_API_URL";
316
+ declare const ENV_SERVICE_ID = "TOLLARA_SERVICE_ID";
317
+ declare const ENV_SERVICE_SECRET = "TOLLARA_SERVICE_SECRET";
318
+
319
+ type TollaraClientOptions = {
320
+ /**
321
+ * API origin (scheme + host). Defaults to `https://api.tollara.ai` or `TOLLARA_API_URL`.
322
+ * All service calls use this origin with fixed paths (validate, usage, gateway polling).
323
+ */
324
+ apiUrl?: string | null;
325
+ /** Service UUID; falls back to `TOLLARA_SERVICE_ID`. */
326
+ serviceId?: string | null;
327
+ /** Shared secret; falls back to `TOLLARA_SERVICE_SECRET`. */
328
+ serviceSecret?: string | null;
329
+ fetch?: typeof globalThis.fetch;
330
+ };
331
+ /**
332
+ * Unified client: validate service key, report usage/progress/complete, poll gateway for async jobs.
333
+ * Omitted options fall back to `TOLLARA_*` environment variables (when `process.env` exists).
334
+ */
335
+ declare class TollaraClient {
336
+ static readonly ENV_API_URL = "TOLLARA_API_URL";
337
+ static readonly ENV_SERVICE_ID = "TOLLARA_SERVICE_ID";
338
+ static readonly ENV_SERVICE_SECRET = "TOLLARA_SERVICE_SECRET";
339
+ static readonly DEFAULT_API_URL = "https://api.tollara.ai";
340
+ static readonly DEFAULT_CORE_PATH_PREFIX = "/api/v1";
341
+ static readonly DEFAULT_GATEWAY_PATH_PREFIX = "/api";
342
+ static readonly DEFAULT_USAGE_PATH_PREFIX = "/api/usage";
343
+ private readonly apiOrigin;
344
+ private readonly serviceId;
345
+ private readonly serviceSecret;
346
+ private readonly fetchFn;
347
+ constructor(options?: TollaraClientOptions);
348
+ validateServiceKey(serviceKey: string): Promise<ServiceKeyValidationResult | null>;
349
+ estimateUsage(serviceKey: string, estimatedUnits: number): Promise<UsageEstimateResult | null>;
350
+ /**
351
+ * Core JWT usage estimate (`POST …/billing/usage/estimate`). Response is not HMAC-signed.
352
+ */
353
+ estimateUsageWithJwt(bearerToken: string, userId: string, serviceId: string, estimatedUnits: number): Promise<UsageEstimateResult | null>;
354
+ /**
355
+ * Gateway service invoke (sync or async). See platform spec §1.1–1.2.
356
+ */
357
+ invokeService(method: GatewayHttpMethod, serviceId: string, endpointId: string, serviceKey: string, options?: {
358
+ body?: string | null;
359
+ async?: boolean;
360
+ }): Promise<GatewayInvokeResult | null>;
361
+ reportUsage(userId: string, serviceId: string, unitsUsed: number): Promise<UsageReportResponse>;
362
+ sendProgressUpdate(progressUrl: string, requestId: string, stage: string, percentageComplete: number, errorMessage?: string | null): Promise<boolean>;
363
+ sendCompletion(callbackUrl: string, requestId: string, status: CompletionStatus, units: number, options?: {
364
+ result?: string | null;
365
+ resultUrl?: string | null;
366
+ contentType?: string | null;
367
+ }): Promise<boolean>;
368
+ getRequestStatus(requestId: string, serviceKey: string): Promise<GatewayPollResult>;
369
+ getRequestResult(requestId: string, serviceKey: string): Promise<GatewayPollResult>;
370
+ }
371
+
372
+ export { CompletionStatus, DEFAULT_API_URL, DEFAULT_CORE_PATH_PREFIX, DEFAULT_GATEWAY_PATH_PREFIX, DEFAULT_USAGE_PATH_PREFIX, ENV_API_URL, ENV_SERVICE_ID, ENV_SERVICE_SECRET, type GatewayHttpMethod, type GatewayInvokeAsyncEnvelope, type GatewayInvokeResult, type GatewayPollResult, type HeaderBag, type InboundHmacRequest, type ReportCompletionParams, type ReportProgressParams, type ServiceKeyValidationResult, type SignedUserContext, TollaraClient, type TollaraClientOptions, type TollaraHeaderName, TollaraHeaders, type UsageEstimateResult, type UsageReportResponse, type UserContext, type VerifySignatureInput, buildGatewayUserContextString, buildGatewayUserContextStringV2, buildUsageReportUrl, calculateHmac, calculateHmacWithTimestamp, constantTimeEquals, createValidationCache, estimateUsage, estimateUsageWithJwt, getRequestResult, getRequestStatus, getUserContext, invokeService, reportCompletion, reportCompletionFull, reportCompletionWithResult, reportProgress, reportUsage, validateHmacSignature, validateServiceKey, verifyInboundHmac, verifySignature, verifySignatureFromHeaders, verifySignatureFromHeadersAndGetUserContext };