@tstdl/base 0.93.136 → 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.
- package/api/client/client.js +8 -8
- package/api/client/tests/api-client.test.js +1 -1
- package/api/server/gateway.d.ts +2 -2
- package/api/server/gateway.js +7 -5
- package/api/server/middlewares/content-type.middleware.js +22 -8
- package/api/server/middlewares/index.d.ts +1 -0
- package/api/server/middlewares/index.js +1 -0
- package/api/server/middlewares/server-sent-events.middleware.d.ts +7 -0
- package/api/server/middlewares/server-sent-events.middleware.js +18 -0
- package/api/types.d.ts +18 -6
- package/api/types.js +17 -3
- package/authentication/client/authentication.service.d.ts +1 -0
- package/authentication/client/authentication.service.js +23 -18
- package/document-management/api/document-management.api.d.ts +0 -12
- package/document-management/api/document-management.api.js +0 -4
- package/http/server/http-server-response.d.ts +13 -14
- package/http/server/http-server-response.js +34 -2
- package/object-storage/s3/s3.object-storage-provider.js +2 -0
- package/package.json +2 -2
package/api/client/client.js
CHANGED
|
@@ -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 {
|
|
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
|
|
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] =
|
|
35
|
+
this[apiDefinitionSymbol] = normalizedDefinition;
|
|
35
36
|
}
|
|
36
37
|
static getEndpointResource(endpoint, parameters) {
|
|
37
|
-
const resource = getFullApiEndpointResource({ api:
|
|
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
|
|
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:
|
|
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 (
|
|
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',
|
|
146
|
+
withBustCache: { method: 'GET', bustCache: true },
|
|
147
147
|
},
|
|
148
148
|
});
|
|
149
149
|
const mockHttpClient = Object.assign(Object.create(HttpClient.prototype), {
|
package/api/server/gateway.d.ts
CHANGED
|
@@ -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
|
|
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:
|
|
42
|
+
definition: NormalizedApiEndpoints[string];
|
|
43
43
|
implementation: ApiEndpointServerImplementation;
|
|
44
44
|
};
|
|
45
45
|
export type ApiItem = {
|
package/api/server/gateway.js
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
}
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
23
|
+
return fromEntries(entries);
|
|
16
24
|
}
|
|
17
25
|
export function normalizedApiDefinitionEndpointsEntries(apiDefinition) {
|
|
18
|
-
return objectEntries(apiDefinition).map(([key, 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
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
413
|
+
if (forceRefresh && (this.token() != currentToken)) {
|
|
408
414
|
this.forceRefreshToken.unset();
|
|
409
415
|
}
|
|
410
416
|
});
|
|
411
|
-
if (!
|
|
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
|
|
425
|
-
const
|
|
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 !=
|
|
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
|
-
|
|
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 !=
|
|
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:
|
|
27
|
-
|
|
28
|
-
|
|
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
|
+
}
|
|
@@ -58,6 +58,8 @@ let S3ObjectStorageProvider = class S3ObjectStorageProvider extends ObjectStorag
|
|
|
58
58
|
secretAccessKey: this.#config.secretKey,
|
|
59
59
|
},
|
|
60
60
|
forcePathStyle: this.#config.forcePathStyle,
|
|
61
|
+
requestChecksumCalculation: 'WHEN_REQUIRED',
|
|
62
|
+
responseChecksumValidation: 'WHEN_REQUIRED',
|
|
61
63
|
});
|
|
62
64
|
#bucket = assertDefinedPass((this.#config.bucketPerModule == true) ? true : this.#config.bucket, 'either bucket or bucketPerModule must be specified');
|
|
63
65
|
constructor() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tstdl/base",
|
|
3
|
-
"version": "0.93.
|
|
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.
|
|
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",
|