@azure-net/kit 6.0.0-next.14 → 6.0.0-next.15
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 +40 -18
- package/dist/infra/http-service/HttpServiceInstance.d.ts +0 -1
- package/dist/infra/http-service/HttpServiceInstance.js +9 -9
- package/dist/shared/app/App.d.ts +1 -6
- package/dist/shared/app/App.js +2 -18
- package/dist/shared/app/index.d.ts +2 -0
- package/dist/shared/app/index.js +2 -0
- package/dist/shared/app/middleware/ClientMiddleware.d.ts +6 -0
- package/dist/shared/app/middleware/ClientMiddleware.js +13 -0
- package/dist/shared/app/middleware/ServerMiddleware.d.ts +6 -0
- package/dist/shared/app/middleware/ServerMiddleware.js +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -189,13 +189,13 @@ export {};
|
|
|
189
189
|
```
|
|
190
190
|
|
|
191
191
|
```ts
|
|
192
|
-
import { createApp } from '@azure-net/kit';
|
|
192
|
+
import { createApp, ServerMiddlewareManager } from '@azure-net/kit';
|
|
193
193
|
import { RequestContext } from '@azure-net/kit/server-context';
|
|
194
194
|
|
|
195
195
|
export const { register } = createApp((app) =>
|
|
196
196
|
app
|
|
197
|
-
.useServer(async ({ event, resolve
|
|
198
|
-
await
|
|
197
|
+
.useServer(async ({ event, resolve }) => {
|
|
198
|
+
await new ServerMiddlewareManager([ServerAuth]).execute();
|
|
199
199
|
return resolve(event);
|
|
200
200
|
})
|
|
201
201
|
.useBoot(({ state }) => {
|
|
@@ -300,7 +300,15 @@ only when its turn is reached, on every request/navigation. Resolve dependencies
|
|
|
300
300
|
inside that instance, not into module-level request-specific variables.
|
|
301
301
|
|
|
302
302
|
```ts
|
|
303
|
-
import
|
|
303
|
+
import {
|
|
304
|
+
ServerMiddlewareManager,
|
|
305
|
+
ClientMiddlewareManager,
|
|
306
|
+
type IServerMiddleware,
|
|
307
|
+
type ServerMiddlewareContext,
|
|
308
|
+
type IClientMiddleware,
|
|
309
|
+
type ClientMiddlewareContext
|
|
310
|
+
} from '@azure-net/kit';
|
|
311
|
+
import { beforeNavigate } from '$app/navigation';
|
|
304
312
|
|
|
305
313
|
class ServerAuth implements IServerMiddleware {
|
|
306
314
|
async execute({ event, next }: ServerMiddlewareContext) {
|
|
@@ -317,16 +325,26 @@ class ClientGuard implements IClientMiddleware {
|
|
|
317
325
|
}
|
|
318
326
|
}
|
|
319
327
|
|
|
328
|
+
const serverMiddlewares = new ServerMiddlewareManager([ServerAuth]);
|
|
329
|
+
const clientMiddlewares = new ClientMiddlewareManager([ClientGuard]);
|
|
330
|
+
|
|
320
331
|
app
|
|
321
|
-
.useServer(async ({
|
|
322
|
-
await
|
|
332
|
+
.useServer(async ({ event, resolve }) => {
|
|
333
|
+
await serverMiddlewares.execute();
|
|
323
334
|
return resolve(event);
|
|
324
335
|
})
|
|
325
|
-
.useClient((
|
|
326
|
-
|
|
336
|
+
.useClient(() => {
|
|
337
|
+
beforeNavigate((navigation) => clientMiddlewares.execute(navigation));
|
|
327
338
|
});
|
|
339
|
+
|
|
340
|
+
// Run the same checks without navigating or registering another subscription.
|
|
341
|
+
clientMiddlewares.execute();
|
|
328
342
|
```
|
|
329
343
|
|
|
344
|
+
Managers snapshot the constructor list and retain no request data or middleware instances, so they can be declared at module scope. `ServerMiddlewareManager.execute()` resolves the current request each time. `ClientMiddlewareManager.execute()` uses the current page URL; `from` and `navigation` are undefined in a manual run. Client execution is a no-op during SSR.
|
|
345
|
+
|
|
346
|
+
`createApp` no longer injects `useMiddlewares` or subscribes to navigation itself. Register `beforeNavigate` synchronously inside `useClient`, before any `await`. A manual run does not create a navigation to cancel: omitting `next()` only stops that run. Avoid calling a manager from middleware that it executes, including indirectly through HTTP recovery. Re-running a full chain also re-runs analytics; use a smaller manager if only auth state must be synchronized.
|
|
347
|
+
|
|
330
348
|
`IMiddleware<Context, Result>` is the common interface. Client `execute` is strictly
|
|
331
349
|
synchronous (`undefined` return, normally `return next()`); server `execute` can
|
|
332
350
|
return `void` or `Promise<void>`. Omitting `next()` blocks the chain. Neither the
|
|
@@ -382,7 +400,11 @@ cases. With no `retry` option, a failed request is not repeated. `limit` counts
|
|
|
382
400
|
additional attempts: `limit: 1` means at most two HTTP attempts.
|
|
383
401
|
|
|
384
402
|
```ts
|
|
403
|
+
// A separate session client: no authentication recovery hooks.
|
|
404
|
+
const sessionHttp = new HttpServiceInstance({ prefixUrl: apiUrl });
|
|
405
|
+
|
|
385
406
|
const http = new HttpServiceInstance({
|
|
407
|
+
prefixUrl: apiUrl,
|
|
386
408
|
onRequest(request) {
|
|
387
409
|
const token = readAccessToken();
|
|
388
410
|
if (token) request.headers.set('Authorization', `Bearer ${token}`);
|
|
@@ -390,11 +412,10 @@ const http = new HttpServiceInstance({
|
|
|
390
412
|
retry: {
|
|
391
413
|
limit: 1,
|
|
392
414
|
statuses: [401],
|
|
393
|
-
async shouldRetry({ error
|
|
415
|
+
async shouldRetry({ error }) {
|
|
394
416
|
if (!isExpiredAccessToken(error.data)) return false;
|
|
395
|
-
const refreshed = await
|
|
396
|
-
json: { refreshToken: readRefreshToken() }
|
|
397
|
-
retry: { limit: 0 }
|
|
417
|
+
const refreshed = await sessionHttp.post('/api/auth/refresh', {
|
|
418
|
+
json: { refreshToken: readRefreshToken() }
|
|
398
419
|
});
|
|
399
420
|
saveTokens(refreshed.data);
|
|
400
421
|
return true;
|
|
@@ -413,8 +434,8 @@ implement token rotation or cross-tab coordination.
|
|
|
413
434
|
- `shouldRetry` runs only after the status filter and only while budget remains.
|
|
414
435
|
Both synchronous and asynchronous booleans are accepted.
|
|
415
436
|
- Per-request `retry` replaces the entire default policy. Use `{ limit: 0 }` to
|
|
416
|
-
disable it
|
|
417
|
-
|
|
437
|
+
disable it. A separate session client with no retry policy does not inherit
|
|
438
|
+
the application client's retries or hooks.
|
|
418
439
|
- `onRequest` runs before every attempt so refreshed credentials are read again.
|
|
419
440
|
`onResponse` runs once on success; `onError` runs once on terminal HTTP failure,
|
|
420
441
|
not for intermediate failures that recover successfully.
|
|
@@ -436,8 +457,8 @@ HTTP hooks use these signatures:
|
|
|
436
457
|
```ts
|
|
437
458
|
retry: {
|
|
438
459
|
limit: 1,
|
|
439
|
-
shouldRetry: async ({ error
|
|
440
|
-
//
|
|
460
|
+
shouldRetry: async ({ error }) => {
|
|
461
|
+
// Delegate refresh to your session service with a separate HTTP client.
|
|
441
462
|
return false;
|
|
442
463
|
}
|
|
443
464
|
},
|
|
@@ -464,5 +485,6 @@ BigInt; provide `stringifyJson` if a custom wire format is required.
|
|
|
464
485
|
|
|
465
486
|
Removing `http` from `onError` does not prevent recursion through a client captured
|
|
466
487
|
in a closure. Do not initiate the same failing operation from its error handler.
|
|
467
|
-
|
|
468
|
-
|
|
488
|
+
Neither `shouldRetry` nor `onError` exposes the HTTP client. Use a separate
|
|
489
|
+
session client without recovery hooks for refresh/logout. Limits apply per HTTP
|
|
490
|
+
call, not to arbitrary nested user code.
|
|
@@ -77,7 +77,6 @@ export interface IHttpInstanceRetryOptions {
|
|
|
77
77
|
statuses?: readonly number[];
|
|
78
78
|
shouldRetry?: (context: {
|
|
79
79
|
error: HttpServiceError<unknown>;
|
|
80
|
-
http: IHttpServiceInstance;
|
|
81
80
|
}) => boolean | Promise<boolean>;
|
|
82
81
|
}
|
|
83
82
|
export interface IHttpInstanceOptions<T = unknown> extends Omit<RequestInit, 'headers' | 'method'> {
|
|
@@ -265,7 +265,7 @@ const waitForSignal = (value, signal) => {
|
|
|
265
265
|
});
|
|
266
266
|
});
|
|
267
267
|
};
|
|
268
|
-
const makeMethod = (method, defaults
|
|
268
|
+
const makeMethod = (method, defaults) => {
|
|
269
269
|
return async (url, options = {}) => {
|
|
270
270
|
const merged = mergeOptions(defaults, options);
|
|
271
271
|
const stringifyJson = merged.stringifyJson ?? stringifyJsonFunc;
|
|
@@ -351,7 +351,7 @@ const makeMethod = (method, defaults, http) => {
|
|
|
351
351
|
const aborted = sourceSignal?.aborted || normalizedError.original?.name === 'AbortError';
|
|
352
352
|
const streaming = typeof ReadableStream !== 'undefined' && normalizedRequest.body instanceof ReadableStream;
|
|
353
353
|
const eligible = !aborted && !streaming && attempt < limit && (!statuses || statuses.includes(normalizedError.status));
|
|
354
|
-
if (eligible && (!shouldRetry || (await waitForSignal(shouldRetry({ error: normalizedError
|
|
354
|
+
if (eligible && (!shouldRetry || (await waitForSignal(shouldRetry({ error: normalizedError }), sourceSignal)))) {
|
|
355
355
|
normalizedRequest.requestInit.signal?.throwIfAborted();
|
|
356
356
|
continue;
|
|
357
357
|
}
|
|
@@ -378,12 +378,12 @@ export class HttpServiceInstance {
|
|
|
378
378
|
options;
|
|
379
379
|
constructor(config = {}) {
|
|
380
380
|
this.defaults = { ...config };
|
|
381
|
-
this.get = makeMethod(HttpInstanceFetchMethods.GET, this.defaults
|
|
382
|
-
this.post = makeMethod(HttpInstanceFetchMethods.POST, this.defaults
|
|
383
|
-
this.put = makeMethod(HttpInstanceFetchMethods.PUT, this.defaults
|
|
384
|
-
this.delete = makeMethod(HttpInstanceFetchMethods.DELETE, this.defaults
|
|
385
|
-
this.patch = makeMethod(HttpInstanceFetchMethods.PATCH, this.defaults
|
|
386
|
-
this.head = makeMethod(HttpInstanceFetchMethods.HEAD, this.defaults
|
|
387
|
-
this.options = makeMethod(HttpInstanceFetchMethods.OPTIONS, this.defaults
|
|
381
|
+
this.get = makeMethod(HttpInstanceFetchMethods.GET, this.defaults);
|
|
382
|
+
this.post = makeMethod(HttpInstanceFetchMethods.POST, this.defaults);
|
|
383
|
+
this.put = makeMethod(HttpInstanceFetchMethods.PUT, this.defaults);
|
|
384
|
+
this.delete = makeMethod(HttpInstanceFetchMethods.DELETE, this.defaults);
|
|
385
|
+
this.patch = makeMethod(HttpInstanceFetchMethods.PATCH, this.defaults);
|
|
386
|
+
this.head = makeMethod(HttpInstanceFetchMethods.HEAD, this.defaults);
|
|
387
|
+
this.options = makeMethod(HttpInstanceFetchMethods.OPTIONS, this.defaults);
|
|
388
388
|
}
|
|
389
389
|
}
|
package/dist/shared/app/App.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { type TranslationManagerSource } from '../../i18n/Internal.js';
|
|
2
2
|
import { type ContextData } from '../../shared/request-context/Internal.js';
|
|
3
|
-
import { type ServerMiddlewareClass } from './middleware/ServerMiddleware.js';
|
|
4
3
|
import type { Handle, HandleFetch, HandleServerError, HandleClientError, Reroute, ServerInit, Transport } from '@sveltejs/kit/hooks';
|
|
5
4
|
import type { RequestEvent } from '@sveltejs/kit';
|
|
6
|
-
import { type ClientMiddlewareClass } from './middleware/ClientMiddleware.js';
|
|
7
5
|
import { useLogger } from '../logger/index.js';
|
|
8
6
|
type MaybePromise<T> = T | Promise<T>;
|
|
9
7
|
type HandleInput = Parameters<Handle>[0];
|
|
@@ -11,9 +9,7 @@ type Resolve = HandleInput['resolve'];
|
|
|
11
9
|
type BuilderFlags = Partial<Record<'useTranslations' | 'useBoot' | 'useClient' | 'useClientInit' | 'useClientError' | 'useServerInit' | 'useServer' | 'useServerError' | 'useServerFetch' | 'useReroute' | 'useTransport', true>>;
|
|
12
10
|
type WithFlag<TFlags extends BuilderFlags, TKey extends keyof BuilderFlags> = TFlags & Record<TKey, true>;
|
|
13
11
|
type OnceMethod<TFlags extends BuilderFlags, TKey extends keyof BuilderFlags, TMethod> = TFlags[TKey] extends true ? object : TMethod;
|
|
14
|
-
export
|
|
15
|
-
useMiddlewares: (middlewares: readonly ClientMiddlewareClass[]) => void;
|
|
16
|
-
}
|
|
12
|
+
export type AppClientLifecycleContext = void;
|
|
17
13
|
export type AppClientInitLifecycleContext = void;
|
|
18
14
|
export type AppClientErrorContext = Parameters<HandleClientError>[0];
|
|
19
15
|
export type AppServerInitLifecycleContext = void;
|
|
@@ -21,7 +17,6 @@ export interface AppServerLifecycleContext {
|
|
|
21
17
|
requestContext: ContextData;
|
|
22
18
|
event: RequestEvent;
|
|
23
19
|
resolve: Resolve;
|
|
24
|
-
useMiddlewares: (middlewares: readonly ServerMiddlewareClass[]) => Promise<void>;
|
|
25
20
|
}
|
|
26
21
|
export type AppServerErrorContext = Parameters<HandleServerError>[0] & {
|
|
27
22
|
requestContext: ContextData;
|
package/dist/shared/app/App.js
CHANGED
|
@@ -4,9 +4,6 @@ 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
6
|
import { AzureNetKitInternalError } from '../app-error/AppError.js';
|
|
7
|
-
import { executeServerMiddlewares } from './middleware/ServerMiddleware.js';
|
|
8
|
-
import { beforeNavigate } from '$app/navigation';
|
|
9
|
-
import { executeClientMiddlewares } from './middleware/ClientMiddleware.js';
|
|
10
7
|
import { useLogger } from '../logger/index.js';
|
|
11
8
|
const clientLifecycleFlags = new WeakMap();
|
|
12
9
|
const getRequestContext = () => {
|
|
@@ -102,7 +99,6 @@ const createBuilder = (callbacks) => {
|
|
|
102
99
|
export const createApp = (callback) => {
|
|
103
100
|
const appKey = {};
|
|
104
101
|
const callbacks = {};
|
|
105
|
-
let clientInitialized = false;
|
|
106
102
|
let translationInitPromise;
|
|
107
103
|
let serverInitPromise;
|
|
108
104
|
callback(createBuilder(callbacks));
|
|
@@ -127,18 +123,7 @@ export const createApp = (callback) => {
|
|
|
127
123
|
});
|
|
128
124
|
return translationInitPromise;
|
|
129
125
|
}
|
|
130
|
-
|
|
131
|
-
if (!clientInitialized) {
|
|
132
|
-
beforeNavigate((navigation) => {
|
|
133
|
-
executeClientMiddlewares(internalMiddlewares, navigation);
|
|
134
|
-
});
|
|
135
|
-
clientInitialized = true;
|
|
136
|
-
}
|
|
137
|
-
return callbacks.useClient?.({
|
|
138
|
-
useMiddlewares: (middlewares) => {
|
|
139
|
-
internalMiddlewares = middlewares;
|
|
140
|
-
}
|
|
141
|
-
});
|
|
126
|
+
return callbacks.useClient?.();
|
|
142
127
|
};
|
|
143
128
|
const runServerInit = () => {
|
|
144
129
|
if (BROWSER)
|
|
@@ -184,8 +169,7 @@ export const createApp = (callback) => {
|
|
|
184
169
|
const serverResult = await callbacks.useServer?.({
|
|
185
170
|
requestContext,
|
|
186
171
|
event,
|
|
187
|
-
resolve
|
|
188
|
-
useMiddlewares: executeServerMiddlewares
|
|
172
|
+
resolve
|
|
189
173
|
});
|
|
190
174
|
if (serverResult)
|
|
191
175
|
return serverResult;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
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
|
+
export { ClientMiddlewareManager } from './middleware/ClientMiddleware.js';
|
|
3
4
|
export type { IServerMiddleware, ServerMiddlewareContext, ServerMiddlewareClass } from './middleware/ServerMiddleware.js';
|
|
5
|
+
export { ServerMiddlewareManager } from './middleware/ServerMiddleware.js';
|
|
4
6
|
export type { IMiddleware, MiddlewareClass } from './middleware/Types.js';
|
|
5
7
|
export type { EnsureRoute, EnsureRouteSource, EnsureRouteTarget } from './middleware/Shared.js';
|
|
6
8
|
export type { AppBootContext, AppBootCallback } from './App.js';
|
package/dist/shared/app/index.js
CHANGED
|
@@ -13,4 +13,10 @@ export interface IClientMiddleware extends IMiddleware<ClientMiddlewareContext,
|
|
|
13
13
|
execute(context: ClientMiddlewareContext): undefined;
|
|
14
14
|
}
|
|
15
15
|
export type ClientMiddlewareClass = MiddlewareClass<IClientMiddleware>;
|
|
16
|
+
/** Execute manually, or pass the navigation supplied by beforeNavigate. */
|
|
17
|
+
export declare class ClientMiddlewareManager {
|
|
18
|
+
private readonly middlewares;
|
|
19
|
+
constructor(middlewares: readonly ClientMiddlewareClass[]);
|
|
20
|
+
execute(navigation?: BeforeNavigate): void;
|
|
21
|
+
}
|
|
16
22
|
export declare const executeClientMiddlewares: (middlewares: readonly ClientMiddlewareClass[], navigation?: BeforeNavigate) => void;
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { ensureRoute } from './Shared.js';
|
|
2
2
|
import { page } from '$app/state';
|
|
3
3
|
import { goto } from '$app/navigation';
|
|
4
|
+
import { BROWSER } from '../../../external/tools/index.js';
|
|
5
|
+
/** Execute manually, or pass the navigation supplied by beforeNavigate. */
|
|
6
|
+
export class ClientMiddlewareManager {
|
|
7
|
+
middlewares;
|
|
8
|
+
constructor(middlewares) {
|
|
9
|
+
this.middlewares = [...middlewares];
|
|
10
|
+
}
|
|
11
|
+
execute(navigation) {
|
|
12
|
+
if (!BROWSER)
|
|
13
|
+
return;
|
|
14
|
+
executeClientMiddlewares(this.middlewares, navigation);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
4
17
|
export const executeClientMiddlewares = (middlewares, navigation) => {
|
|
5
18
|
const from = navigation?.from?.url ?? undefined;
|
|
6
19
|
const to = navigation?.to?.url ?? page.url;
|
|
@@ -13,4 +13,10 @@ export interface IServerMiddleware extends IMiddleware<ServerMiddlewareContext,
|
|
|
13
13
|
execute(context: ServerMiddlewareContext): Promise<void> | void;
|
|
14
14
|
}
|
|
15
15
|
export type ServerMiddlewareClass = MiddlewareClass<IServerMiddleware>;
|
|
16
|
+
/** Stores definitions only; request context and instances are resolved per execution. */
|
|
17
|
+
export declare class ServerMiddlewareManager {
|
|
18
|
+
private readonly middlewares;
|
|
19
|
+
constructor(middlewares: readonly ServerMiddlewareClass[]);
|
|
20
|
+
execute(): Promise<void>;
|
|
21
|
+
}
|
|
16
22
|
export declare const executeServerMiddlewares: (middlewares: readonly ServerMiddlewareClass[]) => Promise<void>;
|
|
@@ -2,6 +2,16 @@ import { error, redirect } from '@sveltejs/kit';
|
|
|
2
2
|
import { ensureRoute } from './Shared.js';
|
|
3
3
|
import { BROWSER } from '../../../external/tools/index.js';
|
|
4
4
|
import { RequestContext } from '../../../shared/request-context/Internal.js';
|
|
5
|
+
/** Stores definitions only; request context and instances are resolved per execution. */
|
|
6
|
+
export class ServerMiddlewareManager {
|
|
7
|
+
middlewares;
|
|
8
|
+
constructor(middlewares) {
|
|
9
|
+
this.middlewares = [...middlewares];
|
|
10
|
+
}
|
|
11
|
+
execute() {
|
|
12
|
+
return executeServerMiddlewares(this.middlewares);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
5
15
|
export const executeServerMiddlewares = async (middlewares) => {
|
|
6
16
|
if (!BROWSER && middlewares.length) {
|
|
7
17
|
const event = RequestContext.current().event;
|
package/package.json
CHANGED