@tinybirdco/sdk 0.0.37 → 0.0.38

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 (48) hide show
  1. package/README.md +50 -1
  2. package/dist/api/api.d.ts +49 -0
  3. package/dist/api/api.d.ts.map +1 -1
  4. package/dist/api/api.js +23 -0
  5. package/dist/api/api.js.map +1 -1
  6. package/dist/api/api.test.js +32 -0
  7. package/dist/api/api.test.js.map +1 -1
  8. package/dist/api/tokens.d.ts +79 -0
  9. package/dist/api/tokens.d.ts.map +1 -0
  10. package/dist/api/tokens.js +80 -0
  11. package/dist/api/tokens.js.map +1 -0
  12. package/dist/api/tokens.test.d.ts +2 -0
  13. package/dist/api/tokens.test.d.ts.map +1 -0
  14. package/dist/api/tokens.test.js +209 -0
  15. package/dist/api/tokens.test.js.map +1 -0
  16. package/dist/client/base.d.ts +3 -0
  17. package/dist/client/base.d.ts.map +1 -1
  18. package/dist/client/base.js +5 -0
  19. package/dist/client/base.js.map +1 -1
  20. package/dist/client/tokens.d.ts +42 -0
  21. package/dist/client/tokens.d.ts.map +1 -0
  22. package/dist/client/tokens.js +67 -0
  23. package/dist/client/tokens.js.map +1 -0
  24. package/dist/client/tokens.test.d.ts +2 -0
  25. package/dist/client/tokens.test.d.ts.map +1 -0
  26. package/dist/client/tokens.test.js +79 -0
  27. package/dist/client/tokens.test.js.map +1 -0
  28. package/dist/index.d.ts +3 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +2 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/schema/project.d.ts +21 -12
  33. package/dist/schema/project.d.ts.map +1 -1
  34. package/dist/schema/project.js +55 -13
  35. package/dist/schema/project.js.map +1 -1
  36. package/dist/schema/project.test.js +11 -10
  37. package/dist/schema/project.test.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/api/api.test.ts +43 -0
  40. package/src/api/api.ts +79 -0
  41. package/src/api/tokens.test.ts +253 -0
  42. package/src/api/tokens.ts +169 -0
  43. package/src/client/base.ts +12 -0
  44. package/src/client/tokens.test.ts +103 -0
  45. package/src/client/tokens.ts +69 -0
  46. package/src/index.ts +15 -0
  47. package/src/schema/project.test.ts +11 -10
  48. package/src/schema/project.ts +95 -27
@@ -0,0 +1,253 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import {
3
+ createJWT,
4
+ TokenApiError,
5
+ type TokenApiConfig,
6
+ } from "./tokens.js";
7
+
8
+ // Mock fetch globally
9
+ const mockFetch = vi.fn();
10
+ global.fetch = mockFetch;
11
+
12
+ function expectFromParam(url: string) {
13
+ const parsed = new URL(url);
14
+ expect(parsed.searchParams.get("from")).toBe("ts-sdk");
15
+ return parsed;
16
+ }
17
+
18
+ describe("Token API client", () => {
19
+ const config: TokenApiConfig = {
20
+ baseUrl: "https://api.tinybird.co",
21
+ token: "p.admin-token",
22
+ };
23
+
24
+ beforeEach(() => {
25
+ mockFetch.mockReset();
26
+ });
27
+
28
+ describe("createJWT", () => {
29
+ it("creates a JWT token with scopes", async () => {
30
+ const mockResponse = { token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test" };
31
+
32
+ mockFetch.mockResolvedValueOnce({
33
+ ok: true,
34
+ json: () => Promise.resolve(mockResponse),
35
+ });
36
+
37
+ const result = await createJWT(config, {
38
+ name: "user_token",
39
+ expiresAt: 1700000000,
40
+ scopes: [
41
+ {
42
+ type: "PIPES:READ",
43
+ resource: "analytics_pipe",
44
+ fixed_params: { user_id: 123 },
45
+ },
46
+ ],
47
+ });
48
+
49
+ expect(result.token).toBe(mockResponse.token);
50
+
51
+ const [url, init] = mockFetch.mock.calls[0];
52
+ const parsed = expectFromParam(url);
53
+ expect(parsed.pathname).toBe("/v0/tokens/");
54
+ expect(parsed.searchParams.get("expiration_time")).toBe("1700000000");
55
+ expect(init.method).toBe("POST");
56
+ const headers = new Headers(init.headers);
57
+ expect(headers.get("Authorization")).toBe("Bearer p.admin-token");
58
+ expect(headers.get("Content-Type")).toBe("application/json");
59
+
60
+ const body = JSON.parse(init.body);
61
+ expect(body.name).toBe("user_token");
62
+ expect(body.scopes).toHaveLength(1);
63
+ expect(body.scopes[0].type).toBe("PIPES:READ");
64
+ expect(body.scopes[0].resource).toBe("analytics_pipe");
65
+ expect(body.scopes[0].fixed_params).toEqual({ user_id: 123 });
66
+ });
67
+
68
+ it("converts Date to Unix timestamp", async () => {
69
+ mockFetch.mockResolvedValueOnce({
70
+ ok: true,
71
+ json: () => Promise.resolve({ token: "jwt-token" }),
72
+ });
73
+
74
+ const expirationDate = new Date("2024-01-01T00:00:00Z");
75
+ await createJWT(config, {
76
+ name: "test",
77
+ expiresAt: expirationDate,
78
+ scopes: [{ type: "PIPES:READ", resource: "pipe" }],
79
+ });
80
+
81
+ const [url] = mockFetch.mock.calls[0];
82
+ const parsed = new URL(url);
83
+ expect(parsed.searchParams.get("expiration_time")).toBe("1704067200");
84
+ });
85
+
86
+ it("converts ISO string to Unix timestamp", async () => {
87
+ mockFetch.mockResolvedValueOnce({
88
+ ok: true,
89
+ json: () => Promise.resolve({ token: "jwt-token" }),
90
+ });
91
+
92
+ await createJWT(config, {
93
+ name: "test",
94
+ expiresAt: "2024-01-01T00:00:00Z",
95
+ scopes: [{ type: "PIPES:READ", resource: "pipe" }],
96
+ });
97
+
98
+ const [url] = mockFetch.mock.calls[0];
99
+ const parsed = new URL(url);
100
+ expect(parsed.searchParams.get("expiration_time")).toBe("1704067200");
101
+ });
102
+
103
+ it("includes limits when provided", async () => {
104
+ mockFetch.mockResolvedValueOnce({
105
+ ok: true,
106
+ json: () => Promise.resolve({ token: "jwt-token" }),
107
+ });
108
+
109
+ await createJWT(config, {
110
+ name: "rate_limited_token",
111
+ expiresAt: 1700000000,
112
+ scopes: [{ type: "PIPES:READ", resource: "pipe" }],
113
+ limits: { rps: 100 },
114
+ });
115
+
116
+ const [, init] = mockFetch.mock.calls[0];
117
+ const body = JSON.parse(init.body);
118
+ expect(body.limits).toEqual({ rps: 100 });
119
+ });
120
+
121
+ it("uses custom fetch implementation when provided", async () => {
122
+ const customFetch = vi.fn().mockResolvedValueOnce({
123
+ ok: true,
124
+ json: () => Promise.resolve({ token: "jwt-token" }),
125
+ });
126
+
127
+ await createJWT(
128
+ {
129
+ ...config,
130
+ fetch: customFetch as typeof fetch,
131
+ },
132
+ {
133
+ name: "custom_fetch_token",
134
+ expiresAt: 1700000000,
135
+ scopes: [{ type: "PIPES:READ", resource: "pipe" }],
136
+ }
137
+ );
138
+
139
+ expect(customFetch).toHaveBeenCalledTimes(1);
140
+ expect(mockFetch).not.toHaveBeenCalled();
141
+ });
142
+
143
+ it("supports datasource scope with filter", async () => {
144
+ mockFetch.mockResolvedValueOnce({
145
+ ok: true,
146
+ json: () => Promise.resolve({ token: "jwt-token" }),
147
+ });
148
+
149
+ await createJWT(config, {
150
+ name: "filtered_token",
151
+ expiresAt: 1700000000,
152
+ scopes: [
153
+ {
154
+ type: "DATASOURCES:READ",
155
+ resource: "events",
156
+ filter: "org_id = 'acme'",
157
+ },
158
+ ],
159
+ });
160
+
161
+ const [, init] = mockFetch.mock.calls[0];
162
+ const body = JSON.parse(init.body);
163
+ expect(body.scopes[0].filter).toBe("org_id = 'acme'");
164
+ });
165
+
166
+ it("throws TokenApiError on 403 with helpful message", async () => {
167
+ mockFetch.mockResolvedValueOnce({
168
+ ok: false,
169
+ status: 403,
170
+ statusText: "Forbidden",
171
+ text: () => Promise.resolve("Insufficient permissions"),
172
+ });
173
+
174
+ await expect(
175
+ createJWT(config, {
176
+ name: "test",
177
+ expiresAt: 1700000000,
178
+ scopes: [],
179
+ })
180
+ ).rejects.toThrow(TokenApiError);
181
+
182
+ mockFetch.mockResolvedValueOnce({
183
+ ok: false,
184
+ status: 403,
185
+ statusText: "Forbidden",
186
+ text: () => Promise.resolve("Insufficient permissions"),
187
+ });
188
+
189
+ try {
190
+ await createJWT(config, {
191
+ name: "test",
192
+ expiresAt: 1700000000,
193
+ scopes: [],
194
+ });
195
+ } catch (error) {
196
+ expect((error as TokenApiError).status).toBe(403);
197
+ expect((error as TokenApiError).message).toContain("TOKENS or ADMIN scope");
198
+ }
199
+ });
200
+
201
+ it("throws TokenApiError on 400 with validation error", async () => {
202
+ mockFetch.mockResolvedValueOnce({
203
+ ok: false,
204
+ status: 400,
205
+ statusText: "Bad Request",
206
+ text: () => Promise.resolve('{"error": "Invalid scope type"}'),
207
+ });
208
+
209
+ await expect(
210
+ createJWT(config, {
211
+ name: "test",
212
+ expiresAt: 1700000000,
213
+ scopes: [],
214
+ })
215
+ ).rejects.toThrow(TokenApiError);
216
+
217
+ mockFetch.mockResolvedValueOnce({
218
+ ok: false,
219
+ status: 400,
220
+ statusText: "Bad Request",
221
+ text: () => Promise.resolve('{"error": "Invalid scope type"}'),
222
+ });
223
+
224
+ try {
225
+ await createJWT(config, {
226
+ name: "test",
227
+ expiresAt: 1700000000,
228
+ scopes: [],
229
+ });
230
+ } catch (error) {
231
+ expect((error as TokenApiError).status).toBe(400);
232
+ expect((error as TokenApiError).message).toContain("Invalid scope type");
233
+ }
234
+ });
235
+
236
+ it("does not include limits if not provided", async () => {
237
+ mockFetch.mockResolvedValueOnce({
238
+ ok: true,
239
+ json: () => Promise.resolve({ token: "jwt-token" }),
240
+ });
241
+
242
+ await createJWT(config, {
243
+ name: "simple_token",
244
+ expiresAt: 1700000000,
245
+ scopes: [{ type: "PIPES:READ", resource: "pipe" }],
246
+ });
247
+
248
+ const [, init] = mockFetch.mock.calls[0];
249
+ const body = JSON.parse(init.body);
250
+ expect(body.limits).toBeUndefined();
251
+ });
252
+ });
253
+ });
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Tinybird Token API client
3
+ */
4
+
5
+ import {
6
+ createTinybirdApi,
7
+ TinybirdApiError,
8
+ } from "./api.js";
9
+
10
+ /**
11
+ * API configuration for token operations.
12
+ * Requires a token with TOKENS or ADMIN scope.
13
+ */
14
+ export interface TokenApiConfig {
15
+ /** Tinybird API base URL */
16
+ baseUrl: string;
17
+ /** Workspace token with TOKENS or ADMIN scope */
18
+ token: string;
19
+ /** Custom fetch implementation (optional, defaults to global fetch) */
20
+ fetch?: typeof fetch;
21
+ /** Default timeout in milliseconds (optional) */
22
+ timeout?: number;
23
+ }
24
+
25
+ /**
26
+ * Error thrown by token API operations
27
+ */
28
+ export class TokenApiError extends Error {
29
+ constructor(
30
+ message: string,
31
+ public readonly status: number,
32
+ public readonly body?: unknown
33
+ ) {
34
+ super(message);
35
+ this.name = "TokenApiError";
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Scope type for JWT tokens
41
+ */
42
+ export type JWTScopeType =
43
+ | "PIPES:READ"
44
+ | "DATASOURCES:READ"
45
+ | "DATASOURCES:APPEND";
46
+
47
+ /**
48
+ * A scope definition for JWT tokens
49
+ */
50
+ export interface JWTScope {
51
+ /** The type of access being granted */
52
+ type: JWTScopeType;
53
+ /** The resource name (pipe or datasource) */
54
+ resource: string;
55
+ /** Fixed parameters embedded in the JWT (for pipes) */
56
+ fixed_params?: Record<string, string | number | boolean>;
57
+ /** SQL filter expression (for datasources) */
58
+ filter?: string;
59
+ }
60
+
61
+ /**
62
+ * Rate limiting configuration for JWT tokens
63
+ */
64
+ export interface JWTLimits {
65
+ /** Requests per second limit */
66
+ rps?: number;
67
+ }
68
+
69
+ /**
70
+ * Options for creating a JWT token
71
+ */
72
+ export interface CreateJWTOptions {
73
+ /** Token name/identifier */
74
+ name: string;
75
+ /** Expiration time as Date, Unix timestamp (number), or ISO string */
76
+ expiresAt: Date | number | string;
77
+ /** Array of scopes defining access permissions */
78
+ scopes: JWTScope[];
79
+ /** Optional rate limiting configuration */
80
+ limits?: JWTLimits;
81
+ }
82
+
83
+ /**
84
+ * Result of creating a JWT token
85
+ */
86
+ export interface CreateJWTResult {
87
+ /** The generated JWT token string */
88
+ token: string;
89
+ }
90
+
91
+ /**
92
+ * Request body for creating a JWT token
93
+ */
94
+ interface CreateJWTRequestBody {
95
+ name: string;
96
+ scopes: JWTScope[];
97
+ limits?: JWTLimits;
98
+ }
99
+
100
+ /**
101
+ * Convert expiration input to Unix timestamp
102
+ */
103
+ function toUnixTimestamp(expiresAt: Date | number | string): number {
104
+ if (typeof expiresAt === "number") {
105
+ return expiresAt;
106
+ }
107
+ if (expiresAt instanceof Date) {
108
+ return Math.floor(expiresAt.getTime() / 1000);
109
+ }
110
+ return Math.floor(new Date(expiresAt).getTime() / 1000);
111
+ }
112
+
113
+ /**
114
+ * Create a JWT token
115
+ * POST /v0/tokens/?expiration_time={unix_timestamp}
116
+ *
117
+ * @param config - API configuration (requires TOKENS or ADMIN scope)
118
+ * @param options - JWT creation options
119
+ * @returns The created JWT token
120
+ */
121
+ export async function createJWT(
122
+ config: TokenApiConfig,
123
+ options: CreateJWTOptions
124
+ ): Promise<CreateJWTResult> {
125
+ const expirationTime = toUnixTimestamp(options.expiresAt);
126
+
127
+ const body: CreateJWTRequestBody = {
128
+ name: options.name,
129
+ scopes: options.scopes,
130
+ };
131
+
132
+ if (options.limits) {
133
+ body.limits = options.limits;
134
+ }
135
+
136
+ const api = createTinybirdApi({
137
+ baseUrl: config.baseUrl,
138
+ token: config.token,
139
+ fetch: config.fetch,
140
+ timeout: config.timeout,
141
+ });
142
+
143
+ try {
144
+ const result = await api.createToken(body, {
145
+ expirationTime,
146
+ });
147
+ return { token: result.token };
148
+ } catch (error) {
149
+ if (!(error instanceof TinybirdApiError)) {
150
+ throw error;
151
+ }
152
+
153
+ const responseBody = error.responseBody ?? error.message;
154
+ let message: string;
155
+
156
+ if (error.statusCode === 403) {
157
+ message =
158
+ `Permission denied creating JWT token. ` +
159
+ `Make sure the token has TOKENS or ADMIN scope. ` +
160
+ `API response: ${responseBody}`;
161
+ } else if (error.statusCode === 400) {
162
+ message = `Invalid JWT token request: ${responseBody}`;
163
+ } else {
164
+ message = `Failed to create JWT token: ${error.statusCode}. API response: ${responseBody}`;
165
+ }
166
+
167
+ throw new TokenApiError(message, error.statusCode, responseBody);
168
+ }
169
+ }
@@ -15,6 +15,7 @@ import type {
15
15
  } from "./types.js";
16
16
  import { TinybirdError } from "./types.js";
17
17
  import { TinybirdApi, TinybirdApiError } from "../api/api.js";
18
+ import { TokensNamespace } from "./tokens.js";
18
19
 
19
20
  /**
20
21
  * Resolved token info from dev mode
@@ -65,6 +66,9 @@ export class TinybirdClient {
65
66
  */
66
67
  readonly datasources: DatasourcesNamespace;
67
68
 
69
+ /** Token operations (JWT creation, etc.) */
70
+ public readonly tokens: TokensNamespace;
71
+
68
72
  constructor(config: ClientConfig) {
69
73
  // Validate required config
70
74
  if (!config.baseUrl) {
@@ -86,6 +90,14 @@ export class TinybirdClient {
86
90
  return this.appendDatasource(datasourceName, options);
87
91
  },
88
92
  };
93
+
94
+ // Initialize tokens namespace
95
+ this.tokens = new TokensNamespace(
96
+ () => this.getToken(),
97
+ this.config.baseUrl,
98
+ this.config.fetch,
99
+ this.config.timeout
100
+ );
89
101
  }
90
102
 
91
103
  /**
@@ -0,0 +1,103 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { TinybirdClient } from "./base.js";
3
+
4
+ const mockFetch = vi.fn();
5
+ global.fetch = mockFetch;
6
+
7
+ describe("TokensNamespace", () => {
8
+ beforeEach(() => {
9
+ mockFetch.mockReset();
10
+ });
11
+
12
+ it("uses client custom fetch for createJWT", async () => {
13
+ const customFetch = vi.fn().mockResolvedValueOnce({
14
+ ok: true,
15
+ json: () => Promise.resolve({ token: "jwt-token" }),
16
+ });
17
+
18
+ const client = new TinybirdClient({
19
+ baseUrl: "https://api.tinybird.co",
20
+ token: "p.admin-token",
21
+ fetch: customFetch as typeof fetch,
22
+ });
23
+
24
+ const result = await client.tokens.createJWT({
25
+ name: "user_token",
26
+ expiresAt: 1700000000,
27
+ scopes: [{ type: "PIPES:READ", resource: "analytics_pipe" }],
28
+ });
29
+
30
+ expect(result).toEqual({ token: "jwt-token" });
31
+ expect(customFetch).toHaveBeenCalledTimes(1);
32
+ expect(mockFetch).not.toHaveBeenCalled();
33
+
34
+ const [url, init] = customFetch.mock.calls[0] as [string, RequestInit];
35
+ const parsed = new URL(url);
36
+ expect(parsed.pathname).toBe("/v0/tokens/");
37
+ expect(parsed.searchParams.get("expiration_time")).toBe("1700000000");
38
+ expect(parsed.searchParams.get("from")).toBe("ts-sdk");
39
+ expect(init.method).toBe("POST");
40
+ const headers = new Headers(init.headers);
41
+ expect(headers.get("Authorization")).toBe("Bearer p.admin-token");
42
+ expect(headers.get("Content-Type")).toBe("application/json");
43
+
44
+ const body = JSON.parse(String(init.body)) as {
45
+ name: string;
46
+ scopes: Array<{ type: string; resource: string }>;
47
+ limits?: { rps?: number };
48
+ };
49
+ expect(body).toEqual({
50
+ name: "user_token",
51
+ scopes: [{ type: "PIPES:READ", resource: "analytics_pipe" }],
52
+ });
53
+ });
54
+
55
+ it("sends full JWT payload when limits and scope fields are provided", async () => {
56
+ const customFetch = vi.fn().mockResolvedValueOnce({
57
+ ok: true,
58
+ json: () => Promise.resolve({ token: "jwt-token" }),
59
+ });
60
+
61
+ const client = new TinybirdClient({
62
+ baseUrl: "https://api.tinybird.co",
63
+ token: "p.admin-token",
64
+ fetch: customFetch as typeof fetch,
65
+ });
66
+
67
+ await client.tokens.createJWT({
68
+ name: "tenant_token",
69
+ expiresAt: 1700000000,
70
+ scopes: [
71
+ {
72
+ type: "PIPES:READ",
73
+ resource: "analytics_pipe",
74
+ fixed_params: { tenant_id: 123 },
75
+ },
76
+ ],
77
+ limits: { rps: 10 },
78
+ });
79
+
80
+ const [, init] = customFetch.mock.calls[0] as [string, RequestInit];
81
+ const body = JSON.parse(String(init.body)) as {
82
+ name: string;
83
+ scopes: Array<{
84
+ type: string;
85
+ resource: string;
86
+ fixed_params?: Record<string, unknown>;
87
+ }>;
88
+ limits?: { rps?: number };
89
+ };
90
+
91
+ expect(body).toEqual({
92
+ name: "tenant_token",
93
+ scopes: [
94
+ {
95
+ type: "PIPES:READ",
96
+ resource: "analytics_pipe",
97
+ fixed_params: { tenant_id: 123 },
98
+ },
99
+ ],
100
+ limits: { rps: 10 },
101
+ });
102
+ });
103
+ });
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Token operations namespace for TinybirdClient
3
+ */
4
+
5
+ import type { CreateJWTOptions, CreateJWTResult } from "../api/tokens.js";
6
+ import { createJWT as apiCreateJWT, TokenApiError } from "../api/tokens.js";
7
+ import { TinybirdError } from "./types.js";
8
+
9
+ /**
10
+ * Token operations namespace for TinybirdClient
11
+ */
12
+ export class TokensNamespace {
13
+ constructor(
14
+ private readonly getToken: () => Promise<string>,
15
+ private readonly baseUrl: string,
16
+ private readonly fetchFn?: typeof globalThis.fetch,
17
+ private readonly timeout?: number
18
+ ) {}
19
+
20
+ /**
21
+ * Create a JWT token
22
+ *
23
+ * Creates a short-lived JWT token with specific scopes for secure,
24
+ * time-limited access to pipes and datasources.
25
+ *
26
+ * @param options - JWT creation options
27
+ * @returns The created JWT token
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * const result = await client.tokens.createJWT({
32
+ * name: "user_123_token",
33
+ * expiresAt: new Date(Date.now() + 3600 * 1000), // 1 hour
34
+ * scopes: [
35
+ * {
36
+ * type: "PIPES:READ",
37
+ * resource: "user_analytics",
38
+ * fixed_params: { user_id: 123 },
39
+ * },
40
+ * ],
41
+ * });
42
+ *
43
+ * console.log(result.token); // "eyJ..."
44
+ * ```
45
+ */
46
+ async createJWT(options: CreateJWTOptions): Promise<CreateJWTResult> {
47
+ const token = await this.getToken();
48
+
49
+ try {
50
+ return await apiCreateJWT(
51
+ {
52
+ baseUrl: this.baseUrl,
53
+ token,
54
+ fetch: this.fetchFn,
55
+ timeout: this.timeout,
56
+ },
57
+ options
58
+ );
59
+ } catch (error) {
60
+ if (error instanceof TokenApiError) {
61
+ throw new TinybirdError(error.message, error.status, {
62
+ error: error.message,
63
+ status: error.status,
64
+ });
65
+ }
66
+ throw error;
67
+ }
68
+ }
69
+ }
package/src/index.ts CHANGED
@@ -232,6 +232,10 @@ export type {
232
232
  TinybirdApiIngestOptions,
233
233
  TinybirdApiAppendOptions,
234
234
  TinybirdApiRequestInit,
235
+ TinybirdApiTokenScope,
236
+ TinybirdApiCreateTokenRequest,
237
+ TinybirdApiCreateTokenOptions,
238
+ TinybirdApiCreateTokenResult,
235
239
  } from "./api/api.js";
236
240
 
237
241
  // ============ Preview Environment ============
@@ -251,6 +255,17 @@ export {
251
255
  } from "./api/dashboard.js";
252
256
  export type { RegionInfo } from "./api/dashboard.js";
253
257
 
258
+ // ============ Token API ============
259
+ export { createJWT, TokenApiError } from "./api/tokens.js";
260
+ export type {
261
+ TokenApiConfig,
262
+ JWTScope,
263
+ JWTScopeType,
264
+ JWTLimits,
265
+ CreateJWTOptions,
266
+ CreateJWTResult,
267
+ } from "./api/tokens.js";
268
+
254
269
  // ============ Config Types ============
255
270
  // Import from config-types.ts to avoid bundling esbuild in client code
256
271
  export type { TinybirdConfig, DevMode } from "./cli/config-types.js";