@freshpointcz/fresh-core 0.0.16 → 0.0.17

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/dist/index.d.mts CHANGED
@@ -1,6 +1,5 @@
1
1
  import dayjs, { Dayjs } from 'dayjs';
2
- import { Logger } from 'winston';
3
- import { BaseEntity, ColumnOptions, Repository, EntityTarget, EntityManager, DataSourceOptions } from 'typeorm';
2
+ import { BaseEntity, ColumnOptions, Repository, EntityTarget, EntityManager, ObjectLiteral, EntitySubscriberInterface, InsertEvent, UpdateEvent, SoftRemoveEvent, TransactionCommitEvent, DataSourceOptions } from 'typeorm';
4
3
  import { Job } from 'node-schedule';
5
4
  import * as _eslint_core from '@eslint/core';
6
5
  import * as typescript_eslint_dist_compatibility_types from 'typescript-eslint/dist/compatibility-types';
@@ -54,8 +53,6 @@ declare class SinglePromiseWaiter<T = any> {
54
53
  get hasPromise(): boolean;
55
54
  }
56
55
 
57
- declare const logger: Logger;
58
-
59
56
  declare function isValidCron(expr: string): boolean;
60
57
 
61
58
  /**
@@ -348,6 +345,50 @@ declare abstract class FreshDao<T extends BaseEntity> {
348
345
  protected getRepo(manager?: EntityManager, entity?: EntityTarget<T>): Repository<T>;
349
346
  }
350
347
 
348
+ /**
349
+ * Change event types for entity lifecycle tracking.
350
+ */
351
+ type EntityChangeEvent = "created" | "updated" | "deleted";
352
+ /**
353
+ * Abstract base class for TypeORM EntitySubscribers that need to:
354
+ * 1. Track entity changes (insert/update/soft-remove)
355
+ * 2. Batch pending notifications within a transaction
356
+ * 3. Send notifications only after transaction commit
357
+ *
358
+ * Subclasses must implement:
359
+ * - `listenTo()` — target entity class
360
+ * - `pendingKey` — unique key for storing pending events in `queryRunner.data`
361
+ * - `subscriberName` — name used in log messages
362
+ * - `handleNotification(id, changeEvent, manager)` — actual notification logic
363
+ */
364
+ declare abstract class BaseEntityChangeSubscriber<Entity extends ObjectLiteral, IdType = number> implements EntitySubscriberInterface<Entity> {
365
+ /**
366
+ * Unique key used to store pending change events in `queryRunner.data`.
367
+ * Must be unique per subscriber to avoid collisions when multiple subscribers
368
+ * share the same queryRunner.
369
+ */
370
+ protected abstract readonly PENDING_KEY: string;
371
+ /**
372
+ * Human-readable name for log/error messages.
373
+ */
374
+ protected abstract readonly SUBSCRIBER_NAME: string;
375
+ /**
376
+ * Return the entity class this subscriber listens to.
377
+ */
378
+ abstract listenTo(): Function;
379
+ /**
380
+ * Implement the actual notification/side-effect logic.
381
+ * Called after transaction commit (or immediately if no transaction is active).
382
+ */
383
+ protected abstract handleNotification(id: IdType, changeEvent: EntityChangeEvent, manager: EntityManager): Promise<void>;
384
+ afterInsert(event: InsertEvent<Entity>): void;
385
+ afterUpdate(event: UpdateEvent<Entity>): void;
386
+ afterSoftRemove(event: SoftRemoveEvent<Entity>): void;
387
+ afterTransactionCommit(event: TransactionCommitEvent): Promise<void>;
388
+ private addPending;
389
+ private sendNotification;
390
+ }
391
+
351
392
  declare class Category extends FreshEntity {
352
393
  }
353
394
 
@@ -641,4 +682,4 @@ interface HealthCheckResult {
641
682
  };
642
683
  }
643
684
 
644
- export { AMOUNT_UNIT, ActionCommandCode, ApiError, type CardNumber, Category, DataHelper, DateUtils, type Deferred, DepotPoolStatus, Device, FreshDao, FreshEntity, FreshHyperEntity, FreshJob, FreshTranslationBase, type HealthCheckResult, HttpStatus, LanguageCode, Manufacturer, type Maybe, PaymentMethod, PG_DATA_SOURCE_OPTIONS as PgDataSourceOptions, Product, SinglePromiseWaiter, Singleton, type Status, StatusDto, Subcategory, TimestampColumn, TransactionType, createDeferred, FRESH_ESLINT_CONFIG as freshEslintConfig, hasOwn, isEnumValue, isFlag01, isMaybe, isNumber, isNumberInRange, isObject, isString, isValidCron, logger };
685
+ export { AMOUNT_UNIT, ActionCommandCode, ApiError, BaseEntityChangeSubscriber, type CardNumber, Category, DataHelper, DateUtils, type Deferred, DepotPoolStatus, Device, type EntityChangeEvent, FreshDao, FreshEntity, FreshHyperEntity, FreshJob, FreshTranslationBase, type HealthCheckResult, HttpStatus, LanguageCode, Manufacturer, type Maybe, PaymentMethod, PG_DATA_SOURCE_OPTIONS as PgDataSourceOptions, Product, SinglePromiseWaiter, Singleton, type Status, StatusDto, Subcategory, TimestampColumn, TransactionType, createDeferred, FRESH_ESLINT_CONFIG as freshEslintConfig, hasOwn, isEnumValue, isFlag01, isMaybe, isNumber, isNumberInRange, isObject, isString, isValidCron };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import dayjs, { Dayjs } from 'dayjs';
2
- import { Logger } from 'winston';
3
- import { BaseEntity, ColumnOptions, Repository, EntityTarget, EntityManager, DataSourceOptions } from 'typeorm';
2
+ import { BaseEntity, ColumnOptions, Repository, EntityTarget, EntityManager, ObjectLiteral, EntitySubscriberInterface, InsertEvent, UpdateEvent, SoftRemoveEvent, TransactionCommitEvent, DataSourceOptions } from 'typeorm';
4
3
  import { Job } from 'node-schedule';
5
4
  import * as _eslint_core from '@eslint/core';
6
5
  import * as typescript_eslint_dist_compatibility_types from 'typescript-eslint/dist/compatibility-types';
@@ -54,8 +53,6 @@ declare class SinglePromiseWaiter<T = any> {
54
53
  get hasPromise(): boolean;
55
54
  }
56
55
 
57
- declare const logger: Logger;
58
-
59
56
  declare function isValidCron(expr: string): boolean;
60
57
 
61
58
  /**
@@ -348,6 +345,50 @@ declare abstract class FreshDao<T extends BaseEntity> {
348
345
  protected getRepo(manager?: EntityManager, entity?: EntityTarget<T>): Repository<T>;
349
346
  }
350
347
 
348
+ /**
349
+ * Change event types for entity lifecycle tracking.
350
+ */
351
+ type EntityChangeEvent = "created" | "updated" | "deleted";
352
+ /**
353
+ * Abstract base class for TypeORM EntitySubscribers that need to:
354
+ * 1. Track entity changes (insert/update/soft-remove)
355
+ * 2. Batch pending notifications within a transaction
356
+ * 3. Send notifications only after transaction commit
357
+ *
358
+ * Subclasses must implement:
359
+ * - `listenTo()` — target entity class
360
+ * - `pendingKey` — unique key for storing pending events in `queryRunner.data`
361
+ * - `subscriberName` — name used in log messages
362
+ * - `handleNotification(id, changeEvent, manager)` — actual notification logic
363
+ */
364
+ declare abstract class BaseEntityChangeSubscriber<Entity extends ObjectLiteral, IdType = number> implements EntitySubscriberInterface<Entity> {
365
+ /**
366
+ * Unique key used to store pending change events in `queryRunner.data`.
367
+ * Must be unique per subscriber to avoid collisions when multiple subscribers
368
+ * share the same queryRunner.
369
+ */
370
+ protected abstract readonly PENDING_KEY: string;
371
+ /**
372
+ * Human-readable name for log/error messages.
373
+ */
374
+ protected abstract readonly SUBSCRIBER_NAME: string;
375
+ /**
376
+ * Return the entity class this subscriber listens to.
377
+ */
378
+ abstract listenTo(): Function;
379
+ /**
380
+ * Implement the actual notification/side-effect logic.
381
+ * Called after transaction commit (or immediately if no transaction is active).
382
+ */
383
+ protected abstract handleNotification(id: IdType, changeEvent: EntityChangeEvent, manager: EntityManager): Promise<void>;
384
+ afterInsert(event: InsertEvent<Entity>): void;
385
+ afterUpdate(event: UpdateEvent<Entity>): void;
386
+ afterSoftRemove(event: SoftRemoveEvent<Entity>): void;
387
+ afterTransactionCommit(event: TransactionCommitEvent): Promise<void>;
388
+ private addPending;
389
+ private sendNotification;
390
+ }
391
+
351
392
  declare class Category extends FreshEntity {
352
393
  }
353
394
 
@@ -641,4 +682,4 @@ interface HealthCheckResult {
641
682
  };
642
683
  }
643
684
 
644
- export { AMOUNT_UNIT, ActionCommandCode, ApiError, type CardNumber, Category, DataHelper, DateUtils, type Deferred, DepotPoolStatus, Device, FreshDao, FreshEntity, FreshHyperEntity, FreshJob, FreshTranslationBase, type HealthCheckResult, HttpStatus, LanguageCode, Manufacturer, type Maybe, PaymentMethod, PG_DATA_SOURCE_OPTIONS as PgDataSourceOptions, Product, SinglePromiseWaiter, Singleton, type Status, StatusDto, Subcategory, TimestampColumn, TransactionType, createDeferred, FRESH_ESLINT_CONFIG as freshEslintConfig, hasOwn, isEnumValue, isFlag01, isMaybe, isNumber, isNumberInRange, isObject, isString, isValidCron, logger };
685
+ export { AMOUNT_UNIT, ActionCommandCode, ApiError, BaseEntityChangeSubscriber, type CardNumber, Category, DataHelper, DateUtils, type Deferred, DepotPoolStatus, Device, type EntityChangeEvent, FreshDao, FreshEntity, FreshHyperEntity, FreshJob, FreshTranslationBase, type HealthCheckResult, HttpStatus, LanguageCode, Manufacturer, type Maybe, PaymentMethod, PG_DATA_SOURCE_OPTIONS as PgDataSourceOptions, Product, SinglePromiseWaiter, Singleton, type Status, StatusDto, Subcategory, TimestampColumn, TransactionType, createDeferred, FRESH_ESLINT_CONFIG as freshEslintConfig, hasOwn, isEnumValue, isFlag01, isMaybe, isNumber, isNumberInRange, isObject, isString, isValidCron };
package/dist/index.js CHANGED
@@ -428,6 +428,7 @@ __export(index_exports, {
428
428
  AMOUNT_UNIT: () => AMOUNT_UNIT,
429
429
  ActionCommandCode: () => ActionCommandCode,
430
430
  ApiError: () => ApiError,
431
+ BaseEntityChangeSubscriber: () => BaseEntityChangeSubscriber,
431
432
  Category: () => Category,
432
433
  DataHelper: () => DataHelper,
433
434
  DateUtils: () => DateUtils,
@@ -460,8 +461,7 @@ __export(index_exports, {
460
461
  isNumberInRange: () => isNumberInRange,
461
462
  isObject: () => isObject,
462
463
  isString: () => isString,
463
- isValidCron: () => isValidCron,
464
- logger: () => logger
464
+ isValidCron: () => isValidCron
465
465
  });
466
466
  module.exports = __toCommonJS(index_exports);
467
467
 
@@ -691,56 +691,6 @@ __name(_SinglePromiseWaiter, "SinglePromiseWaiter");
691
691
  __publicField(_SinglePromiseWaiter, "_instance");
692
692
  var SinglePromiseWaiter = _SinglePromiseWaiter;
693
693
 
694
- // src/common/winston.ts
695
- var import_winston = __toESM(require("winston"));
696
- var SERVICE = process.env.SERVICE_NAME || "depot-ordering-service";
697
- var ENV = process.env.NODE_ENV || "localhost";
698
- var levelToSeverity = {
699
- silly: "debug",
700
- verbose: "debug",
701
- debug: "debug",
702
- http: "info",
703
- info: "info",
704
- warn: "warn",
705
- error: "error"
706
- };
707
- var baseFormat = import_winston.default.format.combine(import_winston.default.format.timestamp({
708
- format: /* @__PURE__ */ __name(() => (/* @__PURE__ */ new Date()).toISOString(), "format")
709
- }), import_winston.default.format.errors({
710
- stack: true
711
- }), import_winston.default.format.printf((info) => {
712
- const payload = {
713
- ts: info.timestamp,
714
- level: info.level,
715
- severity: levelToSeverity[info.level] || info.level,
716
- message: info.message,
717
- service: SERVICE,
718
- env: ENV,
719
- process_name: process.title || process.argv[1] || "node",
720
- pid: process.pid,
721
- device: process.env.DEVICE_ID || void 0,
722
- traceId: info.traceId,
723
- spanId: info.spanId,
724
- extra: info.extra || info.meta || void 0,
725
- stack: info.stack
726
- };
727
- return JSON.stringify(payload);
728
- }));
729
- var transports = [
730
- new import_winston.default.transports.Console({
731
- level: process.env.LOG_LEVEL || "info"
732
- })
733
- ];
734
- var logger = import_winston.default.createLogger({
735
- level: process.env.LOG_LEVEL || "info",
736
- format: baseFormat,
737
- transports,
738
- exitOnError: false
739
- });
740
- logger.stream = {
741
- write: /* @__PURE__ */ __name((message) => logger.info(message.trim()), "write")
742
- };
743
-
744
694
  // src/common/utils/is-cron-valid.ts
745
695
  function isValidCron(expr) {
746
696
  const parts = expr.trim().split(/\s+/);
@@ -1207,6 +1157,83 @@ var _FreshDao = class _FreshDao {
1207
1157
  __name(_FreshDao, "FreshDao");
1208
1158
  var FreshDao = _FreshDao;
1209
1159
 
1160
+ // src/database/subscribers/base-entity-change.subscriber.ts
1161
+ var _BaseEntityChangeSubscriber = class _BaseEntityChangeSubscriber {
1162
+ // ─── TypeORM lifecycle hooks ────────────────────────────────────────
1163
+ afterInsert(event) {
1164
+ var _a;
1165
+ const id = (_a = event.entity) == null ? void 0 : _a.id;
1166
+ if (!id) {
1167
+ return;
1168
+ }
1169
+ if (event.queryRunner.isTransactionActive) {
1170
+ this.addPending(event, id, "created");
1171
+ } else {
1172
+ console.warn(`${this.SUBSCRIBER_NAME} - Notification sent outside transaction for id=${id}`);
1173
+ this.sendNotification(id, "created", event.queryRunner.manager);
1174
+ }
1175
+ }
1176
+ afterUpdate(event) {
1177
+ var _a, _b, _c;
1178
+ const id = (_c = (_a = event.entity) == null ? void 0 : _a.id) != null ? _c : (_b = event.databaseEntity) == null ? void 0 : _b.id;
1179
+ if (!id) {
1180
+ return;
1181
+ }
1182
+ if (event.queryRunner.isTransactionActive) {
1183
+ this.addPending(event, id, "updated");
1184
+ } else {
1185
+ console.warn(`${this.SUBSCRIBER_NAME} - Notification sent outside transaction for id=${id}`);
1186
+ this.sendNotification(id, "updated", event.queryRunner.manager);
1187
+ }
1188
+ }
1189
+ afterSoftRemove(event) {
1190
+ var _a, _b, _c;
1191
+ const id = (_c = (_a = event.entity) == null ? void 0 : _a.id) != null ? _c : (_b = event.databaseEntity) == null ? void 0 : _b.id;
1192
+ if (!id) {
1193
+ return;
1194
+ }
1195
+ if (event.queryRunner.isTransactionActive) {
1196
+ this.addPending(event, id, "deleted");
1197
+ } else {
1198
+ console.warn(`${this.SUBSCRIBER_NAME} - Notification sent outside transaction for id=${id}`);
1199
+ this.sendNotification(id, "deleted", event.queryRunner.manager);
1200
+ }
1201
+ }
1202
+ async afterTransactionCommit(event) {
1203
+ var _a;
1204
+ const pending = (_a = event.queryRunner.data) == null ? void 0 : _a[this.PENDING_KEY];
1205
+ if (!pending || pending.size === 0) {
1206
+ return;
1207
+ }
1208
+ event.queryRunner.data[this.PENDING_KEY] = /* @__PURE__ */ new Map();
1209
+ for (const [id, changeEvent] of pending) {
1210
+ await this.sendNotification(id, changeEvent, event.connection.manager);
1211
+ }
1212
+ }
1213
+ // ─── Private helpers ───────────────────────────────────────────────
1214
+ addPending(event, id, changeEvent) {
1215
+ if (!event.queryRunner.data) {
1216
+ event.queryRunner.data = {};
1217
+ }
1218
+ if (!event.queryRunner.data[this.PENDING_KEY]) {
1219
+ event.queryRunner.data[this.PENDING_KEY] = /* @__PURE__ */ new Map();
1220
+ }
1221
+ const existing = event.queryRunner.data[this.PENDING_KEY].get(id);
1222
+ if (!existing || changeEvent === "created" || changeEvent === "deleted") {
1223
+ event.queryRunner.data[this.PENDING_KEY].set(id, changeEvent);
1224
+ }
1225
+ }
1226
+ async sendNotification(id, changeEvent, manager) {
1227
+ try {
1228
+ await this.handleNotification(id, changeEvent, manager);
1229
+ } catch (error) {
1230
+ console.error(`${this.SUBSCRIBER_NAME} - Failed to send notification: ${error instanceof Error ? error.message : error}`);
1231
+ }
1232
+ }
1233
+ };
1234
+ __name(_BaseEntityChangeSubscriber, "BaseEntityChangeSubscriber");
1235
+ var BaseEntityChangeSubscriber = _BaseEntityChangeSubscriber;
1236
+
1210
1237
  // src/common/schema/entities/category.entity.ts
1211
1238
  function _ts_decorate4(decorators, target, key, desc) {
1212
1239
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -1707,6 +1734,7 @@ var datasource_default = PG_DATA_SOURCE_OPTIONS;
1707
1734
  AMOUNT_UNIT,
1708
1735
  ActionCommandCode,
1709
1736
  ApiError,
1737
+ BaseEntityChangeSubscriber,
1710
1738
  Category,
1711
1739
  DataHelper,
1712
1740
  DateUtils,
@@ -1739,7 +1767,6 @@ var datasource_default = PG_DATA_SOURCE_OPTIONS;
1739
1767
  isNumberInRange,
1740
1768
  isObject,
1741
1769
  isString,
1742
- isValidCron,
1743
- logger
1770
+ isValidCron
1744
1771
  });
1745
1772
  //# sourceMappingURL=index.js.map