@telecrypt-io/storage 0.6.0 → 0.7.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/dist/core/http.js CHANGED
@@ -12,7 +12,6 @@ export class ResponseBodyReadError extends Error {
12
12
  this.bytes = bytes;
13
13
  }
14
14
  }
15
- const RESPONSE_CLEANUP_TIMEOUT_MS = 5_000;
16
15
  function abortError() {
17
16
  return new DOMException("The operation was aborted", "AbortError");
18
17
  }
@@ -53,79 +52,32 @@ export function raceWithAbort(operation, signal, error) {
53
52
  abort();
54
53
  });
55
54
  }
56
- function combineCleanupFailures(primaryError, cleanupFailures, message) {
57
- const errors = primaryError === undefined ? [...cleanupFailures] : [primaryError, ...cleanupFailures];
58
- return new AggregateError(errors, message, {
59
- cause: primaryError ?? cleanupFailures[0],
60
- });
61
- }
62
- async function runBoundedCleanup(cleanup, operation) {
63
- const pending = Promise.resolve().then(cleanup);
64
- let timer;
65
- const deadline = new Promise((_, reject) => {
66
- timer = setTimeout(() => {
67
- reject(new Error(`${operation} cleanup timed out`));
68
- }, RESPONSE_CLEANUP_TIMEOUT_MS);
69
- });
55
+ function startCancellation(cancel) {
70
56
  try {
71
- await Promise.race([pending, deadline]);
57
+ void Promise.resolve(cancel()).catch(() => { });
72
58
  }
73
- finally {
74
- if (timer !== undefined)
75
- clearTimeout(timer);
59
+ catch {
60
+ // Cleanup must not delay or replace the operation result.
76
61
  }
77
62
  }
78
- /** Await cleanup for a response that is being rejected before its body is read. */
79
- export async function cancelResponseBody(response, operation, primaryError) {
80
- if (!response.body)
81
- return;
82
- try {
83
- await runBoundedCleanup(() => response.body.cancel(), `${operation} response`);
84
- }
85
- catch (error) {
86
- throw combineCleanupFailures(primaryError, [error], primaryError === undefined
87
- ? `${operation} response cleanup was incomplete`
88
- : `${operation} failed and response cleanup was incomplete`);
89
- }
63
+ /** Start discarding a rejected response without waiting for provider cleanup. */
64
+ export function cancelResponseBody(response) {
65
+ if (response.body)
66
+ startCancellation(() => response.body.cancel());
90
67
  }
91
- async function cancelReaderAndRelease(reader, operation, primaryError) {
92
- const cleanupFailures = [];
93
- try {
94
- await runBoundedCleanup(() => reader.cancel(), `${operation} reader cancellation`);
95
- }
96
- catch (error) {
97
- cleanupFailures.push(error);
98
- }
99
- // Cancellation is awaited before releasing the reader, so cleanup failures
100
- // stay part of the current operation instead of mutating a later error.
68
+ function cancelReaderAndRelease(reader) {
69
+ startCancellation(() => reader.cancel());
101
70
  try {
102
71
  reader.releaseLock();
103
72
  }
104
- catch (error) {
105
- cleanupFailures.push(error);
106
- }
107
- if (cleanupFailures.length > 0) {
108
- throw combineCleanupFailures(primaryError, cleanupFailures, primaryError === undefined
109
- ? `${operation} reader cleanup was incomplete`
110
- : `${operation} failed and reader cleanup was incomplete`);
73
+ catch {
74
+ // The reader may still have a pending read; the caller's error remains authoritative.
111
75
  }
112
76
  }
113
77
  async function readResponseBodyInternal(response, signal, options = {}, maxBytes) {
114
- const cancelAbortedResponse = async (failure) => {
115
- if (!response.body)
116
- return;
117
- let reader;
118
- try {
119
- reader = response.body.getReader();
120
- }
121
- catch (error) {
122
- throw combineCleanupFailures(failure, [error], "response reader setup failed after abort");
123
- }
124
- await cancelReaderAndRelease(reader, "response body", failure);
125
- };
126
78
  if (signal?.aborted) {
127
79
  const failure = options.abortError?.() ?? abortError();
128
- await cancelAbortedResponse(failure);
80
+ cancelResponseBody(response);
129
81
  throw failure;
130
82
  }
131
83
  if (maxBytes !== undefined &&
@@ -136,7 +88,7 @@ async function readResponseBodyInternal(response, signal, options = {}, maxBytes
136
88
  const contentLength = response.headers.get("content-length");
137
89
  if (contentLength !== null && /^\d+$/u.test(contentLength) && Number(contentLength) > maxBytes) {
138
90
  const tooLarge = new FileTooLargeError();
139
- await cancelResponseBody(response, "media response", tooLarge);
91
+ cancelResponseBody(response);
140
92
  throw tooLarge;
141
93
  }
142
94
  }
@@ -147,18 +99,15 @@ async function readResponseBodyInternal(response, signal, options = {}, maxBytes
147
99
  reader = response.body.getReader();
148
100
  }
149
101
  catch (error) {
150
- let failure = error;
151
- try {
152
- await cancelResponseBody(response, "response body reader setup", error);
153
- }
154
- catch (cleanupError) {
155
- failure = cleanupError;
156
- }
157
- throw new ResponseBodyReadError(new Uint8Array(), failure);
102
+ cancelResponseBody(response);
103
+ throw new ResponseBodyReadError(new Uint8Array(), error);
158
104
  }
159
- let abortCancellation;
105
+ let cancellationStarted = false;
160
106
  const cancelReader = () => {
161
- abortCancellation ??= runBoundedCleanup(() => reader.cancel(), "response body reader");
107
+ if (cancellationStarted)
108
+ return;
109
+ cancellationStarted = true;
110
+ startCancellation(() => reader.cancel());
162
111
  };
163
112
  const chunks = [];
164
113
  let bytesRead = 0;
@@ -213,34 +162,14 @@ async function readResponseBodyInternal(response, signal, options = {}, maxBytes
213
162
  catch (error) {
214
163
  if (signal?.aborted) {
215
164
  const failure = new ResponseBodyReadError(joinBytes(chunks, bytesRead), error);
216
- const cleanupFailures = [];
217
- if (abortCancellation) {
218
- try {
219
- await abortCancellation;
220
- }
221
- catch (cleanupError) {
222
- cleanupFailures.push(cleanupError);
223
- }
224
- }
165
+ cancelReader();
225
166
  try {
226
167
  reader.releaseLock();
227
168
  }
228
- catch (cleanupError) {
229
- cleanupFailures.push(cleanupError);
230
- }
231
- if (cleanupFailures.length > 0) {
232
- throw combineCleanupFailures(failure, cleanupFailures, "response body read failed and reader cleanup was incomplete");
233
- }
169
+ catch { /* Pending reads release with cancellation. */ }
234
170
  throw failure;
235
171
  }
236
- try {
237
- await cancelReaderAndRelease(reader, "response body", error);
238
- }
239
- catch (cleanupError) {
240
- if (error instanceof FileTooLargeError)
241
- throw cleanupError;
242
- throw new ResponseBodyReadError(joinBytes(chunks, bytesRead), cleanupError);
243
- }
172
+ cancelReaderAndRelease(reader);
244
173
  if (error instanceof FileTooLargeError)
245
174
  throw error;
246
175
  throw new ResponseBodyReadError(joinBytes(chunks, bytesRead), error);
@@ -34,9 +34,6 @@ export type OidcTokenEndpointMetadata = Pick<OidcClientConfig, "issuer" | "token
34
34
  * identifiers, or control characters from an untrusted response to cross the
35
35
  * SDK boundary. */
36
36
  export declare function sanitizeDiagnosticText(value: string): string;
37
- /** Copy a transport error's useful details across the SDK boundary after the
38
- * same secret/control-character sanitization used for provider responses. */
39
- export declare function diagnosticCause(error: unknown, seen?: WeakSet<object>): Error;
40
37
  /**
41
38
  * Discovers the OIDC/MAS issuer and endpoints for a homeserver via the
42
39
  * Matrix client's stable `/auth_metadata` endpoint. Matrix 42 performs this
@@ -44,9 +41,8 @@ export declare function diagnosticCause(error: unknown, seen?: WeakSet<object>):
44
41
  */
45
42
  export declare function discoverOidcIssuer(homeserverBaseUrl: string, signal?: AbortSignal): Promise<OidcClientConfig>;
46
43
  /**
47
- * Dynamic client registration (DCR). `clientUri`/`redirectUris` must share a
48
- * host unless the issuer's policy allows a mismatch (our local dev/test MAS
49
- * does; a production issuer may not).
44
+ * Dynamic client registration (DCR). The issuer validates the supplied
45
+ * metadata; the PKCE flow separately binds its callback to the current app.
50
46
  */
51
47
  export declare function registerClient(authMetadata: OidcClientConfig, metadata: OidcRegistrationClientMetadata, signal?: AbortSignal): Promise<string>;
52
48
  /**