@bikefinder/api-client 0.2.0 → 0.4.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.
Files changed (127) hide show
  1. package/README.md +38 -2
  2. package/dist/admin/generated/client/client.gen.d.ts +3 -0
  3. package/dist/admin/generated/client/client.gen.d.ts.map +1 -0
  4. package/dist/admin/generated/client/client.gen.js +218 -0
  5. package/dist/admin/generated/client/client.gen.js.map +1 -0
  6. package/dist/admin/generated/client/index.d.ts +11 -0
  7. package/dist/admin/generated/client/index.d.ts.map +1 -0
  8. package/dist/admin/generated/client/index.js +7 -0
  9. package/dist/admin/generated/client/index.js.map +1 -0
  10. package/dist/admin/generated/client/types.gen.d.ts +121 -0
  11. package/dist/admin/generated/client/types.gen.d.ts.map +1 -0
  12. package/dist/admin/generated/client/types.gen.js +3 -0
  13. package/dist/admin/generated/client/types.gen.js.map +1 -0
  14. package/dist/admin/generated/client/utils.gen.d.ts +38 -0
  15. package/dist/admin/generated/client/utils.gen.d.ts.map +1 -0
  16. package/dist/admin/generated/client/utils.gen.js +232 -0
  17. package/dist/admin/generated/client/utils.gen.js.map +1 -0
  18. package/dist/admin/generated/client.gen.d.ts +13 -0
  19. package/dist/admin/generated/client.gen.d.ts.map +1 -0
  20. package/dist/admin/generated/client.gen.js +4 -0
  21. package/dist/admin/generated/client.gen.js.map +1 -0
  22. package/dist/admin/generated/core/auth.gen.d.ts +26 -0
  23. package/dist/admin/generated/core/auth.gen.d.ts.map +1 -0
  24. package/dist/admin/generated/core/auth.gen.js +15 -0
  25. package/dist/admin/generated/core/auth.gen.js.map +1 -0
  26. package/dist/admin/generated/core/bodySerializer.gen.d.ts +26 -0
  27. package/dist/admin/generated/core/bodySerializer.gen.d.ts.map +1 -0
  28. package/dist/admin/generated/core/bodySerializer.gen.js +58 -0
  29. package/dist/admin/generated/core/bodySerializer.gen.js.map +1 -0
  30. package/dist/admin/generated/core/params.gen.d.ts +44 -0
  31. package/dist/admin/generated/core/params.gen.d.ts.map +1 -0
  32. package/dist/admin/generated/core/params.gen.js +113 -0
  33. package/dist/admin/generated/core/params.gen.js.map +1 -0
  34. package/dist/admin/generated/core/pathSerializer.gen.d.ts +34 -0
  35. package/dist/admin/generated/core/pathSerializer.gen.d.ts.map +1 -0
  36. package/dist/admin/generated/core/pathSerializer.gen.js +115 -0
  37. package/dist/admin/generated/core/pathSerializer.gen.js.map +1 -0
  38. package/dist/admin/generated/core/queryKeySerializer.gen.d.ts +19 -0
  39. package/dist/admin/generated/core/queryKeySerializer.gen.d.ts.map +1 -0
  40. package/dist/admin/generated/core/queryKeySerializer.gen.js +100 -0
  41. package/dist/admin/generated/core/queryKeySerializer.gen.js.map +1 -0
  42. package/dist/admin/generated/core/serverSentEvents.gen.d.ts +72 -0
  43. package/dist/admin/generated/core/serverSentEvents.gen.d.ts.map +1 -0
  44. package/dist/admin/generated/core/serverSentEvents.gen.js +137 -0
  45. package/dist/admin/generated/core/serverSentEvents.gen.js.map +1 -0
  46. package/dist/admin/generated/core/types.gen.d.ts +84 -0
  47. package/dist/admin/generated/core/types.gen.d.ts.map +1 -0
  48. package/dist/admin/generated/core/types.gen.js +3 -0
  49. package/dist/admin/generated/core/types.gen.js.map +1 -0
  50. package/dist/admin/generated/core/utils.gen.d.ts +20 -0
  51. package/dist/admin/generated/core/utils.gen.d.ts.map +1 -0
  52. package/dist/admin/generated/core/utils.gen.js +88 -0
  53. package/dist/admin/generated/core/utils.gen.js.map +1 -0
  54. package/dist/admin/generated/index.d.ts +3 -0
  55. package/dist/admin/generated/index.d.ts.map +1 -0
  56. package/dist/admin/generated/index.js +3 -0
  57. package/dist/admin/generated/index.js.map +1 -0
  58. package/dist/admin/generated/sdk.gen.d.ts +188 -0
  59. package/dist/admin/generated/sdk.gen.d.ts.map +1 -0
  60. package/dist/admin/generated/sdk.gen.js +531 -0
  61. package/dist/admin/generated/sdk.gen.js.map +1 -0
  62. package/dist/admin/generated/types.gen.d.ts +2493 -0
  63. package/dist/admin/generated/types.gen.d.ts.map +1 -0
  64. package/dist/admin/generated/types.gen.js +3 -0
  65. package/dist/admin/generated/types.gen.js.map +1 -0
  66. package/dist/admin/generated/zod.gen.d.ts +2755 -0
  67. package/dist/admin/generated/zod.gen.d.ts.map +1 -0
  68. package/dist/admin/generated/zod.gen.js +606 -0
  69. package/dist/admin/generated/zod.gen.js.map +1 -0
  70. package/dist/admin/index.d.ts +15 -0
  71. package/dist/admin/index.d.ts.map +1 -0
  72. package/dist/admin/index.js +19 -0
  73. package/dist/admin/index.js.map +1 -0
  74. package/dist/generated/index.d.ts +1 -1
  75. package/dist/generated/index.d.ts.map +1 -1
  76. package/dist/generated/sdk.gen.d.ts +80 -45
  77. package/dist/generated/sdk.gen.d.ts.map +1 -1
  78. package/dist/generated/sdk.gen.js +192 -157
  79. package/dist/generated/sdk.gen.js.map +1 -1
  80. package/dist/generated/types.gen.d.ts +887 -453
  81. package/dist/generated/types.gen.d.ts.map +1 -1
  82. package/dist/generated/zod.gen.d.ts +468 -286
  83. package/dist/generated/zod.gen.d.ts.map +1 -1
  84. package/dist/generated/zod.gen.js +188 -201
  85. package/dist/generated/zod.gen.js.map +1 -1
  86. package/dist/http-client.d.ts +1 -1
  87. package/dist/http-client.d.ts.map +1 -1
  88. package/dist/http-client.js +3 -3
  89. package/dist/http-client.js.map +1 -1
  90. package/dist/index.d.ts +1 -1
  91. package/dist/realtime/api-stream-error.d.ts +2 -1
  92. package/dist/realtime/api-stream-error.d.ts.map +1 -1
  93. package/dist/realtime/api-stream-error.js +8 -1
  94. package/dist/realtime/api-stream-error.js.map +1 -1
  95. package/dist/realtime/watch.d.ts +8 -8
  96. package/dist/realtime/watch.d.ts.map +1 -1
  97. package/dist/realtime/watch.js +2 -2
  98. package/dist/realtime/watch.js.map +1 -1
  99. package/dist/transport.js +10 -1
  100. package/dist/transport.js.map +1 -1
  101. package/package.json +7 -2
  102. package/src/admin/generated/client/client.gen.ts +300 -0
  103. package/src/admin/generated/client/index.ts +27 -0
  104. package/src/admin/generated/client/types.gen.ts +238 -0
  105. package/src/admin/generated/client/utils.gen.ts +337 -0
  106. package/src/admin/generated/client.gen.ts +24 -0
  107. package/src/admin/generated/core/auth.gen.ts +49 -0
  108. package/src/admin/generated/core/bodySerializer.gen.ts +96 -0
  109. package/src/admin/generated/core/params.gen.ts +188 -0
  110. package/src/admin/generated/core/pathSerializer.gen.ts +186 -0
  111. package/src/admin/generated/core/queryKeySerializer.gen.ts +134 -0
  112. package/src/admin/generated/core/serverSentEvents.gen.ts +265 -0
  113. package/src/admin/generated/core/types.gen.ts +126 -0
  114. package/src/admin/generated/core/utils.gen.ts +146 -0
  115. package/src/admin/generated/index.ts +231 -0
  116. package/src/admin/generated/sdk.gen.ts +1180 -0
  117. package/src/admin/generated/types.gen.ts +2814 -0
  118. package/src/admin/generated/zod.gen.ts +746 -0
  119. package/src/admin/index.ts +34 -0
  120. package/src/generated/index.ts +6 -2
  121. package/src/generated/sdk.gen.ts +408 -373
  122. package/src/generated/types.gen.ts +956 -516
  123. package/src/generated/zod.gen.ts +217 -231
  124. package/src/http-client.ts +6 -3
  125. package/src/realtime/api-stream-error.ts +12 -1
  126. package/src/realtime/watch.ts +34 -22
  127. package/src/transport.ts +10 -1
@@ -2,9 +2,12 @@ import { createClient } from "./generated/client/index.js";
2
2
  import type { ResolvedClientOptions } from "./options.js";
3
3
  import { createAuthenticatedFetch } from "./transport.js";
4
4
 
5
- export function createHttpClient(options: ResolvedClientOptions) {
5
+ export function createHttpClient(
6
+ options: ResolvedClientOptions,
7
+ clientFactory = createClient,
8
+ ) {
6
9
  const authenticatedFetch = createAuthenticatedFetch(options);
7
- const client = createClient({
10
+ const client = clientFactory({
8
11
  baseUrl: options.baseUrl,
9
12
  redirect: "error",
10
13
  credentials: "omit",
@@ -15,7 +18,7 @@ export function createHttpClient(options: ResolvedClientOptions) {
15
18
  // A per-call fetch would bypass authentication and transport policy.
16
19
  if (requestOptions.fetch !== authenticatedFetch) {
17
20
  throw new TypeError(
18
- "Configure fetch on createBikeFinderClient, not individual requests.",
21
+ "Configure fetch on the client factory, not individual requests.",
19
22
  );
20
23
  }
21
24
  return request;
@@ -4,8 +4,13 @@ export type ApiStreamErrorCode = "http" | "protocol" | "network";
4
4
  export class ApiStreamError extends Error {
5
5
  readonly code: ApiStreamErrorCode;
6
6
  readonly status: number | undefined;
7
+ readonly retryAfterMs: number | undefined;
7
8
 
8
- constructor(code: ApiStreamErrorCode, status?: number) {
9
+ constructor(
10
+ code: ApiStreamErrorCode,
11
+ status?: number,
12
+ retryAfterMs?: number,
13
+ ) {
9
14
  super(
10
15
  code === "http"
11
16
  ? `The tracker stream returned HTTP ${status}.`
@@ -16,5 +21,11 @@ export class ApiStreamError extends Error {
16
21
  this.name = "ApiStreamError";
17
22
  this.code = code;
18
23
  this.status = status;
24
+ this.retryAfterMs =
25
+ retryAfterMs !== undefined &&
26
+ Number.isFinite(retryAfterMs) &&
27
+ retryAfterMs >= 0
28
+ ? Math.min(retryAfterMs, 300_000)
29
+ : undefined;
19
30
  }
20
31
  }
@@ -17,43 +17,48 @@ import type { Options } from "../generated/sdk.gen.js";
17
17
  import { ApiStreamError } from "./api-stream-error.js";
18
18
 
19
19
  type Method = (...args: never[]) => unknown;
20
- type Result<TKey extends keyof BikeFinderSdk> =
21
- BikeFinderSdk[TKey] extends Method
22
- ? Awaited<ReturnType<BikeFinderSdk[TKey]>>
23
- : never;
20
+ type Result<TSdk, TKey extends keyof TSdk> = TSdk[TKey] extends Method
21
+ ? Awaited<ReturnType<TSdk[TKey]>>
22
+ : never;
24
23
 
25
- export type StreamOperation = {
26
- [TKey in keyof BikeFinderSdk]: Result<TKey> extends {
24
+ export type StreamOperation<TSdk = BikeFinderSdk> = {
25
+ [TKey in keyof TSdk]: Result<TSdk, TKey> extends {
27
26
  stream: AsyncIterable<unknown>;
28
27
  }
29
28
  ? TKey
30
29
  : never;
31
- }[keyof BikeFinderSdk];
30
+ }[keyof TSdk];
32
31
 
33
- export type StreamPayload<TKey extends StreamOperation> =
34
- Result<TKey> extends {
32
+ export type StreamPayload<
33
+ TKey extends StreamOperation<TSdk>,
34
+ TSdk = BikeFinderSdk,
35
+ > =
36
+ Result<TSdk, TKey> extends {
35
37
  stream: AsyncIterable<infer TPayload>;
36
38
  }
37
39
  ? Exclude<TPayload, null>
38
40
  : never;
39
41
 
40
- export type StreamOptions<TKey extends StreamOperation> = Pick<
41
- NonNullable<Parameters<BikeFinderSdk[TKey]>[0]>,
42
+ export type StreamOptions<
43
+ TKey extends StreamOperation<TSdk>,
44
+ TSdk = BikeFinderSdk,
45
+ > = Pick<
46
+ NonNullable<Parameters<Extract<TSdk[TKey], Method>>[0]>,
42
47
  Extract<
43
- keyof NonNullable<Parameters<BikeFinderSdk[TKey]>[0]>,
48
+ keyof NonNullable<Parameters<Extract<TSdk[TKey], Method>>[0]>,
44
49
  "path" | "query"
45
50
  >
46
51
  >;
47
52
  /**
48
- * Observe complete snapshots of one tracker page, shared by this observable's consumers.
53
+ * Observe complete snapshots of a generated view, shared by this observable's consumers.
49
54
  * Unsubscribe when leaving the view or changing accounts. Re-subscribe after an error
50
55
  * to start a fresh retry budget. Reconnects never request historical event replay.
51
56
  */
52
- export function watch<TOperation extends StreamOperation>(
53
- requests: BikeFinderSdk,
57
+ export function watch<TSdk, TOperation extends StreamOperation<TSdk>>(
58
+ requests: TSdk,
54
59
  operation: TOperation,
55
- options: StreamOptions<TOperation>,
56
- ): Observable<StreamPayload<TOperation>> {
60
+ options: StreamOptions<TOperation, TSdk>,
61
+ ): Observable<StreamPayload<TOperation, TSdk>> {
57
62
  // The generated SDK supplies paths, arguments and validators for every stream.
58
63
  // This boundary only adds the shared connection lifecycle.
59
64
  const invoke = requests[operation] as (
@@ -61,7 +66,7 @@ export function watch<TOperation extends StreamOperation>(
61
66
  ) => Promise<{ stream: AsyncIterable<unknown> }>;
62
67
  return defer(() => {
63
68
  const disconnected = new Subject<undefined>();
64
- const snapshots = new Observable<StreamPayload<TOperation>>(
69
+ const snapshots = new Observable<StreamPayload<TOperation, TSdk>>(
65
70
  (subscriber) => {
66
71
  disconnected.next(undefined);
67
72
  const controller = new AbortController();
@@ -88,7 +93,7 @@ export function watch<TOperation extends StreamOperation>(
88
93
  if (data === null || typeof data !== "object") {
89
94
  throw new ApiStreamError("protocol");
90
95
  }
91
- subscriber.next(data as StreamPayload<TOperation>);
96
+ subscriber.next(data as StreamPayload<TOperation, TSdk>);
92
97
  },
93
98
  });
94
99
  // Event callbacks retain SSE event names; consuming drives the generated
@@ -131,7 +136,12 @@ export function watch<TOperation extends StreamOperation>(
131
136
  (error.status ?? 0) >= 500)));
132
137
  if (!retryable) throw error;
133
138
  const maximum = Math.min(30_000, 1_000 * 2 ** (attempt - 1));
134
- return timer(maximum * (0.5 + Math.random() * 0.5));
139
+ return timer(
140
+ Math.max(
141
+ maximum * (0.5 + Math.random() * 0.5),
142
+ error.retryAfterMs ?? 0,
143
+ ),
144
+ );
135
145
  },
136
146
  }),
137
147
  ),
@@ -139,13 +149,15 @@ export function watch<TOperation extends StreamOperation>(
139
149
  }).pipe(
140
150
  share({
141
151
  connector: () =>
142
- new ReplaySubject<StreamPayload<TOperation> | undefined>(1),
152
+ new ReplaySubject<StreamPayload<TOperation, TSdk> | undefined>(1),
143
153
  resetOnError: true,
144
154
  resetOnComplete: true,
145
155
  resetOnRefCountZero: true,
146
156
  }),
147
157
  // An internal disconnect marker replaces the cached page during backoff, so a
148
158
  // new subscriber never receives an old snapshot as though it were current.
149
- filter((page): page is StreamPayload<TOperation> => page !== undefined),
159
+ filter(
160
+ (page): page is StreamPayload<TOperation, TSdk> => page !== undefined,
161
+ ),
150
162
  );
151
163
  }
package/src/transport.ts CHANGED
@@ -60,7 +60,7 @@ export function createAuthenticatedFetch(
60
60
  if (response.status !== 200) {
61
61
  await response.body?.cancel().catch(() => {});
62
62
  if (response.status === 401) throw new AuthenticationError();
63
- throw new ApiStreamError("http", response.status);
63
+ throw new ApiStreamError("http", response.status, retryAfter(response));
64
64
  }
65
65
  const contentType = response.headers
66
66
  .get("Content-Type")
@@ -86,6 +86,15 @@ export function createAuthenticatedFetch(
86
86
  };
87
87
  }
88
88
 
89
+ function retryAfter(response: Response): number | undefined {
90
+ const value = response.headers.get("Retry-After")?.trim();
91
+ if (!value) return undefined;
92
+ const delay = /^\d+$/.test(value)
93
+ ? Number(value) * 1_000
94
+ : Date.parse(value) - Date.now();
95
+ return Number.isFinite(delay) ? Math.max(0, delay) : undefined;
96
+ }
97
+
89
98
  function throwIfAborted(signal?: AbortSignal): void {
90
99
  // Expo's React Native AbortSignal supports aborted, but not throwIfAborted().
91
100
  if (signal?.aborted) {