@brokr/sdk 1.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.
package/dist/index.js ADDED
@@ -0,0 +1,568 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+
23
+ // src/auth.ts
24
+ var auth_exports = {};
25
+ __export(auth_exports, {
26
+ BrokrAuthClient: () => BrokrAuthClient,
27
+ authMiddleware: () => authMiddleware
28
+ });
29
+ function parseCookies(cookieHeader) {
30
+ const cookies = /* @__PURE__ */ new Map();
31
+ for (const pair of cookieHeader.split(";")) {
32
+ const eqIdx = pair.indexOf("=");
33
+ if (eqIdx === -1) continue;
34
+ const name = pair.slice(0, eqIdx).trim();
35
+ const value = pair.slice(eqIdx + 1).trim();
36
+ if (name) cookies.set(name, value);
37
+ }
38
+ return cookies;
39
+ }
40
+ function authMiddleware(options) {
41
+ const { protectedRoutes = [], publicOnlyRoutes = [] } = options;
42
+ return async function middleware(request) {
43
+ const url = new URL(request.url);
44
+ const pathname = url.pathname;
45
+ const isProtected = protectedRoutes.some((route) => pathname.startsWith(route));
46
+ const isPublicOnly = publicOnlyRoutes.some((route) => pathname.startsWith(route));
47
+ if (!isProtected && !isPublicOnly) return void 0;
48
+ const cookieHeader = request.headers.get("cookie") ?? "";
49
+ const cookies = parseCookies(cookieHeader);
50
+ const hasSession = cookies.has("better-auth.session_token");
51
+ if (isProtected && !hasSession) {
52
+ return Response.redirect(new URL("/sign-in", request.url).toString(), 302);
53
+ }
54
+ if (isPublicOnly && hasSession) {
55
+ return Response.redirect(new URL("/", request.url).toString(), 302);
56
+ }
57
+ return void 0;
58
+ };
59
+ }
60
+ var BrokrAuthClient;
61
+ var init_auth = __esm({
62
+ "src/auth.ts"() {
63
+ "use strict";
64
+ init_runtime();
65
+ BrokrAuthClient = class {
66
+ constructor(token, gatewayUrl, appUrl) {
67
+ this._token = token;
68
+ this._gatewayUrl = gatewayUrl;
69
+ this._appUrl = appUrl ?? (typeof process !== "undefined" ? process.env.BETTER_AUTH_URL : void 0);
70
+ }
71
+ /**
72
+ * Get current user from an incoming request's cookies.
73
+ * Calls the local Better Auth API (runs inside your app).
74
+ */
75
+ async currentUser(request) {
76
+ const session = await this.getSession(request);
77
+ return session?.user ?? null;
78
+ }
79
+ /**
80
+ * Get the full session (user + metadata) from an incoming request.
81
+ * Calls the local Better Auth API.
82
+ */
83
+ async getSession(request) {
84
+ const appUrl = this._appUrl;
85
+ if (!appUrl) {
86
+ throw new BrokrError(
87
+ "[brokr] BETTER_AUTH_URL is not set. Auth may not be provisioned.",
88
+ "AUTH_NOT_CONFIGURED",
89
+ "auth"
90
+ );
91
+ }
92
+ const cookieHeader = request.headers.get("cookie") ?? "";
93
+ if (!cookieHeader) return null;
94
+ const res = await fetch(`${appUrl}/api/auth/get-session`, {
95
+ method: "GET",
96
+ headers: { cookie: cookieHeader }
97
+ });
98
+ if (!res.ok) return null;
99
+ const data = await res.json();
100
+ if (!data?.user || !data?.session) return null;
101
+ return {
102
+ user: {
103
+ id: data.user.id,
104
+ email: data.user.email,
105
+ name: data.user.name ?? null,
106
+ avatarUrl: data.user.image ?? null,
107
+ emailVerified: data.user.emailVerified ? /* @__PURE__ */ new Date() : null
108
+ },
109
+ sessionId: data.session.id,
110
+ expiresAt: new Date(data.session.expiresAt)
111
+ };
112
+ }
113
+ /**
114
+ * Generate an OAuth authorization URL.
115
+ */
116
+ async getOAuthUrl(provider, options) {
117
+ const appUrl = this._appUrl;
118
+ if (!appUrl) {
119
+ throw new BrokrError("[brokr] BETTER_AUTH_URL is not set.", "AUTH_NOT_CONFIGURED", "auth");
120
+ }
121
+ const redirectTo = options?.redirectTo ?? "/";
122
+ if (!redirectTo.startsWith("/") || redirectTo.startsWith("//")) {
123
+ throw new BrokrError("[brokr] redirectTo must be a relative path (start with /)", "INVALID_REDIRECT", "auth");
124
+ }
125
+ const params = new URLSearchParams({ callbackURL: redirectTo });
126
+ return { redirectUrl: `${appUrl}/api/auth/sign-in/social?provider=${provider}&${params}` };
127
+ }
128
+ /**
129
+ * Send a magic link email (requires email capability).
130
+ */
131
+ async sendMagicLink(email, options) {
132
+ const appUrl = this._appUrl;
133
+ if (!appUrl) {
134
+ throw new BrokrError("[brokr] BETTER_AUTH_URL is not set.", "AUTH_NOT_CONFIGURED", "auth");
135
+ }
136
+ const res = await fetch(`${appUrl}/api/auth/magic-link/send`, {
137
+ method: "POST",
138
+ headers: { "Content-Type": "application/json" },
139
+ body: JSON.stringify({ email, callbackURL: options?.redirectTo ?? "/" })
140
+ });
141
+ if (!res.ok) {
142
+ const data = await res.json().catch(() => ({}));
143
+ throw new BrokrError(
144
+ data.message ?? `[brokr] Failed to send magic link (HTTP ${res.status})`,
145
+ "AUTH_MAGIC_LINK_FAILED",
146
+ "auth"
147
+ );
148
+ }
149
+ }
150
+ };
151
+ }
152
+ });
153
+
154
+ // src/runtime.ts
155
+ function resolveToken() {
156
+ return typeof process !== "undefined" ? process.env.BROKR_TOKEN : void 0;
157
+ }
158
+ function assertToken(token, capability) {
159
+ if (!token) {
160
+ let hint = "brokr env pull --stack <name>";
161
+ try {
162
+ if (typeof process !== "undefined") {
163
+ const fs = require("fs");
164
+ const path = require("path");
165
+ const brokrFile = path.join(process.cwd(), ".brokr");
166
+ if (fs.existsSync(brokrFile)) {
167
+ const data = JSON.parse(fs.readFileSync(brokrFile, "utf8"));
168
+ if (data?.stackName) hint = `brokr env pull --stack ${data.stackName}`;
169
+ }
170
+ }
171
+ } catch {
172
+ }
173
+ throw new BrokrAuthError(
174
+ `[brokr] BROKR_TOKEN is not set.
175
+ Run: ${hint}`,
176
+ "BROKR_TOKEN_MISSING"
177
+ );
178
+ }
179
+ }
180
+ async function gatewayFetch(gatewayUrl, token, path, body, capability) {
181
+ let res;
182
+ try {
183
+ res = await fetch(`${gatewayUrl}${path}`, {
184
+ method: "POST",
185
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
186
+ body: JSON.stringify(body)
187
+ });
188
+ } catch (err) {
189
+ throw new BrokrNetworkError(
190
+ `[brokr] Could not reach Brokr gateway. Check your network.
191
+ ${err instanceof Error ? err.message : String(err)}`,
192
+ capability
193
+ );
194
+ }
195
+ if (res.status === 429) {
196
+ const retryAfter = parseInt(res.headers.get("Retry-After") ?? "60", 10);
197
+ const data = await res.json().catch(() => ({}));
198
+ throw new BrokrRateLimitError(
199
+ data.error ?? `[brokr] Rate limited (retry after ${retryAfter}s)`,
200
+ retryAfter,
201
+ capability
202
+ );
203
+ }
204
+ if (res.status === 401) {
205
+ throw new BrokrAuthError("[brokr] Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
206
+ }
207
+ if (!res.ok) {
208
+ const data = await res.json().catch(() => ({}));
209
+ throw new BrokrError(
210
+ data.error ?? `[brokr] ${capability} request failed (HTTP ${res.status})`,
211
+ data.code ?? `${capability.toUpperCase()}_FAILED`,
212
+ capability
213
+ );
214
+ }
215
+ return res.json();
216
+ }
217
+ function createBrokr(options) {
218
+ return new BrokrRuntime(options);
219
+ }
220
+ var GATEWAY_URL, BrokrError, BrokrAuthError, BrokrRateLimitError, BrokrNetworkError, BrokrAIClient, BrokrStorageClient, BrokrEmailClient, BrokrRuntime;
221
+ var init_runtime = __esm({
222
+ "src/runtime.ts"() {
223
+ "use strict";
224
+ GATEWAY_URL = "https://api.brokr.sh";
225
+ BrokrError = class extends Error {
226
+ constructor(message, code, capability, retryable = false) {
227
+ super(message);
228
+ this.code = code;
229
+ this.capability = capability;
230
+ this.retryable = retryable;
231
+ this.name = "BrokrError";
232
+ }
233
+ };
234
+ BrokrAuthError = class extends BrokrError {
235
+ constructor(message, code) {
236
+ super(message, code, "auth", false);
237
+ this.name = "BrokrAuthError";
238
+ }
239
+ };
240
+ BrokrRateLimitError = class extends BrokrError {
241
+ constructor(message, retryAfter, capability) {
242
+ super(message, "RATE_LIMITED", capability, true);
243
+ this.retryAfter = retryAfter;
244
+ this.name = "BrokrRateLimitError";
245
+ }
246
+ };
247
+ BrokrNetworkError = class extends BrokrError {
248
+ constructor(message, capability) {
249
+ super(message, "NETWORK_ERROR", capability, true);
250
+ this.name = "BrokrNetworkError";
251
+ }
252
+ };
253
+ BrokrAIClient = class {
254
+ constructor(_token, _gatewayUrl) {
255
+ this._token = _token;
256
+ this._gatewayUrl = _gatewayUrl;
257
+ }
258
+ /**
259
+ * Send a chat completion request.
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * const reply = await brokr.ai.chat([
264
+ * { role: 'user', content: 'Explain quantum computing in one sentence.' }
265
+ * ]);
266
+ * console.log(reply.content);
267
+ * ```
268
+ */
269
+ async chat(messages, options) {
270
+ assertToken(this._token, "ai");
271
+ const data = await gatewayFetch(this._gatewayUrl, this._token, "/v1/chat/completions", {
272
+ messages,
273
+ model: options?.model,
274
+ max_tokens: options?.maxTokens,
275
+ temperature: options?.temperature
276
+ }, "ai");
277
+ return {
278
+ content: data.choices?.[0]?.message?.content ?? "",
279
+ model: data.model ?? "",
280
+ usage: {
281
+ promptTokens: data.usage?.prompt_tokens ?? 0,
282
+ completionTokens: data.usage?.completion_tokens ?? 0
283
+ }
284
+ };
285
+ }
286
+ /**
287
+ * Stream a chat completion. Yields text strings directly.
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * for await (const text of brokr.ai.stream(messages)) {
292
+ * process.stdout.write(text);
293
+ * }
294
+ * ```
295
+ */
296
+ async *stream(messages, options) {
297
+ assertToken(this._token, "ai");
298
+ let res;
299
+ try {
300
+ res = await fetch(`${this._gatewayUrl}/v1/chat/completions`, {
301
+ method: "POST",
302
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${this._token}` },
303
+ body: JSON.stringify({ messages, stream: true, model: options?.model, max_tokens: options?.maxTokens })
304
+ });
305
+ } catch (err) {
306
+ throw new BrokrNetworkError(
307
+ `[brokr] Could not reach Brokr gateway.
308
+ ${err instanceof Error ? err.message : String(err)}`,
309
+ "ai"
310
+ );
311
+ }
312
+ if (res.status === 429) {
313
+ const retryAfter = parseInt(res.headers.get("Retry-After") ?? "60", 10);
314
+ throw new BrokrRateLimitError("[brokr] AI rate limited.", retryAfter, "ai");
315
+ }
316
+ if (res.status === 401) {
317
+ throw new BrokrAuthError("[brokr] Invalid or expired BROKR_TOKEN.", "BROKR_TOKEN_INVALID");
318
+ }
319
+ if (!res.ok || !res.body) {
320
+ throw new BrokrError(`[brokr] AI stream failed (HTTP ${res.status})`, "AI_STREAM_FAILED", "ai");
321
+ }
322
+ const reader = res.body.getReader();
323
+ const decoder = new TextDecoder();
324
+ let buffer = "";
325
+ while (true) {
326
+ const { done, value } = await reader.read();
327
+ if (done) break;
328
+ buffer += decoder.decode(value, { stream: true });
329
+ const lines = buffer.split("\n");
330
+ buffer = lines.pop() ?? "";
331
+ for (const line of lines) {
332
+ if (!line.startsWith("data: ")) continue;
333
+ const payload = line.slice(6).trim();
334
+ if (payload === "[DONE]") return;
335
+ try {
336
+ const parsed = JSON.parse(payload);
337
+ const delta = parsed.choices?.[0]?.delta?.content ?? "";
338
+ if (delta) yield delta;
339
+ } catch {
340
+ }
341
+ }
342
+ }
343
+ }
344
+ /**
345
+ * OpenAI-SDK compatible base URL.
346
+ *
347
+ * @example
348
+ * ```typescript
349
+ * const openai = new OpenAI({ baseURL: brokr.ai.baseURL, apiKey: brokr.ai.apiKey });
350
+ * ```
351
+ */
352
+ get baseURL() {
353
+ return `${this._gatewayUrl}/v1`;
354
+ }
355
+ /** Use as `apiKey` with the official OpenAI SDK to route through Brokr's gateway. */
356
+ get apiKey() {
357
+ assertToken(this._token, "ai");
358
+ return this._token;
359
+ }
360
+ };
361
+ BrokrStorageClient = class {
362
+ constructor(_token, _gatewayUrl) {
363
+ this._token = _token;
364
+ this._gatewayUrl = _gatewayUrl;
365
+ }
366
+ /**
367
+ * Get a presigned upload URL for browser-direct or streaming uploads.
368
+ *
369
+ * @example
370
+ * ```typescript
371
+ * const { url, key } = await brokr.storage.getUploadUrl('avatar.png', 'image/png');
372
+ * await fetch(url, { method: 'PUT', body: file });
373
+ * ```
374
+ */
375
+ async getUploadUrl(filename, contentType = "application/octet-stream") {
376
+ assertToken(this._token, "storage");
377
+ return gatewayFetch(
378
+ this._gatewayUrl,
379
+ this._token,
380
+ "/v1/storage/sign-upload",
381
+ { filename, contentType },
382
+ "storage"
383
+ );
384
+ }
385
+ /**
386
+ * Upload data to R2. Returns the stable object key.
387
+ *
388
+ * @example
389
+ * ```typescript
390
+ * const { key } = await brokr.storage.upload(fileBuffer, 'photo.jpg', 'image/jpeg');
391
+ * ```
392
+ */
393
+ async upload(data, filename, contentType = "application/octet-stream") {
394
+ const { url, key } = await this.getUploadUrl(filename, contentType);
395
+ const putRes = await fetch(url, {
396
+ method: "PUT",
397
+ headers: { "Content-Type": contentType },
398
+ body: data
399
+ });
400
+ if (!putRes.ok) {
401
+ throw new BrokrError(`[brokr] Upload failed (HTTP ${putRes.status})`, "STORAGE_UPLOAD_FAILED", "storage");
402
+ }
403
+ return { key };
404
+ }
405
+ /**
406
+ * Get a presigned download URL for a stored object.
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * const { url } = await brokr.storage.url('photos/avatar.jpg');
411
+ * // use url in <img src={url} /> or redirect
412
+ * ```
413
+ */
414
+ async url(key, options) {
415
+ assertToken(this._token, "storage");
416
+ return gatewayFetch(
417
+ this._gatewayUrl,
418
+ this._token,
419
+ "/v1/storage/sign-download",
420
+ { key, expiresIn: options?.expiresIn },
421
+ "storage"
422
+ );
423
+ }
424
+ /** @deprecated Use `url()` instead. */
425
+ async getUrl(key, options) {
426
+ return this.url(key, options);
427
+ }
428
+ };
429
+ BrokrEmailClient = class {
430
+ constructor(_token, _gatewayUrl) {
431
+ this._token = _token;
432
+ this._gatewayUrl = _gatewayUrl;
433
+ }
434
+ /**
435
+ * Send an email. The from address and API credentials are resolved server-side.
436
+ *
437
+ * @example
438
+ * ```typescript
439
+ * await brokr.email.send({
440
+ * to: 'user@example.com',
441
+ * subject: 'Welcome!',
442
+ * html: '<h1>Welcome to the app</h1>',
443
+ * });
444
+ * ```
445
+ */
446
+ async send(params) {
447
+ assertToken(this._token, "email");
448
+ return gatewayFetch(
449
+ this._gatewayUrl,
450
+ this._token,
451
+ "/v1/email/send",
452
+ params,
453
+ "email"
454
+ );
455
+ }
456
+ };
457
+ BrokrRuntime = class {
458
+ constructor(options) {
459
+ this._token = options?.token ?? resolveToken();
460
+ this._gatewayUrl = options?.gatewayUrl ?? GATEWAY_URL;
461
+ this.ai = new BrokrAIClient(this._token, this._gatewayUrl);
462
+ this.storage = new BrokrStorageClient(this._token, this._gatewayUrl);
463
+ this.email = new BrokrEmailClient(this._token, this._gatewayUrl);
464
+ }
465
+ /** Auth client — lazily initialized to avoid pulling in auth deps when not needed. */
466
+ get auth() {
467
+ if (!this._auth) {
468
+ const { BrokrAuthClient: BrokrAuthClient2 } = (init_auth(), __toCommonJS(auth_exports));
469
+ this._auth = new BrokrAuthClient2(this._token, this._gatewayUrl);
470
+ }
471
+ return this._auth;
472
+ }
473
+ };
474
+ }
475
+ });
476
+
477
+ // index.ts
478
+ var index_exports = {};
479
+ __export(index_exports, {
480
+ BrokrAIClient: () => BrokrAIClient,
481
+ BrokrClient: () => BrokrClient,
482
+ BrokrEmailClient: () => BrokrEmailClient,
483
+ BrokrError: () => BrokrError,
484
+ BrokrRuntime: () => BrokrRuntime,
485
+ BrokrStorageClient: () => BrokrStorageClient,
486
+ authMiddleware: () => authMiddleware,
487
+ create: () => create,
488
+ createBrokr: () => createBrokr,
489
+ createBrokrClient: () => createBrokrClient
490
+ });
491
+ module.exports = __toCommonJS(index_exports);
492
+ init_runtime();
493
+ init_auth();
494
+
495
+ // src/management.ts
496
+ async function trpcRequest(config, procedure, input) {
497
+ const url = new URL(`${config.apiUrl}/api/trpc/${procedure}`);
498
+ const response = await fetch(url.toString(), {
499
+ method: "POST",
500
+ headers: {
501
+ "Content-Type": "application/json",
502
+ "Authorization": `Bearer ${config.accessToken}`,
503
+ ...config.headers
504
+ },
505
+ body: JSON.stringify(input),
506
+ signal: config.timeout ? AbortSignal.timeout(config.timeout) : void 0
507
+ });
508
+ if (!response.ok) {
509
+ const error = await response.json().catch(() => ({ message: "Request failed" }));
510
+ throw new Error(error.message || `HTTP ${response.status}`);
511
+ }
512
+ const result = await response.json();
513
+ return result.data;
514
+ }
515
+ var BrokrClient = class {
516
+ constructor(config) {
517
+ this.config = config;
518
+ }
519
+ /** Create a new stack with default providers. */
520
+ async create(input) {
521
+ return trpcRequest(this.config, "v1.stack.create", input);
522
+ }
523
+ /** List all stacks for the current user. */
524
+ async listStacks() {
525
+ return trpcRequest(this.config, "v1.stack.list", {});
526
+ }
527
+ /** Get a specific stack by ID. */
528
+ async getStack(id) {
529
+ return trpcRequest(this.config, "v1.stack.get", { id });
530
+ }
531
+ /** Update a stack. */
532
+ async updateStack(id, input) {
533
+ return trpcRequest(this.config, "v1.stack.update", { id, ...input });
534
+ }
535
+ /** Delete a stack. */
536
+ async deleteStack(id) {
537
+ return trpcRequest(this.config, "v1.stack.delete", { id });
538
+ }
539
+ /** Add a provider to a stack. */
540
+ async add(provider, input) {
541
+ return trpcRequest(this.config, "v1.provider.add", { provider, ...input });
542
+ }
543
+ /** Refresh the access token. */
544
+ async refreshToken(refreshToken) {
545
+ return trpcRequest(this.config, "v1.auth.refresh", { refreshToken });
546
+ }
547
+ /** Update the access token. */
548
+ setAccessToken(accessToken) {
549
+ this.config.accessToken = accessToken;
550
+ }
551
+ };
552
+ function createBrokrClient(config) {
553
+ return new BrokrClient(config);
554
+ }
555
+ var create = createBrokrClient;
556
+ // Annotate the CommonJS export names for ESM import in node:
557
+ 0 && (module.exports = {
558
+ BrokrAIClient,
559
+ BrokrClient,
560
+ BrokrEmailClient,
561
+ BrokrError,
562
+ BrokrRuntime,
563
+ BrokrStorageClient,
564
+ authMiddleware,
565
+ create,
566
+ createBrokr,
567
+ createBrokrClient
568
+ });