@tstdl/base 0.93.5 → 0.93.7

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/response.d.ts CHANGED
@@ -1,5 +1,6 @@
1
+ import type { Logger } from '../logger/logger.js';
2
+ import type { Type, UndefinableJson } from '../types/index.js';
1
3
  import { type CustomError, type CustomErrorStatic } from '../errors/index.js';
2
- import type { UndefinableJson } from '../types/index.js';
3
4
  export type ErrorHandlerData = undefined | UndefinableJson;
4
5
  export type ErrorSerializer<T extends CustomError, TData extends ErrorHandlerData> = (error: T) => TData;
5
6
  export type ErrorDeserializer<T extends CustomError, TData extends ErrorHandlerData> = (data: TData, responseError: ResponseError) => T;
@@ -26,6 +27,10 @@ export declare function hasErrorHandler(typeOrResponseOrName: CustomErrorStatic
26
27
  export declare function getErrorStatusCode(error: CustomError, defaultStatusCode?: number): number;
27
28
  export declare function createErrorResponse(error: Error, details?: any): ErrorResponse;
28
29
  export declare function createErrorResponse(name: string, message: string, details?: any): ErrorResponse;
30
+ export declare function logAndGetErrorResponse(logger: Logger, supressedErrors: Set<Type<Error>>, error: unknown): {
31
+ statusCode: number;
32
+ errorResponse: ErrorResponse;
33
+ };
29
34
  export declare function parseErrorResponse(response: ErrorResponse, fallbackToGenericApiError?: true): Error;
30
35
  export declare function parseErrorResponse(response: ErrorResponse, fallbackToGenericApiError: false): Error | undefined;
31
36
  export declare function isErrorResponse(response: Response<any> | unknown): response is ErrorResponse;
package/api/response.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { SecretRequirementsError } from '../authentication/errors/secret-requirements.error.js';
2
2
  import { SchemaError } from '../schema/schema.error.js';
3
+ import { formatError } from '../utils/format-error.js';
3
4
  import { ApiError, BadRequestError, ForbiddenError, InvalidCredentialsError, InvalidTokenError, MaxBytesExceededError, MethodNotAllowedError, NotFoundError, NotImplementedError, NotSupportedError, UnauthorizedError, UnsupportedMediaTypeError } from '../errors/index.js';
4
5
  import { assertString, isDefined, isFunction, isObject, isString } from '../utils/type-guards.js';
5
6
  import { deserializeSchemaError, serializeSchemaError } from './default-error-handlers.js';
@@ -60,6 +61,31 @@ export function createErrorResponse(errorOrName, message = '', details) {
60
61
  }
61
62
  return response;
62
63
  }
64
+ export function logAndGetErrorResponse(logger, supressedErrors, error) {
65
+ if (error instanceof Error) {
66
+ const errorConstructor = error.constructor;
67
+ const supressed = supressedErrors.has(errorConstructor);
68
+ if (!supressed) {
69
+ logger.error(error);
70
+ }
71
+ if (hasErrorHandler(errorConstructor)) {
72
+ return {
73
+ statusCode: getErrorStatusCode(error),
74
+ errorResponse: createErrorResponse(error),
75
+ };
76
+ }
77
+ return {
78
+ statusCode: 500,
79
+ errorResponse: createErrorResponse('500', 'Internal Server Error'),
80
+ };
81
+ }
82
+ const formattedError = formatError(error, { includeStack: true, includeRest: 'if-no-extra-info', includeExtraInfo: true });
83
+ logger.error(formattedError);
84
+ return {
85
+ statusCode: 500,
86
+ errorResponse: createErrorResponse('500', 'Internal Server Error'),
87
+ };
88
+ }
63
89
  export function parseErrorResponse(response, fallbackToGenericApiError = true) {
64
90
  const handler = errorHandlers.get(response.error.name);
65
91
  if (handler != undefined) {
@@ -1,26 +1,7 @@
1
- import { formatError } from '../../utils/format-error.js';
2
- import { createErrorResponse, getErrorStatusCode, hasErrorHandler } from '../response.js';
1
+ import { logAndGetErrorResponse } from '../response.js';
3
2
  export function handleApiError(error, response, supressedErrors, logger) {
4
- if (error instanceof Error) {
5
- const errorConstructor = error.constructor;
6
- const supressed = supressedErrors.has(errorConstructor);
7
- if (!supressed) {
8
- logger.error(error);
9
- }
10
- if (hasErrorHandler(errorConstructor)) {
11
- response.statusCode = getErrorStatusCode(error);
12
- response.body = { json: createErrorResponse(error) };
13
- }
14
- else {
15
- response.statusCode = 500;
16
- response.body = { json: createErrorResponse('500', 'Internal Server Error') };
17
- }
18
- }
19
- else {
20
- const formattedError = formatError(error, { includeStack: true, includeRest: 'if-no-extra-info', includeExtraInfo: true });
21
- logger.error(formattedError);
22
- response.statusCode = 500;
23
- response.body = { json: createErrorResponse('500', 'Internal Server Error') };
24
- }
3
+ const { statusCode, errorResponse } = logAndGetErrorResponse(logger, supressedErrors, error);
4
+ response.statusCode = statusCode;
5
+ response.body = { json: errorResponse };
25
6
  return response;
26
7
  }
@@ -30,6 +30,7 @@ import { mapObjectValues } from '../../utils/object/object.js';
30
30
  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
+ import { logAndGetErrorResponse } from '../response.js';
33
34
  import { normalizedApiDefinitionEndpointsEntries } from '../types.js';
34
35
  import { getFullApiEndpointResource } from '../utils.js';
35
36
  import { ApiRequestTokenProvider } from './api-request-token.provider.js';
@@ -220,7 +221,13 @@ let ApiGateway = ApiGateway_1 = class ApiGateway {
220
221
  .when(isBlob, (value) => ({ stream: value.stream() }))
221
222
  .when((isReadableStream), (stream) => ({ stream }))
222
223
  .when((value) => value instanceof ServerSentEventsSource, (events) => ({ events }))
223
- .when(() => (context.endpoint.definition.result == DataStream), (value) => ({ events: DataStreamSource.fromIterable(value).eventSource }))
224
+ .when(() => (context.endpoint.definition.result == DataStream), (value) => {
225
+ const errorFormatter = (error) => {
226
+ const { errorResponse } = logAndGetErrorResponse(this.#logger, this.#supressedErrors, error);
227
+ return errorResponse.error;
228
+ };
229
+ return ({ events: DataStreamSource.fromIterable(value, { errorFormatter }).eventSource });
230
+ })
224
231
  .when(() => (context.endpoint.definition.result == String), (text) => ({ text: text }))
225
232
  .otherwise((json) => ({ json }));
226
233
  }
@@ -9,6 +9,7 @@ var DocumentManagementService_1;
9
9
  import { and, eq } from 'drizzle-orm';
10
10
  import { union } from 'drizzle-orm/pg-core';
11
11
  import { Enumerable } from '../../../enumerable/index.js';
12
+ import { BadRequestError } from '../../../errors/bad-request.error.js';
12
13
  import { inject } from '../../../injector/index.js';
13
14
  import { Logger } from '../../../logger/logger.js';
14
15
  import { Transactional, injectRepository, injectTransactional } from '../../../orm/server/index.js';
@@ -87,6 +88,9 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
87
88
  }
88
89
  }
89
90
  async loadData(tenantId, collectionIds) {
91
+ if (collectionIds.length == 0) {
92
+ throw new BadRequestError('At least one collection ID must be provided to load document management data.');
93
+ }
90
94
  return await this.transaction(async (tx) => {
91
95
  const [collections, documentCollectionAssignments, requestAssignments, assignmentScopes, { categories, types }] = await Promise.all([
92
96
  this.#documentCollectionService.repository.withTransaction(tx).loadManyByQuery({ tenantId, id: { $in: collectionIds } }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.93.5",
3
+ "version": "0.93.7",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,10 +1,13 @@
1
+ import type { UndefinableJson } from '../types/types.js';
1
2
  import type { AnyIterable } from '../utils/any-iterable-iterator.js';
2
3
  import { ServerSentEventsSource } from './server-sent-events-source.js';
4
+ export type DataStreamErrorFormatter = (error: unknown) => UndefinableJson;
3
5
  export type DataStreamSourceOptions = {
4
6
  /**
5
7
  * Whether to send deltas (the changes) between the last and current data or always the full data.
6
8
  */
7
9
  delta?: boolean;
10
+ errorFormatter?: DataStreamErrorFormatter;
8
11
  };
9
12
  export declare class DataStreamSource<T> {
10
13
  #private;
@@ -15,11 +15,13 @@ const jsonDiffPatch = createDiffPatch({
15
15
  });
16
16
  export class DataStreamSource {
17
17
  #options;
18
+ #errorFormatter;
18
19
  eventSource = new ServerSentEventsSource();
19
20
  closed = this.eventSource.closed;
20
21
  #lastData;
21
22
  constructor(options = { delta: true }) {
22
23
  this.#options = options;
24
+ this.#errorFormatter = options.errorFormatter ?? defaultErrorFormatter;
23
25
  }
24
26
  static fromIterable(iterable, options) {
25
27
  const source = new DataStreamSource(options);
@@ -60,7 +62,10 @@ export class DataStreamSource {
60
62
  await this.eventSource.close();
61
63
  }
62
64
  async error(error) {
63
- await tryIgnoreAsync(async () => await this.eventSource.sendJson({ name: 'error', data: createErrorResponse(error).error }));
65
+ await tryIgnoreAsync(async () => await this.eventSource.sendJson({ name: 'error', data: this.#errorFormatter(error) }));
64
66
  await tryIgnoreAsync(async () => await this.eventSource.close());
65
67
  }
66
68
  }
69
+ function defaultErrorFormatter(error) {
70
+ return createErrorResponse(error).error;
71
+ }