@tstdl/base 0.93.137 → 0.93.138

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.
@@ -13,7 +13,7 @@ import { toTitleCase } from '../../utils/string/title-case.js';
13
13
  import { isArray, isBlob, isDefined, isObject, isReadableStream, isString, isUint8Array, isUndefined } from '../../utils/type-guards.js';
14
14
  import { buildUrl } from '../../utils/url-builder.js';
15
15
  import { resolveValueOrProvider } from '../../utils/value-or-provider.js';
16
- import { normalizedApiDefinitionEndpointsEntries } from '../types.js';
16
+ import { normalizedApiDefinition } from '../types.js';
17
17
  import { getFullApiEndpointResource } from '../utils.js';
18
18
  export const httpClientSymbol = Symbol('HttpClient for ApiClient');
19
19
  export const apiDefinitionSymbol = Symbol('ApiDefinition');
@@ -22,7 +22,8 @@ export function setDefaultApiClientOptions(options) {
22
22
  copyObjectProperties(options, defaultOptions);
23
23
  }
24
24
  export function compileClient(definition, options = defaultOptions) {
25
- const { resource: path, endpoints } = definition;
25
+ const normalizedDefinition = normalizedApiDefinition(definition);
26
+ const { resource: path } = normalizedDefinition;
26
27
  const constructedApiName = toTitleCase(path[0] ?? '');
27
28
  const apiName = `${constructedApiName}ApiClient`;
28
29
  const api = {
@@ -31,10 +32,10 @@ export function compileClient(definition, options = defaultOptions) {
31
32
  [apiDefinitionSymbol];
32
33
  constructor(httpClientOrOptions) {
33
34
  this[httpClientSymbol] = (httpClientOrOptions instanceof HttpClient) ? httpClientOrOptions : inject(HttpClient, httpClientOrOptions);
34
- this[apiDefinitionSymbol] = definition;
35
+ this[apiDefinitionSymbol] = normalizedDefinition;
35
36
  }
36
37
  static getEndpointResource(endpoint, parameters) {
37
- const resource = getFullApiEndpointResource({ api: definition, endpoint: resolveValueOrProvider(definition.endpoints[endpoint]), defaultPrefix: options.prefix });
38
+ const resource = getFullApiEndpointResource({ api: normalizedDefinition, endpoint: resolveValueOrProvider(normalizedDefinition.endpoints[endpoint]), defaultPrefix: options.prefix });
38
39
  if (isUndefined(parameters)) {
39
40
  return resource;
40
41
  }
@@ -59,8 +60,7 @@ export function compileClient(definition, options = defaultOptions) {
59
60
  return new api(httpClient);
60
61
  },
61
62
  });
62
- const endpointsEntries = normalizedApiDefinitionEndpointsEntries(endpoints);
63
- for (const [name, endpoint] of endpointsEntries) {
63
+ for (const [name, endpoint] of objectEntries(normalizedDefinition.endpoints)) {
64
64
  const methods = isArray(endpoint.method) ? endpoint.method : [endpoint.method ?? 'GET'];
65
65
  let resource;
66
66
  const hasGet = methods.includes('GET');
@@ -84,7 +84,7 @@ export function compileClient(definition, options = defaultOptions) {
84
84
  else {
85
85
  requestOptions = args[0];
86
86
  }
87
- resource ??= getFullApiEndpointResource({ api: definition, endpoint, defaultPrefix: options.prefix });
87
+ resource ??= getFullApiEndpointResource({ api: normalizedDefinition, endpoint, defaultPrefix: options.prefix });
88
88
  const context = { endpoint };
89
89
  const method = (hasGet && isUndefined(parameters)) ? 'GET' : fallbackMethod;
90
90
  if (endpoint.result == ServerSentEvents) {
@@ -99,7 +99,7 @@ export function compileClient(definition, options = defaultOptions) {
99
99
  }
100
100
  return getDataStream(this[httpClientSymbol].options.baseUrl, resource, endpoint, parameters, requestOptions);
101
101
  }
102
- if (context.endpoint.data?.[bustCache] == true || requestOptions?.bustCache == true) {
102
+ if ((endpoint.bustCache == true) || (requestOptions?.bustCache == true)) {
103
103
  context[bustCache] = true;
104
104
  }
105
105
  const request = new HttpClientRequest({
@@ -143,7 +143,7 @@ describe('ApiClient', () => {
143
143
  resource: 'features',
144
144
  endpoints: {
145
145
  withCredentials: { method: 'GET', credentials: true },
146
- withBustCache: { method: 'GET', data: { [bustCache]: true } },
146
+ withBustCache: { method: 'GET', bustCache: true },
147
147
  },
148
148
  });
149
149
  const mockHttpClient = Object.assign(Object.create(HttpClient.prototype), {
@@ -4,7 +4,7 @@ import { HttpServerResponse, type HttpServerRequest } from '../../http/server/in
4
4
  import { resolveArgumentType, type Resolvable } from '../../injector/index.js';
5
5
  import type { Type } from '../../types/index.js';
6
6
  import { type AsyncMiddleware, type AsyncMiddlewareNext } from '../../utils/middleware.js';
7
- import { type ApiController, type ApiDefinition, type ApiEndpointDefinition, type ApiEndpointMethod, type ApiEndpointServerImplementation } from '../types.js';
7
+ import { type ApiController, type ApiDefinition, type ApiEndpointMethod, type ApiEndpointServerImplementation, type NormalizedApiEndpoints } from '../types.js';
8
8
  import type { CorsMiddlewareOptions } from './middlewares/cors.middleware.js';
9
9
  import { type CsrfMiddlewareOptions } from './middlewares/index.js';
10
10
  export type ApiGatewayMiddlewareContext = {
@@ -39,7 +39,7 @@ export declare abstract class ApiGatewayOptions {
39
39
  defaultMaxBytes?: number;
40
40
  }
41
41
  export type GatewayEndpoint = {
42
- definition: ApiEndpointDefinition;
42
+ definition: NormalizedApiEndpoints[string];
43
43
  implementation: ApiEndpointServerImplementation;
44
44
  };
45
45
  export type ApiItem = {
@@ -31,11 +31,11 @@ import { deferThrow } from '../../utils/throw.js';
31
31
  import { isArray, isBlob, isDefined, isNotNull, isNotNullOrUndefined, isNull, isNullOrUndefined, isObject, isReadableStream, isUint8Array, isUndefined } from '../../utils/type-guards.js';
32
32
  import { mebibyte } from '../../utils/units.js';
33
33
  import { logAndGetErrorResponse } from '../response.js';
34
- import { normalizedApiDefinitionEndpointsEntries } from '../types.js';
34
+ import { normalizedApiDefinition, normalizedApiDefinitionEndpointsEntries } from '../types.js';
35
35
  import { getFullApiEndpointResource } from '../utils.js';
36
36
  import { ApiRequestTokenProvider } from './api-request-token.provider.js';
37
37
  import { handleApiError } from './error-handler.js';
38
- import { allowedMethodsMiddleware, contentTypeMiddleware, corsMiddleware, csrfMiddleware, getCatchErrorMiddleware, responseTimeMiddleware } from './middlewares/index.js';
38
+ import { allowedMethodsMiddleware, contentTypeMiddleware, corsMiddleware, csrfMiddleware, getCatchErrorMiddleware, responseTimeMiddleware, serverSentEventsMiddleware } from './middlewares/index.js';
39
39
  import { API_MODULE_OPTIONS } from './tokens.js';
40
40
  const defaultMaxBytes = 10 * mebibyte;
41
41
  export class ApiGatewayOptions {
@@ -87,10 +87,11 @@ let ApiGateway = ApiGateway_1 = class ApiGateway {
87
87
  }
88
88
  }
89
89
  registerApi(definition, implementation) {
90
- for (const [name, endpointDefinition] of normalizedApiDefinitionEndpointsEntries(definition.endpoints)) {
90
+ const normalizedDefinition = normalizedApiDefinition(definition);
91
+ for (const [name, endpointDefinition] of normalizedApiDefinitionEndpointsEntries(normalizedDefinition.endpoints)) {
91
92
  const versionArray = isUndefined(endpointDefinition.version) ? [1] : toArray(endpointDefinition.version);
92
93
  for (const version of versionArray) {
93
- const resource = getFullApiEndpointResource({ api: definition, endpoint: endpointDefinition, defaultPrefix: this.#prefix, explicitVersion: version });
94
+ const resource = getFullApiEndpointResource({ api: normalizedDefinition, endpoint: endpointDefinition, defaultPrefix: this.#prefix, explicitVersion: version });
94
95
  const methods = isArray(endpointDefinition.method) ? endpointDefinition.method : [endpointDefinition.method ?? 'GET'];
95
96
  if (methods.length == 0) {
96
97
  throw new Error(`No method provided for resource ${resource}.`);
@@ -169,10 +170,11 @@ let ApiGateway = ApiGateway_1 = class ApiGateway {
169
170
  contentTypeMiddleware,
170
171
  this.#catchErrorMiddleware,
171
172
  corsMiddleware(this.#options.cors),
173
+ serverSentEventsMiddleware,
172
174
  csrfMiddleware(this.#options.csrf),
173
175
  allowedMethodsMiddleware,
174
176
  ...this.#middlewares,
175
- async (context, next) => await this.endpointMiddleware(context, next)
177
+ async (context, next) => await this.endpointMiddleware(context, next),
176
178
  ];
177
179
  this.#composedMiddleware = composeAsyncMiddleware(middlewares);
178
180
  }
@@ -1,4 +1,3 @@
1
- import { match, P } from 'ts-pattern';
2
1
  import { isDefined } from '../../../utils/type-guards.js';
3
2
  export async function contentTypeMiddleware(context, next) {
4
3
  await next();
@@ -6,11 +5,26 @@ export async function contentTypeMiddleware(context, next) {
6
5
  if (isDefined(response.headers.contentType)) {
7
6
  return;
8
7
  }
9
- response.headers.contentType = match(response.body)
10
- .with({ json: P.select(P.when(isDefined)) }, () => 'application/json; charset=utf-8')
11
- .with({ text: P.select(P.nonNullable) }, () => 'text/plain; charset=utf-8')
12
- .with({ buffer: P.select(P.nonNullable) }, () => 'application/octet-stream')
13
- .with({ stream: P.select(P.nonNullable) }, () => 'application/octet-stream')
14
- .with({ events: P.select(P.nonNullable) }, () => 'text/event-stream')
15
- .otherwise(() => undefined);
8
+ const contentType = matchBodyType(response.bodyType);
9
+ if (isDefined(contentType)) {
10
+ response.headers.contentType = contentType;
11
+ if (response.bodyType == 'events') {
12
+ response.headers.setIfMissing('Cache-Control', 'no-cache');
13
+ response.headers.setIfMissing('Connection', 'keep-alive');
14
+ }
15
+ }
16
+ }
17
+ function matchBodyType(bodyType) {
18
+ switch (bodyType) {
19
+ case 'json':
20
+ return 'application/json; charset=utf-8';
21
+ case 'text':
22
+ return 'text/plain; charset=utf-8';
23
+ case 'buffer':
24
+ case 'stream':
25
+ return 'application/octet-stream';
26
+ case 'events':
27
+ return 'text/event-stream';
28
+ }
29
+ throw new Error(`Unsupported body type: ${bodyType}`);
16
30
  }
@@ -4,3 +4,4 @@ export * from './content-type.middleware.js';
4
4
  export * from './cors.middleware.js';
5
5
  export * from './csrf.middleware.js';
6
6
  export * from './response-time.middleware.js';
7
+ export * from './server-sent-events.middleware.js';
@@ -4,3 +4,4 @@ export * from './content-type.middleware.js';
4
4
  export * from './cors.middleware.js';
5
5
  export * from './csrf.middleware.js';
6
6
  export * from './response-time.middleware.js';
7
+ export * from './server-sent-events.middleware.js';
@@ -0,0 +1,7 @@
1
+ import type { ApiGatewayMiddlewareContext, ApiGatewayMiddlewareNext } from '../gateway.js';
2
+ /**
3
+ * Middleware that adds required CORS headers for SSE and cache busting.
4
+ * @param context Gateway context.
5
+ * @param next Next middleware.
6
+ */
7
+ export declare function serverSentEventsMiddleware(context: ApiGatewayMiddlewareContext, next: ApiGatewayMiddlewareNext): Promise<void>;
@@ -0,0 +1,18 @@
1
+ import { toArray } from '../../../utils/array/array.js';
2
+ /**
3
+ * Middleware that adds required CORS headers for SSE and cache busting.
4
+ * @param context Gateway context.
5
+ * @param next Next middleware.
6
+ */
7
+ export async function serverSentEventsMiddleware(context, next) {
8
+ await next();
9
+ if ((context.response.bodyType != 'events') || (context.request.method != 'OPTIONS')) {
10
+ return;
11
+ }
12
+ const existing = context.response.headers.tryGet('Access-Control-Allow-Headers');
13
+ const items = toArray(existing).flatMap((header) => header?.split(',') ?? []).map((header) => header.trim()).filter((header) => header.length > 0);
14
+ const headers = new Set(items);
15
+ headers.add('Cache-Control');
16
+ headers.add('Last-Event-ID');
17
+ context.response.headers.set('Access-Control-Allow-Headers', [...headers].join(', '));
18
+ }
package/api/types.d.ts CHANGED
@@ -5,9 +5,9 @@ import type { HttpHeaders, HttpHeadersObject, HttpRequestAuthorization } from '.
5
5
  import type { HttpServerRequest, HttpServerResponse } from '../http/server/index.js';
6
6
  import type { HttpMethod } from '../http/types.js';
7
7
  import type { SchemaOutput, SchemaTestable } from '../schema/index.js';
8
- import type { DataStream, DataStreamSource, DataStreamSourceOptions } from '../sse/index.js';
8
+ import { DataStream, type DataStreamSource, type DataStreamSourceOptions } from '../sse/index.js';
9
9
  import type { ServerSentEventsSource } from '../sse/server-sent-events-source.js';
10
- import type { ServerSentEvents } from '../sse/server-sent-events.js';
10
+ import { ServerSentEvents } from '../sse/server-sent-events.js';
11
11
  import type { NonUndefinable, OneOrMany, Record, ReturnTypeOrT } from '../types/index.js';
12
12
  import { type ValueOrProvider } from '../utils/value-or-provider.js';
13
13
  import type { ApiGatewayMiddlewareContext } from './server/index.js';
@@ -68,6 +68,13 @@ export type ApiEndpointDefinition = {
68
68
  description?: string;
69
69
  data?: Record;
70
70
  dataStream?: DataStreamSourceOptions<any>;
71
+ /**
72
+ * If true, it requests cache-busting (implementation dependent on adapter).
73
+ * Useful for example for Angular service workers.
74
+ *
75
+ * @default false
76
+ */
77
+ bustCache?: boolean;
71
78
  /**
72
79
  * If true, sets browsers fetch to { credentials: 'include' } and enables 'Access-Control-Allow-Credentials' header.
73
80
  *
@@ -88,11 +95,15 @@ export type ApiDefinition<Resource extends string = string, Endpoints extends Ap
88
95
  prefix?: string | null;
89
96
  endpoints: Endpoints;
90
97
  };
98
+ export type NormalizedApiDefinition<T extends ApiDefinition = ApiDefinition> = Omit<T, 'endpoints'> & {
99
+ endpoints: NormalizedApiEndpoints<T>;
100
+ };
91
101
  export type ApiEndpointKeys<T extends ApiDefinition> = Extract<keyof T['endpoints'], string>;
92
- export type NormalizedApiEndpoints<T extends ApiDefinition['endpoints']> = {
93
- [P in keyof T]: ReturnTypeOrT<T[P]>;
102
+ export type NormalizedApiEndpoints<T extends ApiDefinition = ApiDefinition> = {
103
+ [P in keyof T['endpoints']]: ReturnTypeOrT<T['endpoints'][P]>;
94
104
  };
95
- export type ApiEndpoint<T extends ApiDefinition, K extends ApiEndpointKeys<T>> = NormalizedApiEndpoints<T['endpoints']>[K];
105
+ export type NormalizedApiEndpoint<T extends ApiDefinition = ApiDefinition> = NormalizedApiEndpoints<T>[keyof NormalizedApiEndpoints<T>];
106
+ export type ApiEndpoint<T extends ApiDefinition, K extends ApiEndpointKeys<T>> = NormalizedApiEndpoints<T>[K];
96
107
  export type ApiEndpointParametersSchema<T extends ApiDefinition, K extends ApiEndpointKeys<T>> = NonUndefinable<ApiEndpoint<T, K>['parameters']>;
97
108
  export type ApiEndpointBodySchema<T extends ApiDefinition, K extends ApiEndpointKeys<T>> = NonUndefinable<ApiEndpoint<T, K>['body']>;
98
109
  export type ApiEndpointResultSchema<T extends ApiDefinition, K extends ApiEndpointKeys<T>> = NonUndefinable<ApiEndpoint<T, K>['result']>;
@@ -162,5 +173,6 @@ export type ApiClientImplementation<T extends ApiDefinition = any> = {
162
173
  };
163
174
  export declare function defineApi<T extends ApiDefinition>(definition: T): T;
164
175
  export declare function resolveApiEndpointDataProvider<T>(request: HttpServerRequest, context: ApiGatewayMiddlewareContext, provider: ApiEndpointDataProvider<T>): Promise<T>;
165
- export declare function normalizedApiDefinitionEndpoints<T extends ApiDefinition['endpoints']>(apiDefinitionEndpoints: T): NormalizedApiEndpoints<T>;
176
+ export declare function normalizedApiDefinition<T extends ApiDefinition>(apiDefinition: T): NormalizedApiDefinition<T>;
177
+ export declare function normalizedApiDefinitionEndpoints<T extends ApiDefinition>(apiDefinitionEndpoints: T['endpoints']): NormalizedApiEndpoints<T>;
166
178
  export declare function normalizedApiDefinitionEndpointsEntries<T extends ApiDefinition['endpoints']>(apiDefinition: T): [keyof T, ApiEndpointDefinition][];
package/api/types.js CHANGED
@@ -1,4 +1,6 @@
1
- import { objectEntries } from '../utils/object/object.js';
1
+ import { DataStream } from '../sse/index.js';
2
+ import { ServerSentEvents } from '../sse/server-sent-events.js';
3
+ import { fromEntries, objectEntries } from '../utils/object/object.js';
2
4
  import { isFunction } from '../utils/type-guards.js';
3
5
  import { resolveValueOrProvider } from '../utils/value-or-provider.js';
4
6
  export function defineApi(definition) {
@@ -10,10 +12,22 @@ export async function resolveApiEndpointDataProvider(request, context, provider)
10
12
  }
11
13
  return provider;
12
14
  }
15
+ export function normalizedApiDefinition(apiDefinition) {
16
+ return {
17
+ ...apiDefinition,
18
+ endpoints: normalizedApiDefinitionEndpoints(apiDefinition.endpoints),
19
+ };
20
+ }
13
21
  export function normalizedApiDefinitionEndpoints(apiDefinitionEndpoints) {
14
22
  const entries = normalizedApiDefinitionEndpointsEntries(apiDefinitionEndpoints);
15
- return Object.fromEntries(entries);
23
+ return fromEntries(entries);
16
24
  }
17
25
  export function normalizedApiDefinitionEndpointsEntries(apiDefinition) {
18
- return objectEntries(apiDefinition).map(([key, def]) => [key, resolveValueOrProvider(def)]);
26
+ return objectEntries(apiDefinition).map(([key, def]) => {
27
+ const endpoint = { ...resolveValueOrProvider(def) };
28
+ if ((endpoint.result == DataStream) || (endpoint.result == ServerSentEvents)) {
29
+ endpoint.bustCache ??= true;
30
+ }
31
+ return [key, endpoint];
32
+ });
19
33
  }
@@ -27,6 +27,7 @@ export declare class AuthenticationClientService<AdditionalTokenPayload extends
27
27
  private readonly logger;
28
28
  private readonly disposeSignal;
29
29
  private clockOffset;
30
+ private initialized;
30
31
  private refreshLoopPromise;
31
32
  /**
32
33
  * Observable for authentication errors.
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
- import { Subject, filter, firstValueFrom, map, race, skip, timer } from 'rxjs';
10
+ import { Subject, filter, firstValueFrom, map, race, skip, takeUntil, timer } from 'rxjs';
11
11
  import { CancellationSignal, CancellationToken } from '../../cancellation/token.js';
12
12
  import { BadRequestError } from '../../errors/bad-request.error.js';
13
13
  import { ForbiddenError } from '../../errors/forbidden.error.js';
@@ -71,6 +71,7 @@ let AuthenticationClientService = class AuthenticationClientService {
71
71
  logger = inject(Logger, 'AuthenticationService');
72
72
  disposeSignal = inject(CancellationSignal).createChild();
73
73
  clockOffset = 0;
74
+ initialized = false;
74
75
  refreshLoopPromise;
75
76
  /**
76
77
  * Observable for authentication errors.
@@ -175,9 +176,15 @@ let AuthenticationClientService = class AuthenticationClientService {
175
176
  * @internal
176
177
  */
177
178
  initialize() {
179
+ if (this.initialized) {
180
+ return;
181
+ }
182
+ this.initialized = true;
178
183
  this.loadToken();
179
- this.tokenUpdateBus.messages$.subscribe((token) => this.token.set(token));
180
- this.refreshLoopPromise = this.refreshLoop();
184
+ this.tokenUpdateBus.messages$
185
+ .pipe(takeUntil(this.disposeSignal))
186
+ .subscribe((token) => this.token.set(token));
187
+ this.refreshLoopPromise ??= this.refreshLoop();
181
188
  }
182
189
  /** @internal */
183
190
  async [Symbol.asyncDispose]() {
@@ -188,6 +195,9 @@ let AuthenticationClientService = class AuthenticationClientService {
188
195
  * Stops refresh loop and completes subjects.
189
196
  */
190
197
  async dispose() {
198
+ if (!this.initialized) {
199
+ return;
200
+ }
191
201
  this.disposeSignal.set();
192
202
  await this.refreshLoopPromise;
193
203
  this.errorSubject.complete();
@@ -230,6 +240,7 @@ let AuthenticationClientService = class AuthenticationClientService {
230
240
  }
231
241
  finally {
232
242
  // Always clear the local token, even if the server call fails.
243
+ this.forceRefreshToken.unset();
233
244
  this.setNewToken(undefined);
234
245
  this.loggedOutBus.publishAndForget();
235
246
  }
@@ -390,9 +401,7 @@ let AuthenticationClientService = class AuthenticationClientService {
390
401
  const refreshBufferSeconds = calculateRefreshBufferSeconds(token);
391
402
  const needsRefresh = forceRefresh || (now >= (token.exp - refreshBufferSeconds));
392
403
  if (needsRefresh) {
393
- let lockAcquired = false;
394
- await this.lock.tryUse(undefined, async () => {
395
- lockAcquired = true;
404
+ const lockResult = await this.lock.tryUse(undefined, async () => {
396
405
  const currentToken = this.token();
397
406
  const currentNow = this.estimatedServerTimestampSeconds();
398
407
  const currentRefreshBufferSeconds = isDefined(currentToken) ? calculateRefreshBufferSeconds(currentToken) : 0;
@@ -400,15 +409,12 @@ let AuthenticationClientService = class AuthenticationClientService {
400
409
  const stillNeedsRefresh = isDefined(currentToken) && (forceRefresh || (currentNow >= (currentToken.exp - currentRefreshBufferSeconds)));
401
410
  if (stillNeedsRefresh) {
402
411
  await this.refresh();
403
- if (forceRefresh && (this.token() == currentToken)) {
404
- this.forceRefreshToken.unset();
405
- }
406
412
  }
407
- else if (forceRefresh && (this.token() != currentToken)) {
413
+ if (forceRefresh && (this.token() != currentToken)) {
408
414
  this.forceRefreshToken.unset();
409
415
  }
410
416
  });
411
- if (!lockAcquired) {
417
+ if (!lockResult.success) {
412
418
  // Lock held by another instance, wait 5 seconds or until token changes (Passive Sync)
413
419
  const changeReason = await firstValueFrom(race([
414
420
  timer(5000).pipe(map(() => 'timer')),
@@ -421,11 +427,12 @@ let AuthenticationClientService = class AuthenticationClientService {
421
427
  continue;
422
428
  }
423
429
  }
424
- const currentRefreshBufferSeconds = calculateRefreshBufferSeconds(token);
425
- const delay = Math.min(maxRefreshDelay, ((this.token()?.exp ?? 0) - this.estimatedServerTimestampSeconds() - currentRefreshBufferSeconds) * millisecondsPerSecond);
430
+ const currentToken = this.token();
431
+ const currentRefreshBufferSeconds = isDefined(currentToken) ? calculateRefreshBufferSeconds(currentToken) : 0;
432
+ const delay = Math.min(maxRefreshDelay, ((currentToken?.exp ?? 0) - this.estimatedServerTimestampSeconds() - currentRefreshBufferSeconds) * millisecondsPerSecond);
426
433
  const wakeUpSignals = [
427
434
  this.disposeSignal,
428
- this.token$.pipe(filter((t) => t != token)),
435
+ this.token$.pipe(filter((t) => t != currentToken)),
429
436
  ];
430
437
  if (!forceRefresh) {
431
438
  wakeUpSignals.push(this.forceRefreshToken);
@@ -439,13 +446,11 @@ let AuthenticationClientService = class AuthenticationClientService {
439
446
  }
440
447
  catch (error) {
441
448
  this.logger.error(error);
442
- if (this.token() != iterationToken) {
443
- continue;
444
- }
449
+ const currentToken = this.token();
445
450
  await firstValueFrom(race([
446
451
  timer(2500),
447
452
  this.disposeSignal.set$,
448
- this.token$.pipe(filter((t) => t != iterationToken)),
453
+ this.token$.pipe(filter((t) => t != currentToken)),
449
454
  this.forceRefreshToken.set$.pipe(skip(this.forceRefreshToken.isSet ? 1 : 0)),
450
455
  ]), { defaultValue: undefined });
451
456
  }
@@ -38,12 +38,6 @@ export declare const documentManagementApiDefinition: {
38
38
  parse<T>(eventSource: import("../../sse/index.js").ServerSentEvents): import("rxjs").Observable<T>;
39
39
  };
40
40
  credentials: true;
41
- data: {
42
- bustCache: boolean;
43
- };
44
- cors: {
45
- accessControlAllowHeaders: string;
46
- };
47
41
  };
48
42
  getDocumentRequestsTemplateData: {
49
43
  resource: string;
@@ -454,12 +448,6 @@ declare const _DocumentManagementApi: import("../../api/client/index.js").ApiCli
454
448
  parse<T>(eventSource: import("../../sse/index.js").ServerSentEvents): import("rxjs").Observable<T>;
455
449
  };
456
450
  credentials: true;
457
- data: {
458
- bustCache: boolean;
459
- };
460
- cors: {
461
- accessControlAllowHeaders: string;
462
- };
463
451
  };
464
452
  getDocumentRequestsTemplateData: {
465
453
  resource: string;
@@ -6,8 +6,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  import { compileClient } from '../../api/client/index.js';
8
8
  import { defineApi } from '../../api/index.js';
9
- import { defaultAccessControlAllowHeaders } from '../../api/utils.js';
10
- import { bustCache } from '../../http/tokens.js';
11
9
  import { ReplaceClass } from '../../injector/index.js';
12
10
  import { array, boolean, literal, number, object, optional, string } from '../../schema/index.js';
13
11
  import { DataStream } from '../../sse/index.js';
@@ -43,8 +41,6 @@ export const documentManagementApiDefinition = defineApi({
43
41
  parameters: getDataParametersSchema,
44
42
  result: (DataStream),
45
43
  credentials: true,
46
- data: { [bustCache]: true },
47
- cors: { accessControlAllowHeaders: `${defaultAccessControlAllowHeaders}, Cache-Control` },
48
44
  },
49
45
  getDocumentRequestsTemplateData: {
50
46
  resource: 'views/document-requests-template-data',
@@ -6,30 +6,29 @@ import { HttpHeaders } from '../http-headers.js';
6
6
  export type SetCookieObject = SetCookieOptions & {
7
7
  value: string;
8
8
  };
9
+ export type HttpServerResponseBody = {
10
+ stream?: ReadableStream<Uint8Array>;
11
+ buffer?: Uint8Array;
12
+ text?: string;
13
+ json?: unknown;
14
+ events?: ServerSentEventsSource;
15
+ };
16
+ export type HttpServerResponseBodyType = 'stream' | 'buffer' | 'text' | 'json' | 'events' | 'none';
9
17
  export type HttpServerResponseOptions = {
10
18
  statusCode?: number | undefined;
11
19
  statusMessage?: string | undefined;
12
20
  headers?: HttpHeadersInput | HttpHeaders;
13
21
  cookies?: Record<string, SetCookieObject>;
14
- body?: {
15
- stream?: ReadableStream<Uint8Array>;
16
- buffer?: Uint8Array;
17
- text?: string;
18
- json?: unknown;
19
- events?: ServerSentEventsSource;
20
- };
22
+ body?: HttpServerResponseBody;
21
23
  };
22
24
  export declare class HttpServerResponse {
25
+ #private;
23
26
  statusCode: number | undefined;
24
27
  statusMessage: string | undefined;
25
28
  headers: HttpHeaders;
26
- body: undefined | {
27
- stream?: ReadableStream<Uint8Array>;
28
- buffer?: Uint8Array;
29
- text?: string;
30
- json?: unknown;
31
- events?: ServerSentEventsSource;
32
- };
29
+ get body(): HttpServerResponseBody | undefined;
30
+ set body(value: HttpServerResponseBody | undefined);
31
+ get bodyType(): HttpServerResponseBodyType;
33
32
  constructor(response?: HttpServerResponseOptions);
34
33
  static fromObject(options?: HttpServerResponseOptions): HttpServerResponse;
35
34
  static redirect(url: string, options?: HttpServerResponseOptions): HttpServerResponse;
@@ -1,12 +1,23 @@
1
1
  import { formatSetCookie } from '../../cookie/cookie.js';
2
2
  import { objectEntries } from '../../utils/object/object.js';
3
- import { isDefined } from '../../utils/type-guards.js';
3
+ import { isDefined, isUndefined } from '../../utils/type-guards.js';
4
4
  import { HttpHeaders } from '../http-headers.js';
5
5
  export class HttpServerResponse {
6
+ #body;
7
+ #bodyType;
6
8
  statusCode;
7
9
  statusMessage;
8
10
  headers;
9
- body;
11
+ get body() {
12
+ return this.#body;
13
+ }
14
+ set body(value) {
15
+ this.#body = value;
16
+ this.#bodyType = getBodyType(value);
17
+ }
18
+ get bodyType() {
19
+ return this.#bodyType;
20
+ }
10
21
  constructor(response = {}) {
11
22
  this.update(response);
12
23
  }
@@ -38,3 +49,24 @@ export class HttpServerResponse {
38
49
  export function redirect(url, options) {
39
50
  return HttpServerResponse.redirect(url, options);
40
51
  }
52
+ function getBodyType(body) {
53
+ if (isUndefined(body)) {
54
+ return 'none';
55
+ }
56
+ if (isDefined(body.stream)) {
57
+ return 'stream';
58
+ }
59
+ if (isDefined(body.buffer)) {
60
+ return 'buffer';
61
+ }
62
+ if (isDefined(body.text)) {
63
+ return 'text';
64
+ }
65
+ if (isDefined(body.json)) {
66
+ return 'json';
67
+ }
68
+ if (isDefined(body.events)) {
69
+ return 'events';
70
+ }
71
+ return 'none';
72
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.93.137",
3
+ "version": "0.93.138",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -181,7 +181,7 @@
181
181
  }
182
182
  },
183
183
  "devDependencies": {
184
- "@stylistic/eslint-plugin": "5.8",
184
+ "@stylistic/eslint-plugin": "5.9",
185
185
  "@types/koa__router": "12.0",
186
186
  "@types/luxon": "3.7",
187
187
  "@types/mjml": "4.7",