@woltz/rich-domain 1.3.2 → 1.4.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.
- package/README.md +21 -0
- package/dist/{aggregate-changes.d.ts → cjs/aggregate-changes.d.ts} +2 -2
- package/dist/cjs/aggregate-changes.d.ts.map +1 -0
- package/dist/cjs/aggregate-changes.js +365 -0
- package/dist/cjs/aggregate-changes.js.map +1 -0
- package/dist/{base-entity.d.ts → cjs/base-entity.d.ts} +5 -5
- package/dist/cjs/base-entity.d.ts.map +1 -0
- package/dist/cjs/base-entity.js +328 -0
- package/dist/cjs/base-entity.js.map +1 -0
- package/dist/{change-tracker.d.ts → cjs/change-tracker.d.ts} +2 -2
- package/dist/cjs/change-tracker.d.ts.map +1 -0
- package/dist/cjs/change-tracker.js +789 -0
- package/dist/cjs/change-tracker.js.map +1 -0
- package/dist/cjs/constants.d.ts.map +1 -0
- package/dist/cjs/constants.js +74 -0
- package/dist/cjs/constants.js.map +1 -0
- package/dist/{criteria.d.ts → cjs/criteria.d.ts} +1 -1
- package/dist/cjs/criteria.d.ts.map +1 -0
- package/dist/cjs/criteria.js +335 -0
- package/dist/{criteria.js.map → cjs/criteria.js.map} +1 -1
- package/dist/cjs/crypto.d.ts.map +1 -0
- package/dist/cjs/crypto.js +32 -0
- package/dist/cjs/crypto.js.map +1 -0
- package/dist/cjs/domain-event-bus.d.ts.map +1 -0
- package/dist/cjs/domain-event-bus.js +108 -0
- package/dist/cjs/domain-event-bus.js.map +1 -0
- package/dist/{domain-event.d.ts → cjs/domain-event.d.ts} +1 -1
- package/dist/cjs/domain-event.d.ts.map +1 -0
- package/dist/cjs/domain-event.js +47 -0
- package/dist/cjs/domain-event.js.map +1 -0
- package/dist/{entity-changes.d.ts → cjs/entity-changes.d.ts} +1 -1
- package/dist/cjs/entity-changes.d.ts.map +1 -0
- package/dist/cjs/entity-changes.js +136 -0
- package/dist/cjs/entity-changes.js.map +1 -0
- package/dist/{entity-schema-registry.d.ts → cjs/entity-schema-registry.d.ts} +2 -2
- package/dist/cjs/entity-schema-registry.d.ts.map +1 -0
- package/dist/cjs/entity-schema-registry.js +370 -0
- package/dist/cjs/entity-schema-registry.js.map +1 -0
- package/dist/{entity.d.ts → cjs/entity.d.ts} +2 -2
- package/dist/cjs/entity.d.ts.map +1 -0
- package/dist/cjs/entity.js +11 -0
- package/dist/cjs/entity.js.map +1 -0
- package/dist/cjs/exceptions.d.ts.map +1 -0
- package/dist/cjs/exceptions.js +365 -0
- package/dist/cjs/exceptions.js.map +1 -0
- package/dist/cjs/id.d.ts.map +1 -0
- package/dist/cjs/id.js +80 -0
- package/dist/cjs/id.js.map +1 -0
- package/dist/{index.d.ts → cjs/index.d.ts} +16 -16
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +47 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/mapper.d.ts.map +1 -0
- package/dist/cjs/mapper.js +7 -0
- package/dist/cjs/mapper.js.map +1 -0
- package/dist/{paginated-result.d.ts → cjs/paginated-result.d.ts} +2 -2
- package/dist/cjs/paginated-result.d.ts.map +1 -0
- package/dist/cjs/paginated-result.js +194 -0
- package/dist/cjs/paginated-result.js.map +1 -0
- package/dist/{repository → cjs/repository}/base-repository.d.ts +4 -4
- package/dist/cjs/repository/base-repository.d.ts.map +1 -0
- package/dist/cjs/repository/base-repository.js +16 -0
- package/dist/cjs/repository/base-repository.js.map +1 -0
- package/dist/cjs/repository/index.d.ts +4 -0
- package/dist/cjs/repository/index.d.ts.map +1 -0
- package/dist/cjs/repository/index.js +24 -0
- package/dist/cjs/repository/index.js.map +1 -0
- package/dist/{repository → cjs/repository}/unit-of-work.d.ts +1 -1
- package/dist/cjs/repository/unit-of-work.d.ts.map +1 -0
- package/dist/cjs/repository/unit-of-work.js +60 -0
- package/dist/cjs/repository/unit-of-work.js.map +1 -0
- package/dist/cjs/types/change-tracker.d.ts +226 -0
- package/dist/cjs/types/change-tracker.d.ts.map +1 -0
- package/dist/cjs/types/change-tracker.js +3 -0
- package/dist/cjs/types/change-tracker.js.map +1 -0
- package/dist/cjs/types/criteria.d.ts +60 -0
- package/dist/cjs/types/criteria.d.ts.map +1 -0
- package/dist/cjs/types/criteria.js +20 -0
- package/dist/cjs/types/criteria.js.map +1 -0
- package/dist/cjs/types/domain-event.d.ts +32 -0
- package/dist/cjs/types/domain-event.d.ts.map +1 -0
- package/dist/cjs/types/domain-event.js +3 -0
- package/dist/{types → cjs/types}/domain-event.js.map +1 -1
- package/dist/{types → cjs/types}/domain.d.ts +2 -2
- package/dist/cjs/types/domain.d.ts.map +1 -0
- package/dist/cjs/types/domain.js +3 -0
- package/dist/{types → cjs/types}/domain.js.map +1 -1
- package/dist/cjs/types/index.d.ts +8 -0
- package/dist/cjs/types/index.d.ts.map +1 -0
- package/dist/cjs/types/index.js +24 -0
- package/dist/cjs/types/index.js.map +1 -0
- package/dist/cjs/types/standard-schema.d.ts.map +1 -0
- package/dist/cjs/types/standard-schema.js +3 -0
- package/dist/cjs/types/standard-schema.js.map +1 -0
- package/dist/{types → cjs/types}/unit-of-work.d.ts +2 -2
- package/dist/cjs/types/unit-of-work.d.ts.map +1 -0
- package/dist/cjs/types/unit-of-work.js +3 -0
- package/dist/{types → cjs/types}/unit-of-work.js.map +1 -1
- package/dist/{types → cjs/types}/utils.d.ts +1 -1
- package/dist/cjs/types/utils.d.ts.map +1 -0
- package/dist/cjs/types/utils.js +3 -0
- package/dist/{types → cjs/types}/utils.js.map +1 -1
- package/dist/{utils → cjs/utils}/criteria-operator-validation.d.ts +1 -1
- package/dist/cjs/utils/criteria-operator-validation.d.ts.map +1 -0
- package/dist/cjs/utils/criteria-operator-validation.js +171 -0
- package/dist/cjs/utils/criteria-operator-validation.js.map +1 -0
- package/dist/cjs/utils/helpers.d.ts.map +1 -0
- package/dist/cjs/utils/helpers.js +37 -0
- package/dist/cjs/utils/helpers.js.map +1 -0
- package/dist/cjs/validation-error.d.ts.map +1 -0
- package/dist/cjs/validation-error.js +117 -0
- package/dist/cjs/validation-error.js.map +1 -0
- package/dist/{value-object.d.ts → cjs/value-object.d.ts} +2 -2
- package/dist/cjs/value-object.d.ts.map +1 -0
- package/dist/cjs/value-object.js +192 -0
- package/dist/cjs/value-object.js.map +1 -0
- package/dist/esm/aggregate-changes.d.ts +206 -0
- package/dist/esm/aggregate-changes.d.ts.map +1 -0
- package/dist/{aggregate-changes.js → esm/aggregate-changes.js} +1 -1
- package/dist/esm/aggregate-changes.js.map +1 -0
- package/dist/esm/base-entity.d.ts +91 -0
- package/dist/esm/base-entity.d.ts.map +1 -0
- package/dist/{base-entity.js → esm/base-entity.js} +5 -5
- package/dist/esm/base-entity.js.map +1 -0
- package/dist/esm/change-tracker.d.ts +98 -0
- package/dist/esm/change-tracker.d.ts.map +1 -0
- package/dist/{change-tracker.js → esm/change-tracker.js} +4 -4
- package/dist/esm/change-tracker.js.map +1 -0
- package/dist/esm/constants.d.ts +9 -0
- package/dist/esm/constants.d.ts.map +1 -0
- package/dist/esm/constants.js.map +1 -0
- package/dist/esm/criteria.d.ts +62 -0
- package/dist/esm/criteria.d.ts.map +1 -0
- package/dist/{criteria.js → esm/criteria.js} +3 -3
- package/dist/esm/criteria.js.map +1 -0
- package/dist/esm/crypto.d.ts +3 -0
- package/dist/esm/crypto.d.ts.map +1 -0
- package/dist/esm/crypto.js.map +1 -0
- package/dist/esm/domain-event-bus.d.ts +56 -0
- package/dist/esm/domain-event-bus.d.ts.map +1 -0
- package/dist/esm/domain-event-bus.js.map +1 -0
- package/dist/esm/domain-event.d.ts +25 -0
- package/dist/esm/domain-event.d.ts.map +1 -0
- package/dist/{domain-event.js → esm/domain-event.js} +1 -1
- package/dist/esm/domain-event.js.map +1 -0
- package/dist/esm/entity-changes.d.ts +84 -0
- package/dist/esm/entity-changes.d.ts.map +1 -0
- package/dist/esm/entity-changes.js.map +1 -0
- package/dist/esm/entity-schema-registry.d.ts +282 -0
- package/dist/esm/entity-schema-registry.d.ts.map +1 -0
- package/dist/{entity-schema-registry.js → esm/entity-schema-registry.js} +5 -5
- package/dist/esm/entity-schema-registry.js.map +1 -0
- package/dist/esm/entity.d.ts +7 -0
- package/dist/esm/entity.d.ts.map +1 -0
- package/dist/{entity.js → esm/entity.js} +1 -1
- package/dist/esm/entity.js.map +1 -0
- package/dist/esm/exceptions.d.ts +251 -0
- package/dist/esm/exceptions.d.ts.map +1 -0
- package/dist/esm/exceptions.js.map +1 -0
- package/dist/esm/id.d.ts +52 -0
- package/dist/esm/id.d.ts.map +1 -0
- package/dist/{id.js → esm/id.js} +1 -1
- package/dist/esm/id.js.map +1 -0
- package/dist/esm/index.d.ts +17 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/mapper.d.ts +4 -0
- package/dist/esm/mapper.d.ts.map +1 -0
- package/dist/esm/mapper.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/paginated-result.d.ts +62 -0
- package/dist/esm/paginated-result.d.ts.map +1 -0
- package/dist/{paginated-result.js → esm/paginated-result.js} +1 -1
- package/dist/esm/paginated-result.js.map +1 -0
- package/dist/esm/repository/base-repository.d.ts +28 -0
- package/dist/esm/repository/base-repository.d.ts.map +1 -0
- package/dist/esm/repository/base-repository.js.map +1 -0
- package/dist/esm/repository/index.d.ts +4 -0
- package/dist/esm/repository/index.d.ts.map +1 -0
- package/dist/esm/repository/index.js +4 -0
- package/dist/esm/repository/index.js.map +1 -0
- package/dist/esm/repository/unit-of-work.d.ts +34 -0
- package/dist/esm/repository/unit-of-work.d.ts.map +1 -0
- package/dist/esm/repository/unit-of-work.js.map +1 -0
- package/dist/esm/types/change-tracker.d.ts +226 -0
- package/dist/esm/types/change-tracker.d.ts.map +1 -0
- package/dist/esm/types/change-tracker.js.map +1 -0
- package/dist/esm/types/criteria.d.ts +60 -0
- package/dist/esm/types/criteria.d.ts.map +1 -0
- package/dist/esm/types/criteria.js.map +1 -0
- package/dist/esm/types/domain-event.d.ts +32 -0
- package/dist/esm/types/domain-event.d.ts.map +1 -0
- package/dist/esm/types/domain-event.js.map +1 -0
- package/dist/esm/types/domain.d.ts +29 -0
- package/dist/esm/types/domain.d.ts.map +1 -0
- package/dist/esm/types/domain.js.map +1 -0
- package/dist/esm/types/index.d.ts +8 -0
- package/dist/esm/types/index.d.ts.map +1 -0
- package/dist/esm/types/index.js +8 -0
- package/dist/esm/types/index.js.map +1 -0
- package/dist/esm/types/standard-schema.d.ts +15 -0
- package/dist/esm/types/standard-schema.d.ts.map +1 -0
- package/dist/esm/types/standard-schema.js.map +1 -0
- package/dist/esm/types/unit-of-work.d.ts +39 -0
- package/dist/esm/types/unit-of-work.d.ts.map +1 -0
- package/dist/esm/types/unit-of-work.js.map +1 -0
- package/dist/esm/types/utils.d.ts +13 -0
- package/dist/esm/types/utils.d.ts.map +1 -0
- package/dist/esm/types/utils.js.map +1 -0
- package/dist/esm/utils/criteria-operator-validation.d.ts +6 -0
- package/dist/esm/utils/criteria-operator-validation.d.ts.map +1 -0
- package/dist/{utils → esm/utils}/criteria-operator-validation.js +1 -1
- package/dist/esm/utils/criteria-operator-validation.js.map +1 -0
- package/dist/esm/utils/helpers.d.ts +3 -0
- package/dist/esm/utils/helpers.d.ts.map +1 -0
- package/dist/esm/utils/helpers.js.map +1 -0
- package/dist/esm/validation-error.d.ts +56 -0
- package/dist/esm/validation-error.d.ts.map +1 -0
- package/dist/esm/validation-error.js.map +1 -0
- package/dist/esm/value-object.d.ts +96 -0
- package/dist/esm/value-object.d.ts.map +1 -0
- package/dist/{value-object.js → esm/value-object.js} +3 -3
- package/dist/esm/value-object.js.map +1 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/tsconfig.types.tsbuildinfo +1 -0
- package/dist/types/aggregate-changes.d.ts +206 -0
- package/dist/types/aggregate-changes.d.ts.map +1 -0
- package/dist/types/base-entity.d.ts +91 -0
- package/dist/types/base-entity.d.ts.map +1 -0
- package/dist/types/change-tracker.d.ts +90 -218
- package/dist/types/change-tracker.d.ts.map +1 -1
- package/dist/types/constants.d.ts +9 -0
- package/dist/types/constants.d.ts.map +1 -0
- package/dist/types/criteria.d.ts +59 -57
- package/dist/types/criteria.d.ts.map +1 -1
- package/dist/types/crypto.d.ts +3 -0
- package/dist/types/crypto.d.ts.map +1 -0
- package/dist/types/domain-event-bus.d.ts +56 -0
- package/dist/types/domain-event-bus.d.ts.map +1 -0
- package/dist/types/domain-event.d.ts +14 -21
- package/dist/types/domain-event.d.ts.map +1 -1
- package/dist/types/entity-changes.d.ts +84 -0
- package/dist/types/entity-changes.d.ts.map +1 -0
- package/dist/types/entity-schema-registry.d.ts +282 -0
- package/dist/types/entity-schema-registry.d.ts.map +1 -0
- package/dist/types/entity.d.ts +7 -0
- package/dist/types/entity.d.ts.map +1 -0
- package/dist/types/exceptions.d.ts +251 -0
- package/dist/types/exceptions.d.ts.map +1 -0
- package/dist/types/id.d.ts +52 -0
- package/dist/types/id.d.ts.map +1 -0
- package/dist/types/index.d.ts +16 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/mapper.d.ts +4 -0
- package/dist/types/mapper.d.ts.map +1 -0
- package/dist/types/paginated-result.d.ts +62 -0
- package/dist/types/paginated-result.d.ts.map +1 -0
- package/dist/types/repository/base-repository.d.ts +28 -0
- package/dist/types/repository/base-repository.d.ts.map +1 -0
- package/dist/types/repository/index.d.ts +4 -0
- package/dist/types/repository/index.d.ts.map +1 -0
- package/dist/types/repository/unit-of-work.d.ts +34 -0
- package/dist/types/repository/unit-of-work.d.ts.map +1 -0
- package/dist/types/types/change-tracker.d.ts +226 -0
- package/dist/types/types/change-tracker.d.ts.map +1 -0
- package/dist/types/types/criteria.d.ts +60 -0
- package/dist/types/types/criteria.d.ts.map +1 -0
- package/dist/types/types/domain-event.d.ts +32 -0
- package/dist/types/types/domain-event.d.ts.map +1 -0
- package/dist/types/types/domain.d.ts +29 -0
- package/dist/types/types/domain.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +8 -0
- package/dist/types/types/index.d.ts.map +1 -0
- package/dist/types/types/standard-schema.d.ts +15 -0
- package/dist/types/types/standard-schema.d.ts.map +1 -0
- package/dist/types/types/unit-of-work.d.ts +39 -0
- package/dist/types/types/unit-of-work.d.ts.map +1 -0
- package/dist/types/types/utils.d.ts +13 -0
- package/dist/types/types/utils.d.ts.map +1 -0
- package/dist/types/utils/criteria-operator-validation.d.ts +6 -0
- package/dist/types/utils/criteria-operator-validation.d.ts.map +1 -0
- package/dist/types/utils/helpers.d.ts +3 -0
- package/dist/types/utils/helpers.d.ts.map +1 -0
- package/dist/types/validation-error.d.ts +56 -0
- package/dist/types/validation-error.d.ts.map +1 -0
- package/dist/types/value-object.d.ts +96 -0
- package/dist/types/value-object.d.ts.map +1 -0
- package/package.json +13 -8
- package/dist/aggregate-changes.d.ts.map +0 -1
- package/dist/aggregate-changes.js.map +0 -1
- package/dist/base-entity.d.ts.map +0 -1
- package/dist/base-entity.js.map +0 -1
- package/dist/change-tracker.d.ts.map +0 -1
- package/dist/change-tracker.js.map +0 -1
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js.map +0 -1
- package/dist/criteria.d.ts.map +0 -1
- package/dist/crypto.d.ts.map +0 -1
- package/dist/crypto.js.map +0 -1
- package/dist/domain-event-bus.d.ts.map +0 -1
- package/dist/domain-event-bus.js.map +0 -1
- package/dist/domain-event.d.ts.map +0 -1
- package/dist/domain-event.js.map +0 -1
- package/dist/entity-changes.d.ts.map +0 -1
- package/dist/entity-changes.js.map +0 -1
- package/dist/entity-schema-registry.d.ts.map +0 -1
- package/dist/entity-schema-registry.js.map +0 -1
- package/dist/entity.d.ts.map +0 -1
- package/dist/entity.js.map +0 -1
- package/dist/exceptions.d.ts.map +0 -1
- package/dist/exceptions.js.map +0 -1
- package/dist/id.d.ts.map +0 -1
- package/dist/id.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -16
- package/dist/index.js.map +0 -1
- package/dist/mapper.d.ts.map +0 -1
- package/dist/mapper.js.map +0 -1
- package/dist/paginated-result.d.ts.map +0 -1
- package/dist/paginated-result.js.map +0 -1
- package/dist/repository/base-repository.d.ts.map +0 -1
- package/dist/repository/base-repository.js.map +0 -1
- package/dist/repository/index.d.ts +0 -4
- package/dist/repository/index.d.ts.map +0 -1
- package/dist/repository/index.js +0 -4
- package/dist/repository/index.js.map +0 -1
- package/dist/repository/unit-of-work.d.ts.map +0 -1
- package/dist/repository/unit-of-work.js.map +0 -1
- package/dist/types/change-tracker.js.map +0 -1
- package/dist/types/criteria.js.map +0 -1
- package/dist/types/domain.d.ts.map +0 -1
- package/dist/types/index.js +0 -8
- package/dist/types/index.js.map +0 -1
- package/dist/types/standard-schema.d.ts.map +0 -1
- package/dist/types/standard-schema.js.map +0 -1
- package/dist/types/unit-of-work.d.ts.map +0 -1
- package/dist/types/utils.d.ts.map +0 -1
- package/dist/utils/criteria-operator-validation.d.ts.map +0 -1
- package/dist/utils/criteria-operator-validation.js.map +0 -1
- package/dist/utils/helpers.d.ts.map +0 -1
- package/dist/utils/helpers.js.map +0 -1
- package/dist/validation-error.d.ts.map +0 -1
- package/dist/validation-error.js.map +0 -1
- package/dist/value-object.d.ts.map +0 -1
- package/dist/value-object.js.map +0 -1
- /package/dist/{constants.d.ts → cjs/constants.d.ts} +0 -0
- /package/dist/{crypto.d.ts → cjs/crypto.d.ts} +0 -0
- /package/dist/{domain-event-bus.d.ts → cjs/domain-event-bus.d.ts} +0 -0
- /package/dist/{exceptions.d.ts → cjs/exceptions.d.ts} +0 -0
- /package/dist/{id.d.ts → cjs/id.d.ts} +0 -0
- /package/dist/{mapper.d.ts → cjs/mapper.d.ts} +0 -0
- /package/dist/{types → cjs/types}/standard-schema.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/helpers.d.ts +0 -0
- /package/dist/{validation-error.d.ts → cjs/validation-error.d.ts} +0 -0
- /package/dist/{constants.js → esm/constants.js} +0 -0
- /package/dist/{crypto.js → esm/crypto.js} +0 -0
- /package/dist/{domain-event-bus.js → esm/domain-event-bus.js} +0 -0
- /package/dist/{entity-changes.js → esm/entity-changes.js} +0 -0
- /package/dist/{exceptions.js → esm/exceptions.js} +0 -0
- /package/dist/{mapper.js → esm/mapper.js} +0 -0
- /package/dist/{repository → esm/repository}/base-repository.js +0 -0
- /package/dist/{repository → esm/repository}/unit-of-work.js +0 -0
- /package/dist/{types → esm/types}/change-tracker.js +0 -0
- /package/dist/{types → esm/types}/criteria.js +0 -0
- /package/dist/{types → esm/types}/domain-event.js +0 -0
- /package/dist/{types → esm/types}/domain.js +0 -0
- /package/dist/{types → esm/types}/standard-schema.js +0 -0
- /package/dist/{types → esm/types}/unit-of-work.js +0 -0
- /package/dist/{types → esm/types}/utils.js +0 -0
- /package/dist/{utils → esm/utils}/helpers.js +0 -0
- /package/dist/{validation-error.js → esm/validation-error.js} +0 -0
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ChangeTracker = void 0;
|
|
4
|
+
const id_js_1 = require("./id.js");
|
|
5
|
+
const entity_js_1 = require("./entity.js");
|
|
6
|
+
const value_object_js_1 = require("./value-object.js");
|
|
7
|
+
const aggregate_changes_js_1 = require("./aggregate-changes.js");
|
|
8
|
+
/**
|
|
9
|
+
* Tracks changes in Aggregates using Proxy.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - Tracks changes in primitive properties
|
|
13
|
+
* - Tracks changes in nested entities (1:1)
|
|
14
|
+
* - Tracks changes in collections (1:N)
|
|
15
|
+
* - Supports Value Objects with identityKey
|
|
16
|
+
* - Calculates depth automatically
|
|
17
|
+
* - Generates AggregateChanges for persistence
|
|
18
|
+
* - Supports validation on change via onChangeValidator
|
|
19
|
+
*/
|
|
20
|
+
class ChangeTracker {
|
|
21
|
+
target;
|
|
22
|
+
rootEntityName;
|
|
23
|
+
path;
|
|
24
|
+
depth;
|
|
25
|
+
parentId;
|
|
26
|
+
parentEntity;
|
|
27
|
+
rootTracker;
|
|
28
|
+
history = [];
|
|
29
|
+
originalValues = new Map();
|
|
30
|
+
trackedArrays = new Map();
|
|
31
|
+
trackedEntities = new Map();
|
|
32
|
+
onChangeValidator;
|
|
33
|
+
constructor(target, rootEntityName, path = "", depth = 0,
|
|
34
|
+
// @ts-expect-error - This is a private property
|
|
35
|
+
parentId,
|
|
36
|
+
// @ts-expect-error - This is a private property
|
|
37
|
+
parentEntity, rootTracker) {
|
|
38
|
+
this.target = target;
|
|
39
|
+
this.rootEntityName = rootEntityName;
|
|
40
|
+
this.path = path;
|
|
41
|
+
this.depth = depth;
|
|
42
|
+
this.parentId = parentId;
|
|
43
|
+
this.parentEntity = parentEntity;
|
|
44
|
+
this.rootTracker = rootTracker;
|
|
45
|
+
if (!rootTracker) {
|
|
46
|
+
this.rootTracker = this;
|
|
47
|
+
}
|
|
48
|
+
this.captureInitialState();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Sets a validator callback that will be called on every property change.
|
|
52
|
+
* The validator can:
|
|
53
|
+
* - Return false to reject the change (value will be reverted)
|
|
54
|
+
* - Throw an error to reject the change with an error
|
|
55
|
+
* - Return true/undefined to accept the change
|
|
56
|
+
*/
|
|
57
|
+
setOnChangeValidator(validator) {
|
|
58
|
+
this.getRootTracker().onChangeValidator = validator;
|
|
59
|
+
}
|
|
60
|
+
captureInitialState() {
|
|
61
|
+
if (this.depth > 0)
|
|
62
|
+
return;
|
|
63
|
+
this.captureEntityState(this.target, this.rootEntityName, "", 0);
|
|
64
|
+
}
|
|
65
|
+
captureEntityState(obj, entityName, path, depth, parentId, parentEntity) {
|
|
66
|
+
if (!obj || typeof obj !== "object")
|
|
67
|
+
return;
|
|
68
|
+
const id = this.getEntityId(obj);
|
|
69
|
+
const key = path || "root";
|
|
70
|
+
this.trackedEntities.set(key, {
|
|
71
|
+
entity: obj,
|
|
72
|
+
metadata: {
|
|
73
|
+
entityName,
|
|
74
|
+
depth,
|
|
75
|
+
parentId,
|
|
76
|
+
parentEntity,
|
|
77
|
+
path,
|
|
78
|
+
},
|
|
79
|
+
originalState: this.deepClone(obj),
|
|
80
|
+
});
|
|
81
|
+
const propsToScan = obj.props || obj;
|
|
82
|
+
for (const [propName, value] of Object.entries(propsToScan)) {
|
|
83
|
+
if (propName === "id")
|
|
84
|
+
continue;
|
|
85
|
+
const propPath = path ? `${path}.${propName}` : propName;
|
|
86
|
+
if (Array.isArray(value)) {
|
|
87
|
+
this.captureArrayState(value, propPath, depth + 1, id, entityName);
|
|
88
|
+
}
|
|
89
|
+
else if (this.isEntityOrVO(value)) {
|
|
90
|
+
const nestedName = this.getEntityName(value);
|
|
91
|
+
this.captureEntityState(value, nestedName, propPath, depth + 1, id, entityName);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
captureArrayState(arr, path, depth, parentId, parentEntity) {
|
|
96
|
+
const entityName = arr.length > 0 ? this.getEntityName(arr[0]) : "Unknown";
|
|
97
|
+
this.trackedArrays.set(path, {
|
|
98
|
+
cloned: this.cloneArray(arr),
|
|
99
|
+
original: arr.slice(),
|
|
100
|
+
metadata: {
|
|
101
|
+
entityName,
|
|
102
|
+
depth,
|
|
103
|
+
parentId,
|
|
104
|
+
parentEntity,
|
|
105
|
+
path,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
arr.forEach((item, index) => {
|
|
109
|
+
if (this.isEntityOrVO(item)) {
|
|
110
|
+
const itemPath = `${path}[${index}]`;
|
|
111
|
+
this.captureEntityState(item, this.getEntityName(item), itemPath, depth, parentId, parentEntity);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
createProxy() {
|
|
116
|
+
const handler = {
|
|
117
|
+
get: (target, prop, receiver) => {
|
|
118
|
+
const value = Reflect.get(target, prop, receiver);
|
|
119
|
+
if (this.shouldSkipProperty(prop)) {
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
if (typeof value === "function") {
|
|
123
|
+
return value.bind(target);
|
|
124
|
+
}
|
|
125
|
+
const currentPath = this.buildPath(String(prop));
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
return this.createArrayProxy(value, currentPath);
|
|
128
|
+
}
|
|
129
|
+
if (this.isEntityOrVO(value)) {
|
|
130
|
+
const nestedTracker = new ChangeTracker(value, this.getEntityName(value), currentPath, this.depth + 1, this.getEntityId(this.target), this.rootEntityName, this.rootTracker);
|
|
131
|
+
return nestedTracker.createProxy();
|
|
132
|
+
}
|
|
133
|
+
return value;
|
|
134
|
+
},
|
|
135
|
+
set: (target, prop, newValue, receiver) => {
|
|
136
|
+
const currentPath = this.buildPath(String(prop));
|
|
137
|
+
const oldValue = Reflect.get(target, prop, receiver);
|
|
138
|
+
if (!Array.isArray(newValue) && oldValue === newValue) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
const rootTracker = this.getRootTracker();
|
|
142
|
+
if (rootTracker.onChangeValidator) {
|
|
143
|
+
try {
|
|
144
|
+
const result = rootTracker.onChangeValidator(currentPath, newValue);
|
|
145
|
+
if (result === false) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (!rootTracker.originalValues.has(currentPath)) {
|
|
154
|
+
rootTracker.originalValues.set(currentPath, oldValue);
|
|
155
|
+
}
|
|
156
|
+
rootTracker.history.push({
|
|
157
|
+
path: currentPath,
|
|
158
|
+
previousValue: oldValue,
|
|
159
|
+
currentValue: newValue,
|
|
160
|
+
timestamp: Date.now(),
|
|
161
|
+
});
|
|
162
|
+
const result = Reflect.set(target, prop, newValue, receiver);
|
|
163
|
+
if (Array.isArray(newValue)) {
|
|
164
|
+
this.handleArrayAssignment(currentPath, oldValue);
|
|
165
|
+
}
|
|
166
|
+
else if (this.isEntityOrVO(newValue) || this.isEntityOrVO(oldValue)) {
|
|
167
|
+
this.handleEntityChange(currentPath, oldValue, newValue);
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
const proxy = new Proxy(this.target, handler);
|
|
173
|
+
Object.defineProperty(proxy, "__isProxy", { value: true, writable: false });
|
|
174
|
+
return proxy;
|
|
175
|
+
}
|
|
176
|
+
createArrayProxy(array, path) {
|
|
177
|
+
const tracker = this;
|
|
178
|
+
const rootTracker = this.getRootTracker();
|
|
179
|
+
if (!rootTracker.trackedArrays.has(path)) {
|
|
180
|
+
const parentId = this.getEntityId(this.target);
|
|
181
|
+
rootTracker.captureArrayState(array, path, this.depth + 1, parentId, this.rootEntityName);
|
|
182
|
+
}
|
|
183
|
+
return new Proxy(array, {
|
|
184
|
+
get(target, prop, receiver) {
|
|
185
|
+
const value = Reflect.get(target, prop, receiver);
|
|
186
|
+
if (typeof value === "function") {
|
|
187
|
+
const mutatingMethods = [
|
|
188
|
+
"push",
|
|
189
|
+
"pop",
|
|
190
|
+
"shift",
|
|
191
|
+
"unshift",
|
|
192
|
+
"splice",
|
|
193
|
+
"sort",
|
|
194
|
+
"reverse",
|
|
195
|
+
];
|
|
196
|
+
if (mutatingMethods.includes(String(prop))) {
|
|
197
|
+
return function (...args) {
|
|
198
|
+
const oldArray = target.slice();
|
|
199
|
+
if (rootTracker.onChangeValidator) {
|
|
200
|
+
try {
|
|
201
|
+
const result = rootTracker.onChangeValidator(path, [
|
|
202
|
+
...oldArray,
|
|
203
|
+
...args,
|
|
204
|
+
]);
|
|
205
|
+
if (result === false) {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const result = value.apply(target, args);
|
|
214
|
+
rootTracker.history.push({
|
|
215
|
+
path,
|
|
216
|
+
previousValue: oldArray,
|
|
217
|
+
currentValue: target.slice(),
|
|
218
|
+
timestamp: Date.now(),
|
|
219
|
+
});
|
|
220
|
+
return result;
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return value.bind(target);
|
|
224
|
+
}
|
|
225
|
+
if (!isNaN(Number(prop)) && tracker.isEntityOrVO(value)) {
|
|
226
|
+
const nestedPath = `${path}[${String(prop)}]`;
|
|
227
|
+
const nestedTracker = new ChangeTracker(value, tracker.getEntityName(value), nestedPath, tracker.depth + 1, tracker.getEntityId(tracker.target), tracker.rootEntityName, rootTracker);
|
|
228
|
+
return nestedTracker.createProxy();
|
|
229
|
+
}
|
|
230
|
+
return value;
|
|
231
|
+
},
|
|
232
|
+
set(target, prop, newValue, receiver) {
|
|
233
|
+
if (!isNaN(Number(prop))) {
|
|
234
|
+
const oldArray = target.slice();
|
|
235
|
+
if (rootTracker.onChangeValidator) {
|
|
236
|
+
try {
|
|
237
|
+
const result = rootTracker.onChangeValidator(path, newValue);
|
|
238
|
+
if (result === false) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const result = Reflect.set(target, prop, newValue, receiver);
|
|
247
|
+
rootTracker.history.push({
|
|
248
|
+
path,
|
|
249
|
+
previousValue: oldArray,
|
|
250
|
+
currentValue: target.slice(),
|
|
251
|
+
timestamp: Date.now(),
|
|
252
|
+
});
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
return Reflect.set(target, prop, newValue, receiver);
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Returns all detected changes as AggregateChanges.
|
|
261
|
+
*/
|
|
262
|
+
getChanges() {
|
|
263
|
+
const changes = new aggregate_changes_js_1.AggregateChanges();
|
|
264
|
+
const rootTracker = this.getRootTracker();
|
|
265
|
+
this.analyzeRootChanges(changes, rootTracker);
|
|
266
|
+
this.analyzeCollectionChanges(changes, rootTracker);
|
|
267
|
+
this.analyzeEntityChanges(changes, rootTracker);
|
|
268
|
+
return changes;
|
|
269
|
+
}
|
|
270
|
+
analyzeRootChanges(changes, rootTracker) {
|
|
271
|
+
const changedFields = {};
|
|
272
|
+
let hasChanges = false;
|
|
273
|
+
for (const [path, originalValue] of rootTracker.originalValues) {
|
|
274
|
+
if (path.includes(".") || path.includes("["))
|
|
275
|
+
continue;
|
|
276
|
+
const currentValue = this.target[path];
|
|
277
|
+
if (!this.isEqual(originalValue, currentValue)) {
|
|
278
|
+
changedFields[path] = currentValue;
|
|
279
|
+
hasChanges = true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (hasChanges) {
|
|
283
|
+
const id = this.getEntityId(this.target);
|
|
284
|
+
if (id) {
|
|
285
|
+
changes.addUpdate(this.rootEntityName, id, this.target, changedFields, 0);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
analyzeCollectionChanges(changes, rootTracker) {
|
|
290
|
+
const allTrackedArrays = new Map();
|
|
291
|
+
const processedArrays = new Set();
|
|
292
|
+
for (const [path, arrayState] of rootTracker.trackedArrays) {
|
|
293
|
+
const currentArray = this.getValueAtPath(this.target, path);
|
|
294
|
+
if (Array.isArray(currentArray) && !processedArrays.has(currentArray)) {
|
|
295
|
+
allTrackedArrays.set(path, arrayState);
|
|
296
|
+
processedArrays.add(currentArray);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
this.collectNestedArrays(this.target, "", allTrackedArrays, processedArrays);
|
|
300
|
+
for (const [path, arrayState] of allTrackedArrays) {
|
|
301
|
+
const currentArray = this.getValueAtPath(this.target, path);
|
|
302
|
+
if (!Array.isArray(currentArray))
|
|
303
|
+
continue;
|
|
304
|
+
const { created, updated, deleted } = this.detectArrayChanges(arrayState.cloned, arrayState.original, currentArray);
|
|
305
|
+
const { depth, parentId, parentEntity } = arrayState.metadata;
|
|
306
|
+
const relationField = this.extractRelationField(path);
|
|
307
|
+
for (const item of created) {
|
|
308
|
+
const itemEntityName = this.getEntityName(item);
|
|
309
|
+
changes.addCreate(itemEntityName, item, depth, parentId, parentEntity, relationField);
|
|
310
|
+
this.markNestedItemsAsCreated(item, depth, changes);
|
|
311
|
+
}
|
|
312
|
+
for (const item of updated) {
|
|
313
|
+
const id = this.getEntityId(item);
|
|
314
|
+
if (id) {
|
|
315
|
+
const original = arrayState.cloned.find((o) => this.getEntityId(o) === id);
|
|
316
|
+
const changedFields = this.detectChangedFields(original, item);
|
|
317
|
+
if (Object.keys(changedFields).length > 0) {
|
|
318
|
+
const itemEntityName = this.getEntityName(item);
|
|
319
|
+
changes.addUpdate(itemEntityName, id, item, changedFields, depth);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
for (const item of deleted) {
|
|
324
|
+
const id = this.getEntityId(item);
|
|
325
|
+
const key = this.getItemKey(item);
|
|
326
|
+
if (id || key) {
|
|
327
|
+
const itemEntityName = this.getEntityName(item);
|
|
328
|
+
const deleteId = id || key;
|
|
329
|
+
changes.addDelete(itemEntityName, deleteId, item, depth, relationField, parentId, parentEntity);
|
|
330
|
+
this.markNestedItemsAsDeleted(item, depth, changes, rootTracker);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Recursively marks all nested items as created when a parent is created.
|
|
337
|
+
*/
|
|
338
|
+
markNestedItemsAsCreated(item, parentDepth, changes) {
|
|
339
|
+
if (!item || typeof item !== "object")
|
|
340
|
+
return;
|
|
341
|
+
const propsToScan = item.props || item;
|
|
342
|
+
const parentId = this.getEntityId(item);
|
|
343
|
+
const parentEntity = this.getEntityName(item);
|
|
344
|
+
for (const [propName, value] of Object.entries(propsToScan)) {
|
|
345
|
+
if (propName === "id")
|
|
346
|
+
continue;
|
|
347
|
+
if (Array.isArray(value)) {
|
|
348
|
+
const relationField = propName;
|
|
349
|
+
for (const child of value) {
|
|
350
|
+
if (this.isEntityOrVO(child)) {
|
|
351
|
+
const childEntityName = this.getEntityName(child);
|
|
352
|
+
changes.addCreate(childEntityName, child, parentDepth + 1, parentId, parentEntity, relationField);
|
|
353
|
+
this.markNestedItemsAsCreated(child, parentDepth + 1, changes);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else if (this.isEntityOrVO(value)) {
|
|
358
|
+
const childEntityName = this.getEntityName(value);
|
|
359
|
+
changes.addCreate(childEntityName, value, parentDepth + 1, parentId, parentEntity, propName);
|
|
360
|
+
this.markNestedItemsAsCreated(value, parentDepth + 1, changes);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Recursively marks all nested items as deleted when a parent is deleted.
|
|
366
|
+
* Uses the original captured state to find nested items.
|
|
367
|
+
*/
|
|
368
|
+
markNestedItemsAsDeleted(item, parentDepth, changes, rootTracker) {
|
|
369
|
+
if (!item || typeof item !== "object")
|
|
370
|
+
return;
|
|
371
|
+
const itemId = this.getEntityId(item);
|
|
372
|
+
if (!itemId)
|
|
373
|
+
return;
|
|
374
|
+
for (const [path, arrayState] of rootTracker.trackedArrays) {
|
|
375
|
+
if (arrayState.metadata.parentId === itemId) {
|
|
376
|
+
const relationField = this.extractRelationField(path);
|
|
377
|
+
const parentEntity = arrayState.metadata.parentEntity;
|
|
378
|
+
const parentId = arrayState.metadata.parentId;
|
|
379
|
+
for (const nestedItem of arrayState.cloned) {
|
|
380
|
+
const id = typeof nestedItem === "object" && nestedItem !== null
|
|
381
|
+
? nestedItem.id
|
|
382
|
+
: undefined;
|
|
383
|
+
if (id) {
|
|
384
|
+
const entityName = arrayState.metadata.entityName;
|
|
385
|
+
changes.addDelete(entityName, id, nestedItem, parentDepth + 1, relationField, parentEntity, parentId);
|
|
386
|
+
this.markNestedJsonItemAsDeleted(id, parentDepth + 1, changes, rootTracker);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Recursively marks nested items as deleted from a JSON object.
|
|
394
|
+
* This is used when processing cloned (JSON) state.
|
|
395
|
+
*/
|
|
396
|
+
markNestedJsonItemAsDeleted(itemId, parentDepth, changes, rootTracker) {
|
|
397
|
+
for (const [path, arrayState] of rootTracker.trackedArrays) {
|
|
398
|
+
if (arrayState.metadata.parentId === itemId) {
|
|
399
|
+
const relationField = this.extractRelationField(path);
|
|
400
|
+
for (const nestedJsonItem of arrayState.cloned) {
|
|
401
|
+
if (typeof nestedJsonItem !== "object" || nestedJsonItem === null)
|
|
402
|
+
continue;
|
|
403
|
+
const nestedId = nestedJsonItem.id;
|
|
404
|
+
const entityName = arrayState.metadata.entityName;
|
|
405
|
+
const parentEntity = arrayState.metadata.parentEntity;
|
|
406
|
+
const parentId = arrayState.metadata.parentId;
|
|
407
|
+
if (nestedId) {
|
|
408
|
+
changes.addDelete(entityName, nestedId, nestedJsonItem, parentDepth + 1, relationField, parentId, parentEntity);
|
|
409
|
+
this.markNestedJsonItemAsDeleted(nestedId, parentDepth + 1, changes, rootTracker);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
const key = this.extractIdentityKeyFromJson(nestedJsonItem, arrayState.original);
|
|
413
|
+
if (key) {
|
|
414
|
+
changes.addDelete(entityName, key, nestedJsonItem, parentDepth + 1, relationField, parentId, parentEntity);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Extracts identity key from a JSON object by looking at the original ValueObject instances.
|
|
423
|
+
*/
|
|
424
|
+
extractIdentityKeyFromJson(jsonItem, originalArray) {
|
|
425
|
+
for (const originalItem of originalArray) {
|
|
426
|
+
if (this.isEntityOrVO(originalItem)) {
|
|
427
|
+
const originalJson = this.deepClone(originalItem);
|
|
428
|
+
if (JSON.stringify(originalJson) === JSON.stringify(jsonItem)) {
|
|
429
|
+
const key = this.getItemKey(originalItem);
|
|
430
|
+
if (key)
|
|
431
|
+
return key;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (jsonItem.id)
|
|
436
|
+
return jsonItem.id;
|
|
437
|
+
return undefined;
|
|
438
|
+
}
|
|
439
|
+
collectNestedArrays(obj, basePath, allArrays, processedArrays) {
|
|
440
|
+
if (!obj || typeof obj !== "object")
|
|
441
|
+
return;
|
|
442
|
+
for (const [propName, value] of Object.entries(obj)) {
|
|
443
|
+
if (propName === "id" || propName === "proxy" || propName === "_props")
|
|
444
|
+
continue;
|
|
445
|
+
const propPath = basePath ? `${basePath}.${propName}` : propName;
|
|
446
|
+
if (Array.isArray(value)) {
|
|
447
|
+
value.forEach((item, index) => {
|
|
448
|
+
if (this.isEntityOrVO(item)) {
|
|
449
|
+
this.collectNestedArrays(item, `${propPath}[${index}]`, allArrays, processedArrays);
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
else if (this.isEntityOrVO(value)) {
|
|
454
|
+
this.collectNestedArrays(value, propPath, allArrays, processedArrays);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
analyzeEntityChanges(changes, rootTracker) {
|
|
459
|
+
for (const [path, trackedItem] of rootTracker.trackedEntities) {
|
|
460
|
+
if (path === "root")
|
|
461
|
+
continue;
|
|
462
|
+
if (path.includes("["))
|
|
463
|
+
continue;
|
|
464
|
+
const currentValue = this.getValueAtPath(this.target, path);
|
|
465
|
+
const originalValue = trackedItem.originalState;
|
|
466
|
+
const originalEntity = trackedItem.entity;
|
|
467
|
+
const { entityName, depth, parentId, parentEntity } = trackedItem.metadata;
|
|
468
|
+
const relationField = this.extractRelationField(path);
|
|
469
|
+
const state = this.detectEntityChangeState(originalValue, currentValue);
|
|
470
|
+
switch (state) {
|
|
471
|
+
case "created":
|
|
472
|
+
changes.addCreate(entityName, currentValue, depth, parentId, parentEntity, relationField);
|
|
473
|
+
break;
|
|
474
|
+
case "deleted":
|
|
475
|
+
const id = this.getEntityId(originalValue);
|
|
476
|
+
if (id) {
|
|
477
|
+
changes.addDelete(entityName, id, originalEntity, depth, relationField, parentId, parentEntity);
|
|
478
|
+
}
|
|
479
|
+
break;
|
|
480
|
+
case "replaced":
|
|
481
|
+
const oldId = this.getEntityId(originalValue);
|
|
482
|
+
if (oldId) {
|
|
483
|
+
changes.addDelete(entityName, oldId, originalEntity, depth, relationField, parentId, parentEntity);
|
|
484
|
+
}
|
|
485
|
+
changes.addCreate(entityName, currentValue, depth, parentId, parentEntity, relationField);
|
|
486
|
+
break;
|
|
487
|
+
case "updated":
|
|
488
|
+
const updateId = this.getEntityId(currentValue);
|
|
489
|
+
if (updateId) {
|
|
490
|
+
const changedFields = this.detectChangedFields(originalValue, currentValue);
|
|
491
|
+
if (Object.keys(changedFields).length > 0) {
|
|
492
|
+
changes.addUpdate(entityName, updateId, currentValue, changedFields, depth);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
detectEntityChangeState(previous, current) {
|
|
500
|
+
if (previous === null && current !== null) {
|
|
501
|
+
return "created";
|
|
502
|
+
}
|
|
503
|
+
if (previous !== null && current === null) {
|
|
504
|
+
return "deleted";
|
|
505
|
+
}
|
|
506
|
+
if (previous !== null && current !== null) {
|
|
507
|
+
const prevId = this.getEntityId(previous);
|
|
508
|
+
const currId = this.getEntityId(current);
|
|
509
|
+
if (prevId && currId && prevId === currId) {
|
|
510
|
+
return this.hasChanged(previous, current) ? "updated" : "unchanged";
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
return "replaced";
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return "unchanged";
|
|
517
|
+
}
|
|
518
|
+
detectArrayChanges(oldCloned, oldOriginal, newArray) {
|
|
519
|
+
const created = [];
|
|
520
|
+
const updated = [];
|
|
521
|
+
const deleted = [];
|
|
522
|
+
const oldMap = new Map();
|
|
523
|
+
const newMap = new Map();
|
|
524
|
+
oldCloned.forEach((item) => {
|
|
525
|
+
const key = this.getItemKey(item);
|
|
526
|
+
if (key)
|
|
527
|
+
oldMap.set(key, item);
|
|
528
|
+
});
|
|
529
|
+
newArray.forEach((item) => {
|
|
530
|
+
const key = this.getItemKey(item);
|
|
531
|
+
if (key)
|
|
532
|
+
newMap.set(key, item);
|
|
533
|
+
});
|
|
534
|
+
newArray.forEach((item) => {
|
|
535
|
+
const key = this.getItemKey(item);
|
|
536
|
+
if (!key) {
|
|
537
|
+
created.push(item);
|
|
538
|
+
}
|
|
539
|
+
else if (!oldMap.has(key)) {
|
|
540
|
+
created.push(item);
|
|
541
|
+
}
|
|
542
|
+
else if (this.hasChanged(oldMap.get(key), item)) {
|
|
543
|
+
updated.push(item);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
oldOriginal.forEach((item) => {
|
|
547
|
+
const key = this.getItemKey(item);
|
|
548
|
+
if (key && !newMap.has(key)) {
|
|
549
|
+
deleted.push(item);
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
return { created, updated, deleted };
|
|
553
|
+
}
|
|
554
|
+
detectChangedFields(original, current) {
|
|
555
|
+
const changes = {};
|
|
556
|
+
if (!original || !current)
|
|
557
|
+
return changes;
|
|
558
|
+
const origProps = original.props || original;
|
|
559
|
+
const currProps = current.props || current;
|
|
560
|
+
for (const key of Object.keys(currProps)) {
|
|
561
|
+
if (key === "id")
|
|
562
|
+
continue;
|
|
563
|
+
const origValue = origProps[key];
|
|
564
|
+
const currValue = currProps[key];
|
|
565
|
+
if (Array.isArray(currValue) || this.isEntityOrVO(currValue)) {
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
if (!this.isEqual(origValue, currValue)) {
|
|
569
|
+
changes[key] = currValue;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return changes;
|
|
573
|
+
}
|
|
574
|
+
handleArrayAssignment(path, oldValue) {
|
|
575
|
+
const rootTracker = this.getRootTracker();
|
|
576
|
+
if (!rootTracker.trackedArrays.has(path)) {
|
|
577
|
+
const parentId = this.getEntityId(this.target);
|
|
578
|
+
rootTracker.captureArrayState(Array.isArray(oldValue) ? oldValue : [], path, this.depth + 1, parentId, this.rootEntityName);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
handleEntityChange(path, oldValue, newValue) {
|
|
582
|
+
const rootTracker = this.getRootTracker();
|
|
583
|
+
const entityName = newValue
|
|
584
|
+
? this.getEntityName(newValue)
|
|
585
|
+
: this.getEntityName(oldValue);
|
|
586
|
+
const existingTracked = rootTracker.trackedEntities.get(path);
|
|
587
|
+
rootTracker.trackedEntities.set(path, {
|
|
588
|
+
entity: existingTracked?.entity || oldValue,
|
|
589
|
+
metadata: {
|
|
590
|
+
entityName,
|
|
591
|
+
depth: this.depth + 1,
|
|
592
|
+
parentId: this.getEntityId(this.target),
|
|
593
|
+
parentEntity: this.rootEntityName,
|
|
594
|
+
path,
|
|
595
|
+
},
|
|
596
|
+
originalState: existingTracked?.originalState,
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
getRootTracker() {
|
|
600
|
+
return this.rootTracker || this;
|
|
601
|
+
}
|
|
602
|
+
buildPath(prop) {
|
|
603
|
+
return this.path ? `${this.path}.${prop}` : prop;
|
|
604
|
+
}
|
|
605
|
+
shouldSkipProperty(prop) {
|
|
606
|
+
const skipProps = [
|
|
607
|
+
"__isProxy",
|
|
608
|
+
"__tracker",
|
|
609
|
+
"__originalTarget",
|
|
610
|
+
"__path",
|
|
611
|
+
"constructor",
|
|
612
|
+
"prototype",
|
|
613
|
+
];
|
|
614
|
+
return skipProps.includes(String(prop));
|
|
615
|
+
}
|
|
616
|
+
getValueAtPath(obj, path) {
|
|
617
|
+
if (!path)
|
|
618
|
+
return obj;
|
|
619
|
+
const parts = path.split(/[.\[\]]+/).filter(Boolean);
|
|
620
|
+
let current = obj;
|
|
621
|
+
for (const part of parts) {
|
|
622
|
+
if (current === null || current === undefined)
|
|
623
|
+
return undefined;
|
|
624
|
+
const propsToAccess = current.props || current;
|
|
625
|
+
current = propsToAccess[part];
|
|
626
|
+
}
|
|
627
|
+
return current;
|
|
628
|
+
}
|
|
629
|
+
extractRelationField(path) {
|
|
630
|
+
const withoutIndices = path.replace(/\[\d+\]/g, "");
|
|
631
|
+
const parts = withoutIndices.split(".");
|
|
632
|
+
return parts[parts.length - 1];
|
|
633
|
+
}
|
|
634
|
+
getItemKey(item) {
|
|
635
|
+
const id = this.getEntityId(item);
|
|
636
|
+
if (id)
|
|
637
|
+
return id;
|
|
638
|
+
if (item instanceof value_object_js_1.ValueObject && item.hasIdentityKey()) {
|
|
639
|
+
return item.getIdentityKey() || undefined;
|
|
640
|
+
}
|
|
641
|
+
return undefined;
|
|
642
|
+
}
|
|
643
|
+
getEntityId(item) {
|
|
644
|
+
if (!item)
|
|
645
|
+
return undefined;
|
|
646
|
+
if (item.id instanceof id_js_1.Id)
|
|
647
|
+
return item.id.value;
|
|
648
|
+
if (item.id !== undefined)
|
|
649
|
+
return String(item.id);
|
|
650
|
+
return undefined;
|
|
651
|
+
}
|
|
652
|
+
getEntityName(item) {
|
|
653
|
+
if (!item)
|
|
654
|
+
return "Unknown";
|
|
655
|
+
return item.constructor?.name || "Unknown";
|
|
656
|
+
}
|
|
657
|
+
isEntityOrVO(value) {
|
|
658
|
+
if (value === null || value === undefined)
|
|
659
|
+
return false;
|
|
660
|
+
return value instanceof entity_js_1.Entity || value instanceof value_object_js_1.ValueObject;
|
|
661
|
+
}
|
|
662
|
+
isEqual(a, b) {
|
|
663
|
+
if (a === b)
|
|
664
|
+
return true;
|
|
665
|
+
if (a instanceof id_js_1.Id && b instanceof id_js_1.Id)
|
|
666
|
+
return a.value === b.value;
|
|
667
|
+
if (a instanceof Date && b instanceof Date)
|
|
668
|
+
return a.getTime() === b.getTime();
|
|
669
|
+
try {
|
|
670
|
+
return this.hasChanged(a, b) === false;
|
|
671
|
+
}
|
|
672
|
+
catch {
|
|
673
|
+
return this.deepEqual(a, b);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
deepEqual(a, b) {
|
|
677
|
+
if (a === b)
|
|
678
|
+
return true;
|
|
679
|
+
if (a == null || b == null)
|
|
680
|
+
return a === b;
|
|
681
|
+
if (typeof a !== typeof b)
|
|
682
|
+
return false;
|
|
683
|
+
if (typeof a !== "object")
|
|
684
|
+
return a === b;
|
|
685
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
686
|
+
return false;
|
|
687
|
+
if (Array.isArray(a)) {
|
|
688
|
+
if (a.length !== b.length)
|
|
689
|
+
return false;
|
|
690
|
+
for (let i = 0; i < a.length; i++) {
|
|
691
|
+
if (!this.deepEqual(a[i], b[i]))
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
return true;
|
|
695
|
+
}
|
|
696
|
+
const keysA = Object.keys(a).filter((key) => {
|
|
697
|
+
const value = a[key];
|
|
698
|
+
return (typeof value !== "object" ||
|
|
699
|
+
value instanceof Date ||
|
|
700
|
+
value instanceof id_js_1.Id ||
|
|
701
|
+
value === null);
|
|
702
|
+
});
|
|
703
|
+
const keysB = Object.keys(b).filter((key) => {
|
|
704
|
+
const value = b[key];
|
|
705
|
+
return (typeof value !== "object" ||
|
|
706
|
+
value instanceof Date ||
|
|
707
|
+
value instanceof id_js_1.Id ||
|
|
708
|
+
value === null);
|
|
709
|
+
});
|
|
710
|
+
if (keysA.length !== keysB.length)
|
|
711
|
+
return false;
|
|
712
|
+
for (const key of keysA) {
|
|
713
|
+
if (!keysB.includes(key))
|
|
714
|
+
return false;
|
|
715
|
+
if (!this.isEqual(a[key], b[key]))
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
hasChanged(obj1, obj2) {
|
|
721
|
+
const json1 = this.normalizeAndStringify(this.deepClone(obj1));
|
|
722
|
+
const json2 = this.normalizeAndStringify(this.deepClone(obj2));
|
|
723
|
+
return json1 !== json2;
|
|
724
|
+
}
|
|
725
|
+
cloneArray(arr) {
|
|
726
|
+
return arr.map((item) => this.deepClone(item));
|
|
727
|
+
}
|
|
728
|
+
deepClone(obj) {
|
|
729
|
+
if (obj === null || obj === undefined || typeof obj !== "object") {
|
|
730
|
+
return obj;
|
|
731
|
+
}
|
|
732
|
+
if (obj instanceof id_js_1.Id) {
|
|
733
|
+
return obj.value;
|
|
734
|
+
}
|
|
735
|
+
if (typeof obj.toJSON === "function") {
|
|
736
|
+
return obj.toJSON();
|
|
737
|
+
}
|
|
738
|
+
if (Array.isArray(obj)) {
|
|
739
|
+
return obj.map((item) => this.deepClone(item));
|
|
740
|
+
}
|
|
741
|
+
if (obj instanceof Date) {
|
|
742
|
+
return new Date(obj.getTime());
|
|
743
|
+
}
|
|
744
|
+
try {
|
|
745
|
+
return structuredClone(obj);
|
|
746
|
+
}
|
|
747
|
+
catch {
|
|
748
|
+
const cloned = {};
|
|
749
|
+
for (const key in obj) {
|
|
750
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
751
|
+
cloned[key] = this.deepClone(obj[key]);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return cloned;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
normalizeAndStringify(obj) {
|
|
758
|
+
if (obj === null || typeof obj !== "object") {
|
|
759
|
+
return JSON.stringify(obj);
|
|
760
|
+
}
|
|
761
|
+
if (Array.isArray(obj)) {
|
|
762
|
+
return `[${obj
|
|
763
|
+
.map((item) => this.normalizeAndStringify(item))
|
|
764
|
+
.join(",")}]`;
|
|
765
|
+
}
|
|
766
|
+
const keys = Object.keys(obj).sort();
|
|
767
|
+
const parts = keys.map((key) => `"${key}":${this.normalizeAndStringify(obj[key])}`);
|
|
768
|
+
return `{${parts.join(",")}}`;
|
|
769
|
+
}
|
|
770
|
+
getHistory() {
|
|
771
|
+
return [...this.getRootTracker().history];
|
|
772
|
+
}
|
|
773
|
+
clearHistory() {
|
|
774
|
+
const rootTracker = this.getRootTracker();
|
|
775
|
+
rootTracker.history = [];
|
|
776
|
+
rootTracker.originalValues.clear();
|
|
777
|
+
rootTracker.trackedArrays.clear();
|
|
778
|
+
rootTracker.trackedEntities.clear();
|
|
779
|
+
this.captureInitialState();
|
|
780
|
+
}
|
|
781
|
+
markAsClean() {
|
|
782
|
+
this.clearHistory();
|
|
783
|
+
}
|
|
784
|
+
getTarget() {
|
|
785
|
+
return this.target;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
exports.ChangeTracker = ChangeTracker;
|
|
789
|
+
//# sourceMappingURL=change-tracker.js.map
|