@s2-dev/streamstore 0.3.9 → 0.3.14

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 (188) hide show
  1. package/README.md +15 -12
  2. package/docs/sdks/account/README.md +38 -35
  3. package/docs/sdks/basin/README.md +38 -35
  4. package/docs/sdks/stream/README.md +22 -27
  5. package/funcs/accountCreateBasin.d.ts +2 -1
  6. package/funcs/accountCreateBasin.d.ts.map +1 -1
  7. package/funcs/accountCreateBasin.js +3 -4
  8. package/funcs/accountCreateBasin.js.map +1 -1
  9. package/funcs/accountDeleteBasin.d.ts +1 -1
  10. package/funcs/accountDeleteBasin.d.ts.map +1 -1
  11. package/funcs/accountDeleteBasin.js +3 -2
  12. package/funcs/accountDeleteBasin.js.map +1 -1
  13. package/funcs/accountGetBasinConfig.d.ts +2 -1
  14. package/funcs/accountGetBasinConfig.d.ts.map +1 -1
  15. package/funcs/accountGetBasinConfig.js +3 -4
  16. package/funcs/accountGetBasinConfig.js.map +1 -1
  17. package/funcs/accountListBasins.d.ts +1 -1
  18. package/funcs/accountListBasins.d.ts.map +1 -1
  19. package/funcs/accountListBasins.js +2 -4
  20. package/funcs/accountListBasins.js.map +1 -1
  21. package/funcs/accountReconfigureBasin.d.ts +2 -1
  22. package/funcs/accountReconfigureBasin.d.ts.map +1 -1
  23. package/funcs/accountReconfigureBasin.js +3 -4
  24. package/funcs/accountReconfigureBasin.js.map +1 -1
  25. package/funcs/basinCreateStream.d.ts +2 -1
  26. package/funcs/basinCreateStream.d.ts.map +1 -1
  27. package/funcs/basinCreateStream.js +14 -4
  28. package/funcs/basinCreateStream.js.map +1 -1
  29. package/funcs/basinDeleteStream.d.ts +1 -1
  30. package/funcs/basinDeleteStream.d.ts.map +1 -1
  31. package/funcs/basinDeleteStream.js +3 -2
  32. package/funcs/basinDeleteStream.js.map +1 -1
  33. package/funcs/basinGetStreamConfig.d.ts +2 -1
  34. package/funcs/basinGetStreamConfig.d.ts.map +1 -1
  35. package/funcs/basinGetStreamConfig.js +3 -4
  36. package/funcs/basinGetStreamConfig.js.map +1 -1
  37. package/funcs/basinListStreams.d.ts +1 -1
  38. package/funcs/basinListStreams.d.ts.map +1 -1
  39. package/funcs/basinListStreams.js +3 -3
  40. package/funcs/basinListStreams.js.map +1 -1
  41. package/funcs/basinReconfigureStream.d.ts +2 -1
  42. package/funcs/basinReconfigureStream.d.ts.map +1 -1
  43. package/funcs/basinReconfigureStream.js +3 -4
  44. package/funcs/basinReconfigureStream.js.map +1 -1
  45. package/funcs/streamAppend.d.ts +2 -1
  46. package/funcs/streamAppend.d.ts.map +1 -1
  47. package/funcs/streamAppend.js +3 -4
  48. package/funcs/streamAppend.js.map +1 -1
  49. package/funcs/streamCheckTail.d.ts +2 -1
  50. package/funcs/streamCheckTail.d.ts.map +1 -1
  51. package/funcs/streamCheckTail.js +3 -4
  52. package/funcs/streamCheckTail.js.map +1 -1
  53. package/funcs/streamRead.d.ts +1 -1
  54. package/funcs/streamRead.d.ts.map +1 -1
  55. package/funcs/streamRead.js +2 -2
  56. package/funcs/streamRead.js.map +1 -1
  57. package/index.extras.d.ts +16 -5
  58. package/index.extras.d.ts.map +1 -1
  59. package/index.extras.js +89 -26
  60. package/index.extras.js.map +1 -1
  61. package/jsr.json +1 -1
  62. package/lib/config.d.ts +2 -2
  63. package/lib/config.d.ts.map +1 -1
  64. package/lib/config.js +2 -2
  65. package/lib/config.js.map +1 -1
  66. package/lib/matchers.d.ts +1 -1
  67. package/lib/matchers.d.ts.map +1 -1
  68. package/lib/matchers.js +11 -10
  69. package/lib/matchers.js.map +1 -1
  70. package/models/components/index.d.ts +0 -1
  71. package/models/components/index.d.ts.map +1 -1
  72. package/models/components/index.js +0 -1
  73. package/models/components/index.js.map +1 -1
  74. package/models/errors/apierror.d.ts +5 -8
  75. package/models/errors/apierror.d.ts.map +1 -1
  76. package/models/errors/apierror.js +9 -3
  77. package/models/errors/apierror.js.map +1 -1
  78. package/models/errors/index.d.ts +1 -0
  79. package/models/errors/index.d.ts.map +1 -1
  80. package/models/errors/index.js +1 -0
  81. package/models/errors/index.js.map +1 -1
  82. package/models/errors/retryableerror.d.ts +31 -0
  83. package/models/errors/retryableerror.d.ts.map +1 -0
  84. package/models/errors/retryableerror.js +77 -0
  85. package/models/errors/retryableerror.js.map +1 -0
  86. package/models/operations/append.d.ts +0 -27
  87. package/models/operations/append.d.ts.map +1 -1
  88. package/models/operations/append.js +1 -40
  89. package/models/operations/append.js.map +1 -1
  90. package/models/operations/checktail.d.ts +0 -28
  91. package/models/operations/checktail.d.ts.map +1 -1
  92. package/models/operations/checktail.js +1 -42
  93. package/models/operations/checktail.js.map +1 -1
  94. package/models/operations/createbasin.d.ts +0 -27
  95. package/models/operations/createbasin.d.ts.map +1 -1
  96. package/models/operations/createbasin.js +1 -40
  97. package/models/operations/createbasin.js.map +1 -1
  98. package/models/operations/createstream.d.ts +0 -27
  99. package/models/operations/createstream.d.ts.map +1 -1
  100. package/models/operations/createstream.js +1 -40
  101. package/models/operations/createstream.js.map +1 -1
  102. package/models/operations/deletebasin.d.ts +0 -26
  103. package/models/operations/deletebasin.d.ts.map +1 -1
  104. package/models/operations/deletebasin.js +1 -38
  105. package/models/operations/deletebasin.js.map +1 -1
  106. package/models/operations/deletestream.d.ts +0 -26
  107. package/models/operations/deletestream.d.ts.map +1 -1
  108. package/models/operations/deletestream.js +1 -38
  109. package/models/operations/deletestream.js.map +1 -1
  110. package/models/operations/getbasinconfig.d.ts +0 -28
  111. package/models/operations/getbasinconfig.d.ts.map +1 -1
  112. package/models/operations/getbasinconfig.js +1 -42
  113. package/models/operations/getbasinconfig.js.map +1 -1
  114. package/models/operations/getstreamconfig.d.ts +0 -28
  115. package/models/operations/getstreamconfig.d.ts.map +1 -1
  116. package/models/operations/getstreamconfig.js +1 -42
  117. package/models/operations/getstreamconfig.js.map +1 -1
  118. package/models/operations/listbasins.d.ts +2 -4
  119. package/models/operations/listbasins.d.ts.map +1 -1
  120. package/models/operations/listbasins.js +4 -8
  121. package/models/operations/listbasins.js.map +1 -1
  122. package/models/operations/liststreams.d.ts +2 -4
  123. package/models/operations/liststreams.d.ts.map +1 -1
  124. package/models/operations/liststreams.js +4 -8
  125. package/models/operations/liststreams.js.map +1 -1
  126. package/models/operations/read.d.ts +2 -10
  127. package/models/operations/read.d.ts.map +1 -1
  128. package/models/operations/read.js +6 -23
  129. package/models/operations/read.js.map +1 -1
  130. package/models/operations/reconfigurebasin.d.ts +0 -27
  131. package/models/operations/reconfigurebasin.d.ts.map +1 -1
  132. package/models/operations/reconfigurebasin.js +1 -40
  133. package/models/operations/reconfigurebasin.js.map +1 -1
  134. package/models/operations/reconfigurestream.d.ts +0 -27
  135. package/models/operations/reconfigurestream.d.ts.map +1 -1
  136. package/models/operations/reconfigurestream.js +1 -40
  137. package/models/operations/reconfigurestream.js.map +1 -1
  138. package/package.json +1 -1
  139. package/sdk/account.d.ts +5 -4
  140. package/sdk/account.d.ts.map +1 -1
  141. package/sdk/account.js.map +1 -1
  142. package/sdk/basin.d.ts +5 -4
  143. package/sdk/basin.d.ts.map +1 -1
  144. package/sdk/basin.js.map +1 -1
  145. package/sdk/stream.d.ts +3 -2
  146. package/sdk/stream.d.ts.map +1 -1
  147. package/sdk/stream.js.map +1 -1
  148. package/src/funcs/accountCreateBasin.ts +12 -10
  149. package/src/funcs/accountDeleteBasin.ts +12 -8
  150. package/src/funcs/accountGetBasinConfig.ts +12 -10
  151. package/src/funcs/accountListBasins.ts +11 -9
  152. package/src/funcs/accountReconfigureBasin.ts +12 -10
  153. package/src/funcs/basinCreateStream.ts +23 -10
  154. package/src/funcs/basinDeleteStream.ts +12 -8
  155. package/src/funcs/basinGetStreamConfig.ts +12 -10
  156. package/src/funcs/basinListStreams.ts +11 -7
  157. package/src/funcs/basinReconfigureStream.ts +12 -10
  158. package/src/funcs/streamAppend.ts +12 -10
  159. package/src/funcs/streamCheckTail.ts +12 -10
  160. package/src/funcs/streamRead.ts +10 -7
  161. package/src/index.extras.ts +170 -52
  162. package/src/lib/config.ts +3 -2
  163. package/src/lib/matchers.ts +16 -10
  164. package/src/models/components/index.ts +0 -1
  165. package/src/models/errors/apierror.ts +13 -7
  166. package/src/models/errors/index.ts +1 -0
  167. package/src/models/errors/retryableerror.ts +69 -0
  168. package/src/models/operations/append.ts +0 -68
  169. package/src/models/operations/checktail.ts +0 -74
  170. package/src/models/operations/createbasin.ts +0 -72
  171. package/src/models/operations/createstream.ts +0 -72
  172. package/src/models/operations/deletebasin.ts +0 -68
  173. package/src/models/operations/deletestream.ts +0 -68
  174. package/src/models/operations/getbasinconfig.ts +0 -74
  175. package/src/models/operations/getstreamconfig.ts +0 -74
  176. package/src/models/operations/listbasins.ts +6 -12
  177. package/src/models/operations/liststreams.ts +6 -12
  178. package/src/models/operations/read.ts +10 -33
  179. package/src/models/operations/reconfigurebasin.ts +0 -72
  180. package/src/models/operations/reconfigurestream.ts +0 -72
  181. package/src/sdk/account.ts +5 -4
  182. package/src/sdk/basin.ts +5 -4
  183. package/src/sdk/stream.ts +3 -2
  184. package/models/components/httpmetadata.d.ts +0 -37
  185. package/models/components/httpmetadata.d.ts.map +0 -1
  186. package/models/components/httpmetadata.js +0 -86
  187. package/models/components/httpmetadata.js.map +0 -1
  188. package/src/models/components/httpmetadata.ts +0 -87
@@ -10,6 +10,7 @@ import { safeParse } from "../lib/schemas.js";
10
10
  import { RequestOptions } from "../lib/sdks.js";
11
11
  import { extractSecurity, resolveGlobalSecurity } from "../lib/security.js";
12
12
  import { pathToFunc } from "../lib/url.js";
13
+ import * as components from "../models/components/index.js";
13
14
  import { APIError } from "../models/errors/apierror.js";
14
15
  import {
15
16
  ConnectionError,
@@ -36,9 +37,10 @@ export async function streamAppend(
36
37
  options?: RequestOptions,
37
38
  ): Promise<
38
39
  Result<
39
- operations.AppendResponse,
40
- | errors.ErrorResponse
40
+ components.AppendOutput,
41
41
  | errors.ErrorResponse
42
+ | errors.RetryableError
43
+ | errors.RetryableError
42
44
  | APIError
43
45
  | SDKValidationError
44
46
  | UnexpectedClientError
@@ -115,7 +117,7 @@ export async function streamAppend(
115
117
 
116
118
  const doResult = await client._do(req, {
117
119
  context,
118
- errorCodes: ["400", "401", "404", "4XX", "500", "5XX"],
120
+ errorCodes: ["400", "401", "404", "499", "4XX", "500", "503", "504", "5XX"],
119
121
  retryConfig: context.retryConfig,
120
122
  retryCodes: context.retryCodes,
121
123
  });
@@ -129,9 +131,10 @@ export async function streamAppend(
129
131
  };
130
132
 
131
133
  const [result] = await M.match<
132
- operations.AppendResponse,
133
- | errors.ErrorResponse
134
+ components.AppendOutput,
134
135
  | errors.ErrorResponse
136
+ | errors.RetryableError
137
+ | errors.RetryableError
135
138
  | APIError
136
139
  | SDKValidationError
137
140
  | UnexpectedClientError
@@ -140,14 +143,13 @@ export async function streamAppend(
140
143
  | RequestTimeoutError
141
144
  | ConnectionError
142
145
  >(
143
- M.json(200, operations.AppendResponse$inboundSchema, {
144
- key: "AppendOutput",
145
- }),
146
+ M.json(200, components.AppendOutput$inboundSchema),
146
147
  M.jsonErr([400, 401, 404], errors.ErrorResponse$inboundSchema),
147
- M.jsonErr(500, errors.ErrorResponse$inboundSchema),
148
+ M.jsonErr(499, errors.RetryableError$inboundSchema),
149
+ M.jsonErr([500, 503, 504], errors.RetryableError$inboundSchema),
148
150
  M.fail("4XX"),
149
151
  M.fail("5XX"),
150
- )(response, req, { extraFields: responseFields });
152
+ )(response, { extraFields: responseFields });
151
153
  if (!result.ok) {
152
154
  return result;
153
155
  }
@@ -10,6 +10,7 @@ import { safeParse } from "../lib/schemas.js";
10
10
  import { RequestOptions } from "../lib/sdks.js";
11
11
  import { extractSecurity, resolveGlobalSecurity } from "../lib/security.js";
12
12
  import { pathToFunc } from "../lib/url.js";
13
+ import * as components from "../models/components/index.js";
13
14
  import { APIError } from "../models/errors/apierror.js";
14
15
  import {
15
16
  ConnectionError,
@@ -36,9 +37,10 @@ export async function streamCheckTail(
36
37
  options?: RequestOptions,
37
38
  ): Promise<
38
39
  Result<
39
- operations.CheckTailResponse,
40
- | errors.ErrorResponse
40
+ components.CheckTailResponse,
41
41
  | errors.ErrorResponse
42
+ | errors.RetryableError
43
+ | errors.RetryableError
42
44
  | APIError
43
45
  | SDKValidationError
44
46
  | UnexpectedClientError
@@ -110,7 +112,7 @@ export async function streamCheckTail(
110
112
 
111
113
  const doResult = await client._do(req, {
112
114
  context,
113
- errorCodes: ["400", "401", "404", "4XX", "500", "5XX"],
115
+ errorCodes: ["400", "401", "404", "499", "4XX", "500", "503", "504", "5XX"],
114
116
  retryConfig: context.retryConfig,
115
117
  retryCodes: context.retryCodes,
116
118
  });
@@ -124,9 +126,10 @@ export async function streamCheckTail(
124
126
  };
125
127
 
126
128
  const [result] = await M.match<
127
- operations.CheckTailResponse,
128
- | errors.ErrorResponse
129
+ components.CheckTailResponse,
129
130
  | errors.ErrorResponse
131
+ | errors.RetryableError
132
+ | errors.RetryableError
130
133
  | APIError
131
134
  | SDKValidationError
132
135
  | UnexpectedClientError
@@ -135,14 +138,13 @@ export async function streamCheckTail(
135
138
  | RequestTimeoutError
136
139
  | ConnectionError
137
140
  >(
138
- M.json(200, operations.CheckTailResponse$inboundSchema, {
139
- key: "CheckTailResponse",
140
- }),
141
+ M.json(200, components.CheckTailResponse$inboundSchema),
141
142
  M.jsonErr([400, 401, 404], errors.ErrorResponse$inboundSchema),
142
- M.jsonErr(500, errors.ErrorResponse$inboundSchema),
143
+ M.jsonErr(499, errors.RetryableError$inboundSchema),
144
+ M.jsonErr([500, 503, 504], errors.RetryableError$inboundSchema),
143
145
  M.fail("4XX"),
144
146
  M.fail("5XX"),
145
- )(response, req, { extraFields: responseFields });
147
+ )(response, { extraFields: responseFields });
146
148
  if (!result.ok) {
147
149
  return result;
148
150
  }
@@ -43,7 +43,8 @@ export async function streamRead(
43
43
  Result<
44
44
  operations.ReadResponse,
45
45
  | errors.ErrorResponse
46
- | errors.ErrorResponse
46
+ | errors.RetryableError
47
+ | errors.RetryableError
47
48
  | APIError
48
49
  | SDKValidationError
49
50
  | UnexpectedClientError
@@ -126,7 +127,7 @@ export async function streamRead(
126
127
 
127
128
  const doResult = await client._do(req, {
128
129
  context,
129
- errorCodes: ["400", "401", "404", "4XX", "500", "5XX"],
130
+ errorCodes: ["400", "401", "404", "499", "4XX", "500", "503", "504", "5XX"],
130
131
  retryConfig: context.retryConfig,
131
132
  retryCodes: context.retryCodes,
132
133
  });
@@ -142,7 +143,8 @@ export async function streamRead(
142
143
  const [result] = await M.match<
143
144
  operations.ReadResponse,
144
145
  | errors.ErrorResponse
145
- | errors.ErrorResponse
146
+ | errors.RetryableError
147
+ | errors.RetryableError
146
148
  | APIError
147
149
  | SDKValidationError
148
150
  | UnexpectedClientError
@@ -151,13 +153,14 @@ export async function streamRead(
151
153
  | RequestTimeoutError
152
154
  | ConnectionError
153
155
  >(
154
- M.json(200, operations.ReadResponse$inboundSchema, { key: "Output" }),
155
- M.sse(200, operations.ReadResponse$inboundSchema, { key: "ReadResponse" }),
156
+ M.json(200, operations.ReadResponse$inboundSchema),
157
+ M.sse(200, operations.ReadResponse$inboundSchema),
156
158
  M.jsonErr([400, 401, 404], errors.ErrorResponse$inboundSchema),
157
- M.jsonErr(500, errors.ErrorResponse$inboundSchema),
159
+ M.jsonErr(499, errors.RetryableError$inboundSchema),
160
+ M.jsonErr([500, 503, 504], errors.RetryableError$inboundSchema),
158
161
  M.fail("4XX"),
159
162
  M.fail("5XX"),
160
- )(response, req, { extraFields: responseFields });
163
+ )(response, { extraFields: responseFields });
161
164
  if (!result.ok) {
162
165
  return result;
163
166
  }
@@ -14,7 +14,7 @@ import {
14
14
  StreamConfig,
15
15
  StreamInfo,
16
16
  } from "./models/components";
17
- import { ErrorResponse, NotFoundError } from "./models/errors";
17
+ import { NotFoundError, RetryableError } from "./models/errors";
18
18
  import {
19
19
  GetBasinConfigRequest,
20
20
  ListBasinsRequest,
@@ -37,6 +37,7 @@ import { v4 as uuidv4 } from "uuid";
37
37
  import { basinDeleteStream } from "./funcs/basinDeleteStream";
38
38
  import { EventStream } from "./lib/event-streams";
39
39
  import { ClientKind, S2Cloud, S2Endpoints } from "./endpoints";
40
+ import { HTTPClient } from "./lib/http";
40
41
 
41
42
  export type ReadRequest = Omit<ReadRequestInner, "stream">;
42
43
  export type AppendRequest = Omit<AppendRequestInner, "stream">;
@@ -64,19 +65,39 @@ export * from "./endpoints";
64
65
 
65
66
  export type S2ClientConfig = {
66
67
  authToken?: string;
68
+ userAgent?: string;
67
69
  requestTimeout?: number;
68
70
  endpoints?: S2Endpoints;
71
+ appedRetryPolicy?: AppendRetryPolicy;
72
+ maxRetries?: number;
73
+ initialBackoffMs?: number;
74
+ maxBackoffMs?: number;
75
+ httpClient?: HTTPClient;
69
76
  };
70
77
 
78
+ export enum AppendRetryPolicy {
79
+ All,
80
+ NoSideEffects,
81
+ }
82
+
71
83
  const defaultS2ClientConfig: S2ClientConfig = {
72
84
  requestTimeout: 3000,
73
85
  endpoints: S2Endpoints.forCloud(S2Cloud.Aws),
86
+ appedRetryPolicy: AppendRetryPolicy.All,
87
+ httpClient: new HTTPClient({
88
+ fetcher: async (request) => fetch(request),
89
+ }),
74
90
  };
75
91
 
92
+
76
93
  export class S2Client {
77
94
  private config: S2ClientConfig;
78
- private _account?: S2Account;
95
+ private _account?: S2Account;
96
+
79
97
  get account(): S2Account {
98
+ // this.httpClient.addHook("beforeRequest", (request) => {
99
+ // if (this.config.userAgent) request.headers.set("User-Agent", this.config.userAgent);
100
+ // });
80
101
  return (this._account ??= new S2Account(this.config));
81
102
  }
82
103
 
@@ -96,9 +117,16 @@ class S2Account {
96
117
  private readonly accountURLSuffx = "/v1alpha";
97
118
 
98
119
  constructor(config: S2ClientConfig) {
120
+ if (config.userAgent !== undefined) {
121
+ config.httpClient?.addHook("beforeRequest", (request) => {
122
+ request.headers.set("user-agent", config.userAgent ?? "s2-sdk-typescript");
123
+ console.log(request)
124
+ });
125
+ }
99
126
  this._account = new InnerAccount({
100
127
  ...(config.authToken !== undefined && { bearerAuth: config.authToken }),
101
- ...(config.requestTimeout !== undefined && { timeoutMs: config.requestTimeout })
128
+ ...(config.requestTimeout !== undefined && { timeoutMs: config.requestTimeout }),
129
+ ...(config.httpClient !== undefined && { httpClient: config.httpClient }),
102
130
  });
103
131
  this.config = config;
104
132
  }
@@ -106,7 +134,7 @@ class S2Account {
106
134
  get URL(): string | undefined {
107
135
  if (!this.config.endpoints) return undefined;
108
136
  return `https://${ClientKind.toAuthority({ kind: "Account" }, this.config.endpoints)}${this.accountURLSuffx}`;
109
- }
137
+ }
110
138
 
111
139
  basin(basinName: string): S2Basin {
112
140
  return new S2Basin(basinName, this.config);
@@ -116,15 +144,23 @@ class S2Account {
116
144
  request?: ListBasinsRequest
117
145
  ): Promise<PageIterator<ListBasinsResponse, { cursor: string }>> {
118
146
  const url = this.URL;
119
- return this._account.listBasins(request ?? {}, url ? { serverURL: url } : {});
147
+ return retryWithExponentialBackoff(
148
+ () => this._account.listBasins(request ?? {}, url ? { serverURL: url } : {}),
149
+ this.config.maxRetries,
150
+ this.config.initialBackoffMs,
151
+ this.config.maxBackoffMs
152
+ );
120
153
  }
121
154
 
122
155
  async getBasinConfig(basin: string): Promise<BasinConfig | undefined> {
123
156
  const _request: GetBasinConfigRequest = { basin };
124
157
  const url = this.URL;
125
- return (await this._account.getBasinConfig(_request
126
- , url ? { serverURL: url } : {}
127
- )).basinConfig;
158
+ return retryWithExponentialBackoff(
159
+ () => this._account.getBasinConfig(_request, url ? { serverURL: url } : {}),
160
+ this.config.maxRetries,
161
+ this.config.initialBackoffMs,
162
+ this.config.maxBackoffMs
163
+ );
128
164
  }
129
165
 
130
166
  async createBasin(basin: string, request?: CreateBasinRequest): Promise<BasinInfo | undefined> {
@@ -134,14 +170,22 @@ class S2Account {
134
170
  createBasinRequest: request ?? {},
135
171
  };
136
172
  const url = this.URL;
137
- return (await this._account.createBasin(_request,
138
- url ? { serverURL: url } : {}
139
- )).basinInfo;
173
+ return retryWithExponentialBackoff(
174
+ () => this._account.createBasin(_request, url ? { serverURL: url } : {}),
175
+ this.config.maxRetries,
176
+ this.config.initialBackoffMs,
177
+ this.config.maxBackoffMs
178
+ );
140
179
  }
141
180
 
142
- async deleteBasin(basin: string, if_exists?: boolean): Promise<void | undefined> {
181
+ async deleteBasin(basin: string, if_exists?: boolean): Promise<void> {
143
182
  const url = this.URL;
144
- const response = await accountDeleteBasin(this._account, { basin }, url ? { serverURL: url } : {});
183
+ const response = await retryWithExponentialBackoff(
184
+ () => accountDeleteBasin(this._account, { basin }, url ? { serverURL: url } : {}),
185
+ this.config.maxRetries,
186
+ this.config.initialBackoffMs,
187
+ this.config.maxBackoffMs
188
+ );
145
189
  if (if_exists && response.error instanceof NotFoundError) return;
146
190
  if (response.error) throw new Error(response.error.message);
147
191
  return;
@@ -150,9 +194,12 @@ class S2Account {
150
194
  async reconfigureBasin(basin: string, config: BasinConfig): Promise<BasinConfig | undefined> {
151
195
  const url = this.URL;
152
196
  const _request: ReconfigureBasinRequest = { basin, basinConfig: config };
153
- return (await this._account.reconfigureBasin(_request
154
- , url ? { serverURL: url } : {}
155
- )).basinConfig;
197
+ return retryWithExponentialBackoff(
198
+ () => this._account.reconfigureBasin(_request, url ? { serverURL: url } : {}),
199
+ this.config.maxRetries,
200
+ this.config.initialBackoffMs,
201
+ this.config.maxBackoffMs
202
+ );
156
203
  }
157
204
  }
158
205
 
@@ -162,13 +209,18 @@ class S2Basin {
162
209
  private basinName: string;
163
210
  private config: S2ClientConfig;
164
211
  private clientKind: ClientKind;
165
- private readonly basinURLSuffx = "/v1alpha";
212
+ private readonly basinURLSuffx = "/v1alpha";
166
213
 
167
214
  private get URL(): string {
168
215
  return `https://${ClientKind.toAuthority(this.clientKind, this.config.endpoints ?? S2Endpoints.forCloud(S2Cloud.Aws))}${this.basinURLSuffx}`;
169
216
  }
170
217
 
171
218
  constructor(basinName: string, config: S2ClientConfig) {
219
+ if (config.userAgent !== undefined) {
220
+ config.httpClient?.addHook("beforeRequest", (request) => {
221
+ request.headers.set("user-agent", config.userAgent ?? "s2-sdk-typescript");
222
+ });
223
+ }
172
224
  this._basin = new InnerBasin({
173
225
  ...(config.authToken !== undefined && { bearerAuth: config.authToken }),
174
226
  ...(config.requestTimeout !== undefined && { timeoutMs: config.requestTimeout })
@@ -185,13 +237,21 @@ class S2Basin {
185
237
  async listStreams(
186
238
  request: ListStreamsRequest
187
239
  ): Promise<PageIterator<ListStreamsResponse, { cursor: string }>> {
188
- return this._basin.listStreams(request, { serverURL: this.URL });
240
+ return retryWithExponentialBackoff(
241
+ () => this._basin.listStreams(request, { serverURL: this.URL }),
242
+ this.config.maxRetries,
243
+ this.config.initialBackoffMs,
244
+ this.config.maxBackoffMs
245
+ );
189
246
  }
190
247
 
191
248
  async getStreamConfig(stream: string): Promise<StreamConfig | undefined> {
192
- return (
193
- await this._basin.getStreamConfig({ stream }, { serverURL: this.URL })
194
- ).streamConfig;
249
+ return retryWithExponentialBackoff(
250
+ () => this._basin.getStreamConfig({ stream }, { serverURL: this.URL }),
251
+ this.config.maxRetries,
252
+ this.config.initialBackoffMs,
253
+ this.config.maxBackoffMs
254
+ );
195
255
  }
196
256
 
197
257
  async createStream(stream: string, request?: CreateStreamRequest): Promise<StreamInfo | undefined> {
@@ -200,27 +260,33 @@ class S2Basin {
200
260
  s2RequestToken: genS2RequestToken(),
201
261
  createStreamRequest: request ?? {},
202
262
  };
203
- return (
204
- await this._basin.createStream(_request, { serverURL: this.URL })
205
- ).streamInfo;
263
+ return retryWithExponentialBackoff(
264
+ () => this._basin.createStream(_request, { serverURL: this.URL }),
265
+ this.config.maxRetries,
266
+ this.config.initialBackoffMs,
267
+ this.config.maxBackoffMs
268
+ );
206
269
  }
207
270
 
208
271
  async deleteStream(stream: string, if_exists?: boolean): Promise<void | undefined> {
209
- const response = await basinDeleteStream(this._basin, { stream }, {
210
- serverURL: this.URL,
211
- });
272
+ const response = await retryWithExponentialBackoff(
273
+ () => basinDeleteStream(this._basin, { stream }, { serverURL: this.URL }),
274
+ this.config.maxRetries,
275
+ this.config.initialBackoffMs,
276
+ this.config.maxBackoffMs
277
+ );
212
278
  if (if_exists && response instanceof NotFoundError) return;
213
279
  if (response.error) throw new Error(response.error.message);
214
280
  return;
215
281
  }
216
282
 
217
- async reconfigureStream(stream: string, config: StreamConfig): Promise<StreamConfig | undefined> {
218
- return (
219
- await this._basin.reconfigureStream(
220
- { stream, streamConfig: config },
221
- { serverURL: this.URL }
222
- )
223
- ).streamConfig;
283
+ async reconfigureStream(stream: string, config: StreamConfig): Promise<StreamConfig> {
284
+ return retryWithExponentialBackoff(
285
+ () => this._basin.reconfigureStream({ stream, streamConfig: config }, { serverURL: this.URL }),
286
+ this.config.maxRetries,
287
+ this.config.initialBackoffMs,
288
+ this.config.maxBackoffMs
289
+ );
224
290
  }
225
291
  }
226
292
 
@@ -239,35 +305,55 @@ class Stream {
239
305
  this.config = config;
240
306
  this.clientKind = { kind: "Basin" as const, basin: basinName };
241
307
  this.streamName = streamName;
308
+ if (config.userAgent !== undefined) {
309
+ config.httpClient?.addHook("beforeRequest", (request) => {
310
+ request.headers.set("user-agent", config.userAgent ?? "s2-sdk-typescript");
311
+ });
312
+ }
242
313
  this._stream = new InnerStream({
243
314
  ...(config.authToken !== undefined && { bearerAuth: config.authToken }),
244
315
  ...(config.requestTimeout !== undefined && { timeoutMs: config.requestTimeout })
245
316
  });
246
317
  }
247
318
 
248
- async checkTail(): Promise<CheckTailResponse | undefined> {
319
+ async checkTail(): Promise<CheckTailResponse> {
249
320
  return (
250
321
  await this._stream.checkTail({ stream: this.streamName }, { serverURL: this.basinURL })
251
- ).checkTailResponse;
322
+ );
252
323
  }
253
324
 
254
- async append(request: AppendRequest): Promise<AppendOutput | undefined> {
255
- return (
256
- await this._stream.append({ ...request, stream: this.streamName }, { serverURL: this.basinURL })
257
- ).appendOutput;
325
+ async append(request: AppendRequest): Promise<AppendOutput> {
326
+ switch (this.config.appedRetryPolicy) {
327
+ case AppendRetryPolicy.All:
328
+ case undefined:
329
+ return retryWithExponentialBackoff(
330
+ () => this._stream.append({ ...request, stream: this.streamName }, { serverURL: this.basinURL }),
331
+ this.config.maxRetries,
332
+ this.config.initialBackoffMs,
333
+ this.config.maxBackoffMs
334
+ );
335
+ case AppendRetryPolicy.NoSideEffects:
336
+ return (
337
+ await this._stream.append({ ...request, stream: this.streamName }, {
338
+ serverURL: this.basinURL,
339
+ })
340
+ );
341
+ default:
342
+ throw new Error("Invalid AppendRetryPolicy");
343
+ }
258
344
  }
259
345
 
260
- async read(request: ReadRequest): Promise<Output | undefined> {
346
+ async read(request: ReadRequest): Promise<Output> {
261
347
  return (
262
- await this._stream.read({ ...request, stream: this.streamName }, { serverURL: this.basinURL })
263
- ).output;
348
+ await this._stream.read({ ...request, stream: this.streamName }, { serverURL: this.basinURL }) as Output
349
+ );
264
350
  }
265
351
 
266
352
  async *readStream(request: ReadRequest): AsyncGenerator<ReadResponse, void, undefined> {
267
353
  let currentRequest: ReadRequest = { ...request };
268
- let backoffMs = 100;
354
+ let initialBackoffMs = this.config.initialBackoffMs ?? 100;
269
355
  const maxBackoffMs = 5000;
270
- const maxRetries = 5;
356
+ const maxRetries = this.config.maxRetries ?? 5;
271
357
  let retryCount = 0;
272
358
 
273
359
  while (true) {
@@ -281,7 +367,7 @@ class Stream {
281
367
  acceptHeaderOverride: ReadAcceptEnum.textEventStream
282
368
  }
283
369
  );
284
- stream = response.readResponse;
370
+ stream = response as EventStream<ReadResponse>;
285
371
  if (!stream) return;
286
372
 
287
373
  for await (const event of stream) {
@@ -310,13 +396,16 @@ class Stream {
310
396
  }
311
397
  return;
312
398
  } catch (error) {
313
- if (error instanceof ErrorResponse || error instanceof NotFoundError) return;
314
- if (retryCount >= maxRetries) {
399
+ if (error instanceof RetryableError) {
400
+ if (retryCount >= maxRetries) {
401
+ throw error;
402
+ }
403
+ retryCount++;
404
+ await new Promise(resolve => setTimeout(resolve, initialBackoffMs));
405
+ initialBackoffMs = Math.min(initialBackoffMs * 2, maxBackoffMs);
406
+ } else {
315
407
  throw error;
316
408
  }
317
- retryCount++;
318
- await new Promise(resolve => setTimeout(resolve, backoffMs));
319
- backoffMs = Math.min(backoffMs * 2, maxBackoffMs);
320
409
  }
321
410
  }
322
411
  }
@@ -336,4 +425,33 @@ function meteredBatchSize(batch: SequencedRecordBatch): number {
336
425
 
337
426
  export function genS2RequestToken(): string {
338
427
  return uuidv4().replace(/-/g, "");
339
- }
428
+ }
429
+
430
+ async function retryWithExponentialBackoff<T>(
431
+ operation: () => Promise<T>,
432
+ maxAttempts: number = 5,
433
+ initialBackoffMs: number = 100,
434
+ maxBackoffMs: number = 5000
435
+ ): Promise<T> {
436
+ let attempt = 0;
437
+
438
+ while (attempt < maxAttempts) {
439
+ try {
440
+ return await operation();
441
+ } catch (error) {
442
+ if (error instanceof RetryableError) {
443
+ attempt++;
444
+ if (attempt >= maxAttempts) throw error;
445
+
446
+ const jitter = Math.random() * 0.3 + 0.85;
447
+ const delayMs = Math.min(initialBackoffMs * Math.pow(2, attempt) * jitter, maxBackoffMs);
448
+
449
+ await new Promise(resolve => setTimeout(resolve, delayMs));
450
+ } else {
451
+ throw error;
452
+ }
453
+ }
454
+ }
455
+
456
+ throw new Error("Max retry attempts reached");
457
+ }
package/src/lib/config.ts CHANGED
@@ -54,7 +54,8 @@ export function serverURLFromOptions(options: SDKOptions): URL | null {
54
54
  export const SDK_METADATA = {
55
55
  language: "typescript",
56
56
  openapiDocVersion: "1.0.0",
57
- sdkVersion: "0.3.9",
57
+ sdkVersion: "0.3.14",
58
58
  genVersion: "2.506.0",
59
- userAgent: "speakeasy-sdk/typescript 0.3.9 2.506.0 1.0.0 @s2-dev/streamstore",
59
+ userAgent:
60
+ "speakeasy-sdk/typescript 0.3.14 2.506.0 1.0.0 @s2-dev/streamstore",
60
61
  } as const;
@@ -160,7 +160,6 @@ export type MatchedError<Matchers> = Matchers extends Matcher<any, infer E>[]
160
160
  : never;
161
161
  export type MatchFunc<T, E> = (
162
162
  response: Response,
163
- request: Request,
164
163
  options?: { resultKey?: string; extraFields?: Record<string, unknown> },
165
164
  ) => Promise<[result: Result<T, E>, raw: unknown]>;
166
165
 
@@ -169,7 +168,6 @@ export function match<T, E>(
169
168
  ): MatchFunc<T, E | APIError | SDKValidationError> {
170
169
  return async function matchFunc(
171
170
  response: Response,
172
- request: Request,
173
171
  options?: { resultKey?: string; extraFields?: Record<string, unknown> },
174
172
  ): Promise<
175
173
  [result: Result<T, E | APIError | SDKValidationError>, raw: unknown]
@@ -191,14 +189,15 @@ export function match<T, E>(
191
189
  }
192
190
 
193
191
  if (!matcher) {
194
- await discardResponseBody(response);
192
+ const responseBody = await response.text();
195
193
  return [{
196
194
  ok: false,
197
- error: new APIError("Unexpected API response status or content-type", {
195
+ error: new APIError(
196
+ "Unexpected API response status or content-type",
198
197
  response,
199
- request,
200
- }),
201
- }, raw];
198
+ responseBody,
199
+ ),
200
+ }, responseBody];
202
201
  }
203
202
 
204
203
  const encoding = matcher.enc;
@@ -224,7 +223,7 @@ export function match<T, E>(
224
223
  raw = await discardResponseBody(response);
225
224
  break;
226
225
  case "fail":
227
- raw = await discardResponseBody(response);
226
+ raw = await response.text();
228
227
  break;
229
228
  default:
230
229
  encoding satisfies never;
@@ -234,7 +233,11 @@ export function match<T, E>(
234
233
  if (matcher.enc === "fail") {
235
234
  return [{
236
235
  ok: false,
237
- error: new APIError("API error occurred", { response, request }),
236
+ error: new APIError(
237
+ "API error occurred",
238
+ response,
239
+ typeof raw === "string" ? raw : "",
240
+ ),
238
241
  }, raw];
239
242
  }
240
243
 
@@ -253,11 +256,14 @@ export function match<T, E>(
253
256
  ...(matcher.hdrs ? { Headers: unpackHeaders(response.headers) } : null),
254
257
  [resultKey]: raw,
255
258
  };
256
- } else {
259
+ } else if (matcher.hdrs) {
257
260
  data = {
258
261
  ...options?.extraFields,
259
262
  ...(matcher.hdrs ? { Headers: unpackHeaders(response.headers) } : null),
263
+ ...(isPlainObject(raw) ? raw : null),
260
264
  };
265
+ } else {
266
+ data = raw;
261
267
  }
262
268
 
263
269
  if ("err" in matcher) {
@@ -14,7 +14,6 @@ export * from "./createbasinrequest.js";
14
14
  export * from "./createstreamrequest.js";
15
15
  export * from "./formatoption.js";
16
16
  export * from "./header.js";
17
- export * from "./httpmetadata.js";
18
17
  export * from "./listbasinsresponse.js";
19
18
  export * from "./liststreamsresponse.js";
20
19
  export * from "./output.js";
@@ -3,19 +3,25 @@
3
3
  */
4
4
 
5
5
  export class APIError extends Error {
6
+ public readonly statusCode: number;
7
+ public readonly contentType: string;
8
+
6
9
  constructor(
7
10
  message: string,
8
- public readonly httpMeta: {
9
- response: Response;
10
- request: Request;
11
- },
11
+ public readonly rawResponse: Response,
12
+ public readonly body: string = "",
12
13
  ) {
14
+ const statusCode = rawResponse.status;
15
+ const contentType = rawResponse.headers.get("content-type") || "";
16
+ const bodyString = body.length > 0 ? `\n${body}` : "";
17
+
13
18
  super(
14
- `${message}: Status ${httpMeta.response.status} Content-Type ${
15
- httpMeta.response.headers.get("content-type") || ""
16
- }`,
19
+ `${message}: Status ${statusCode} Content-Type ${contentType} Body ${bodyString}`,
17
20
  );
18
21
 
22
+ this.statusCode = statusCode;
23
+ this.contentType = contentType;
24
+
19
25
  this.name = "APIError";
20
26
  }
21
27
  }