@azure-net/kit 6.0.0-next.10 → 6.0.0-next.12

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/README.md CHANGED
@@ -55,10 +55,11 @@ skips checks. Review `.gitignore` before staging.
55
55
  ## Class Presenters And Async Helpers
56
56
 
57
57
  Import `createAsyncResource` and `createAsyncAction` directly from
58
- `@azure-net/kit/delivery`. `createAsyncHelpers()` has been removed. The error hook
59
- configured by `createApp().useAzureNetKitError` is still resolved per execution;
60
- there is no per-presenter parser or shared server error state. A custom error
61
- payload can be typed as the third generic: `createAsyncAction<Response, Request, CustomError>()`.
58
+ `@azure-net/kit/delivery`. Helpers execute the action once and support `beforeSend`,
59
+ `onSuccess`, `onError` and `fallbackResponse`. Resources additionally support `reject`.
60
+ Schema failures are converted locally to `error.validation`. There is no global
61
+ error resolver, `useAzureNetKitError`, or helper-level retry. Exceptions thrown by
62
+ `onSuccess` or `onError` propagate unchanged, without replaying the action.
62
63
 
63
64
  ```ts
64
65
  import { RegisterProvider, createAsyncResource } from '@azure-net/kit/delivery';
@@ -373,3 +374,59 @@ Declare roots once, rather than creating new roots per component render.
373
374
  `name` is now a symbol, not a serializable string. Its description is for diagnostics
374
375
  only; equal descriptions never share a cache. Existing string keys passed directly
375
376
  to `CreateServiceContainer` and `cleanupProvider` remain supported.
377
+
378
+ ## HTTP Retries
379
+
380
+ Retries belong to `HttpServiceInstance`, including calls made directly from use
381
+ cases. With no `retry` option, a failed request is not repeated. `limit` counts
382
+ additional attempts: `limit: 1` means at most two HTTP attempts.
383
+
384
+ ```ts
385
+ const http = new HttpServiceInstance({
386
+ onRequest(request) {
387
+ const token = readAccessToken();
388
+ if (token) request.headers.set('Authorization', `Bearer ${token}`);
389
+ },
390
+ retry: {
391
+ limit: 1,
392
+ statuses: [401],
393
+ async shouldRetry({ error }) {
394
+ if (!isExpiredAccessToken(error.data)) return false;
395
+ const refreshed = await http.post('/api/auth/refresh', {
396
+ json: { refreshToken: readRefreshToken() },
397
+ retry: { limit: 0 }
398
+ });
399
+ saveTokens(refreshed.data);
400
+ return true;
401
+ }
402
+ }
403
+ });
404
+ ```
405
+
406
+ Token helpers above are application-defined. Read/write tokens within the current
407
+ request on SSR, never in shared module-level user state. Coordinate concurrent
408
+ refreshes per session/request as appropriate; the generic HTTP client does not
409
+ implement token rotation or cross-tab coordination.
410
+
411
+ - `{ limit: N }` retries any failed HTTP attempt, including network failures.
412
+ - `statuses` restricts retries to those error statuses; `[]` allows none.
413
+ - `shouldRetry` runs only after the status filter and only while budget remains.
414
+ Both synchronous and asynchronous booleans are accepted.
415
+ - Per-request `retry` replaces the entire default policy. Use `{ limit: 0 }` to
416
+ disable it, **especially for refresh requests made inside `shouldRetry`**.
417
+ A nested HTTP call has its own budget; forgetting this override can recurse.
418
+ - `onRequest` runs before every attempt so refreshed credentials are read again.
419
+ `onResponse` runs once on success; `onError` runs once on terminal HTTP failure,
420
+ not for intermediate failures that recover successfully.
421
+ - Exceptions from hooks (including `shouldRetry`) propagate without further
422
+ recovery. They are not treated as another failed transport attempt.
423
+ - Cancelled requests and streaming request bodies are never replayed. Cancellation
424
+ is checked again after awaiting recovery; the callback must manage its own async
425
+ work and cancellation if needed.
426
+ - `limit` must be a finite non-negative safe integer. There is no implicit retry
427
+ delay/backoff. Timeout is applied separately to each HTTP attempt.
428
+
429
+ Retries are opt-in for all HTTP methods. Do not blindly retry writes after network
430
+ errors: the server may already have applied them. Use a narrow status/code policy
431
+ or backend-supported idempotency keys. Numerical `retry: 3` is no longer supported;
432
+ write `retry: { limit: 3 }` instead.
@@ -1,4 +1,4 @@
1
- export { createAsyncAction, createAsyncResource, type AsyncActionSettings, type AsyncHelperRetry, type AsyncResourceSettings, type AsyncActionResponse } from './injectable-dependencies/index.js';
1
+ export { createAsyncAction, createAsyncResource, type AsyncActionSettings, type AsyncResourceSettings, type AsyncActionResponse } from './injectable-dependencies/index.js';
2
2
  export { RegisterProvider, type ProviderLifecycle, type RegisterProviderOptions, type ProviderMethodKey } from './presenter/RegisterProvider.js';
3
3
  export { Bound } from './presenter/Bound.js';
4
4
  export { createSchemaFactory, schema, type Schema, type RequestErrors, type ISchemaError, type ValidationResult, type ValidationRuleResult, type ValidationParams, type ValidationMessage, type ValidationErrorsMap } from './schema/index.js';
@@ -1,5 +1,4 @@
1
- import { type AppError, type IAppError } from '../../shared/app-error/AppError.js';
2
- export type { AsyncHelperRetry } from '../../shared/app-error/ErrorRuntime.js';
1
+ import { AppError, type IAppError } from '../../shared/app-error/AppError.js';
3
2
  export interface AsyncActionResponse<T, D = never, ErrorResult = Record<never, never>> {
4
3
  success: boolean;
5
4
  response: T;
@@ -20,3 +19,4 @@ export interface AsyncResourceSettings<Res = never, Req = never, ErrorResult = A
20
19
  }
21
20
  export declare const createAsyncAction: <Res = never, Req = never, ErrorResult extends object = Record<never, never>>(action: Action<Res>, args?: AsyncActionSettings<Res, Req, ErrorResult>) => Promise<AsyncActionResponse<Res, Req, ErrorResult>>;
22
21
  export declare const createAsyncResource: <Res = never, Req = never, ErrorResult extends object = Record<never, never>>(action: Action<Res>, args?: AsyncResourceSettings<Res, Req, ErrorResult>) => Promise<Res>;
22
+ export {};
@@ -1,23 +1,9 @@
1
- import { AsyncHelperError } from '../../shared/app-error/AppError.js';
2
- import { resolveAzureNetKitError } from '../../shared/app-error/ErrorRuntime.js';
1
+ import { AsyncHelperError, AppError } from '../../shared/app-error/AppError.js';
3
2
  const normalizeError = (err) => (err instanceof Error ? err : new Error(String(err)));
4
- const prepareErrors = async (error, retry) => {
5
- let retryResult = undefined;
6
- const retryFunc = async () => {
7
- if (retry) {
8
- return await retry()
9
- .then((res) => {
10
- retryResult = res;
11
- return undefined;
12
- })
13
- .catch((err) => normalizeError(err));
14
- }
15
- };
16
- const parsedError = await resolveAzureNetKitError(normalizeError(error), {
17
- can: !!retry,
18
- call: retry ? () => retryFunc() : undefined
19
- });
20
- return { error: parsedError, retryResult };
3
+ const prepareError = (error) => {
4
+ const normalized = normalizeError(error);
5
+ const appError = normalized instanceof AppError ? normalized : new AppError(normalized);
6
+ return appError.toPlainObject();
21
7
  };
22
8
  const beforeSendResolver = async (settings) => {
23
9
  if (!settings?.beforeSend)
@@ -55,7 +41,7 @@ const beforeSendResolver = async (settings) => {
55
41
  });
56
42
  if (!aborted)
57
43
  return undefined;
58
- const { error } = await prepareErrors(abortReason ?? new AsyncHelperError('Aborted in beforeSend'));
44
+ const error = prepareError(abortReason ?? new AsyncHelperError('Aborted in beforeSend'));
59
45
  return {
60
46
  success: false,
61
47
  error,
@@ -66,39 +52,18 @@ export const createAsyncAction = async (action, args) => {
66
52
  const beforeSendResult = await beforeSendResolver(args);
67
53
  if (beforeSendResult)
68
54
  return beforeSendResult;
69
- const executeAction = async () => {
70
- const response = await Promise.resolve(action());
71
- return { response, success: true };
72
- };
73
- const handleFailure = async (err, retry) => {
74
- const { error, retryResult } = await prepareErrors(normalizeError(err), retry);
75
- if (retryResult) {
76
- return completeSuccess(retryResult);
77
- }
78
- const result = { error, response: args?.fallbackResponse, success: false };
79
- try {
80
- await args?.onError?.(result);
81
- }
82
- catch (onErrorException) {
83
- throw await resolveAzureNetKitError(new AsyncHelperError('onError caught exception', { cause: normalizeError(onErrorException) }));
84
- }
85
- return result;
86
- };
87
- const completeSuccess = async (result) => {
88
- try {
89
- await args?.onSuccess?.(result);
90
- return result;
91
- }
92
- catch (err) {
93
- return handleFailure(err);
94
- }
95
- };
55
+ let response;
96
56
  try {
97
- return completeSuccess(await executeAction());
57
+ response = await action();
98
58
  }
99
59
  catch (err) {
100
- return handleFailure(err, executeAction);
60
+ const result = { error: prepareError(err), response: args?.fallbackResponse, success: false };
61
+ await args?.onError?.(result);
62
+ return result;
101
63
  }
64
+ const result = { response, success: true };
65
+ await args?.onSuccess?.(result);
66
+ return result;
102
67
  };
103
68
  export const createAsyncResource = async (action, args) => {
104
69
  const result = await createAsyncAction(action, args);
@@ -55,7 +55,7 @@ export interface IHttpInstanceNormalizedRequest {
55
55
  responseFormat: 'json' | 'blob' | 'text' | 'arrayBuffer' | 'body';
56
56
  includeRaw: boolean;
57
57
  timeout: number | false;
58
- retry: number;
58
+ retry?: IHttpInstanceRetryOptions;
59
59
  stringifyJson: (data: unknown) => string | undefined;
60
60
  fetch: typeof fetch;
61
61
  requestInit: Omit<RequestInit, 'method' | 'headers' | 'body'>;
@@ -63,6 +63,14 @@ export interface IHttpInstanceNormalizedRequest {
63
63
  export type IHttpInstanceOnRequest = (request: IHttpInstanceNormalizedRequest) => void | Promise<void>;
64
64
  export type IHttpInstanceOnError = (error: HttpServiceError<unknown>, request: IHttpInstanceNormalizedRequest) => void | Promise<void>;
65
65
  export type IHttpInstanceDoFetch = <T = unknown>(request: IHttpInstanceNormalizedRequest) => Promise<IHttpServiceResponse<T>>;
66
+ export interface IHttpInstanceRetryOptions {
67
+ /** Number of additional attempts. Zero disables retries, including inherited policies. */
68
+ limit: number;
69
+ statuses?: readonly number[];
70
+ shouldRetry?: (context: {
71
+ error: HttpServiceError<unknown>;
72
+ }) => boolean | Promise<boolean>;
73
+ }
66
74
  export interface IHttpInstanceOptions<T = unknown> extends Omit<RequestInit, 'headers' | 'method'> {
67
75
  headers?: NonNullable<RequestInit['headers']> | Record<string, string | undefined>;
68
76
  json?: unknown;
@@ -70,7 +78,7 @@ export interface IHttpInstanceOptions<T = unknown> extends Omit<RequestInit, 'he
70
78
  searchParams?: SearchParamsOption;
71
79
  prefixUrl?: URL | string;
72
80
  timeout?: number | false;
73
- retry?: number;
81
+ retry?: IHttpInstanceRetryOptions;
74
82
  includeRaw?: boolean;
75
83
  responseFormat?: 'json' | 'blob' | 'text' | 'arrayBuffer' | 'body';
76
84
  onRequest?: IHttpInstanceOnRequest;
@@ -196,15 +196,6 @@ const withTimeout = (signal, timeout) => {
196
196
  }
197
197
  };
198
198
  };
199
- const shouldRetryError = (error) => {
200
- if (error instanceof Error && error.name === 'AbortError')
201
- return false;
202
- if (error instanceof TypeError)
203
- return true;
204
- if (!(error instanceof HttpServiceError) || error.type !== HttpErrorTypes.External)
205
- return false;
206
- return error.status === 408 || error.status === 425 || error.status === 429 || error.status >= 500;
207
- };
208
199
  const normalizeUnknownError = (error) => {
209
200
  if (error instanceof Error)
210
201
  return error;
@@ -239,39 +230,21 @@ const parseResponse = async (response, request) => {
239
230
  return { data, headers: Object.fromEntries(response.headers.entries()), status: response.status, raw };
240
231
  };
241
232
  const defaultDoFetch = async (request) => {
242
- const maxAttempts = Math.max(1, Math.floor(request.retry) + 1);
243
- let lastError;
244
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
245
- const timeoutState = withTimeout(request.requestInit.signal ?? null, request.timeout);
246
- try {
247
- const response = await request.fetch(request.url, {
248
- ...request.requestInit,
249
- method: request.method,
250
- headers: request.headers,
251
- body: request.body,
252
- signal: timeoutState.signal ?? undefined
253
- });
254
- const { data, headers, status, raw } = await parseResponse(response, request);
255
- return new HttpServiceResponse({
256
- headers,
257
- status,
258
- success: true,
259
- data,
260
- message: 'Request completed',
261
- raw
262
- });
263
- }
264
- catch (error) {
265
- lastError = error;
266
- if (attempt >= maxAttempts || !shouldRetryError(error)) {
267
- throw error;
268
- }
269
- }
270
- finally {
271
- timeoutState.clear();
272
- }
233
+ const timeoutState = withTimeout(request.requestInit.signal, request.timeout);
234
+ try {
235
+ const response = await request.fetch(request.url, {
236
+ ...request.requestInit,
237
+ method: request.method,
238
+ headers: request.headers,
239
+ body: request.body,
240
+ signal: timeoutState.signal ?? undefined
241
+ });
242
+ const { data, headers, status, raw } = await parseResponse(response, request);
243
+ return new HttpServiceResponse({ headers, status, success: true, data, message: 'Request completed', raw });
244
+ }
245
+ finally {
246
+ timeoutState.clear();
273
247
  }
274
- throw lastError;
275
248
  };
276
249
  const makeMethod = (method, defaults) => {
277
250
  return async (url, options = {}) => {
@@ -298,7 +271,7 @@ const makeMethod = (method, defaults) => {
298
271
  responseFormat,
299
272
  includeRaw: merged.includeRaw ?? false,
300
273
  timeout: merged.timeout ?? 20000,
301
- retry: merged.retry ?? 0,
274
+ retry: merged.retry,
302
275
  stringifyJson,
303
276
  fetch: fetcher,
304
277
  requestInit: {
@@ -315,33 +288,47 @@ const makeMethod = (method, defaults) => {
315
288
  window: merged.window
316
289
  }
317
290
  };
318
- const onRequest = options.onRequest ?? defaults.onRequest;
319
- if (onRequest) {
320
- await onRequest(normalizedRequest);
291
+ // Snapshot the policy per call: hooks and concurrent requests cannot extend its budget.
292
+ const policy = merged.retry;
293
+ const limit = policy?.limit ?? 0;
294
+ if (!Number.isSafeInteger(limit) || limit < 0) {
295
+ throw new RangeError('retry.limit must be a non-negative safe integer');
321
296
  }
322
- const doFetch = merged.doFetch ?? defaults.doFetch ?? defaultDoFetch;
323
- try {
324
- const fetched = await doFetch(normalizedRequest);
325
- const onResponse = options.onResponse ?? defaults.onResponse;
326
- if (onResponse) {
327
- await onResponse(fetched);
297
+ const statuses = policy?.statuses ? [...policy.statuses] : undefined;
298
+ const shouldRetry = policy?.shouldRetry;
299
+ const doFetch = merged.doFetch ?? defaultDoFetch;
300
+ let fetched;
301
+ for (let attempt = 0;; attempt++) {
302
+ normalizedRequest.requestInit.signal?.throwIfAborted();
303
+ // Re-run authentication/header hooks after recovery, without accumulating headers.
304
+ normalizedRequest.headers = new Headers(headers);
305
+ await merged.onRequest?.(normalizedRequest);
306
+ normalizedRequest.requestInit.signal?.throwIfAborted();
307
+ try {
308
+ fetched = await doFetch(normalizedRequest);
309
+ break;
328
310
  }
329
- return fetched;
330
- }
331
- catch (error) {
332
- const normalizedError = error instanceof HttpServiceError
333
- ? error
334
- : new HttpServiceError({
335
- data: undefined,
336
- message: normalizeUnknownError(error).message ?? 'Unknown error',
337
- original: normalizeUnknownError(error)
338
- });
339
- const onError = options.onError ?? defaults.onError;
340
- if (!onError)
311
+ catch (error) {
312
+ const normalizedError = error instanceof HttpServiceError
313
+ ? error
314
+ : new HttpServiceError({
315
+ message: normalizeUnknownError(error).message,
316
+ original: normalizeUnknownError(error)
317
+ });
318
+ const aborted = normalizedRequest.requestInit.signal?.aborted || normalizedError.original?.name === 'AbortError';
319
+ const streaming = typeof ReadableStream !== 'undefined' && normalizedRequest.body instanceof ReadableStream;
320
+ const eligible = !aborted && !streaming && attempt < limit && (!statuses || statuses.includes(normalizedError.status));
321
+ if (eligible && (!shouldRetry || (await shouldRetry({ error: normalizedError })))) {
322
+ normalizedRequest.requestInit.signal?.throwIfAborted();
323
+ continue;
324
+ }
325
+ await merged.onError?.(normalizedError, normalizedRequest);
341
326
  throw normalizedError;
342
- await onError(normalizedError, normalizedRequest);
343
- throw normalizedError;
327
+ }
344
328
  }
329
+ // Consumer callback failures must never replay a successful HTTP operation.
330
+ await merged.onResponse?.(fetched);
331
+ return fetched;
345
332
  };
346
333
  };
347
334
  export class HttpServiceInstance {
@@ -1,5 +1,5 @@
1
1
  export { BaseHttpDatasource, type CreateRequestCallbackType } from './datasource/index.js';
2
2
  export { DTOMapper, type IDTOMapper } from './resource/index.js';
3
- export { HttpStatusCode, HttpServiceInstance, HttpInstanceFetchMethods, HttpErrorTypes, HttpServiceResponse, HttpServiceError, type IHttpServiceError, type IHttpServiceResponse, type IHttpServiceInstance, type IHttpInstanceOptions, type IHttpInstanceNormalizedRequest, type IHttpInstanceOnRequest, type IHttpInstanceOnError, type IHttpInstanceDoFetch, type IHttpInstanceRequestMethod } from './http-service/index.js';
3
+ export { HttpStatusCode, HttpServiceInstance, HttpInstanceFetchMethods, HttpErrorTypes, HttpServiceResponse, HttpServiceError, type IHttpServiceError, type IHttpServiceResponse, type IHttpServiceInstance, type IHttpInstanceOptions, type IHttpInstanceRetryOptions, type IHttpInstanceNormalizedRequest, type IHttpInstanceOnRequest, type IHttpInstanceOnError, type IHttpInstanceDoFetch, type IHttpInstanceRequestMethod } from './http-service/index.js';
4
4
  export { ResponseBuilder } from './response/index.js';
5
5
  export { QueryBuilder, type IQueryBuilderInstance, type ArrayFormat, type ObjectFormat, type IQueryBuilderSettings } from './query/QueryBuilder.js';
@@ -1,7 +1,5 @@
1
1
  import { type TranslationManagerSource } from '../../i18n/Internal.js';
2
2
  import { type ContextData } from '../../shared/request-context/Internal.js';
3
- import { AppError, type IAppError } from '../app-error/AppError.js';
4
- import { type AsyncHelperRetry } from '../app-error/ErrorRuntime.js';
5
3
  import { type ServerMiddlewareClass } from './middleware/ServerMiddleware.js';
6
4
  import type { Handle, HandleFetch, HandleServerError, HandleClientError, Reroute, ServerInit, Transport } from '@sveltejs/kit/hooks';
7
5
  import type { RequestEvent } from '@sveltejs/kit';
@@ -10,15 +8,9 @@ import { useLogger } from '../logger/index.js';
10
8
  type MaybePromise<T> = T | Promise<T>;
11
9
  type HandleInput = Parameters<Handle>[0];
12
10
  type Resolve = HandleInput['resolve'];
13
- type BuilderFlags = Partial<Record<'useTranslations' | 'useBoot' | 'use' | 'useClient' | 'useClientInit' | 'useClientError' | 'useServerInit' | 'useServer' | 'useServerError' | 'useServerFetch' | 'useAzureNetKitError' | 'useReroute' | 'useTransport', true>>;
11
+ type BuilderFlags = Partial<Record<'useTranslations' | 'useBoot' | 'useClient' | 'useClientInit' | 'useClientError' | 'useServerInit' | 'useServer' | 'useServerError' | 'useServerFetch' | 'useReroute' | 'useTransport', true>>;
14
12
  type WithFlag<TFlags extends BuilderFlags, TKey extends keyof BuilderFlags> = TFlags & Record<TKey, true>;
15
13
  type OnceMethod<TFlags extends BuilderFlags, TKey extends keyof BuilderFlags, TMethod> = TFlags[TKey] extends true ? object : TMethod;
16
- export interface AppUniversalLifecycleContext {
17
- isClient: boolean;
18
- isServer: boolean;
19
- requestContext?: ContextData;
20
- event?: RequestEvent;
21
- }
22
14
  export interface AppClientLifecycleContext {
23
15
  useMiddlewares: (middlewares: readonly ClientMiddlewareClass[]) => void;
24
16
  }
@@ -38,15 +30,6 @@ export type AppServerErrorContext = Parameters<HandleServerError>[0] & {
38
30
  export type AppServerFetchContext = Parameters<HandleFetch>[0] & {
39
31
  requestContext: ContextData;
40
32
  };
41
- export interface AppAzureNetKitErrorContext {
42
- error: AppError;
43
- retry: AsyncHelperRetry;
44
- isClient: boolean;
45
- isServer: boolean;
46
- requestContext?: ContextData;
47
- event?: RequestEvent;
48
- }
49
- export type AppUniversalLifecycleCallback = (context: AppUniversalLifecycleContext) => MaybePromise<void>;
50
33
  export type AppClientLifecycleCallback = (context: AppClientLifecycleContext) => MaybePromise<void>;
51
34
  export type AppClientInitLifecycleCallback = (context: AppClientInitLifecycleContext) => MaybePromise<void>;
52
35
  export type AppClientErrorCallback = (context: AppClientErrorContext) => ReturnType<HandleClientError>;
@@ -54,7 +37,6 @@ export type AppServerInitLifecycleCallback = ServerInit;
54
37
  export type AppServerLifecycleCallback = (context: AppServerLifecycleContext) => MaybePromise<Response | void>;
55
38
  export type AppServerErrorCallback = (context: AppServerErrorContext) => ReturnType<HandleServerError>;
56
39
  export type AppServerFetchCallback = (context: AppServerFetchContext) => ReturnType<HandleFetch>;
57
- export type AppAzureNetKitErrorCallback = (context: AppAzureNetKitErrorContext) => MaybePromise<IAppError & object>;
58
40
  export type AppRerouteCallback = Reroute;
59
41
  export interface AppBootContext {
60
42
  state: App.RequestContextState;
@@ -64,8 +46,6 @@ export type CreateAppBuilder<TFlags extends BuilderFlags = object> = OnceMethod<
64
46
  useBoot(callback: AppBootCallback): CreateAppBuilder<WithFlag<TFlags, 'useBoot'>>;
65
47
  }> & OnceMethod<TFlags, 'useTranslations', {
66
48
  useTranslations(manager: TranslationManagerSource): CreateAppBuilder<WithFlag<TFlags, 'useTranslations'>>;
67
- }> & OnceMethod<TFlags, 'use', {
68
- use(callback: AppUniversalLifecycleCallback): CreateAppBuilder<WithFlag<TFlags, 'use'>>;
69
49
  }> & OnceMethod<TFlags, 'useClient', {
70
50
  useClient(callback: AppClientLifecycleCallback): CreateAppBuilder<WithFlag<TFlags, 'useClient'>>;
71
51
  }> & OnceMethod<TFlags, 'useClientInit', {
@@ -80,8 +60,6 @@ export type CreateAppBuilder<TFlags extends BuilderFlags = object> = OnceMethod<
80
60
  useServerError(callback: AppServerErrorCallback): CreateAppBuilder<WithFlag<TFlags, 'useServerError'>>;
81
61
  }> & OnceMethod<TFlags, 'useServerFetch', {
82
62
  useServerFetch(callback: AppServerFetchCallback): CreateAppBuilder<WithFlag<TFlags, 'useServerFetch'>>;
83
- }> & OnceMethod<TFlags, 'useAzureNetKitError', {
84
- useAzureNetKitError(callback: AppAzureNetKitErrorCallback): CreateAppBuilder<WithFlag<TFlags, 'useAzureNetKitError'>>;
85
63
  }> & OnceMethod<TFlags, 'useReroute', {
86
64
  useReroute(callback: AppRerouteCallback): CreateAppBuilder<WithFlag<TFlags, 'useReroute'>>;
87
65
  }> & OnceMethod<TFlags, 'useTransport', {
@@ -3,8 +3,7 @@ import { hydratable } from 'svelte';
3
3
  import { getContextInternals } from '../../shared/request-context/Internal.js';
4
4
  import { BROWSER } from '../../external/tools/index.js';
5
5
  import { RequestContext } from '../../shared/request-context/Internal.js';
6
- import { AppError, AzureNetKitInternalError } from '../app-error/AppError.js';
7
- import { bindAzureNetKitErrorResolver } from '../app-error/ErrorRuntime.js';
6
+ import { AzureNetKitInternalError } from '../app-error/AppError.js';
8
7
  import { executeServerMiddlewares } from './middleware/ServerMiddleware.js';
9
8
  import { beforeNavigate } from '$app/navigation';
10
9
  import { executeClientMiddlewares } from './middleware/ClientMiddleware.js';
@@ -61,10 +60,6 @@ const createBuilder = (callbacks) => {
61
60
  setCallback(callbacks, 'useBoot', callback);
62
61
  return builder;
63
62
  },
64
- use(callback) {
65
- setCallback(callbacks, 'use', callback);
66
- return builder;
67
- },
68
63
  useClient(callback) {
69
64
  setCallback(callbacks, 'useClient', callback);
70
65
  return builder;
@@ -93,10 +88,6 @@ const createBuilder = (callbacks) => {
93
88
  setCallback(callbacks, 'useServerFetch', callback);
94
89
  return builder;
95
90
  },
96
- useAzureNetKitError(callback) {
97
- setCallback(callbacks, 'useAzureNetKitError', callback);
98
- return builder;
99
- },
100
91
  useReroute(callback) {
101
92
  setCallback(callbacks, 'useReroute', callback);
102
93
  return builder;
@@ -115,42 +106,15 @@ export const createApp = (callback) => {
115
106
  let translationInitPromise;
116
107
  let serverInitPromise;
117
108
  callback(createBuilder(callbacks));
118
- const errorResolver = callbacks.useAzureNetKitError
119
- ? (error, retry) => {
120
- const requestContext = BROWSER ? undefined : getRequestContext();
121
- return callbacks.useAzureNetKitError({
122
- error,
123
- retry,
124
- isClient: BROWSER,
125
- isServer: !BROWSER,
126
- requestContext,
127
- event: requestContext?.event
128
- });
129
- }
130
- : undefined;
131
- const bindErrorResolver = (requestContext) => {
132
- bindAzureNetKitErrorResolver(errorResolver, requestContext);
133
- };
134
109
  const runClient = (kind) => {
135
110
  if (!BROWSER)
136
111
  return undefined;
137
- bindErrorResolver();
138
112
  const flags = getLifecycleFlags(appKey);
139
113
  if (flags[kind])
140
114
  return kind === 'clientInit' ? translationInitPromise : undefined;
141
115
  flags[kind] = true;
142
116
  if (kind === 'clientInit') {
143
- const initialize = () => {
144
- const universalContext = {
145
- isClient: true,
146
- isServer: false
147
- };
148
- const universalResult = callbacks.use?.(universalContext);
149
- if (universalResult && typeof universalResult.then === 'function') {
150
- return universalResult.then(() => callbacks.useClientInit?.());
151
- }
152
- return callbacks.useClientInit?.();
153
- };
117
+ const initialize = () => callbacks.useClientInit?.();
154
118
  if (!callbacks.useTranslations)
155
119
  return initialize();
156
120
  translationInitPromise = getTranslationManagerLifecycle(callbacks.useTranslations)
@@ -185,29 +149,11 @@ export const createApp = (callback) => {
185
149
  const runServerError = (input) => {
186
150
  if (BROWSER)
187
151
  return undefined;
188
- const requestContext = getServerRequestContext();
189
- bindErrorResolver(requestContext);
190
- try {
191
- const result = callbacks.useServerError?.({
192
- requestContext,
193
- ...input,
194
- useLogger
195
- });
196
- if (result && typeof result.then === 'function') {
197
- return Promise.resolve(result).finally(() => bindAzureNetKitErrorResolver(undefined, requestContext));
198
- }
199
- bindAzureNetKitErrorResolver(undefined, requestContext);
200
- return result;
201
- }
202
- catch (error) {
203
- bindAzureNetKitErrorResolver(undefined, requestContext);
204
- throw error;
205
- }
152
+ return callbacks.useServerError?.({ requestContext: getServerRequestContext(), ...input, useLogger });
206
153
  };
207
154
  const runClientError = (input) => {
208
155
  if (!BROWSER)
209
156
  return undefined;
210
- bindErrorResolver();
211
157
  return callbacks.useClientError?.({
212
158
  ...input
213
159
  });
@@ -230,36 +176,21 @@ export const createApp = (callback) => {
230
176
  })
231
177
  : originalResolve;
232
178
  const requestContext = getServerRequestContext();
233
- bindErrorResolver(requestContext);
234
- try {
235
- const flags = getLifecycleFlags(appKey, requestContext);
236
- if (!flags.server) {
237
- flags.server = true;
238
- if (translations)
239
- await translations.preloadTranslation();
240
- const universalResult = callbacks.use?.({
241
- isClient: false,
242
- isServer: true,
243
- requestContext,
244
- event
245
- });
246
- if (universalResult && typeof universalResult.then === 'function') {
247
- await universalResult;
248
- }
249
- const serverResult = await callbacks.useServer?.({
250
- requestContext,
251
- event,
252
- resolve,
253
- useMiddlewares: executeServerMiddlewares
254
- });
255
- if (serverResult)
256
- return serverResult;
257
- }
258
- return await resolve(event);
259
- }
260
- finally {
261
- bindAzureNetKitErrorResolver(undefined, requestContext);
179
+ const flags = getLifecycleFlags(appKey, requestContext);
180
+ if (!flags.server) {
181
+ flags.server = true;
182
+ if (translations)
183
+ await translations.preloadTranslation();
184
+ const serverResult = await callbacks.useServer?.({
185
+ requestContext,
186
+ event,
187
+ resolve,
188
+ useMiddlewares: executeServerMiddlewares
189
+ });
190
+ if (serverResult)
191
+ return serverResult;
262
192
  }
193
+ return await resolve(event);
263
194
  };
264
195
  const runBoot = () => {
265
196
  if (!callbacks.useBoot && !callbacks.useTranslations)
@@ -1,4 +1,4 @@
1
- export { createApp, type AppAzureNetKitErrorCallback, type AppAzureNetKitErrorContext, type AppClientErrorCallback, type AppClientErrorContext, type AppClientInitLifecycleCallback, type AppClientInitLifecycleContext, type AppClientLifecycleCallback, type AppClientLifecycleContext, type AppRegistrar, type AppServerErrorCallback, type AppServerErrorContext, type AppServerFetchCallback, type AppServerFetchContext, type AppServerInitLifecycleCallback, type AppServerInitLifecycleContext, type AppServerLifecycleCallback, type AppServerLifecycleContext, type AppRerouteCallback, type AppUniversalLifecycleCallback, type AppUniversalLifecycleContext, type CreateAppInstance } from './App.js';
1
+ export { createApp, type AppClientErrorCallback, type AppClientErrorContext, type AppClientInitLifecycleCallback, type AppClientInitLifecycleContext, type AppClientLifecycleCallback, type AppClientLifecycleContext, type AppRegistrar, type AppServerErrorCallback, type AppServerErrorContext, type AppServerFetchCallback, type AppServerFetchContext, type AppServerInitLifecycleCallback, type AppServerInitLifecycleContext, type AppServerLifecycleCallback, type AppServerLifecycleContext, type AppRerouteCallback, type CreateAppInstance } from './App.js';
2
2
  export type { IClientMiddleware, ClientMiddlewareContext, ClientMiddlewareClass } from './middleware/ClientMiddleware.js';
3
3
  export type { IServerMiddleware, ServerMiddlewareContext, ServerMiddlewareClass } from './middleware/ServerMiddleware.js';
4
4
  export type { IMiddleware, MiddlewareClass } from './middleware/Types.js';
@@ -17,6 +17,7 @@ export interface IAppError<T = unknown> {
17
17
  validation?: RequestErrors<T>;
18
18
  external: boolean;
19
19
  appErrorConvert?: boolean;
20
+ statusCode: number;
20
21
  }
21
22
  export declare class AppError<T = unknown> extends Error {
22
23
  error: Error;
@@ -24,6 +25,7 @@ export declare class AppError<T = unknown> extends Error {
24
25
  message: string;
25
26
  external: boolean;
26
27
  validation?: RequestErrors<T>;
28
+ statusCode: number;
27
29
  constructor(error: Error);
28
30
  tryToGetExternalError<HttpServiceDataType = unknown>(): HttpServiceError<HttpServiceDataType> | undefined;
29
31
  redeclareValidation<D = T>(validation: D): void;
@@ -18,6 +18,7 @@ export class AppError extends Error {
18
18
  message;
19
19
  external = false;
20
20
  validation;
21
+ statusCode = 500;
21
22
  constructor(error) {
22
23
  super(error.message);
23
24
  this.error = error;
@@ -41,6 +42,7 @@ export class AppError extends Error {
41
42
  external: this.external,
42
43
  validation: this.validation,
43
44
  appErrorConvert: true,
45
+ statusCode: this.statusCode,
44
46
  ...customData
45
47
  };
46
48
  }
@@ -49,10 +51,12 @@ export class AppError extends Error {
49
51
  case this.error instanceof HttpServiceError:
50
52
  this.type = ErrorTypes.Http;
51
53
  this.external = this.error.type === HttpErrorTypes.External;
54
+ this.statusCode = this.error.status;
52
55
  break;
53
56
  case this.error instanceof SchemaFail:
54
57
  this.type = ErrorTypes.Schema;
55
58
  this.validation = this.error.getSelfTypedData();
59
+ this.statusCode = 422;
56
60
  break;
57
61
  case this.error instanceof AzureNetKitInternalError:
58
62
  this.type = ErrorTypes.KitInternalError;
@@ -1,2 +1 @@
1
1
  export * from './AppError.js';
2
- export * from './ErrorRuntime.js';
@@ -1,2 +1 @@
1
1
  export * from './AppError.js';
2
- export * from './ErrorRuntime.js';
@@ -1,5 +1,5 @@
1
1
  import { CreateServiceContainer } from './Provider.js';
2
- const toProvider = (dependency) => typeof dependency === 'function' ? dependency : dependency.resolve;
2
+ const toProvider = (dependency) => (typeof dependency === 'function' ? dependency : dependency.resolve);
3
3
  /** Holds registration metadata only. Resolving always uses the current request's container. */
4
4
  export class CompositionRoot {
5
5
  name = Symbol('@azure-net/kit:CompositionRoot');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-net/kit",
3
- "version": "6.0.0-next.10",
3
+ "version": "6.0.0-next.12",
4
4
  "license": "MIT",
5
5
  "author": "Pixel1917",
6
6
  "description": "Universal toolkit and library collection, developed as part of the Azure-net ecosystem",
@@ -1,11 +0,0 @@
1
- import { type ContextData } from '../../shared/request-context/Internal.js';
2
- import { AppError, type IAppError } from './AppError.js';
3
- type MaybePromise<T> = T | Promise<T>;
4
- export interface AsyncHelperRetry {
5
- can: boolean;
6
- call?: () => Promise<Error | void>;
7
- }
8
- export type AzureNetKitErrorResolver = (error: AppError, retry: AsyncHelperRetry) => MaybePromise<IAppError & object>;
9
- export declare const bindAzureNetKitErrorResolver: (resolver?: AzureNetKitErrorResolver, requestContext?: ContextData) => void;
10
- export declare const resolveAzureNetKitError: <Validation = unknown, CustomData extends object = Record<never, never>>(error: unknown, retry?: AsyncHelperRetry) => Promise<IAppError<Validation> & CustomData>;
11
- export {};
@@ -1,58 +0,0 @@
1
- import { getContextInternals } from '../../shared/request-context/Internal.js';
2
- import { BROWSER } from '../../external/tools/index.js';
3
- import { RequestContext } from '../../shared/request-context/Internal.js';
4
- import { AppError, AzureNetKitInternalError } from './AppError.js';
5
- const SERVER_BINDING_KEY = Symbol('azureNetKitErrorResolver');
6
- let clientBinding;
7
- const createBinding = (resolver) => ({ resolver });
8
- const getServerData = (requestContext) => {
9
- if (requestContext)
10
- return getContextInternals(requestContext);
11
- try {
12
- return getContextInternals(RequestContext.current());
13
- }
14
- catch {
15
- return undefined;
16
- }
17
- };
18
- export const bindAzureNetKitErrorResolver = (resolver, requestContext) => {
19
- if (BROWSER) {
20
- clientBinding = resolver ? createBinding(resolver) : undefined;
21
- return;
22
- }
23
- const data = getServerData(requestContext);
24
- if (!data)
25
- return;
26
- if (resolver) {
27
- data[SERVER_BINDING_KEY] = createBinding(resolver);
28
- }
29
- else {
30
- delete data[SERVER_BINDING_KEY];
31
- }
32
- };
33
- const getBinding = () => {
34
- if (BROWSER)
35
- return clientBinding;
36
- return getServerData()?.[SERVER_BINDING_KEY];
37
- };
38
- const normalizeError = (error) => (error instanceof Error ? error : new Error(String(error)));
39
- export const resolveAzureNetKitError = async (error, retry = { can: false }) => {
40
- const normalizedError = normalizeError(error);
41
- const appError = normalizedError instanceof AppError ? normalizedError : new AppError(normalizedError);
42
- const binding = getBinding();
43
- if (!binding || binding.activeErrors?.has(normalizedError)) {
44
- return appError.toPlainObject();
45
- }
46
- const activeErrors = (binding.activeErrors ??= new WeakSet());
47
- activeErrors.add(normalizedError);
48
- try {
49
- const result = await binding.resolver(appError, retry);
50
- if (!result?.appErrorConvert) {
51
- throw new AzureNetKitInternalError("[createApp] 'useAzureNetKitError' must return the result of AppError.toPlainObject().");
52
- }
53
- return result;
54
- }
55
- finally {
56
- activeErrors.delete(normalizedError);
57
- }
58
- };