@apifuse/provider-sdk 2.0.0-beta.1 → 2.1.0-beta.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.
Files changed (79) hide show
  1. package/AUTHORING.md +93 -0
  2. package/CHANGELOG.md +21 -0
  3. package/README.md +133 -28
  4. package/bin/apifuse-check.ts +78 -71
  5. package/bin/apifuse-create.ts +12 -0
  6. package/bin/apifuse-dev.ts +24 -61
  7. package/bin/apifuse-pack-check.ts +87 -0
  8. package/bin/apifuse-pack-smoke.ts +122 -0
  9. package/bin/apifuse-perf.ts +33 -32
  10. package/bin/apifuse-record.ts +17 -7
  11. package/bin/apifuse-test.ts +6 -4
  12. package/bin/apifuse.ts +36 -35
  13. package/package.json +29 -9
  14. package/src/ceremonies/index.ts +768 -0
  15. package/src/cli/commands.ts +87 -0
  16. package/src/cli/create.ts +845 -0
  17. package/src/cli/templates/provider/Dockerfile.tpl +7 -0
  18. package/src/cli/templates/provider/README.md.tpl +41 -0
  19. package/src/cli/templates/provider/dev.ts.tpl +5 -0
  20. package/src/cli/templates/provider/index.test.ts.tpl +13 -0
  21. package/src/cli/templates/provider/index.ts.tpl +58 -0
  22. package/src/cli/templates/provider/start.ts.tpl +5 -0
  23. package/src/config/loader.ts +61 -1
  24. package/src/define.ts +565 -41
  25. package/src/dev.ts +2 -6
  26. package/src/errors.ts +42 -0
  27. package/src/index.ts +44 -38
  28. package/src/lint.ts +574 -0
  29. package/src/provider.ts +13 -0
  30. package/src/runtime/auth-flow.ts +67 -0
  31. package/src/runtime/credential.ts +95 -0
  32. package/src/runtime/env.ts +13 -0
  33. package/src/runtime/executor.ts +13 -14
  34. package/src/runtime/http.ts +36 -12
  35. package/src/runtime/insights.ts +3 -3
  36. package/src/runtime/key-derivation.ts +122 -0
  37. package/src/runtime/keyring.ts +148 -0
  38. package/src/runtime/namespace.ts +33 -0
  39. package/src/runtime/prevalidate.ts +252 -0
  40. package/src/runtime/tls.ts +41 -17
  41. package/src/runtime/waterfall.ts +0 -1
  42. package/src/schema.ts +77 -0
  43. package/src/serve.ts +1 -664
  44. package/src/server/index.ts +22 -0
  45. package/src/server/serve.ts +624 -0
  46. package/src/server/types.ts +78 -0
  47. package/src/stealth/profiles.ts +10 -93
  48. package/src/testing/run.ts +391 -32
  49. package/src/types.ts +390 -41
  50. package/bin/apifuse-init.ts +0 -387
  51. package/src/__tests__/auth.test.ts +0 -396
  52. package/src/__tests__/browser-auth.test.ts +0 -180
  53. package/src/__tests__/browser.test.ts +0 -632
  54. package/src/__tests__/define.test.ts +0 -225
  55. package/src/__tests__/errors.test.ts +0 -69
  56. package/src/__tests__/executor.test.ts +0 -214
  57. package/src/__tests__/http.test.ts +0 -238
  58. package/src/__tests__/insights.test.ts +0 -210
  59. package/src/__tests__/instrumentation.test.ts +0 -290
  60. package/src/__tests__/otlp.test.ts +0 -141
  61. package/src/__tests__/perf.test.ts +0 -60
  62. package/src/__tests__/providers-yaml.test.ts +0 -135
  63. package/src/__tests__/proxy.test.ts +0 -359
  64. package/src/__tests__/recipes.test.ts +0 -36
  65. package/src/__tests__/serve.test.ts +0 -233
  66. package/src/__tests__/session.test.ts +0 -231
  67. package/src/__tests__/state.test.ts +0 -100
  68. package/src/__tests__/stealth.test.ts +0 -57
  69. package/src/__tests__/testing.test.ts +0 -97
  70. package/src/__tests__/tls.test.ts +0 -345
  71. package/src/__tests__/types.test.ts +0 -142
  72. package/src/__tests__/utils.test.ts +0 -62
  73. package/src/__tests__/waterfall.test.ts +0 -270
  74. package/src/config/providers-yaml.ts +0 -370
  75. package/src/index.test.ts +0 -1
  76. package/src/protocol.ts +0 -183
  77. package/src/runtime/auth.ts +0 -245
  78. package/src/runtime/session.ts +0 -573
  79. package/src/runtime/state.ts +0 -124
package/src/serve.ts CHANGED
@@ -1,664 +1 @@
1
- import { Hono } from "hono";
2
- import { type ZodType, z } from "zod";
3
-
4
- import { ProviderError } from "./errors";
5
- import type {
6
- ConnectRequest,
7
- ConnectResponse,
8
- ContainerErrorResponse,
9
- DisconnectRequest,
10
- DisconnectResponse,
11
- ExecuteRequest,
12
- ExecuteResponse,
13
- HealthResponse,
14
- RefreshRequest,
15
- RefreshResponse,
16
- ResumeRequest,
17
- ResumeResponse,
18
- SchemaResponse,
19
- } from "./protocol";
20
- import { createBrowserClient } from "./runtime/browser";
21
- import { executeOperation } from "./runtime/executor";
22
- import { createHttpClient } from "./runtime/http";
23
- import { getProviderBaseUrl } from "./runtime/provider";
24
- import { createStateContext } from "./runtime/state";
25
- import { createTlsClient } from "./runtime/tls";
26
- import { createTraceContext, type TraceContext } from "./runtime/trace";
27
- import type {
28
- AuthContext,
29
- BrowserClient,
30
- ProviderContext,
31
- ProviderDefinition,
32
- SessionStore,
33
- TlsClient,
34
- } from "./types";
35
-
36
- const AUTH_SESSION_KEY = "__auth__";
37
- const DEFAULT_PORT = 3900;
38
-
39
- type ErrorStatusCode = 400 | 404 | 500;
40
-
41
- type SessionPatch = Record<string, string | null>;
42
-
43
- type PendingAuthState = {
44
- credentials: Record<string, string>;
45
- resolvedFields: Record<string, string>;
46
- requestedField: string;
47
- };
48
-
49
- class PendingFieldError extends Error {
50
- constructor(readonly field: string) {
51
- super(`Pending auth field: ${field}`);
52
- this.name = "PendingFieldError";
53
- }
54
- }
55
-
56
- function createSessionOverlay(snapshot: Record<string, string> = {}): {
57
- store: SessionStore;
58
- getPatch: () => SessionPatch;
59
- } {
60
- const data = new Map<string, string>(Object.entries(snapshot));
61
- const patch: SessionPatch = {};
62
-
63
- return {
64
- store: {
65
- async get(key: string): Promise<string | null> {
66
- return data.get(key) ?? null;
67
- },
68
- async set(key: string, value: string): Promise<void> {
69
- data.set(key, value);
70
- patch[key] = value;
71
- },
72
- async delete(key: string): Promise<void> {
73
- data.delete(key);
74
- patch[key] = null;
75
- },
76
- },
77
- getPatch: () => ({ ...patch }),
78
- };
79
- }
80
-
81
- function createBrowserStub(): BrowserClient {
82
- return {
83
- engine: "playwright-stealth",
84
- async newPage() {
85
- throw new ProviderError("Browser runtime is not available", {
86
- code: "BROWSER_RUNTIME_UNSUPPORTED",
87
- });
88
- },
89
- };
90
- }
91
-
92
- async function closeBrowserRuntime(browser: BrowserClient): Promise<void> {
93
- if (!Reflect.has(browser as object, "close")) {
94
- return;
95
- }
96
-
97
- await (browser as BrowserClient & { close(): Promise<void> }).close();
98
- }
99
-
100
- function createTlsStub(): TlsClient {
101
- return {
102
- async fetch() {
103
- throw new ProviderError("TLS runtime is not available", {
104
- code: "TLS_RUNTIME_UNSUPPORTED",
105
- });
106
- },
107
- createSession() {
108
- throw new ProviderError("TLS runtime is not available", {
109
- code: "TLS_RUNTIME_UNSUPPORTED",
110
- });
111
- },
112
- };
113
- }
114
-
115
- function createAuthSessionMarker(
116
- marker: Record<string, boolean | number>,
117
- ): string {
118
- return JSON.stringify(marker);
119
- }
120
-
121
- function toSessionSnapshot(
122
- snapshot: Record<string, string> | undefined,
123
- ): Record<string, string> {
124
- return snapshot ?? {};
125
- }
126
-
127
- function toStringRecord(value: unknown): Record<string, string> {
128
- if (!value || typeof value !== "object" || Array.isArray(value)) {
129
- return {};
130
- }
131
-
132
- return Object.fromEntries(
133
- Object.entries(value).map(([key, entryValue]) => [key, String(entryValue)]),
134
- );
135
- }
136
-
137
- function toErrorEnvelope(
138
- error: unknown,
139
- overrides?: Partial<ContainerErrorResponse["error"]>,
140
- ): ContainerErrorResponse {
141
- const providerError = error instanceof ProviderError ? error : null;
142
- const fallbackMessage =
143
- error instanceof Error ? error.message : "Unknown error";
144
-
145
- return {
146
- error: {
147
- code: overrides?.code ?? providerError?.code ?? "INTERNAL_ERROR",
148
- message: overrides?.message ?? fallbackMessage,
149
- ...(overrides?.details !== undefined
150
- ? { details: overrides.details }
151
- : providerError?.options?.fix
152
- ? { details: { fix: providerError.options.fix } }
153
- : {}),
154
- },
155
- };
156
- }
157
-
158
- function getStatusCode(error: unknown): ErrorStatusCode {
159
- if (error instanceof ProviderError && error.code === "NOT_FOUND") {
160
- return 404;
161
- }
162
-
163
- if (error instanceof z.ZodError) {
164
- return 400;
165
- }
166
-
167
- return 500;
168
- }
169
-
170
- function getFieldLabel(
171
- provider: ProviderDefinition,
172
- fieldName: string,
173
- ): string | undefined {
174
- return provider.auth?.fields?.find((field) => field.name === fieldName)
175
- ?.label;
176
- }
177
-
178
- function toConnectFailedResponse(error: unknown): ConnectResponse {
179
- return {
180
- status: "failed",
181
- error:
182
- error instanceof ProviderError
183
- ? (error.code ?? "AUTH_FAILED")
184
- : "AUTH_FAILED",
185
- message: error instanceof Error ? error.message : "Authentication failed",
186
- };
187
- }
188
-
189
- function toRefreshFailedResponse(error: unknown): RefreshResponse {
190
- return {
191
- status: "failed",
192
- error:
193
- error instanceof ProviderError
194
- ? (error.code ?? "REFRESH_FAILED")
195
- : "REFRESH_FAILED",
196
- message: error instanceof Error ? error.message : "Refresh failed",
197
- };
198
- }
199
-
200
- function toDisconnectFailedResponse(error: unknown): DisconnectResponse {
201
- return {
202
- status: "failed",
203
- error:
204
- error instanceof ProviderError
205
- ? (error.code ?? "DISCONNECT_FAILED")
206
- : error instanceof Error
207
- ? error.message
208
- : "DISCONNECT_FAILED",
209
- };
210
- }
211
-
212
- function toSchema(schema: ZodType): Record<string, unknown> {
213
- const zodModule = z as typeof z & {
214
- toJSONSchema?: (schema: ZodType) => Record<string, unknown>;
215
- };
216
- const schemaWithMethod = schema as ZodType & {
217
- toJSONSchema?: () => Record<string, unknown>;
218
- };
219
-
220
- if (typeof zodModule.toJSONSchema === "function") {
221
- return zodModule.toJSONSchema(schema);
222
- }
223
-
224
- if (typeof schemaWithMethod.toJSONSchema === "function") {
225
- return schemaWithMethod.toJSONSchema();
226
- }
227
-
228
- throw new ProviderError("Zod JSON Schema conversion is unavailable", {
229
- code: "SCHEMA_CONVERSION_UNAVAILABLE",
230
- });
231
- }
232
-
233
- function getTracePayload(
234
- trace: TraceContext,
235
- ): ExecuteResponse["trace"] | undefined {
236
- const spans = trace.getSpans();
237
- return spans.length > 0 ? { spans } : undefined;
238
- }
239
-
240
- function toStringSessionPatch(
241
- sessionPatch: SessionPatch,
242
- ): Record<string, string> {
243
- return Object.fromEntries(
244
- Object.entries(sessionPatch).filter(
245
- (entry): entry is [string, string] => entry[1] !== null,
246
- ),
247
- );
248
- }
249
-
250
- function assertAuthConfigured(
251
- provider: ProviderDefinition,
252
- ): asserts provider is ProviderDefinition & {
253
- auth: NonNullable<ProviderDefinition["auth"]>;
254
- } {
255
- if (!provider.auth || provider.auth.mode === "none") {
256
- throw new ProviderError("Auth is not configured", {
257
- code: "AUTH_NOT_CONFIGURED",
258
- });
259
- }
260
-
261
- if (!provider.auth.exchange) {
262
- throw new ProviderError("Auth exchange is not configured", {
263
- code: "AUTH_EXCHANGE_NOT_CONFIGURED",
264
- });
265
- }
266
- }
267
-
268
- function createContainerContext(
269
- provider: ProviderDefinition,
270
- sessionSnapshot: Record<string, string> = {},
271
- options: {
272
- auth?: AuthContext;
273
- baseUrl?: string;
274
- stateSecret?: string;
275
- } = {},
276
- ): {
277
- ctx: ProviderContext;
278
- getSessionPatch: () => SessionPatch;
279
- close: () => Promise<void>;
280
- trace: TraceContext;
281
- } {
282
- const overlay = createSessionOverlay(sessionSnapshot);
283
- const trace = createTraceContext({ onSpan: () => {} });
284
- const resolvedBaseUrl = options.baseUrl ?? getProviderBaseUrl(provider);
285
- const browser =
286
- provider.runtime === "browser"
287
- ? createBrowserClient({
288
- cdpUrl: process.env.CDP_POOL_URL ?? process.env.APIFUSE_CDP_POOL_URL,
289
- headless: true,
290
- stealth: true,
291
- engine: provider.browser?.engine,
292
- })
293
- : createBrowserStub();
294
- const tls = resolvedBaseUrl
295
- ? createTlsClient(resolvedBaseUrl)
296
- : createTlsStub();
297
-
298
- const ctx: ProviderContext = {
299
- http: createHttpClient(resolvedBaseUrl),
300
- tls,
301
- browser,
302
- session: overlay.store,
303
- state: createStateContext(options.stateSecret),
304
- trace,
305
- auth:
306
- options.auth ??
307
- ({
308
- async requestField(name: string): Promise<string> {
309
- throw new ProviderError(`Deferred auth field requested: ${name}`, {
310
- code: "AUTH_FIELD_REQUESTED",
311
- });
312
- },
313
- } satisfies AuthContext),
314
- };
315
-
316
- return {
317
- ctx,
318
- getSessionPatch: overlay.getPatch,
319
- close: async () => await closeBrowserRuntime(browser),
320
- trace,
321
- };
322
- }
323
-
324
- async function runAuthExchange(
325
- provider: ProviderDefinition,
326
- credentials: Record<string, string>,
327
- sessionSnapshot: Record<string, string>,
328
- resolvedFields: Record<string, string>,
329
- options: {
330
- baseUrl?: string;
331
- stateSecret?: string;
332
- },
333
- ): Promise<
334
- | { type: "success"; sessionPatch: Record<string, string> }
335
- | {
336
- type: "pending_field";
337
- field: string;
338
- fieldLabel?: string;
339
- resumeToken: string;
340
- sessionPatch?: Record<string, string>;
341
- }
342
- > {
343
- assertAuthConfigured(provider);
344
- const auth = provider.auth as NonNullable<ProviderDefinition["auth"]> & {
345
- exchange: NonNullable<NonNullable<ProviderDefinition["auth"]>["exchange"]>;
346
- };
347
-
348
- let requestedField: string | null = null;
349
- const { ctx, getSessionPatch, close } = createContainerContext(
350
- provider,
351
- sessionSnapshot,
352
- {
353
- baseUrl: options.baseUrl,
354
- stateSecret: options.stateSecret,
355
- auth: {
356
- async requestField(name: string): Promise<string> {
357
- const resolvedValue = resolvedFields[name];
358
- if (resolvedValue !== undefined) {
359
- return resolvedValue;
360
- }
361
-
362
- requestedField = name;
363
- throw new PendingFieldError(name);
364
- },
365
- },
366
- },
367
- );
368
-
369
- try {
370
- await auth.exchange(ctx, credentials);
371
- await ctx.session.set(
372
- AUTH_SESSION_KEY,
373
- createAuthSessionMarker({ authenticated: true, timestamp: Date.now() }),
374
- );
375
- return {
376
- type: "success",
377
- sessionPatch: toStringSessionPatch(getSessionPatch()),
378
- };
379
- } catch (error) {
380
- if (error instanceof PendingFieldError && requestedField) {
381
- const resumeToken = await ctx.state.seal(
382
- {
383
- credentials,
384
- resolvedFields,
385
- requestedField,
386
- } satisfies PendingAuthState,
387
- { ttl: "15m" },
388
- );
389
-
390
- const sessionPatch = toStringSessionPatch(getSessionPatch());
391
- return {
392
- type: "pending_field",
393
- field: requestedField,
394
- fieldLabel: getFieldLabel(provider, requestedField),
395
- resumeToken,
396
- ...(Object.keys(sessionPatch).length > 0 ? { sessionPatch } : {}),
397
- };
398
- }
399
-
400
- throw error;
401
- } finally {
402
- await close();
403
- }
404
- }
405
-
406
- async function parseJsonBody<T>(request: Request): Promise<T> {
407
- try {
408
- return (await request.json()) as T;
409
- } catch (error) {
410
- throw new ProviderError("Invalid JSON body", {
411
- code: "INVALID_JSON",
412
- cause: error instanceof Error ? error : undefined,
413
- });
414
- }
415
- }
416
-
417
- export function createProviderServer(
418
- provider: ProviderDefinition,
419
- options: {
420
- port?: number;
421
- baseUrl?: string;
422
- stateSecret?: string;
423
- sessionDbPath?: string;
424
- } = {},
425
- ): { app: Hono; start: () => void } {
426
- const startedAt = Date.now();
427
- const app = new Hono();
428
-
429
- app.post("/execute/:operationId", async (c) => {
430
- try {
431
- const operationId = c.req.param("operationId");
432
- const body = await parseJsonBody<ExecuteRequest>(c.req.raw);
433
- const { ctx, getSessionPatch, trace, close } = createContainerContext(
434
- provider,
435
- toSessionSnapshot(body.session),
436
- { baseUrl: options.baseUrl, stateSecret: options.stateSecret },
437
- );
438
-
439
- try {
440
- const data = await executeOperation(
441
- provider,
442
- operationId,
443
- ctx,
444
- body.input,
445
- );
446
- const response: ExecuteResponse = {
447
- data,
448
- sessionPatch: getSessionPatch(),
449
- ...(getTracePayload(trace) ? { trace: getTracePayload(trace) } : {}),
450
- };
451
-
452
- return c.json(response);
453
- } finally {
454
- await close();
455
- }
456
- } catch (error) {
457
- return c.json(toErrorEnvelope(error), getStatusCode(error));
458
- }
459
- });
460
-
461
- app.post("/connect", async (c) => {
462
- try {
463
- const body = await parseJsonBody<ConnectRequest>(c.req.raw);
464
- const result = await runAuthExchange(
465
- provider,
466
- toStringRecord(body.credentials),
467
- toSessionSnapshot(body.session),
468
- {},
469
- { baseUrl: options.baseUrl, stateSecret: options.stateSecret },
470
- );
471
-
472
- const response: ConnectResponse =
473
- result.type === "success"
474
- ? {
475
- status: "success",
476
- sessionPatch: result.sessionPatch,
477
- }
478
- : {
479
- status: "pending_field",
480
- field: result.field,
481
- resumeToken: result.resumeToken,
482
- ...(result.fieldLabel ? { fieldLabel: result.fieldLabel } : {}),
483
- ...(result.sessionPatch
484
- ? { sessionPatch: result.sessionPatch }
485
- : {}),
486
- };
487
-
488
- return c.json(response);
489
- } catch (error) {
490
- return c.json(toConnectFailedResponse(error));
491
- }
492
- });
493
-
494
- app.post("/connect/resume", async (c) => {
495
- try {
496
- const body = await parseJsonBody<ResumeRequest>(c.req.raw);
497
- const state = createStateContext(options.stateSecret);
498
- const pendingState = await state.unseal<PendingAuthState>(
499
- body.resumeToken,
500
- );
501
-
502
- if (!pendingState) {
503
- const response: ResumeResponse = {
504
- status: "failed",
505
- error: "INVALID_RESUME_TOKEN",
506
- message: "Resume token is invalid or expired",
507
- };
508
- return c.json(response);
509
- }
510
-
511
- const result = await runAuthExchange(
512
- provider,
513
- pendingState.credentials,
514
- toSessionSnapshot(body.session),
515
- {
516
- ...pendingState.resolvedFields,
517
- [pendingState.requestedField]: body.fieldValue,
518
- },
519
- { baseUrl: options.baseUrl, stateSecret: options.stateSecret },
520
- );
521
-
522
- const response: ResumeResponse =
523
- result.type === "success"
524
- ? {
525
- status: "success",
526
- sessionPatch: result.sessionPatch,
527
- }
528
- : {
529
- status: "pending_field",
530
- field: result.field,
531
- resumeToken: result.resumeToken,
532
- ...(result.fieldLabel ? { fieldLabel: result.fieldLabel } : {}),
533
- ...(result.sessionPatch
534
- ? { sessionPatch: result.sessionPatch }
535
- : {}),
536
- };
537
-
538
- return c.json(response);
539
- } catch (error) {
540
- return c.json(toConnectFailedResponse(error));
541
- }
542
- });
543
-
544
- app.post("/refresh", async (c) => {
545
- try {
546
- if (!provider.auth?.refresh) {
547
- throw new ProviderError("Auth refresh is not configured", {
548
- code: "AUTH_REFRESH_NOT_CONFIGURED",
549
- });
550
- }
551
-
552
- const body = await parseJsonBody<RefreshRequest>(c.req.raw);
553
- const { ctx, getSessionPatch, close } = createContainerContext(
554
- provider,
555
- body.session,
556
- { baseUrl: options.baseUrl, stateSecret: options.stateSecret },
557
- );
558
-
559
- try {
560
- await provider.auth.refresh(ctx);
561
- await ctx.session.set(
562
- AUTH_SESSION_KEY,
563
- createAuthSessionMarker({
564
- authenticated: true,
565
- refreshed: true,
566
- timestamp: Date.now(),
567
- }),
568
- );
569
- const response: RefreshResponse = {
570
- status: "success",
571
- sessionPatch: toStringSessionPatch(getSessionPatch()),
572
- };
573
-
574
- return c.json(response);
575
- } finally {
576
- await close();
577
- }
578
- } catch (error) {
579
- return c.json(toRefreshFailedResponse(error));
580
- }
581
- });
582
-
583
- app.post("/disconnect", async (c) => {
584
- try {
585
- const body = await parseJsonBody<DisconnectRequest>(c.req.raw);
586
- const { ctx, close } = createContainerContext(provider, body.session, {
587
- baseUrl: options.baseUrl,
588
- stateSecret: options.stateSecret,
589
- });
590
-
591
- try {
592
- if (provider.auth?.disconnect) {
593
- await provider.auth.disconnect(ctx);
594
- }
595
-
596
- await ctx.session.delete(AUTH_SESSION_KEY);
597
- const response: DisconnectResponse = { status: "success" };
598
- return c.json(response);
599
- } finally {
600
- await close();
601
- }
602
- } catch (error) {
603
- return c.json(toDisconnectFailedResponse(error));
604
- }
605
- });
606
-
607
- app.get("/health", (c) => {
608
- const response: HealthResponse = {
609
- status: "ok",
610
- provider: provider.id,
611
- version: provider.version,
612
- uptime: Date.now() - startedAt,
613
- };
614
-
615
- return c.json(response);
616
- });
617
-
618
- app.get("/schema/:operationId", (c) => {
619
- try {
620
- const operationId = c.req.param("operationId");
621
- const operation = provider.operations[operationId];
622
-
623
- if (!operation) {
624
- throw new ProviderError(
625
- `Unknown operation: ${provider.id}/${operationId}`,
626
- {
627
- code: "NOT_FOUND",
628
- fix: `Valid operations: ${Object.keys(provider.operations).join(", ")}`,
629
- },
630
- );
631
- }
632
-
633
- const response: SchemaResponse = {
634
- operationId,
635
- ...(operation.description
636
- ? { description: operation.description }
637
- : {}),
638
- input: toSchema(operation.input),
639
- output: toSchema(operation.output),
640
- ...(operation.hints ? { hints: operation.hints } : {}),
641
- };
642
-
643
- return c.json(response);
644
- } catch (error) {
645
- return c.json(toErrorEnvelope(error), getStatusCode(error));
646
- }
647
- });
648
-
649
- return {
650
- app,
651
- start() {
652
- if (typeof Bun === "undefined") {
653
- throw new ProviderError("Bun runtime is required to start the server", {
654
- code: "RUNTIME_UNSUPPORTED",
655
- });
656
- }
657
-
658
- Bun.serve({
659
- port: options.port ?? DEFAULT_PORT,
660
- fetch: app.fetch,
661
- });
662
- },
663
- };
664
- }
1
+ export { createServerApp, type ServeOptions, serve } from "./server/serve";
@@ -0,0 +1,22 @@
1
+ export { createServerApp, type ServeOptions, serve } from "./serve";
2
+ export type {
3
+ AuthFlowRequest,
4
+ AuthFlowResponse,
5
+ AuthFlowSuccessResponse,
6
+ ConnectionMode,
7
+ OperationConnection,
8
+ OperationErrorResponse,
9
+ OperationRequest,
10
+ OperationResponse,
11
+ OperationSuccessResponse,
12
+ } from "./types";
13
+ export {
14
+ AuthFlowRequestSchema,
15
+ AuthFlowSuccessResponseSchema,
16
+ ConnectionModeSchema,
17
+ ErrorEnvelopeSchema,
18
+ OperationConnectionSchema,
19
+ OperationErrorResponseSchema,
20
+ OperationRequestSchema,
21
+ OperationSuccessResponseSchema,
22
+ } from "./types";