@bike4mind/sdk 0.0.0-stage → 1.1.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.
package/dist/index.mjs ADDED
@@ -0,0 +1,613 @@
1
+ import { createParser } from "eventsource-parser";
2
+ //#region src/errors.ts
3
+ /**
4
+ * A non-2xx response. `message` is the envelope's `error` (`ErrorResponse` in the spec); branch on `status` and
5
+ * `errorCode`, never on the prose. The deprecated `name` field is deliberately not surfaced.
6
+ */
7
+ var B4mApiError = class B4mApiError extends Error {
8
+ status;
9
+ errorCode;
10
+ requestId;
11
+ retryAfterSeconds;
12
+ body;
13
+ headers;
14
+ constructor(status, body, headers) {
15
+ const record = body && typeof body === "object" ? body : {};
16
+ super(typeof record.error === "string" ? record.error : `HTTP ${status}`);
17
+ this.name = "B4mApiError";
18
+ this.status = status;
19
+ this.body = body;
20
+ this.headers = headers;
21
+ if (typeof record.errorCode === "string") this.errorCode = record.errorCode;
22
+ const requestId = typeof record.request_id === "string" ? record.request_id : headers.get("x-request-id");
23
+ if (requestId) this.requestId = requestId;
24
+ const retryAfterSeconds = parseRetryAfterSeconds(headers.get("retry-after"));
25
+ if (retryAfterSeconds !== void 0) this.retryAfterSeconds = retryAfterSeconds;
26
+ }
27
+ static async fromResponse(response) {
28
+ const text = await response.text().catch(() => "");
29
+ let body = text;
30
+ try {
31
+ body = JSON.parse(text);
32
+ } catch {}
33
+ return new B4mApiError(response.status, body, response.headers);
34
+ }
35
+ };
36
+ /**
37
+ * A completions stream that closed without `[DONE]` and without an `error` event: the connection was cut
38
+ * mid-response, so nothing about the request itself failed. Safe to retry when no output was used yet.
39
+ */
40
+ var B4mStreamEndedError = class extends Error {
41
+ constructor() {
42
+ super("completion stream ended before [DONE]");
43
+ this.name = "B4mStreamEndedError";
44
+ }
45
+ };
46
+ /**
47
+ * A polled quest (chat turn, image generation or edit) that did not succeed: it carries `type: "error"`
48
+ * (`reason: 'error'`), ended `stopped` (`'stopped'`), or was still running when the poll's `timeoutMs` ran out
49
+ * (`'timeout'`). `quest` is the last state seen; its `reply` carries the server's explanation.
50
+ */
51
+ var B4mQuestError = class extends Error {
52
+ reason;
53
+ quest;
54
+ constructor(reason, quest) {
55
+ super(reason === "timeout" ? `quest ${quest.id} still ${quest.status ?? "pending"} when the poll timed out` : quest.reply ?? `quest ${quest.id} ${reason === "error" ? "failed" : "was stopped"}`);
56
+ this.name = "B4mQuestError";
57
+ this.reason = reason;
58
+ this.quest = quest;
59
+ }
60
+ };
61
+ /**
62
+ * Normalize a Retry-After header (RFC 7231: delta-seconds or an HTTP-date) to whole, non-negative seconds;
63
+ * undefined when absent or unparseable.
64
+ */
65
+ function parseRetryAfterSeconds(value) {
66
+ if (value === void 0 || value === null) return void 0;
67
+ const raw = String(value).trim();
68
+ if (/^\d+$/.test(raw)) return Number(raw);
69
+ const dateMs = Date.parse(raw);
70
+ if (Number.isNaN(dateMs)) return void 0;
71
+ return Math.max(0, Math.ceil((dateMs - Date.now()) / 1e3));
72
+ }
73
+ //#endregion
74
+ //#region src/generated/operations.ts
75
+ const operations = {
76
+ addDataLakeFile: {
77
+ method: "POST",
78
+ path: "/api/v1/data-lakes/{id}/files/{file_id}"
79
+ },
80
+ cancelMusicGeneration: {
81
+ method: "POST",
82
+ path: "/api/v1/music-generations/{id}/cancel"
83
+ },
84
+ cancelVideoGeneration: {
85
+ method: "POST",
86
+ path: "/api/v1/video-generations/{id}/cancel"
87
+ },
88
+ cloneSession: {
89
+ method: "POST",
90
+ path: "/api/v1/sessions/{id}/clone"
91
+ },
92
+ createAgent: {
93
+ method: "POST",
94
+ path: "/api/v1/agents"
95
+ },
96
+ createArtifact: {
97
+ method: "POST",
98
+ path: "/api/v1/artifacts"
99
+ },
100
+ createCompletion: {
101
+ method: "POST",
102
+ path: "/api/ai/v1/completions"
103
+ },
104
+ createDecision: {
105
+ method: "POST",
106
+ path: "/api/v1/decisions"
107
+ },
108
+ createEmbeddings: {
109
+ method: "POST",
110
+ path: "/api/v1/embeddings"
111
+ },
112
+ createFileUpload: {
113
+ method: "POST",
114
+ path: "/api/v1/files"
115
+ },
116
+ createMusicGeneration: {
117
+ method: "POST",
118
+ path: "/api/v1/music-generations"
119
+ },
120
+ createProject: {
121
+ method: "POST",
122
+ path: "/api/v1/projects"
123
+ },
124
+ createSession: {
125
+ method: "POST",
126
+ path: "/api/v1/sessions"
127
+ },
128
+ createTranscription: {
129
+ method: "POST",
130
+ path: "/api/v1/transcriptions"
131
+ },
132
+ createTranscriptionUpload: {
133
+ method: "POST",
134
+ path: "/api/v1/transcriptions/uploads"
135
+ },
136
+ createVideoGeneration: {
137
+ method: "POST",
138
+ path: "/api/v1/video-generations"
139
+ },
140
+ createVoiceSession: {
141
+ method: "POST",
142
+ path: "/api/v1/voice/sessions"
143
+ },
144
+ deleteAgent: {
145
+ method: "DELETE",
146
+ path: "/api/v1/agents/{id}"
147
+ },
148
+ deleteArtifact: {
149
+ method: "DELETE",
150
+ path: "/api/v1/artifacts/{id}"
151
+ },
152
+ deleteFile: {
153
+ method: "DELETE",
154
+ path: "/api/v1/files/{id}"
155
+ },
156
+ deleteProject: {
157
+ method: "DELETE",
158
+ path: "/api/v1/projects/{id}"
159
+ },
160
+ deleteSession: {
161
+ method: "DELETE",
162
+ path: "/api/sessions/{id}"
163
+ },
164
+ editImage: {
165
+ method: "POST",
166
+ path: "/api/v1/image-edits"
167
+ },
168
+ endVoiceSession: {
169
+ method: "POST",
170
+ path: "/api/v1/voice/sessions/{id}/end"
171
+ },
172
+ executeTool: {
173
+ method: "POST",
174
+ path: "/api/ai/v1/tools"
175
+ },
176
+ generateImage: {
177
+ method: "POST",
178
+ path: "/api/v1/image-generations"
179
+ },
180
+ generateMusic: {
181
+ method: "POST",
182
+ path: "/api/ai/music"
183
+ },
184
+ generateSoundEffect: {
185
+ method: "POST",
186
+ path: "/api/ai/sound-effects"
187
+ },
188
+ getAgent: {
189
+ method: "GET",
190
+ path: "/api/v1/agents/{id}"
191
+ },
192
+ getAgentExecution: {
193
+ method: "GET",
194
+ path: "/api/v1/agent-executions/{id}"
195
+ },
196
+ getArtifact: {
197
+ method: "GET",
198
+ path: "/api/v1/artifacts/{id}"
199
+ },
200
+ getArtifactVersion: {
201
+ method: "GET",
202
+ path: "/api/v1/artifacts/{id}/versions/{version}"
203
+ },
204
+ getCreditBalance: {
205
+ method: "GET",
206
+ path: "/api/v1/credits"
207
+ },
208
+ getDataLake: {
209
+ method: "GET",
210
+ path: "/api/v1/data-lakes/{id}"
211
+ },
212
+ getDataLakeFile: {
213
+ method: "GET",
214
+ path: "/api/v1/data-lakes/{id}/files/{file_id}"
215
+ },
216
+ getFile: {
217
+ method: "GET",
218
+ path: "/api/v1/files/{id}"
219
+ },
220
+ getMe: {
221
+ method: "GET",
222
+ path: "/api/v1/me"
223
+ },
224
+ getMusicGeneration: {
225
+ method: "GET",
226
+ path: "/api/v1/music-generations/{id}"
227
+ },
228
+ getProject: {
229
+ method: "GET",
230
+ path: "/api/v1/projects/{id}"
231
+ },
232
+ getQuest: {
233
+ method: "GET",
234
+ path: "/api/v1/quests/{id}"
235
+ },
236
+ getSession: {
237
+ method: "GET",
238
+ path: "/api/sessions/{id}"
239
+ },
240
+ getVideoGeneration: {
241
+ method: "GET",
242
+ path: "/api/v1/video-generations/{id}"
243
+ },
244
+ listAgents: {
245
+ method: "GET",
246
+ path: "/api/v1/agents"
247
+ },
248
+ listArtifactVersions: {
249
+ method: "GET",
250
+ path: "/api/v1/artifacts/{id}/versions"
251
+ },
252
+ listArtifacts: {
253
+ method: "GET",
254
+ path: "/api/v1/artifacts"
255
+ },
256
+ listDataLakes: {
257
+ method: "GET",
258
+ path: "/api/v1/data-lakes"
259
+ },
260
+ listDecisionModels: {
261
+ method: "GET",
262
+ path: "/api/v1/decision-models"
263
+ },
264
+ listFiles: {
265
+ method: "GET",
266
+ path: "/api/v1/files"
267
+ },
268
+ listModels: {
269
+ method: "GET",
270
+ path: "/api/v1/models"
271
+ },
272
+ listMusicGenerations: {
273
+ method: "GET",
274
+ path: "/api/v1/music-generations"
275
+ },
276
+ listMusicModels: {
277
+ method: "GET",
278
+ path: "/api/v1/music-models"
279
+ },
280
+ listProjects: {
281
+ method: "GET",
282
+ path: "/api/v1/projects"
283
+ },
284
+ listQuestFiles: {
285
+ method: "GET",
286
+ path: "/api/v1/quests/{id}/files"
287
+ },
288
+ listSessions: {
289
+ method: "GET",
290
+ path: "/api/v1/sessions"
291
+ },
292
+ listVideoGenerations: {
293
+ method: "GET",
294
+ path: "/api/v1/video-generations"
295
+ },
296
+ listVideoModels: {
297
+ method: "GET",
298
+ path: "/api/v1/video-models"
299
+ },
300
+ listVoices: {
301
+ method: "GET",
302
+ path: "/api/v1/voice/voices"
303
+ },
304
+ listWhatsNew: {
305
+ method: "GET",
306
+ path: "/api/v1/whats-new"
307
+ },
308
+ removeDataLakeFile: {
309
+ method: "DELETE",
310
+ path: "/api/v1/data-lakes/{id}/files/{file_id}"
311
+ },
312
+ searchDataLake: {
313
+ method: "POST",
314
+ path: "/api/v1/data-lakes/{id}/search"
315
+ },
316
+ sendChatMessage: {
317
+ method: "POST",
318
+ path: "/api/chat"
319
+ },
320
+ startAgentExecution: {
321
+ method: "POST",
322
+ path: "/api/v1/agent-executions"
323
+ },
324
+ synthesizeSpeech: {
325
+ method: "POST",
326
+ path: "/api/ai/tts"
327
+ },
328
+ updateAgent: {
329
+ method: "PATCH",
330
+ path: "/api/v1/agents/{id}"
331
+ },
332
+ updateArtifact: {
333
+ method: "PATCH",
334
+ path: "/api/v1/artifacts/{id}"
335
+ },
336
+ updateFile: {
337
+ method: "PATCH",
338
+ path: "/api/v1/files/{id}"
339
+ },
340
+ updateProject: {
341
+ method: "PATCH",
342
+ path: "/api/v1/projects/{id}"
343
+ },
344
+ updateSession: {
345
+ method: "PUT",
346
+ path: "/api/sessions/{id}"
347
+ }
348
+ };
349
+ //#endregion
350
+ //#region src/sse.ts
351
+ /**
352
+ * Yield each server-sent event's `data` from a fetch body, until `[DONE]`, the end of the stream, or `signal`
353
+ * aborts (which throws the signal's reason). `onDone` is called when `[DONE]` is seen, so a caller can tell it
354
+ * from a truncated stream. Breaking out of the loop early cancels the body.
355
+ */
356
+ async function* parseSse(body, signal, onDone) {
357
+ const queue = [];
358
+ const parser = createParser({ onEvent: (event) => queue.push(event.data) });
359
+ const reader = body.getReader();
360
+ const decoder = new TextDecoder();
361
+ const onAbort = () => void reader.cancel().catch(() => {});
362
+ signal?.addEventListener("abort", onAbort, { once: true });
363
+ let finished = false;
364
+ try {
365
+ for (;;) {
366
+ signal?.throwIfAborted();
367
+ const { done, value } = await reader.read();
368
+ signal?.throwIfAborted();
369
+ parser.feed(done ? decoder.decode() : decoder.decode(value, { stream: true }));
370
+ while (queue.length > 0) {
371
+ const data = queue.shift();
372
+ if (data === "[DONE]") {
373
+ onDone?.();
374
+ return;
375
+ }
376
+ yield data;
377
+ }
378
+ if (done) {
379
+ finished = true;
380
+ return;
381
+ }
382
+ }
383
+ } finally {
384
+ signal?.removeEventListener("abort", onAbort);
385
+ if (!finished) await reader.cancel().catch(() => {});
386
+ }
387
+ }
388
+ //#endregion
389
+ //#region src/client.ts
390
+ const ABSOLUTE_URL = /^https?:\/\//i;
391
+ function buildUrl(baseUrl, path, params, query) {
392
+ const values = params ?? {};
393
+ const resolved = path.replace(/\{([^}]+)\}/g, (_, name) => {
394
+ const value = values[name];
395
+ if (value === void 0 || value === null || value === "" || value === "." || value === "..") throw new Error(`Invalid path parameter ${name}: ${JSON.stringify(value)}`);
396
+ return encodeURIComponent(String(value));
397
+ });
398
+ const search = new URLSearchParams();
399
+ for (const [key, value] of Object.entries(query ?? {})) for (const item of Array.isArray(value) ? value : [value]) if (item !== void 0 && item !== null) search.append(key, String(item));
400
+ const qs = search.toString();
401
+ const relative = resolved.startsWith("/") ? resolved : `/${resolved}`;
402
+ const url = ABSOLUTE_URL.test(resolved) ? resolved : `${baseUrl.replace(/\/+$/, "")}${relative}`;
403
+ return qs ? `${url}${url.includes("?") ? "&" : "?"}${qs}` : url;
404
+ }
405
+ function assertBaseUrl(baseUrl) {
406
+ let parsed;
407
+ try {
408
+ parsed = new URL(baseUrl);
409
+ } catch {
410
+ throw new Error(`createClient: baseUrl must be an absolute http(s) URL, got ${JSON.stringify(baseUrl)}`);
411
+ }
412
+ if (!/^https?:$/.test(parsed.protocol) || parsed.search || parsed.hash) throw new Error(`createClient: baseUrl must be an http(s) URL with no query or fragment, got ${JSON.stringify(baseUrl)}`);
413
+ }
414
+ function createClient(options) {
415
+ if (!options.baseUrl) throw new Error("createClient: baseUrl is required");
416
+ assertBaseUrl(options.baseUrl);
417
+ const fetchImpl = options.fetch ?? globalThis.fetch;
418
+ async function send(method, path, args = {}) {
419
+ const headers = new Headers(options.headers);
420
+ for (const [name, value] of Object.entries(args.headers ?? {})) headers.set(name, value);
421
+ const token = await options.getAuthToken?.() || options.apiKey;
422
+ if (token) headers.set("authorization", `Bearer ${token}`);
423
+ let body;
424
+ if (args.body !== void 0) {
425
+ headers.set("content-type", "application/json");
426
+ body = JSON.stringify(args.body);
427
+ }
428
+ const response = await fetchImpl(buildUrl(options.baseUrl, path, args.params, args.query), {
429
+ method,
430
+ headers,
431
+ body,
432
+ signal: args.signal,
433
+ ...args.redirect ? { redirect: args.redirect } : {}
434
+ });
435
+ if (!response.ok) throw await B4mApiError.fromResponse(response);
436
+ return response;
437
+ }
438
+ /** Any spec operation, returning the raw 2xx `Response` (for binary or streamed bodies). Non-2xx throws. */
439
+ function raw(operationId, ...[args]) {
440
+ const { method, path } = operations[operationId];
441
+ return send(method, path, args);
442
+ }
443
+ /** Any spec operation, typed from the spec; returns the parsed JSON body (`undefined` for an empty one). A non-JSON 2xx body throws. */
444
+ async function call(operationId, ...rest) {
445
+ const response = await raw(operationId, ...rest);
446
+ const text = await response.text();
447
+ const contentType = response.headers.get("content-type") ?? "";
448
+ if (text && !/json/i.test(contentType)) throw new Error(`${operationId} returned ${contentType || "a non-JSON body"}; use raw(), completions() or the tts/music/soundEffects helpers`);
449
+ return text ? JSON.parse(text) : void 0;
450
+ }
451
+ async function pollQuest(questId, poll = {}) {
452
+ const { intervalMs = 1e3, maxIntervalMs = 5e3, timeoutMs, signal } = poll;
453
+ const deadline = timeoutMs === void 0 ? void 0 : Date.now() + timeoutMs;
454
+ let delay = intervalMs;
455
+ for (;;) {
456
+ const quest = await call("getQuest", {
457
+ params: { id: questId },
458
+ signal
459
+ });
460
+ if (quest.status === "stopped") throw new B4mQuestError("stopped", quest);
461
+ if (quest.type === "error") throw new B4mQuestError("error", quest);
462
+ if (quest.status === "done") return quest;
463
+ if (deadline !== void 0 && Date.now() + delay > deadline) throw new B4mQuestError("timeout", quest);
464
+ await sleep(delay, signal);
465
+ delay = Math.min(delay * 1.5, maxIntervalMs);
466
+ }
467
+ }
468
+ const questHandle = (questId, ack) => ({
469
+ questId,
470
+ ack,
471
+ poll: (poll) => pollQuest(questId, poll)
472
+ });
473
+ async function audio(operationId, body, signal) {
474
+ const { method, path } = operations[operationId];
475
+ const response = await send(method, path, {
476
+ body: {
477
+ ...body,
478
+ encoding: "base64"
479
+ },
480
+ signal
481
+ });
482
+ const contentType = response.headers.get("content-type") ?? "application/octet-stream";
483
+ if (/json/i.test(contentType)) return await response.json();
484
+ const saved = response.headers.get("x-b4m-audio-saved") === "true";
485
+ const header = (name) => saved ? response.headers.get(name) ?? void 0 : void 0;
486
+ const result = {
487
+ delivery: "inline",
488
+ audio: toBase64(new Uint8Array(await response.arrayBuffer())),
489
+ contentType,
490
+ saved
491
+ };
492
+ const fabFileId = header("x-b4m-audio-fab-file-id");
493
+ const fileName = header("x-b4m-audio-file-name");
494
+ const fileUrl = header("x-b4m-audio-file-url");
495
+ if (fabFileId) result.fabFileId = fabFileId;
496
+ if (fileName) result.fileName = fileName;
497
+ if (fileUrl) result.fileUrl = fileUrl;
498
+ return result;
499
+ }
500
+ return {
501
+ call,
502
+ raw,
503
+ /**
504
+ * Stream `POST /api/ai/v1/completions` as parsed events. A non-2xx before the stream throws `B4mApiError`; a
505
+ * server `{ type: 'error' }` event is yielded, not thrown. A stream that ends without `[DONE]` and without an
506
+ * error event throws. `url` overrides the path: an absolute http(s) URL is used as-is, anything else is joined
507
+ * under `baseUrl`. An absolute `url` receives the same `Authorization` header, so pass only an origin you trust.
508
+ */
509
+ async *completions(body, opts = {}) {
510
+ const response = await send("POST", opts.url ?? operations.createCompletion.path, {
511
+ body,
512
+ signal: opts.signal,
513
+ headers: {
514
+ accept: "text/event-stream",
515
+ ...opts.headers
516
+ }
517
+ });
518
+ if (!response.body) return;
519
+ let sawDone = false;
520
+ let sawError = false;
521
+ for await (const data of parseSse(response.body, opts.signal, () => {
522
+ sawDone = true;
523
+ })) {
524
+ let event;
525
+ try {
526
+ event = JSON.parse(data);
527
+ } catch {
528
+ continue;
529
+ }
530
+ if (event.type === "error") sawError = true;
531
+ yield event;
532
+ }
533
+ if (!sawDone && !sawError) throw new B4mStreamEndedError();
534
+ },
535
+ /** `POST /api/chat`, queued (the default): the turn's outcome is on the returned handle's `poll()`. */
536
+ async chat(body, opts = {}) {
537
+ const ack = await call("sendChatMessage", {
538
+ body,
539
+ signal: opts.signal
540
+ });
541
+ return questHandle(ack.id, ack);
542
+ },
543
+ async generateImage(body, opts = {}) {
544
+ const ack = await call("generateImage", {
545
+ body,
546
+ signal: opts.signal
547
+ });
548
+ return questHandle(ack.quest.id, ack);
549
+ },
550
+ /** Edited images arrive on the finished quest's `files[].url`. */
551
+ async editImage(body, opts = {}) {
552
+ const ack = await call("editImage", {
553
+ body,
554
+ signal: opts.signal
555
+ });
556
+ return questHandle(ack.id, ack);
557
+ },
558
+ pollQuest,
559
+ async tts(body, opts = {}) {
560
+ try {
561
+ return {
562
+ kind: "audio",
563
+ data: await audio("synthesizeSpeech", body, opts.signal)
564
+ };
565
+ } catch (error) {
566
+ const tooLarge = error instanceof B4mApiError && error.status === 413 ? error : void 0;
567
+ const data = tooLarge?.body;
568
+ if (!tooLarge || !data?.saved || typeof data.fabFileId !== "string" || typeof data.provider !== "string") throw error;
569
+ const fallbackFrom = tooLarge.headers.get("x-b4m-tts-provider-fallback-from");
570
+ const result = {
571
+ kind: "saved-too-large",
572
+ data: {
573
+ ...data,
574
+ fabFileId: data.fabFileId
575
+ }
576
+ };
577
+ if (fallbackFrom) result.fallbackFrom = fallbackFrom;
578
+ return result;
579
+ }
580
+ },
581
+ /**
582
+ * @deprecated `POST /api/ai/music` sunsets 2027-01-15. Queue a job with
583
+ * `call('createMusicGeneration', ...)` and poll `getMusicGeneration` instead.
584
+ */
585
+ music(body, opts = {}) {
586
+ return audio("generateMusic", body, opts.signal);
587
+ },
588
+ soundEffects(body, opts = {}) {
589
+ return audio("generateSoundEffect", body, opts.signal);
590
+ }
591
+ };
592
+ }
593
+ function sleep(ms, signal) {
594
+ return new Promise((resolve, reject) => {
595
+ if (signal?.aborted) return reject(signal.reason);
596
+ const onAbort = () => {
597
+ clearTimeout(timer);
598
+ reject(signal?.reason);
599
+ };
600
+ const timer = setTimeout(() => {
601
+ signal?.removeEventListener("abort", onAbort);
602
+ resolve();
603
+ }, ms);
604
+ signal?.addEventListener("abort", onAbort, { once: true });
605
+ });
606
+ }
607
+ function toBase64(bytes) {
608
+ let binary = "";
609
+ for (let i = 0; i < bytes.length; i += 32768) binary += String.fromCharCode(...bytes.subarray(i, i + 32768));
610
+ return btoa(binary);
611
+ }
612
+ //#endregion
613
+ export { B4mApiError, B4mQuestError, B4mStreamEndedError, createClient, operations, parseRetryAfterSeconds };
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "@bike4mind/sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.1.1",
4
+ "description": "Typed client for the Bike4Mind HTTP API, generated from its OpenAPI spec",
5
+ "license": "BUSL-1.1",
6
+ "author": "Bike4Mind",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Bike4Mind/bike4mind.git",
10
+ "directory": "packages/sdk"
11
+ },
12
+ "homepage": "https://github.com/bike4mind/bike4mind/tree/main/packages/sdk#readme",
13
+ "bugs": "https://github.com/bike4mind/bike4mind/issues",
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "keywords": [
18
+ "bike4mind",
19
+ "api",
20
+ "sdk",
21
+ "openapi"
22
+ ],
23
+ "type": "module",
24
+ "sideEffects": false,
25
+ "main": "dist/index.cjs",
26
+ "module": "dist/index.mjs",
27
+ "types": "dist/index.d.mts",
28
+ "exports": {
29
+ ".": {
30
+ "import": {
31
+ "types": "./dist/index.d.mts",
32
+ "default": "./dist/index.mjs"
33
+ },
34
+ "require": {
35
+ "types": "./dist/index.d.cts",
36
+ "default": "./dist/index.cjs"
37
+ }
38
+ },
39
+ "./package.json": "./package.json"
40
+ },
41
+ "files": [
42
+ "dist"
43
+ ],
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsdown",
49
+ "generate": "node scripts/generate.mjs",
50
+ "typecheck": "tsc --noEmit",
51
+ "typecheck:fast": "tsgo --noEmit --types node || tsc --noEmit",
52
+ "test": "vitest run"
53
+ },
54
+ "dependencies": {
55
+ "eventsource-parser": "^3.1.0"
56
+ },
57
+ "devDependencies": {
58
+ "@types/node": "^24.0.0",
59
+ "openapi-typescript": "^7.13.0",
60
+ "prettier": "^3.9.6",
61
+ "tsdown": "^0.22.3",
62
+ "typescript": "^5.9.3",
63
+ "vitest": "^4.1.11"
64
+ }
65
+ }