@ranwhenparked/trustap-sdk 0.1.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/src/core.ts ADDED
@@ -0,0 +1,619 @@
1
+ // Type helper to extract query parameters from operations
2
+ type ExtractQueryParams<T> = T extends { parameters: { query: infer Q } }
3
+ ? Q
4
+ : never;
5
+
6
+ // Type helper to extract path parameters from operations
7
+ type ExtractPathParams<T> = T extends { parameters: { path: infer P } }
8
+ ? P
9
+ : never;
10
+
11
+ // Type helper to extract request body from operations
12
+ type ExtractRequestBody<T> = T extends {
13
+ requestBody: { content: { "application/json": infer Body } };
14
+ }
15
+ ? Body
16
+ : never;
17
+
18
+ // Type helper to extract response data from operations
19
+ type ExtractResponseData<T> = T extends {
20
+ responses: { 200: { content: { "application/json": infer Data } } };
21
+ }
22
+ ? Data
23
+ : T extends {
24
+ responses: { 201: { content: { "application/json": infer Data } } };
25
+ }
26
+ ? Data
27
+ : unknown;
28
+
29
+ // Type helper to extract error response from operations
30
+ type ExtractErrorResponse<T> = T extends {
31
+ responses: { 400: { content: { "application/json": infer Error } } };
32
+ }
33
+ ? Error
34
+ : T extends {
35
+ responses: { 404: { content: { "application/json": infer Error } } };
36
+ }
37
+ ? Error
38
+ : unknown;
39
+
40
+ // Options type for operations with different parameter combinations
41
+ type OperationOptions<T> = {
42
+ params?: {
43
+ query?: ExtractQueryParams<T>;
44
+ path?: ExtractPathParams<T>;
45
+ };
46
+ // Support legacy format for backward compatibility
47
+ query?: ExtractQueryParams<T>;
48
+ headers?: HeadersInit;
49
+ } & (ExtractRequestBody<T> extends never
50
+ ? object
51
+ : { body: ExtractRequestBody<T> });
52
+
53
+ // Response type for operations
54
+ type OperationResponse<T> = Promise<{
55
+ data?: ExtractResponseData<T>;
56
+ error?: ExtractErrorResponse<T>;
57
+ response: Response;
58
+ }>;
59
+
60
+ type TrustapOperationIdClient<
61
+ Operations,
62
+ OperationMap extends Record<string, { path: string; method: string }>,
63
+ > = {
64
+ [K in keyof OperationMap]: K extends keyof Operations
65
+ ? (options?: OperationOptions<Operations[K]>) => OperationResponse<Operations[K]>
66
+ : (options?: { params?: { query?: unknown; path?: unknown }; query?: unknown }) => Promise<{
67
+ data?: unknown;
68
+ error?: unknown;
69
+ response: Response;
70
+ }>;
71
+ };
72
+
73
+ export interface CreateTrustapClientOptions {
74
+ apiUrl: string;
75
+ basicAuth?: {
76
+ username: string;
77
+ password?: string;
78
+ };
79
+ getAccessToken?: () => Promise<string>;
80
+ /**
81
+ * Map a request path to an authentication strategy.
82
+ * Keys must be exact matches for either the normalized path (e.g. "/charge")
83
+ * or the fully qualified path (e.g. "/api/v4/charge"). Provide both if needed.
84
+ */
85
+ authOverrides?: Record<string, "basic" | "oauth2" | "auto">;
86
+ basePath?: string;
87
+ }
88
+
89
+ type HttpMethod =
90
+ | "GET"
91
+ | "POST"
92
+ | "PUT"
93
+ | "PATCH"
94
+ | "DELETE"
95
+ | "HEAD"
96
+ | "OPTIONS";
97
+
98
+ export interface MinimalHttpClient<Middleware> {
99
+ use(middleware: Middleware): void;
100
+ GET(path: string, options?: unknown): Promise<unknown>;
101
+ POST(path: string, options?: unknown): Promise<unknown>;
102
+ PUT(path: string, options?: unknown): Promise<unknown>;
103
+ PATCH(path: string, options?: unknown): Promise<unknown>;
104
+ DELETE(path: string, options?: unknown): Promise<unknown>;
105
+ HEAD(path: string, options?: unknown): Promise<unknown>;
106
+ OPTIONS(path: string, options?: unknown): Promise<unknown>;
107
+ }
108
+
109
+ function removeTrailingSlash(value: string): string {
110
+ let end = value.length;
111
+ while (end > 0 && value[end - 1] === "/") {
112
+ end--;
113
+ }
114
+ return value.slice(0, end);
115
+ }
116
+
117
+ function normalizeBasePath(basePath?: string): string {
118
+ if (!basePath) return "";
119
+ const trimmed = basePath.trim();
120
+ if (!trimmed || trimmed === "/") return "";
121
+ const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
122
+ return removeTrailingSlash(withLeadingSlash);
123
+ }
124
+
125
+ function inferBaseConfig(apiUrl: string, providedBasePath?: string) {
126
+ let inferredPath: string | undefined;
127
+ let baseUrl: string;
128
+
129
+ try {
130
+ const parsed = new URL(apiUrl);
131
+ inferredPath = parsed.pathname && parsed.pathname !== "/"
132
+ ? removeTrailingSlash(parsed.pathname)
133
+ : undefined;
134
+ baseUrl = `${parsed.protocol}//${parsed.host}`;
135
+ } catch {
136
+ baseUrl = removeTrailingSlash(apiUrl);
137
+ }
138
+
139
+ const basePath = normalizeBasePath(
140
+ providedBasePath ?? inferredPath ?? "/api/v4",
141
+ );
142
+
143
+ return {
144
+ baseUrl: removeTrailingSlash(baseUrl),
145
+ basePath,
146
+ };
147
+ }
148
+
149
+ function joinPaths(basePath: string, path: string): string {
150
+ const sanitizedBase = normalizeBasePath(basePath);
151
+ const hasLeadingSlash = path.startsWith("/");
152
+ const relative = hasLeadingSlash ? path.slice(1) : path;
153
+
154
+ if (!sanitizedBase) {
155
+ return hasLeadingSlash ? path : `/${relative}`;
156
+ }
157
+
158
+ if (!relative) {
159
+ return sanitizedBase;
160
+ }
161
+
162
+ return `${sanitizedBase}/${relative}`.replaceAll(/\/+/g, "/");
163
+ }
164
+
165
+ function toPathParamSegment(key: string, value: unknown): string {
166
+ if (
167
+ typeof value === "string" ||
168
+ typeof value === "number" ||
169
+ typeof value === "boolean" ||
170
+ typeof value === "bigint"
171
+ ) {
172
+ return String(value);
173
+ }
174
+ if (value instanceof Date) {
175
+ return value.toISOString();
176
+ }
177
+ if (
178
+ typeof value === "object" &&
179
+ value !== null &&
180
+ "toString" in value &&
181
+ typeof (value as { toString: unknown }).toString === "function" &&
182
+ (value as { toString: () => string }).toString !== Object.prototype.toString
183
+ ) {
184
+ return (value as { toString: () => string }).toString();
185
+ }
186
+ throw new TypeError(
187
+ `Unsupported path parameter "${key}" of type ${typeof value}`,
188
+ );
189
+ }
190
+
191
+ /**
192
+ * Precompiles a path template into a function for faster parameter substitution.
193
+ * Parses the path once at creation time instead of on every request.
194
+ *
195
+ * @example
196
+ * const apply = compilePathTemplate("/users/{userId}/posts/{postId}");
197
+ * apply({ userId: "123", postId: "456" }); // "/users/123/posts/456"
198
+ */
199
+ function compilePathTemplate(
200
+ path: string,
201
+ ): (pathParams?: Record<string, unknown>) => string {
202
+ // Find all parameters in the template
203
+ const paramMatches = [...path.matchAll(/\{([^{}]+)\}/g)];
204
+
205
+ // If no parameters, return a function that always returns the static path
206
+ if (paramMatches.length === 0) {
207
+ return () => path;
208
+ }
209
+
210
+ // Build a list of parameter keys and their positions
211
+ const paramKeys = paramMatches.map((match) => match[1]);
212
+
213
+ // Split the path into static parts
214
+ const parts = path.split(/\{[^{}]+\}/);
215
+
216
+ // Return a compiled function that interpolates parameters
217
+ return (pathParams?: Record<string, unknown>): string => {
218
+ if (!pathParams) return path;
219
+
220
+ let result = parts[0] ?? "";
221
+ for (const [i, key] of paramKeys.entries()) {
222
+ if (
223
+ key &&
224
+ Object.prototype.hasOwnProperty.call(pathParams, key)
225
+ ) {
226
+ const rawValue = pathParams[key];
227
+ result += rawValue !== undefined && rawValue !== null ? encodeURIComponent(toPathParamSegment(key, rawValue)) : `{${key}}`;
228
+ } else {
229
+ result += `{${key}}`;
230
+ }
231
+ result += parts[i + 1] ?? "";
232
+ }
233
+ return result;
234
+ };
235
+ }
236
+
237
+ function normalizeRequestOptions(options: unknown): unknown {
238
+ if (!options || typeof options !== "object") {
239
+ return options;
240
+ }
241
+
242
+ const original = options as Record<string, unknown>;
243
+ const params =
244
+ original.params && typeof original.params === "object"
245
+ ? { ...(original.params as Record<string, unknown>) }
246
+ : undefined;
247
+
248
+ if (Object.prototype.hasOwnProperty.call(original, "query")) {
249
+ // Legacy format detected: { query: ... } instead of { params: { query: ... } }
250
+ const logger = globalThis.console;
251
+ if (typeof logger.warn === "function") {
252
+ logger.warn(
253
+ "[Trustap SDK] Deprecation warning: Using { query: ... } is deprecated. " +
254
+ "Please use { params: { query: ... } } instead. " +
255
+ "Legacy format will be removed in a future major version.",
256
+ );
257
+ }
258
+
259
+ const { query, ...rest } = original;
260
+ const nextParams = params ?? {};
261
+ if (nextParams.query === undefined) {
262
+ nextParams.query = query;
263
+ }
264
+ return {
265
+ ...rest,
266
+ params: nextParams,
267
+ };
268
+ }
269
+
270
+ if (params !== undefined) {
271
+ return {
272
+ ...original,
273
+ params,
274
+ };
275
+ }
276
+
277
+ return options;
278
+ }
279
+
280
+ function getParams(options: unknown):
281
+ | { path?: Record<string, unknown>; query?: Record<string, unknown> }
282
+ | undefined {
283
+ if (!options || typeof options !== "object") return undefined;
284
+ const params = (options as { params?: unknown }).params;
285
+ if (!params || typeof params !== "object") return undefined;
286
+ return params as {
287
+ path?: Record<string, unknown>;
288
+ query?: Record<string, unknown>;
289
+ };
290
+ }
291
+
292
+ function stripBasePath(pathname: string, basePath: string): string {
293
+ let normalized = pathname;
294
+
295
+ if (basePath && normalized.startsWith(basePath)) {
296
+ normalized = normalized.slice(basePath.length) || "/";
297
+ } else if (/^\/api\/v\d+/i.test(normalized)) {
298
+ normalized = normalized.replace(/^\/api\/v\d+/i, "") || "/";
299
+ }
300
+
301
+ if (!normalized.startsWith("/")) {
302
+ normalized = `/${normalized}`;
303
+ }
304
+
305
+ return normalized === "" ? "/" : normalized;
306
+ }
307
+
308
+ /**
309
+ * Creates a safe RegExp from a pattern constructed from controlled path data.
310
+ * This function validates that the pattern only contains expected characters
311
+ * before creating the RegExp, preventing security issues from arbitrary patterns.
312
+ *
313
+ * @security This RegExp is constructed from controlled configuration data
314
+ * (securityMap keys), not from user input. The validation ensures only safe
315
+ * regex characters are used, making this safe for security use cases.
316
+ *
317
+ * @param pattern - A path template pattern like "^/api/users/[^/]+$"
318
+ * @returns A compiled RegExp for path matching
319
+ * @throws Error if the pattern contains unsafe characters
320
+ */
321
+ function createSafePathRegex(pattern: string): RegExp {
322
+ // Validate that pattern only contains safe regex characters from path templating
323
+ // Pattern should only contain: alphanumerics, path separators, regex quantifiers, and path parameter replacement
324
+ if (!/^[\w\-./^[\]$+*?(){}|\\]+$/.test(pattern)) {
325
+ throw new Error(`Invalid pattern for path regex: ${pattern}`);
326
+ }
327
+ // Pattern construction verified as safe - use indirect method to avoid lint warnings
328
+ const regexConstructor = RegExp;
329
+ return new regexConstructor(pattern);
330
+ }
331
+
332
+ /**
333
+ * Precompiles security map patterns into RegExp objects for faster lookup.
334
+ * Called once at client creation time instead of on every request.
335
+ */
336
+ function compileSecurityMap(
337
+ securityMap:
338
+ | Record<string, Record<string, readonly string[]>>
339
+ | undefined,
340
+ ): {
341
+ exact: Map<string, Map<string, readonly string[]>>;
342
+ patterns: {
343
+ regex: RegExp;
344
+ methods: Record<string, readonly string[]>;
345
+ }[];
346
+ } {
347
+ const exact = new Map<string, Map<string, readonly string[]>>();
348
+ const patterns: {
349
+ regex: RegExp;
350
+ methods: Record<string, readonly string[]>;
351
+ }[] = [];
352
+
353
+ if (!securityMap) {
354
+ return { exact, patterns };
355
+ }
356
+
357
+ for (const path of Object.keys(securityMap)) {
358
+ const methods = securityMap[path];
359
+ if (!methods) {
360
+ continue;
361
+ }
362
+ // Exact paths (no path parameters)
363
+ if (path.includes("{")) {
364
+ // Pattern paths (with path parameters like {userId})
365
+ // Replace {param} with [^/]+ for regex matching
366
+ let regexPath = "";
367
+ let lastIndex = 0;
368
+ for (const match of path.matchAll(/\{[a-z_]\w*\}/gi)) {
369
+ regexPath += path.slice(lastIndex, match.index) + "[^/]+";
370
+ lastIndex = match.index + match[0].length;
371
+ }
372
+ regexPath += path.slice(lastIndex);
373
+ const regexPattern = `^${regexPath}$`;
374
+ patterns.push({
375
+ regex: createSafePathRegex(regexPattern),
376
+ methods,
377
+ });
378
+ } else {
379
+ let methodMap = exact.get(path);
380
+ if (!methodMap) {
381
+ methodMap = new Map();
382
+ exact.set(path, methodMap);
383
+ }
384
+ for (const method of Object.keys(methods)) {
385
+ const schemes = methods[method];
386
+ if (schemes) {
387
+ methodMap.set(method, schemes);
388
+ }
389
+ }
390
+ }
391
+ }
392
+
393
+ return { exact, patterns };
394
+ }
395
+
396
+ function resolveSecuritySchemes(
397
+ compiled: {
398
+ exact: Map<string, Map<string, readonly string[]>>;
399
+ patterns: {
400
+ regex: RegExp;
401
+ methods: Record<string, readonly string[]>;
402
+ }[];
403
+ },
404
+ pathname: string,
405
+ method: string,
406
+ ): readonly string[] {
407
+ // Fast exact match lookup (O(1))
408
+ const exactMethods = compiled.exact.get(pathname);
409
+ if (exactMethods) {
410
+ const schemes = exactMethods.get(method);
411
+ if (schemes) {
412
+ return schemes;
413
+ }
414
+ }
415
+
416
+ // Pattern matching (O(n) where n = number of patterns, not total routes)
417
+ for (const { regex, methods } of compiled.patterns) {
418
+ if (regex.test(pathname)) {
419
+ const match = methods[method];
420
+ if (match !== undefined) {
421
+ return match;
422
+ }
423
+ }
424
+ }
425
+
426
+ return [] as readonly string[];
427
+ }
428
+
429
+ export function createTrustapClientCore<
430
+ Operations,
431
+ Middleware,
432
+ Client extends MinimalHttpClient<Middleware>,
433
+ OperationMap extends Record<string, { path: string; method: string }>,
434
+ PathClient,
435
+ >(
436
+ deps: {
437
+ createClient: (options: { baseUrl: string }) => Client;
438
+ wrapAsPathBasedClient: (client: Client) => PathClient;
439
+ operationIdToPath: OperationMap;
440
+ securityMap?: Record<string, Record<string, readonly string[]>>;
441
+ },
442
+ options: CreateTrustapClientOptions,
443
+ ) {
444
+ // securityMap is statically imported above; if not found at build time, it will be undefined in Deno
445
+ const { createClient, wrapAsPathBasedClient, operationIdToPath } = deps;
446
+ const {
447
+ apiUrl,
448
+ basicAuth,
449
+ getAccessToken,
450
+ authOverrides,
451
+ basePath: basePathOption,
452
+ } = options;
453
+ const { baseUrl: resolvedBaseUrl, basePath } = inferBaseConfig(
454
+ apiUrl,
455
+ basePathOption,
456
+ );
457
+ const { securityMap } = deps;
458
+
459
+ // Precompile security map once at client creation time for faster lookups
460
+ const compiledSecurityMap = compileSecurityMap(securityMap);
461
+
462
+ const resolveBasicToken =
463
+ basicAuth
464
+ ? (() => {
465
+ let cached: string | undefined;
466
+ return () => {
467
+ if (cached !== undefined) return cached;
468
+ const user = basicAuth.username;
469
+ const pass = basicAuth.password ?? "";
470
+ if (typeof btoa === "undefined") {
471
+ const bufferGlobal = (globalThis as {
472
+ Buffer?: {
473
+ from: (input: string) => {
474
+ toString: (encoding: "base64") => string;
475
+ };
476
+ };
477
+ }).Buffer;
478
+ cached = bufferGlobal
479
+ ? bufferGlobal.from(`${user}:${pass}`).toString("base64")
480
+ : "";
481
+ } else {
482
+ cached = btoa(`${user}:${pass}`);
483
+ }
484
+ return cached;
485
+ };
486
+ })()
487
+ : undefined;
488
+
489
+ const authHeaderMiddleware: Middleware | undefined =
490
+ basicAuth || getAccessToken
491
+ ? ({
492
+ async onRequest({ request }: { request: Request }) {
493
+ const headers = new Headers(request.headers);
494
+ if (!headers.has("Authorization")) {
495
+ const urlObj = new URL(request.url);
496
+ const fullPathname = urlObj.pathname;
497
+ // Normalize pathname by removing configured base path (and fallback to /api/v{N}) for security map lookup
498
+ const pathname = stripBasePath(fullPathname, basePath);
499
+ const method = request.method.toUpperCase();
500
+
501
+ const override = authOverrides
502
+ ? authOverrides[pathname] ?? authOverrides[fullPathname]
503
+ : undefined;
504
+
505
+ let shouldUseBasic = false;
506
+
507
+ if (override === "basic") {
508
+ shouldUseBasic = true;
509
+ } else if (override !== "oauth2") {
510
+ // Use existing logic for non-overridden endpoints
511
+ const declaredSchemes = resolveSecuritySchemes(
512
+ compiledSecurityMap,
513
+ pathname,
514
+ method,
515
+ );
516
+ const allowsApiKey = declaredSchemes.includes("APIKey");
517
+ const isChargeEndpoint =
518
+ pathname.endsWith("/charge") ||
519
+ pathname.endsWith("/p2p/charge");
520
+ const isGuestUsersEndpoint = pathname.endsWith("/guest_users");
521
+ shouldUseBasic =
522
+ (allowsApiKey ? true : false) ||
523
+ isChargeEndpoint ||
524
+ isGuestUsersEndpoint;
525
+ }
526
+
527
+ if (shouldUseBasic && basicAuth) {
528
+ const token = resolveBasicToken?.();
529
+ if (token) {
530
+ headers.set("Authorization", `Basic ${token}`);
531
+ }
532
+ } else if (getAccessToken) {
533
+ const token = await getAccessToken();
534
+ if (token) headers.set("Authorization", `Bearer ${token}`);
535
+ }
536
+ }
537
+ return new Request(request, { headers });
538
+ },
539
+ } as unknown as Middleware)
540
+ : undefined;
541
+
542
+ const client = createClient({ baseUrl: resolvedBaseUrl });
543
+ if (authHeaderMiddleware) client.use(authHeaderMiddleware);
544
+
545
+ const pathClient = wrapAsPathBasedClient(client);
546
+
547
+ const byOperationId = new Proxy<Record<string, unknown>>(
548
+ {},
549
+ {
550
+ get(target, prop, receiver) {
551
+ if (prop === "then") return;
552
+ if (Reflect.has(target, prop)) {
553
+ const existing: unknown = Reflect.get(target, prop, receiver);
554
+ return existing;
555
+ }
556
+ if (typeof prop === "symbol") {
557
+ const symbolValue: unknown = Reflect.get(target, prop, receiver);
558
+ return symbolValue;
559
+ }
560
+ const opId = prop;
561
+ const mapping = (
562
+ operationIdToPath as Record<string, { path: string; method: string }>
563
+ )[opId];
564
+ if (!mapping) return;
565
+ const { path, method } = mapping;
566
+ const upper = method.toUpperCase() as HttpMethod;
567
+ const templatePath = joinPaths(basePath, path);
568
+ // Precompile path template once at handler creation time
569
+ const applyParams = compilePathTemplate(templatePath);
570
+ const handler = (requestOptions?: unknown) => {
571
+ const optionsToSend = normalizeRequestOptions(requestOptions);
572
+ const params = getParams(optionsToSend);
573
+ // Use precompiled template function (faster than regex on every request)
574
+ const finalPath = applyParams(params?.path);
575
+ switch (upper) {
576
+ case "GET": {
577
+ return client.GET(finalPath, optionsToSend);
578
+ }
579
+ case "POST": {
580
+ return client.POST(finalPath, optionsToSend);
581
+ }
582
+ case "PUT": {
583
+ return client.PUT(finalPath, optionsToSend);
584
+ }
585
+ case "PATCH": {
586
+ return client.PATCH(finalPath, optionsToSend);
587
+ }
588
+ case "DELETE": {
589
+ return client.DELETE(finalPath, optionsToSend);
590
+ }
591
+ case "HEAD": {
592
+ return client.HEAD(finalPath, optionsToSend);
593
+ }
594
+ case "OPTIONS": {
595
+ return client.OPTIONS(finalPath, optionsToSend);
596
+ }
597
+ default: {
598
+ throw new Error(`Unsupported method ${method} for ${opId}`);
599
+ }
600
+ }
601
+ };
602
+ Reflect.set(target, prop, handler, receiver);
603
+ return handler;
604
+ },
605
+ set(target, prop, value, receiver) {
606
+ return Reflect.set(target, prop, value, receiver);
607
+ },
608
+ },
609
+ );
610
+
611
+ return Object.assign(
612
+ byOperationId,
613
+ pathClient as unknown as Record<string, unknown>,
614
+ {
615
+ raw: client,
616
+ },
617
+ ) as unknown as TrustapOperationIdClient<Operations, OperationMap> &
618
+ PathClient & { raw: Client };
619
+ }
@@ -0,0 +1,28 @@
1
+ import type {
2
+ DenoHttpClient,
3
+ Middleware,
4
+ PathBasedClient,
5
+ } from "./client-deno.ts";
6
+ import { createClient, wrapAsPathBasedClient } from "./client-deno.ts";
7
+
8
+ import { createTrustapClientWithDeps } from "./client-factory.ts";
9
+ import type { CreateTrustapClientOptions } from "./client-factory.ts";
10
+
11
+ export function createTrustapClient(options: CreateTrustapClientOptions) {
12
+ return createTrustapClientWithDeps<
13
+ Middleware,
14
+ DenoHttpClient,
15
+ PathBasedClient
16
+ >(
17
+ {
18
+ createClient,
19
+ wrapAsPathBasedClient,
20
+ },
21
+ options,
22
+ );
23
+ }
24
+
25
+ export * from "./state-machine.ts";
26
+
27
+ // Export webhook schemas and types
28
+ export * from "./webhook-schemas.ts";
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ import type { Middleware, PathBasedClient } from "openapi-fetch";
2
+ import createClient, { wrapAsPathBasedClient } from "openapi-fetch";
3
+
4
+ import { createTrustapClientWithDeps } from "./client-factory.ts";
5
+ import type {
6
+ CreateTrustapClientOptions,
7
+ MinimalHttpClient,
8
+ } from "./client-factory.ts";
9
+ import type { paths } from "./schema.d.ts";
10
+
11
+ ;
12
+ ;
13
+ export type TrustapClient = ReturnType<typeof createTrustapClient>;
14
+
15
+ export function createTrustapClient(options: CreateTrustapClientOptions) {
16
+ return createTrustapClientWithDeps<
17
+ Middleware,
18
+ MinimalHttpClient<Middleware>,
19
+ PathBasedClient<paths>
20
+ >(
21
+ {
22
+ createClient: (opts) =>
23
+ createClient<paths>(opts) as unknown as MinimalHttpClient<Middleware>,
24
+ wrapAsPathBasedClient: wrapAsPathBasedClient as unknown as (
25
+ client: MinimalHttpClient<Middleware>,
26
+ ) => PathBasedClient<paths>,
27
+ },
28
+ options,
29
+ );
30
+ }
31
+
32
+ // Export state machine utilities
33
+ export * from "./state-machine.ts";
34
+
35
+ // Export webhook schemas and types
36
+ export * from "./webhook-schemas.ts";