@tstdl/base 0.92.167 → 0.93.0

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.
Files changed (317) hide show
  1. package/application/application.d.ts +12 -18
  2. package/application/application.js +48 -69
  3. package/application/index.d.ts +1 -5
  4. package/application/index.js +1 -5
  5. package/application/providers.d.ts +10 -0
  6. package/application/providers.js +54 -0
  7. package/audit/audit.model.d.ts +15 -0
  8. package/audit/audit.model.js +73 -0
  9. package/audit/auditor.d.ts +42 -0
  10. package/audit/auditor.js +111 -0
  11. package/audit/index.d.ts +3 -0
  12. package/audit/index.js +3 -0
  13. package/audit/types.d.ts +22 -0
  14. package/audit/types.js +19 -0
  15. package/authentication/client/authentication.service.d.ts +1 -3
  16. package/authentication/client/authentication.service.js +4 -5
  17. package/browser/browser-context-controller.d.ts +2 -4
  18. package/browser/browser-context-controller.js +5 -6
  19. package/browser/browser-controller.d.ts +2 -4
  20. package/browser/browser-controller.js +3 -4
  21. package/browser/browser.service.d.ts +1 -3
  22. package/browser/browser.service.js +1 -2
  23. package/browser/page-controller.d.ts +2 -4
  24. package/browser/page-controller.js +7 -8
  25. package/browser/utils.js +3 -3
  26. package/cancellation/token.d.ts +104 -41
  27. package/cancellation/token.js +125 -54
  28. package/core.d.ts +1 -13
  29. package/core.js +1 -46
  30. package/disposable/disposable.d.ts +0 -8
  31. package/disposable/disposable.js +1 -3
  32. package/disposable/index.d.ts +0 -6
  33. package/disposable/index.js +0 -6
  34. package/disposable/using.d.ts +0 -1
  35. package/disposable/using.js +2 -3
  36. package/distributed-loop/distributed-loop.js +2 -2
  37. package/errors/utils.js +4 -1
  38. package/examples/api/authentication.js +11 -5
  39. package/examples/api/basic-overview.js +17 -12
  40. package/examples/api/custom-authentication.js +13 -7
  41. package/examples/api/streaming.js +15 -12
  42. package/examples/browser/basic.js +6 -3
  43. package/examples/document-management/main.js +6 -3
  44. package/examples/http/client.js +7 -3
  45. package/examples/mail/basic.js +9 -7
  46. package/examples/pdf/basic.js +8 -6
  47. package/examples/template/basic.js +7 -5
  48. package/http/client/http-client-request.d.ts +1 -2
  49. package/http/client/http-client-request.js +1 -2
  50. package/http/server/http-server.d.ts +1 -3
  51. package/http/server/http-server.js +0 -1
  52. package/http/server/node/node-http-server.d.ts +1 -2
  53. package/http/server/node/node-http-server.js +1 -2
  54. package/import.js +1 -1
  55. package/injector/injector.d.ts +1 -1
  56. package/injector/types.d.ts +3 -4
  57. package/lock/lock.d.ts +40 -21
  58. package/lock/lock.js +74 -1
  59. package/lock/postgres/drizzle/0000_busy_tattoo.sql +7 -0
  60. package/lock/postgres/drizzle/meta/0000_snapshot.json +65 -0
  61. package/lock/postgres/drizzle/meta/_journal.json +13 -0
  62. package/lock/postgres/drizzle.config.js +11 -0
  63. package/lock/postgres/index.d.ts +2 -0
  64. package/lock/postgres/index.js +2 -0
  65. package/lock/postgres/lock.d.ts +14 -0
  66. package/lock/postgres/lock.js +127 -0
  67. package/lock/postgres/models/index.d.ts +2 -0
  68. package/lock/postgres/models/index.js +2 -0
  69. package/lock/postgres/models/lock.model.d.ts +7 -0
  70. package/{examples/orm/user.model.js → lock/postgres/models/lock.model.js} +22 -30
  71. package/lock/postgres/models/schemas.d.ts +3 -0
  72. package/lock/postgres/models/schemas.js +4 -0
  73. package/lock/postgres/module.d.ts +6 -0
  74. package/lock/postgres/module.js +26 -0
  75. package/lock/postgres/provider.d.ts +6 -0
  76. package/lock/postgres/provider.js +29 -0
  77. package/lock/provider.d.ts +12 -2
  78. package/lock/provider.js +24 -1
  79. package/lock/web/web-lock.d.ts +4 -3
  80. package/lock/web/web-lock.js +49 -42
  81. package/lock/web/web-lock.provider.d.ts +0 -3
  82. package/lock/web/web-lock.provider.js +5 -22
  83. package/logger/formatter.d.ts +13 -0
  84. package/logger/formatter.js +3 -0
  85. package/logger/formatters/index.d.ts +2 -0
  86. package/logger/formatters/index.js +2 -0
  87. package/logger/formatters/json.d.ts +5 -0
  88. package/logger/formatters/json.js +33 -0
  89. package/logger/formatters/pretty-print.d.ts +5 -0
  90. package/logger/formatters/pretty-print.js +55 -0
  91. package/logger/index.d.ts +5 -2
  92. package/logger/index.js +5 -2
  93. package/logger/level.d.ts +10 -8
  94. package/logger/level.js +9 -9
  95. package/logger/logger.d.ts +21 -30
  96. package/logger/logger.js +98 -26
  97. package/logger/manager.d.ts +20 -0
  98. package/logger/manager.js +86 -0
  99. package/logger/tokens.d.ts +1 -1
  100. package/logger/tokens.js +1 -1
  101. package/logger/transport.d.ts +14 -0
  102. package/logger/transport.js +16 -0
  103. package/logger/transports/console.d.ts +14 -0
  104. package/logger/transports/console.js +36 -0
  105. package/logger/transports/index.d.ts +1 -0
  106. package/logger/transports/index.js +1 -0
  107. package/mail/clients/nodemailer.mail-client.d.ts +0 -1
  108. package/mail/clients/nodemailer.mail-client.js +9 -7
  109. package/message-bus/local/local-message-bus.js +2 -2
  110. package/message-bus/message-bus-base.d.ts +2 -3
  111. package/message-bus/message-bus-base.js +5 -6
  112. package/message-bus/message-bus.d.ts +1 -2
  113. package/message-bus/message-bus.js +1 -2
  114. package/module/index.d.ts +0 -2
  115. package/module/index.js +0 -2
  116. package/module/module.d.ts +17 -18
  117. package/module/module.js +47 -12
  118. package/module/modules/function.module.d.ts +6 -6
  119. package/module/modules/function.module.js +25 -9
  120. package/module/modules/web-server.module.d.ts +2 -10
  121. package/module/modules/web-server.module.js +3 -11
  122. package/openid-connect/index.d.ts +0 -2
  123. package/openid-connect/index.js +0 -2
  124. package/openid-connect/oidc-state.model.d.ts +4 -5
  125. package/openid-connect/oidc-state.model.js +51 -1
  126. package/openid-connect/oidc.service-model.d.ts +1 -1
  127. package/openid-connect/oidc.service.d.ts +2 -6
  128. package/openid-connect/oidc.service.js +24 -37
  129. package/orm/decorators.d.ts +10 -1
  130. package/orm/decorators.js +8 -0
  131. package/orm/server/repository.d.ts +3 -1
  132. package/orm/server/repository.js +32 -3
  133. package/package.json +19 -29
  134. package/pdf/pdf.service.js +9 -9
  135. package/pool/pool.d.ts +1 -3
  136. package/pool/pool.js +3 -4
  137. package/queue/postgres/job.model.d.ts +1 -2
  138. package/queue/postgres/job.model.js +1 -2
  139. package/queue/postgres/module.js +1 -1
  140. package/threading/thread-pool.d.ts +1 -3
  141. package/threading/thread-pool.js +7 -8
  142. package/utils/format-error.d.ts +7 -0
  143. package/utils/format-error.js +59 -17
  144. package/utils/function/memoize.d.ts +22 -7
  145. package/utils/function/memoize.js +82 -23
  146. package/utils/index.d.ts +1 -0
  147. package/utils/index.js +1 -0
  148. package/utils/object/dereference.d.ts +51 -19
  149. package/utils/object/dereference.js +52 -43
  150. package/utils/timing.js +2 -2
  151. package/utils/try-chain.d.ts +22 -0
  152. package/utils/try-chain.js +46 -0
  153. package/database/entity-repository.d.ts +0 -50
  154. package/database/entity-repository.js +0 -3
  155. package/database/entity.d.ts +0 -7
  156. package/database/entity.js +0 -1
  157. package/database/id.d.ts +0 -1
  158. package/database/id.js +0 -9
  159. package/database/index.d.ts +0 -11
  160. package/database/index.js +0 -11
  161. package/database/module.d.ts +0 -8
  162. package/database/module.js +0 -11
  163. package/database/mongo/classes.d.ts +0 -21
  164. package/database/mongo/classes.js +0 -26
  165. package/database/mongo/index.d.ts +0 -15
  166. package/database/mongo/index.js +0 -15
  167. package/database/mongo/model/document.d.ts +0 -29
  168. package/database/mongo/model/document.js +0 -63
  169. package/database/mongo/model/index.d.ts +0 -1
  170. package/database/mongo/model/index.js +0 -1
  171. package/database/mongo/module.d.ts +0 -8
  172. package/database/mongo/module.js +0 -68
  173. package/database/mongo/mongo-base.repository.d.ts +0 -103
  174. package/database/mongo/mongo-base.repository.js +0 -263
  175. package/database/mongo/mongo-bulk.d.ts +0 -35
  176. package/database/mongo/mongo-bulk.js +0 -90
  177. package/database/mongo/mongo-entity-repository.d.ts +0 -98
  178. package/database/mongo/mongo-entity-repository.js +0 -278
  179. package/database/mongo/operations.d.ts +0 -10
  180. package/database/mongo/operations.js +0 -54
  181. package/database/mongo/query-converter.d.ts +0 -6
  182. package/database/mongo/query-converter.js +0 -83
  183. package/database/mongo/simple-entity-repository.d.ts +0 -7
  184. package/database/mongo/simple-entity-repository.js +0 -6
  185. package/database/mongo/types.d.ts +0 -50
  186. package/database/mongo/types.js +0 -3
  187. package/database/query.d.ts +0 -121
  188. package/database/query.js +0 -7
  189. package/database/utils.d.ts +0 -2
  190. package/database/utils.js +0 -3
  191. package/disposable/async-disposer.d.ts +0 -35
  192. package/disposable/async-disposer.js +0 -125
  193. package/examples/orm/drizzle.config.js +0 -6
  194. package/examples/orm/schemas.d.ts +0 -3
  195. package/examples/orm/schemas.js +0 -4
  196. package/examples/orm/test.d.ts +0 -1
  197. package/examples/orm/test.js +0 -11
  198. package/examples/orm/user.model.d.ts +0 -13
  199. package/key-value-store/mongo/index.d.ts +0 -6
  200. package/key-value-store/mongo/index.js +0 -6
  201. package/key-value-store/mongo/module.d.ts +0 -8
  202. package/key-value-store/mongo/module.js +0 -18
  203. package/key-value-store/mongo/mongo-key-value-store.provider.d.ts +0 -8
  204. package/key-value-store/mongo/mongo-key-value-store.provider.js +0 -26
  205. package/key-value-store/mongo/mongo-key-value.model.d.ts +0 -7
  206. package/key-value-store/mongo/mongo-key-value.model.js +0 -1
  207. package/key-value-store/mongo/mongo-key-value.repository.d.ts +0 -10
  208. package/key-value-store/mongo/mongo-key-value.repository.js +0 -31
  209. package/key-value-store/mongo/mongo-key-value.store.d.ts +0 -15
  210. package/key-value-store/mongo/mongo-key-value.store.js +0 -82
  211. package/key-value-store/mongo/tokens.d.ts +0 -3
  212. package/key-value-store/mongo/tokens.js +0 -2
  213. package/lock/mongo/index.d.ts +0 -5
  214. package/lock/mongo/index.js +0 -5
  215. package/lock/mongo/lock.d.ts +0 -14
  216. package/lock/mongo/lock.js +0 -125
  217. package/lock/mongo/model.d.ts +0 -6
  218. package/lock/mongo/model.js +0 -1
  219. package/lock/mongo/module.d.ts +0 -12
  220. package/lock/mongo/module.js +0 -20
  221. package/lock/mongo/mongo-lock-repository.d.ts +0 -14
  222. package/lock/mongo/mongo-lock-repository.js +0 -67
  223. package/lock/mongo/provider.d.ts +0 -8
  224. package/lock/mongo/provider.js +0 -36
  225. package/logger/console/index.d.ts +0 -1
  226. package/logger/console/index.js +0 -1
  227. package/logger/console/logger.d.ts +0 -11
  228. package/logger/console/logger.js +0 -64
  229. package/logger/noop/index.d.ts +0 -1
  230. package/logger/noop/index.js +0 -1
  231. package/logger/noop/logger.d.ts +0 -9
  232. package/logger/noop/logger.js +0 -21
  233. package/migration/index.d.ts +0 -9
  234. package/migration/index.js +0 -9
  235. package/migration/migration-state-repository.d.ts +0 -4
  236. package/migration/migration-state-repository.js +0 -3
  237. package/migration/migration-state.d.ts +0 -6
  238. package/migration/migration-state.js +0 -1
  239. package/migration/migrator.d.ts +0 -23
  240. package/migration/migrator.js +0 -76
  241. package/migration/mongo/index.d.ts +0 -2
  242. package/migration/mongo/index.js +0 -2
  243. package/migration/mongo/migration-state-repository.d.ts +0 -11
  244. package/migration/mongo/migration-state-repository.js +0 -32
  245. package/migration/mongo/module.d.ts +0 -12
  246. package/migration/mongo/module.js +0 -17
  247. package/module/module-base.d.ts +0 -18
  248. package/module/module-base.js +0 -40
  249. package/module/module-metric-reporter.d.ts +0 -29
  250. package/module/module-metric-reporter.js +0 -62
  251. package/openid-connect/mongo-oidc-state.repository.d.ts +0 -21
  252. package/openid-connect/mongo-oidc-state.repository.js +0 -52
  253. package/openid-connect/oidc-state.repository.d.ts +0 -4
  254. package/openid-connect/oidc-state.repository.js +0 -3
  255. package/process-shutdown.d.ts +0 -9
  256. package/process-shutdown.js +0 -65
  257. package/queue/mongo/index.d.ts +0 -4
  258. package/queue/mongo/index.js +0 -4
  259. package/queue/mongo/job.d.ts +0 -12
  260. package/queue/mongo/job.js +0 -1
  261. package/queue/mongo/mongo-job.repository.d.ts +0 -13
  262. package/queue/mongo/mongo-job.repository.js +0 -54
  263. package/queue/mongo/queue.d.ts +0 -38
  264. package/queue/mongo/queue.js +0 -266
  265. package/queue/mongo/queue.provider.d.ts +0 -18
  266. package/queue/mongo/queue.provider.js +0 -38
  267. package/search-index/elastic/config.d.ts +0 -8
  268. package/search-index/elastic/config.js +0 -26
  269. package/search-index/elastic/index.d.ts +0 -8
  270. package/search-index/elastic/index.js +0 -8
  271. package/search-index/elastic/keyword-rewriter.d.ts +0 -8
  272. package/search-index/elastic/keyword-rewriter.js +0 -18
  273. package/search-index/elastic/model/elastic-query.d.ts +0 -16
  274. package/search-index/elastic/model/elastic-query.js +0 -1
  275. package/search-index/elastic/model/index-mapping.d.ts +0 -26
  276. package/search-index/elastic/model/index-mapping.js +0 -4
  277. package/search-index/elastic/model/index.d.ts +0 -3
  278. package/search-index/elastic/model/index.js +0 -3
  279. package/search-index/elastic/model/sort.d.ts +0 -8
  280. package/search-index/elastic/model/sort.js +0 -1
  281. package/search-index/elastic/module.d.ts +0 -10
  282. package/search-index/elastic/module.js +0 -49
  283. package/search-index/elastic/query-builder/boolean-query-builder.d.ts +0 -11
  284. package/search-index/elastic/query-builder/boolean-query-builder.js +0 -52
  285. package/search-index/elastic/query-builder/index.d.ts +0 -1
  286. package/search-index/elastic/query-builder/index.js +0 -1
  287. package/search-index/elastic/query-converter.d.ts +0 -9
  288. package/search-index/elastic/query-converter.js +0 -183
  289. package/search-index/elastic/search-index.d.ts +0 -30
  290. package/search-index/elastic/search-index.js +0 -144
  291. package/search-index/elastic/sort-converter.d.ts +0 -4
  292. package/search-index/elastic/sort-converter.js +0 -14
  293. package/search-index/elastic/types.d.ts +0 -5
  294. package/search-index/elastic/types.js +0 -1
  295. package/search-index/error.d.ts +0 -10
  296. package/search-index/error.js +0 -14
  297. package/search-index/index.d.ts +0 -3
  298. package/search-index/index.js +0 -3
  299. package/search-index/memory/index.d.ts +0 -1
  300. package/search-index/memory/index.js +0 -1
  301. package/search-index/memory/memory-search-index.d.ts +0 -19
  302. package/search-index/memory/memory-search-index.js +0 -144
  303. package/search-index/search-index.d.ts +0 -46
  304. package/search-index/search-index.js +0 -31
  305. package/search-index/search-result.d.ts +0 -12
  306. package/search-index/search-result.js +0 -1
  307. package/theme/adapters/css-adapter.d.ts +0 -5
  308. package/theme/adapters/css-adapter.js +0 -29
  309. package/theme/adapters/index.d.ts +0 -2
  310. package/theme/adapters/index.js +0 -2
  311. package/theme/adapters/tailwind-adapter.d.ts +0 -18
  312. package/theme/adapters/tailwind-adapter.js +0 -32
  313. package/theme/index.d.ts +0 -1
  314. package/theme/index.js +0 -1
  315. package/theme/theme-service.d.ts +0 -43
  316. package/theme/theme-service.js +0 -128
  317. /package/{examples/orm → lock/postgres}/drizzle.config.d.ts +0 -0
package/orm/decorators.js CHANGED
@@ -145,3 +145,11 @@ export function Index(columnsOrOptions, options) {
145
145
  }
146
146
  return createColumnDecorator({ index: { options: columnsOrOptions } });
147
147
  }
148
+ /**
149
+ * Decorator to mark a column as an expiration field. This will be used to automatically delete records when expired.
150
+ * @param mode - Whether to delete soft or hard.
151
+ * @returns A property decorator.
152
+ */
153
+ export function ExpiresAt(mode = 'soft') {
154
+ return createColumnDecorator({ expirationField: { mode } });
155
+ }
@@ -1,6 +1,6 @@
1
1
  import { SQL } from 'drizzle-orm';
2
2
  import type { PgColumn, PgInsertValue, PgUpdateSetSource } from 'drizzle-orm/pg-core';
3
- import { type Resolvable, resolveArgumentType } from '../../injector/interfaces.js';
3
+ import { afterResolve, type Resolvable, resolveArgumentType } from '../../injector/interfaces.js';
4
4
  import type { DeepPartial, OneOrMany, Paths, Type, UntaggedDeep } from '../../types/index.js';
5
5
  import { Entity, type EntityMetadataAttributes, type EntityType, type EntityWithoutMetadata } from '../entity.js';
6
6
  import type { Query } from '../query.js';
@@ -37,6 +37,8 @@ export declare class EntityRepository<T extends Entity | EntityWithoutMetadata =
37
37
  */
38
38
  get table(): PgTableFromType<EntityType<T>>;
39
39
  readonly [resolveArgumentType]: EntityType<T>;
40
+ [afterResolve](): void;
41
+ private expirationLoop;
40
42
  protected getTransactionalContextData(): EntityRepositoryContext;
41
43
  /**
42
44
  * Loads a single entity by its ID.
@@ -4,20 +4,24 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { and, asc, count, desc, eq, inArray, isNull, isSQLWrapper, SQL, sql } from 'drizzle-orm';
7
+ import { and, asc, count, desc, eq, inArray, isNull, isSQLWrapper, lte, or, SQL, sql } from 'drizzle-orm';
8
8
  import { match } from 'ts-pattern';
9
+ import { CancellationSignal } from '../../cancellation/token.js';
9
10
  import { NotFoundError } from '../../errors/not-found.error.js';
10
11
  import { Singleton } from '../../injector/decorators.js';
11
12
  import { inject, injectArgument } from '../../injector/inject.js';
12
- import { resolveArgumentType } from '../../injector/interfaces.js';
13
+ import { afterResolve, resolveArgumentType } from '../../injector/interfaces.js';
13
14
  import { Schema } from '../../schema/schema.js';
14
15
  import { distinct, toArray } from '../../utils/array/array.js';
15
16
  import { mapAsync } from '../../utils/async-iterable-helpers/map.js';
16
17
  import { toArrayAsync } from '../../utils/async-iterable-helpers/to-array.js';
17
18
  import { importSymmetricKey } from '../../utils/cryptography.js';
18
19
  import { fromDeepObjectEntries, fromEntries, objectEntries } from '../../utils/object/object.js';
19
- import { assertDefinedPass, isArray, isDefined, isString, isUndefined } from '../../utils/type-guards.js';
20
+ import { cancelableTimeout } from '../../utils/timing.js';
21
+ import { tryIgnoreAsync } from '../../utils/try-ignore.js';
22
+ import { assertDefined, assertDefinedPass, isArray, isDefined, isString, isUndefined } from '../../utils/type-guards.js';
20
23
  import { typeExtends } from '../../utils/type/index.js';
24
+ import { millisecondsPerSecond } from '../../utils/units.js';
21
25
  import { Entity } from '../entity.js';
22
26
  import { TRANSACTION_TIMESTAMP } from '../sqls.js';
23
27
  import { getColumnDefinitions, getColumnDefinitionsMap, getDrizzleTableFromType } from './drizzle/schema-converter.js';
@@ -37,6 +41,7 @@ const entityTypeToken = Symbol('EntityType');
37
41
  let EntityRepository = class EntityRepository extends Transactional {
38
42
  #context = isInTransactionalContext() ? getTransactionalContextData(this) : {};
39
43
  #encryptionSecret = isInTransactionalContext() ? this.#context.encryptionSecret : inject(ENCRYPTION_SECRET, undefined, { optional: true });
44
+ #cancellationSignal = isInTransactionalContext() ? undefined : inject(CancellationSignal);
40
45
  #transformContext = this.#context.transformContext;
41
46
  type = this.#context.type ?? injectArgument(this, { optional: true }) ?? assertDefinedPass(this.constructor[entityTypeToken], 'Missing entity type.');
42
47
  typeName = this.type.entityName ?? this.type.name;
@@ -51,6 +56,30 @@ let EntityRepository = class EntityRepository extends Transactional {
51
56
  get table() {
52
57
  return this.#table;
53
58
  }
59
+ [afterResolve]() {
60
+ if (!this.isInTransaction) {
61
+ void this.expirationLoop();
62
+ }
63
+ }
64
+ async expirationLoop() {
65
+ const softExpirationColumns = this.#columnDefinitions.filter((column) => column.reflectionData?.expirationField?.mode == 'soft');
66
+ const hardExpirationColumns = this.#columnDefinitions.filter((column) => column.reflectionData?.expirationField?.mode == 'hard');
67
+ if ((softExpirationColumns.length + hardExpirationColumns.length) == 0) {
68
+ return;
69
+ }
70
+ const softDeletionQuery = or(...softExpirationColumns.map((column) => lte(this.getColumn(column), TRANSACTION_TIMESTAMP)));
71
+ const hardDeletionQuery = or(...hardExpirationColumns.map((column) => lte(this.getColumn(column), TRANSACTION_TIMESTAMP)));
72
+ assertDefined(this.#cancellationSignal);
73
+ while (this.#cancellationSignal.isUnset) {
74
+ if (isDefined(softDeletionQuery)) {
75
+ await tryIgnoreAsync(async () => await this.deleteManyByQuery(softDeletionQuery));
76
+ }
77
+ if (isDefined(hardDeletionQuery)) {
78
+ await tryIgnoreAsync(async () => await this.hardDeleteManyByQuery(hardDeletionQuery));
79
+ }
80
+ await cancelableTimeout(30 * millisecondsPerSecond, this.#cancellationSignal);
81
+ }
82
+ }
54
83
  getTransactionalContextData() {
55
84
  const context = {
56
85
  type: this.type,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstdl/base",
3
- "version": "0.92.167",
3
+ "version": "0.93.0",
4
4
  "author": "Patrick Hein",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -11,27 +11,32 @@
11
11
  "build:watch": "concurrently --raw --kill-others npm:tsc:watch npm:tsc-alias:watch",
12
12
  "build:production": "rm -rf dist && npm run build && npm run build:production:copy-files",
13
13
  "build:production:copy-files": "cp package.json eslint.config.js tsconfig.server.json dist/ && cp tsconfig.base.json dist/tsconfig.json && npm run copy:orm",
14
- "build:dts": "tsc -p tsconfig.dts.json",
14
+ "build:dts": "rm -rf dist && tsc -p tsconfig.dts.json && tsc-alias",
15
15
  "build:docs": "typedoc",
16
16
  "build:docs:watch": "typedoc --watch",
17
17
  "lint": "eslint --cache source/",
18
- "pub": "npm run build:production && rm -vf dist/test* && rm -vrf dist/tools/ && npm publish dist/",
18
+ "pub": "npm run build:production && npm run cleanup:dist && npm publish dist/",
19
19
  "tsc:watch": "tsc --watch",
20
20
  "tsc-alias:watch": "tsc-alias --watch",
21
- "generate:migration": "npm run generate:migration:document-management && npm run generate:migration:authentication && npm run generate:migration:mail && npm run generate:migration:queue && npm run generate:migration:key-value-store",
21
+ "cleanup:dist": "rm -vf dist/test* && rm -vrf dist/tools/",
22
+ "generate:migration": "npm run generate:migration:document-management && npm run generate:migration:authentication && npm run generate:migration:mail && npm run generate:migration:queue && npm run generate:migration:key-value-store && npm run generate:migration:lock",
22
23
  "generate:migration:document-management": "drizzle-kit generate --config dist/document-management/server/drizzle.config.js",
23
24
  "generate:migration:authentication": "drizzle-kit generate --config dist/authentication/server/drizzle.config.js",
24
25
  "generate:migration:mail": "drizzle-kit generate --config dist/mail/drizzle.config.js",
25
26
  "generate:migration:queue": "drizzle-kit generate --config dist/queue/postgres/drizzle.config.js",
26
27
  "generate:migration:key-value-store": "drizzle-kit generate --config dist/key-value-store/postgres/drizzle.config.js",
27
- "generate:readmes": "deno run --allow-run=repomix --allow-read=source --allow-write=source --allow-net=generativelanguage.googleapis.com --allow-env=GEMINI_API_KEY generate-readmes.ts",
28
+ "generate:migration:lock": "drizzle-kit generate --config dist/lock/postgres/drizzle.config.js",
29
+ "generate:readmes": "deno run --allow-run=code2prompt --allow-read --allow-write=source --allow-net=generativelanguage.googleapis.com --allow-env=GEMINI_API_KEY generate-readmes.ts",
28
30
  "generate:readmes:new-only": "npm run generate:readmes -- --skip-existing",
29
- "copy:orm": "npm run copy:orm:document-management && npm run copy:orm:authentication && npm run copy:orm:mail && npm run copy:orm:queue && npm run copy:orm:key-value-store",
31
+ "generate:llms.md": "npm run build:dts && npm run cleanup:dist && ./scripts/generate-llms-docs.sh",
32
+ "generate:context7-docs": "npm run build:dts && npm run cleanup:dist && ./scripts/generate-context7-docs.sh",
33
+ "copy:orm": "npm run copy:orm:document-management && npm run copy:orm:authentication && npm run copy:orm:mail && npm run copy:orm:queue && npm run copy:orm:key-value-store && npm run copy:orm:lock",
30
34
  "copy:orm:document-management": "rm -rf ./dist/document-management/server/drizzle && cp -r ./source/document-management/server/drizzle ./dist/document-management/server/",
31
35
  "copy:orm:authentication": "rm -rf ./dist/authentication/server/drizzle && cp -r ./source/authentication/server/drizzle ./dist/authentication/server/",
32
36
  "copy:orm:mail": "rm -rf ./dist/mail/drizzle && cp -r ./source/mail/drizzle ./dist/mail/",
33
37
  "copy:orm:queue": "rm -rf ./dist/queue/postgres/drizzle && cp -r ./source/queue/postgres/drizzle ./dist/queue/postgres/",
34
- "copy:orm:key-value-store": "rm -rf ./dist/key-value-store/postgres/drizzle && cp -r ./source/key-value-store/postgres/drizzle ./dist/key-value-store/postgres/"
38
+ "copy:orm:key-value-store": "rm -rf ./dist/key-value-store/postgres/drizzle && cp -r ./source/key-value-store/postgres/drizzle ./dist/key-value-store/postgres/",
39
+ "copy:orm:lock": "rm -rf ./dist/lock/postgres/drizzle && cp -r ./source/lock/postgres/drizzle ./dist/lock/postgres/"
35
40
  },
36
41
  "exports": {
37
42
  "./tsconfig.json": "./tsconfig.json",
@@ -40,7 +45,6 @@
40
45
  "./environment": "./environment.js",
41
46
  "./interfaces": "./interfaces.js",
42
47
  "./polyfills": "./polyfills.js",
43
- "./process-shutdown": "./process-shutdown.js",
44
48
  "./supports": "./supports.js",
45
49
  "./tokens": "./tokens.js",
46
50
  "./ai": "./ai/index.js",
@@ -55,8 +59,6 @@
55
59
  "./cookie": "./cookie/index.js",
56
60
  "./css": "./css/index.js",
57
61
  "./data-structures": "./data-structures/index.js",
58
- "./database": "./database/index.js",
59
- "./database/mongo": "./database/mongo/index.js",
60
62
  "./decorators": "./decorators/index.js",
61
63
  "./disposable": "./disposable/index.js",
62
64
  "./distributed-loop": "./distributed-loop/index.js",
@@ -79,10 +81,9 @@
79
81
  "./json-path": "./json-path/index.js",
80
82
  "./jsx": "./jsx/index.js",
81
83
  "./key-value-store": "./key-value-store/index.js",
82
- "./key-value-store/mongo": "./key-value-store/mongo/index.js",
83
84
  "./key-value-store/postgres": "./key-value-store/postgres/index.js",
84
85
  "./lock": "./lock/index.js",
85
- "./lock/mongo": "./lock/mongo/index.js",
86
+ "./lock/postgres": "./lock/postgres/index.js",
86
87
  "./lock/web": "./lock/web/index.js",
87
88
  "./logger": "./logger/index.js",
88
89
  "./mail": "./mail/index.js",
@@ -100,7 +101,6 @@
100
101
  "./process": "./process/index.js",
101
102
  "./promise": "./promise/index.js",
102
103
  "./queue": "./queue/index.js",
103
- "./queue/mongo": "./queue/mongo/index.js",
104
104
  "./queue/postgres": "./queue/postgres/index.js",
105
105
  "./random": "./random/index.js",
106
106
  "./reflection": "./reflection/index.js",
@@ -109,9 +109,6 @@
109
109
  "./rxjs-utils": "./rxjs-utils/index.js",
110
110
  "./schema": "./schema/index.js",
111
111
  "./schema/converters": "./schema/converters/index.js",
112
- "./search-index": "./search-index/index.js",
113
- "./search-index/elastic": "./search-index/elastic/index.js",
114
- "./search-index/memory": "./search-index/memory/index.js",
115
112
  "./serializer": "./serializer/index.js",
116
113
  "./serializer/handlers": "./serializer/handlers/index.js",
117
114
  "./signals": "./signals/index.js",
@@ -123,8 +120,6 @@
123
120
  "./templates/resolvers": "./templates/resolvers/index.js",
124
121
  "./templates/types": "./templates/types/index.js",
125
122
  "./text": "./text/index.js",
126
- "./theme": "./theme/index.js",
127
- "./theme/adapters": "./theme/adapters/index.js",
128
123
  "./threading": "./threading/index.js",
129
124
  "./types": "./types/index.js",
130
125
  "./utils": "./utils/index.js",
@@ -148,28 +143,25 @@
148
143
  "type-fest": "4.37"
149
144
  },
150
145
  "peerDependencies": {
151
- "@elastic/elasticsearch": "^9.1",
152
146
  "@google-cloud/storage": "^7.17",
153
- "@google/genai": "^1.15",
147
+ "@google/genai": "^1.19",
154
148
  "@tstdl/angular": "^0.92",
155
149
  "@zxcvbn-ts/core": "^3.0",
156
150
  "@zxcvbn-ts/language-common": "^3.0",
157
151
  "@zxcvbn-ts/language-de": "^3.0",
158
152
  "@zxcvbn-ts/language-en": "^3.0",
159
- "chroma-js": "^2.6",
160
153
  "drizzle-orm": "^0.44",
161
154
  "file-type": "^21.0",
162
155
  "handlebars": "^4.7",
163
156
  "minio": "^8.0",
164
157
  "mjml": "^4.15",
165
- "mongodb": "^6.18",
166
158
  "nodemailer": "^7.0",
167
159
  "pg": "^8.16",
168
160
  "playwright": "^1.55",
169
161
  "preact": "^10.27",
170
162
  "preact-render-to-string": "^6.6",
171
163
  "sharp": "^0.34",
172
- "undici": "^7.14",
164
+ "undici": "^7.16",
173
165
  "urlpattern-polyfill": "^10.1"
174
166
  },
175
167
  "peerDependenciesMeta": {
@@ -178,8 +170,7 @@
178
170
  }
179
171
  },
180
172
  "devDependencies": {
181
- "@stylistic/eslint-plugin": "5.2",
182
- "@types/chroma-js": "2.4",
173
+ "@stylistic/eslint-plugin": "5.3",
183
174
  "@types/koa__router": "12.0",
184
175
  "@types/luxon": "3.7",
185
176
  "@types/minio": "7.1",
@@ -189,15 +180,14 @@
189
180
  "@types/pg": "8.15",
190
181
  "concurrently": "9.2",
191
182
  "drizzle-kit": "0.31",
192
- "eslint": "9.33",
193
- "globals": "16.3",
194
- "repomix": "1.3",
183
+ "eslint": "9.35",
184
+ "globals": "16.4",
195
185
  "tsc-alias": "1.8",
196
186
  "typedoc-github-wiki-theme": "2.1",
197
187
  "typedoc-plugin-markdown": "4.8",
198
188
  "typedoc-plugin-missing-exports": "4.1",
199
189
  "typescript": "5.9",
200
- "typescript-eslint": "8.40"
190
+ "typescript-eslint": "8.43"
201
191
  },
202
192
  "overrides": {
203
193
  "drizzle-kit": {
@@ -24,7 +24,7 @@ import { finalizeStream } from '../utils/stream/finalize-stream.js';
24
24
  import { readableStreamFromPromise } from '../utils/stream/from-promise.js';
25
25
  import { readBinaryStream } from '../utils/stream/stream-reader.js';
26
26
  import { timeout } from '../utils/timing.js';
27
- import { isDefined } from '../utils/type-guards.js';
27
+ import { isDefined, isNumber } from '../utils/type-guards.js';
28
28
  export class PdfServiceRenderOptions extends PdfRenderOptions {
29
29
  browserContext;
30
30
  locale;
@@ -81,7 +81,7 @@ let PdfService = class PdfService {
81
81
  * @returns pdf stream
82
82
  */
83
83
  renderHtmlStream(html, options) {
84
- return this.renderStream(async (page) => page.setContent(html), options);
84
+ return this.renderStream(async (page) => await page.setContent(html), options);
85
85
  }
86
86
  /**
87
87
  * Renders HTML to pdf
@@ -91,7 +91,7 @@ let PdfService = class PdfService {
91
91
  */
92
92
  async renderHtml(html, options) {
93
93
  const stream = this.renderHtmlStream(html, options);
94
- return readBinaryStream(stream);
94
+ return await readBinaryStream(stream);
95
95
  }
96
96
  /**
97
97
  * Renders an url to pdf stream
@@ -112,7 +112,7 @@ let PdfService = class PdfService {
112
112
  */
113
113
  async renderUrl(url, options) {
114
114
  const stream = this.renderUrlStream(url, options);
115
- return readBinaryStream(stream);
115
+ return await readBinaryStream(stream);
116
116
  }
117
117
  /**
118
118
  * Renders a template to pdf
@@ -137,15 +137,15 @@ let PdfService = class PdfService {
137
137
  */
138
138
  async renderTemplate(keyOrTemplate, templateContext, options) {
139
139
  const stream = this.renderTemplateStream(keyOrTemplate, templateContext, options);
140
- return readBinaryStream(stream);
140
+ return await readBinaryStream(stream);
141
141
  }
142
142
  renderStream(handler, options = {}) {
143
143
  return readableStreamFromPromise(async () => {
144
144
  const context = options.browserContext ?? await this.browserController.newContext({ locale: options.locale ?? this.defaultLocale });
145
145
  const page = await context.newPage();
146
146
  if (isDefined(options.log) && (options.log != false)) {
147
- const level = (options.log == true) ? LogLevel.Trace : options.log;
148
- const logger = this.logger.fork({ level, subModule: 'PAGE' });
147
+ const level = isNumber(options.log) ? options.log : LogLevel.Trace;
148
+ const logger = this.logger.fork('PAGE', { level });
149
149
  page.attachLogger(logger);
150
150
  }
151
151
  const optionsFromHandler = await handler(page);
@@ -165,7 +165,7 @@ let PdfService = class PdfService {
165
165
  return finalizeStream(pdfStream, {
166
166
  beforeDone: close,
167
167
  beforeCancel: close,
168
- error: close
168
+ error: close,
169
169
  });
170
170
  });
171
171
  }
@@ -178,6 +178,6 @@ export function pdfTemplate(name, fields, options) {
178
178
  return {
179
179
  name,
180
180
  fields,
181
- options
181
+ options,
182
182
  };
183
183
  }
package/pool/pool.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import type { AsyncDisposable } from '../disposable/disposable.js';
2
- import { disposeAsync } from '../disposable/disposable.js';
3
1
  import type { Logger } from '../logger/index.js';
4
2
  export type PoolOptions = {
5
3
  /**
@@ -45,5 +43,5 @@ export declare class Pool<T extends object> implements AsyncDisposable {
45
43
  use<R>(handler: (instance: T) => R | Promise<R>, options?: PoolUseOptions): Promise<R>;
46
44
  disposeInstance(instance: T): Promise<void>;
47
45
  dispose(): Promise<void>;
48
- [disposeAsync](): Promise<void>;
46
+ [Symbol.asyncDispose](): Promise<void>;
49
47
  }
package/pool/pool.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { ArrayList } from '../data-structures/array-list.js';
2
2
  import { SetCollection } from '../data-structures/set-collection.js';
3
- import { disposeAsync } from '../disposable/disposable.js';
4
3
  import { hardwareConcurrency } from '../environment.js';
5
4
  import { isDefined } from '../utils/type-guards.js';
6
5
  export class Pool {
@@ -57,7 +56,7 @@ export class Pool {
57
56
  while (!this.freeInstances.hasItems) {
58
57
  await this.freeInstances.$onItems;
59
58
  }
60
- return this.get();
59
+ return await this.get();
61
60
  }
62
61
  async release(instance) {
63
62
  if (this.freeInstances.includes(instance)) {
@@ -110,9 +109,9 @@ export class Pool {
110
109
  await this.disposer(instance);
111
110
  }
112
111
  async dispose() {
113
- return this[disposeAsync]();
112
+ await this[Symbol.asyncDispose]();
114
113
  }
115
- async [disposeAsync]() {
114
+ async [Symbol.asyncDispose]() {
116
115
  this.disposed = true;
117
116
  if (this.length == 0) {
118
117
  return;
@@ -1,5 +1,4 @@
1
- import { EntityWithoutMetadata } from '../../orm/entity.js';
2
- import { Integer, Json, Timestamp } from '../../orm/index.js';
1
+ import { EntityWithoutMetadata, Integer, Json, Timestamp } from '../../orm/index.js';
3
2
  import type { ObjectLiteral } from '../../types/index.js';
4
3
  import type { Job } from '../queue.js';
5
4
  export declare class PostgresJob<T extends ObjectLiteral = ObjectLiteral> extends EntityWithoutMetadata implements Job<T> {
@@ -8,8 +8,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
10
  import { Index, Table } from '../../orm/decorators.js';
11
- import { EntityWithoutMetadata } from '../../orm/entity.js';
12
- import { Integer, Json, Timestamp } from '../../orm/index.js';
11
+ import { EntityWithoutMetadata, Integer, Json, Timestamp } from '../../orm/index.js';
13
12
  import { StringProperty } from '../../schema/index.js';
14
13
  let PostgresJob = class PostgresJob extends EntityWithoutMetadata {
15
14
  queue;
@@ -24,6 +24,6 @@ export async function migratePostgresQueueSchema() {
24
24
  await migrate(database, {
25
25
  migrationsSchema: 'queue',
26
26
  migrationsTable: '_migrations',
27
- migrationsFolder: import.meta.resolve('./drizzle').replace('file://', '')
27
+ migrationsFolder: import.meta.resolve('./drizzle').replace('file://', ''),
28
28
  });
29
29
  }
@@ -1,7 +1,5 @@
1
1
  import type * as NodeWorkerThreads from 'node:worker_threads';
2
2
  import type { LiteralUnion } from 'type-fest';
3
- import type { AsyncDisposable } from '../disposable/index.js';
4
- import { disposeAsync } from '../disposable/index.js';
5
3
  import type { Logger } from '../logger/index.js';
6
4
  import type { ThreadWorker } from './thread-worker.js';
7
5
  export type ThreadOptions = (WorkerOptions | NodeWorkerThreads.WorkerOptions) & {
@@ -13,7 +11,7 @@ export declare class ThreadPool implements AsyncDisposable {
13
11
  readonly options: ThreadOptions | undefined;
14
12
  constructor(url: string | URL, logger: Logger, options?: ThreadOptions);
15
13
  dispose(): Promise<void>;
16
- [disposeAsync](): Promise<void>;
14
+ [Symbol.asyncDispose](): Promise<void>;
17
15
  getProcessor<T extends ThreadWorker>(name?: string): (...args: Parameters<T>) => Promise<ReturnType<T>>;
18
16
  process<T extends ThreadWorker>(name: LiteralUnion<'default', string>, ...args: Parameters<T>): Promise<ReturnType<T>>;
19
17
  private spawn;
@@ -1,4 +1,3 @@
1
- import { disposeAsync } from '../disposable/index.js';
2
1
  import { isNode } from '../environment.js';
3
2
  import { dynamicImport } from '../import.js';
4
3
  import { Pool } from '../pool/index.js';
@@ -9,7 +8,7 @@ if (isNode) {
9
8
  spawnWorker = async (url, options) => {
10
9
  const workerThreads = await dynamicImport('node:worker_threads');
11
10
  spawnWorker = () => new workerThreads.Worker(url, options);
12
- return spawnWorker(url, options);
11
+ return await spawnWorker(url, options);
13
12
  };
14
13
  }
15
14
  else {
@@ -22,26 +21,26 @@ export class ThreadPool {
22
21
  constructor(url, logger, options) {
23
22
  this.url = url;
24
23
  this.options = options;
25
- this.pool = new Pool(async () => this.spawn(), async ({ worker }) => worker.terminate(), logger, { size: options?.threadCount });
24
+ this.pool = new Pool(async () => await this.spawn(), async ({ worker }) => await worker.terminate(), logger, { size: options?.threadCount });
26
25
  }
27
26
  async dispose() {
28
- return this[disposeAsync]();
27
+ await this[Symbol.asyncDispose]();
29
28
  }
30
- async [disposeAsync]() {
29
+ async [Symbol.asyncDispose]() {
31
30
  await this.pool.dispose();
32
31
  }
33
32
  getProcessor(name = 'default') {
34
- const processor = async (...args) => this.process(name, ...args);
33
+ const processor = async (...args) => await this.process(name, ...args);
35
34
  return processor;
36
35
  }
37
36
  async process(name, ...args) {
38
- return this.pool.use(async (entry) => {
37
+ return await this.pool.use(async (entry) => {
39
38
  if (!entry.remotes.has(name)) {
40
39
  const rpcEndpoint = MessagePortRpcEndpoint.from(entry.worker);
41
40
  const remote = await Rpc.connect(rpcEndpoint, `thread-worker:${name}`);
42
41
  entry.remotes.set(name, remote);
43
42
  }
44
- return entry.remotes.get(name)(...args);
43
+ return await entry.remotes.get(name)(...args);
45
44
  });
46
45
  }
47
46
  async spawn() {
@@ -16,9 +16,16 @@ export type FormatErrorOptions = {
16
16
  * Include stack trace
17
17
  */
18
18
  includeStack?: boolean;
19
+ /**
20
+ * Maximum recursion depth for nested causes and aggregate errors (default: 5)
21
+ */
22
+ depth?: number;
19
23
  };
20
24
  export interface ErrorExtraInfo {
21
25
  /** Format extra data (without message and stack) as JSON */
22
26
  getExtraInfo(): UndefinableJson | undefined;
23
27
  }
28
+ /**
29
+ * Enhanced error formatting
30
+ */
24
31
  export declare function formatError(error: any, options?: FormatErrorOptions): string;
@@ -1,35 +1,77 @@
1
1
  import { unwrapError } from '../errors/utils.js';
2
2
  import { decycle } from './object/decycle.js';
3
3
  import { objectKeys } from './object/object.js';
4
- import { isDefined, isFunction, isUndefined } from './type-guards.js';
4
+ import { tryChain } from './try-chain.js';
5
+ import { isDefined, isFunction, isObject, isUndefined } from './type-guards.js';
6
+ /**
7
+ * Enhanced error formatting
8
+ */
5
9
  export function formatError(error, options = {}) {
6
- const { includeRest = 'if-no-extra-info', includeExtraInfo = true, includeStack = true } = options;
10
+ const { includeName = true, includeRest = 'if-no-extra-info', includeExtraInfo = true, includeStack = true, depth = 5, } = options;
11
+ if (depth <= 0) {
12
+ return '[Max recursion depth reached]';
13
+ }
14
+ const actualError = unwrapError(error);
7
15
  let name;
8
16
  let message;
9
17
  let stack;
10
18
  let rest;
11
19
  let extraInfo;
12
- const actualError = unwrapError(error);
13
- if ((actualError instanceof Error)) {
14
- ({ name, message, stack, ...rest } = actualError);
15
- // eslint-disable-next-line @typescript-eslint/unbound-method
16
- if (includeExtraInfo && isFunction(actualError.getExtraInfo)) {
20
+ let cause;
21
+ let aggregateErrors;
22
+ if (isObject(actualError)) {
23
+ if (actualError instanceof Error) {
24
+ ({ name, message, stack, cause, ...rest } = actualError);
25
+ if (actualError instanceof AggregateError) {
26
+ aggregateErrors = actualError.errors;
27
+ }
28
+ }
29
+ else {
30
+ // Handle plain objects that might have error-like properties
31
+ ({ name, message, stack, cause, ...rest } = actualError);
32
+ }
33
+ // Check for extra info (duck-typing)
34
+ if (includeExtraInfo && isFunction(actualError.getExtraInfo)) { // eslint-disable-line @typescript-eslint/unbound-method
17
35
  extraInfo = actualError.getExtraInfo();
18
36
  }
19
37
  }
20
- if (isUndefined(name) && (isUndefined(message) || message.trim().length == 0)) {
21
- try {
22
- const decycledError = decycle(actualError);
23
- message = JSON.stringify(decycledError, null, 2);
38
+ // If no message, serialize the whole object as a fallback
39
+ if (isUndefined(message) || String(message).trim().length === 0) { // eslint-disable-line @typescript-eslint/no-unnecessary-type-conversion
40
+ // Handle primitives directly
41
+ if (!isObject(actualError)) {
42
+ message = String(actualError);
24
43
  }
25
- catch {
26
- throw actualError;
44
+ else {
45
+ message = tryChain([
46
+ () => JSON.stringify(actualError, null, 2), // Try normal serialization first
47
+ () => {
48
+ // Fallback with decycle for objects with circular references
49
+ const decycledError = decycle(actualError);
50
+ return JSON.stringify(decycledError, null, 2);
51
+ },
52
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
53
+ () => `[Unstringifiable object: ${String(actualError)}]`, // Safe fallback if JSON.stringify still fails
54
+ ]);
27
55
  }
28
56
  }
29
- const nameString = (options.includeName ?? true) ? `${name ?? 'Error'}: ` : '';
30
- const decycledRest = isDefined(rest) ? decycle(rest) : undefined;
31
- const restString = (((includeRest == true) || ((includeRest == 'if-no-extra-info') && isUndefined(extraInfo))) && isDefined(decycledRest) && (objectKeys(rest ?? {}).length > 0)) ? `\n${JSON.stringify(decycledRest, null, 2)}` : '';
57
+ // --- String Assembly ---
58
+ const nameString = includeName ? `${name ?? 'Error'}: ` : '';
59
+ let restString = '';
60
+ const shouldIncludeRest = (includeRest == true) || (includeRest == 'if-no-extra-info' && isUndefined(extraInfo));
61
+ if (shouldIncludeRest && isDefined(rest) && objectKeys(rest).length > 0) {
62
+ restString = `\n${JSON.stringify(decycle(rest), null, 2)}`;
63
+ }
32
64
  const extraInfoString = isDefined(extraInfo) ? `\n${JSON.stringify(extraInfo, null, 2)}` : '';
33
65
  const stackString = (includeStack && isDefined(stack)) ? `\n${stack}` : '';
34
- return `${nameString}${message}${restString}${extraInfoString}${stackString}`;
66
+ const causeString = includeStack && cause
67
+ ? formatNestedError('Caused by:', cause, { ...options, depth: depth - 1 })
68
+ : '';
69
+ const aggregateErrorsString = Array.isArray(aggregateErrors)
70
+ ? aggregateErrors.map((err, i) => formatNestedError(`Sub-error #${i + 1}:`, err, { ...options, depth: depth - 1 })).join('')
71
+ : '';
72
+ return `${nameString}${message}${restString}${extraInfoString}${stackString}${causeString}${aggregateErrorsString}`;
73
+ }
74
+ function formatNestedError(prefix, error, options) {
75
+ const formatted = formatError(error, options).replace(/\n/g, '\n ');
76
+ return `\n\n${prefix}\n ${formatted}`;
35
77
  }
@@ -17,20 +17,35 @@ export declare function memoizeSingle<Fn extends (parameter: any) => any>(fn: Fn
17
17
  export declare function memoizeClass<T extends Constructor>(type: T, options?: MemoizeOptions): (...parameters: ConstructorParameters<T>) => InstanceType<T>;
18
18
  export declare function memoizeClassSingle<T extends Constructor<any, [any]>>(type: T, options?: MemoizeOptions): (...parameters: ConstructorParameters<T>) => InstanceType<T>;
19
19
  /**
20
- * Memoizes an accessor (getter)
20
+ * Memoizes the result of a class getter, ensuring the getter's logic is
21
+ * executed only once per instance.
22
+ *
23
+ * Subsequent accesses to the getter will return the cached value.
24
+ *
25
+ * @param strategy The memoization strategy to use.
26
+ * - `attach`: (Default) Uses `Symbol` properties to store the cached value directly on the class instance. This is generally faster but adds hidden properties to the instance.
27
+ * - `weak-map`: Uses a `WeakMap` to store the cached value, with the class instance as the key. This avoids adding any properties to the instance, keeping it "clean".
21
28
  *
22
29
  * @example
23
30
  * ```typescript
24
- * class MyClass {
31
+ * class MyEvaluator {
25
32
  * @Memoize()
26
- * get myValue() {
27
- * // expensive calculation
28
- * return 123;
33
+ * get result() {
34
+ * // This expensive result creation only happens once per instance.
35
+ * return this.computeExpensiveResult();
29
36
  * }
30
37
  * }
38
+ *
39
+ * const service = new MyEvaluator();
40
+ * const s1 = service.result; // Logs "Creating new result..."
41
+ * const s2 = service.result; // Does not log anything, returns cached result.
42
+ * console.log(s1 === s2); // true
31
43
  * ```
32
44
  *
33
45
  * @remarks
34
- * The getter will be called only once for each instance of the class.
46
+ * If the decorated property has a corresponding setter, calling the setter will
47
+ * invalidate the cache. The getter will be re-evaluated on its next access.
48
+ *
49
+ * @returns An accessor decorator function.
35
50
  */
36
- export declare function Memoize(): MethodDecorator;
51
+ export declare function Memoize(strategy?: 'attach' | 'weak-map'): MethodDecorator;