diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 819fcd7b..ce59eed6 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -790,3 +790,129 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: pnpm --filter architectura ci:publish + + ## DDD + + lint-ddd: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - name: PNPM installation + uses: pnpm/action-setup@v4 + id: pnpm-install + with: + version: 10 + run_install: true + + - name: Building workspace dependencies + run: | + pnpm --filter testing-ground ci:build + pnpm --filter ts-predicate ci:build + pnpm --filter toolbox ci:build + + - name: Package dependencies installation + run: pnpm install --filter ddd + + - name: Lint with ESlint + run: pnpm --filter ddd ci:lint + + typescript-syntax-check-ddd: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - name: PNPM installation + uses: pnpm/action-setup@v4 + id: pnpm-install + with: + version: 10 + run_install: true + + - name: Building workspace dependencies + run: | + pnpm --filter testing-ground ci:build + pnpm --filter ts-predicate ci:build + pnpm --filter toolbox ci:build + + - name: Package dependencies installation + run: pnpm install --filter ddd + + - name: Check TypeScript syntax + run: pnpm --filter ddd ci:ts:check + + test-ddd: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - name: PNPM installation + uses: pnpm/action-setup@v4 + id: pnpm-install + with: + version: 10 + run_install: true + + - uses: actions/setup-node@v4 + with: + node-version: "22.x" + + - name: Building workspace dependencies + run: | + pnpm --filter testing-ground ci:build + pnpm --filter ts-predicate ci:build + pnpm --filter toolbox ci:build + + - name: Package dependencies installation + run: pnpm install --filter ddd + + - name: Test with Node Test runner + run: pnpm --filter ddd ci:test:unit + + publish-ddd: + if: ${{ github.ref == 'refs/heads/main' && ! failure() && ! cancelled() && github.event_name == 'push' }} + needs: + [ + spell-check-global, + lint-ddd, + typescript-syntax-check-ddd, + test-ddd, + ] + runs-on: ubuntu-latest + environment: npm + steps: + - name: Check out repository code + uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "22.x" + registry-url: "https://registry.npmjs.org" + + - name: PNPM installation + uses: pnpm/action-setup@v4 + id: pnpm-install + with: + version: 10 + run_install: true + + - name: Building workspace dependencies + run: | + pnpm --filter testing-ground ci:build + pnpm --filter ts-predicate ci:build + pnpm --filter toolbox ci:build + + - name: Package dependencies installation + run: pnpm install --filter ddd + + - name: Package build + run: pnpm --filter ddd ci:build + + - name: Publish package + id: package-version + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: pnpm --filter ddd ci:publish diff --git a/packages/ddd/.c8rc.json b/packages/ddd/.c8rc.json new file mode 100644 index 00000000..bc0a834b --- /dev/null +++ b/packages/ddd/.c8rc.json @@ -0,0 +1,21 @@ +{ + "src": "./src", + "exclude": [ + "mock", + "test" + ], + "extension": [ + ".mts" + ], + "reporter": [ + "html" + ], + "report-dir": "./coverage", + "skip-full": true, + "check-coverage": true, + "all": true, + "statements": 100, + "branches": 100, + "functions": 100, + "lines": 100 +} diff --git a/packages/ddd/.gitignore b/packages/ddd/.gitignore new file mode 100644 index 00000000..47904470 --- /dev/null +++ b/packages/ddd/.gitignore @@ -0,0 +1,10 @@ +**/node_modules/** +build +temp/logs/*.log +yarn.lock +package-lock.json +nodemon.json +yarn-error.log +.vscode/ +**/database/** +coverage/ diff --git a/packages/ddd/.mocharc.json b/packages/ddd/.mocharc.json new file mode 100644 index 00000000..01c8aa53 --- /dev/null +++ b/packages/ddd/.mocharc.json @@ -0,0 +1,16 @@ +{ + "extension": [ + "mts" + ], + "node-option": [ + "loader=tsx" + ], + "spec": [ + "test/**/*.spec.mts" + ], + "timeout": 5000, + "parallel": true, + "checkLeaks": true, + "diff": true, + "forbidPending": true +} diff --git a/packages/ddd/LICENSE b/packages/ddd/LICENSE new file mode 100644 index 00000000..11ce4ee2 --- /dev/null +++ b/packages/ddd/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 VitruviusLabs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/ddd/README.md b/packages/ddd/README.md new file mode 100644 index 00000000..08674762 --- /dev/null +++ b/packages/ddd/README.md @@ -0,0 +1,3 @@ +# DDD + +TODO diff --git a/packages/ddd/eslint.config.mjs b/packages/ddd/eslint.config.mjs new file mode 100644 index 00000000..bb6f95cb --- /dev/null +++ b/packages/ddd/eslint.config.mjs @@ -0,0 +1,3 @@ +import configurations from "../../eslint.config.mjs"; + +export default configurations; diff --git a/packages/ddd/package.json b/packages/ddd/package.json new file mode 100755 index 00000000..8265c196 --- /dev/null +++ b/packages/ddd/package.json @@ -0,0 +1,52 @@ +{ + "name": "@vitruvius-labs/ddd", + "version": "0.1.0", + "description": "", + "author": { + "name": "VitruviusLabs" + }, + "contributors": [ + "Nicolas \"SmashingQuasar\" Lebacq ", + "Benjamin Blum " + ], + "license": "MIT", + "private": false, + "type": "module", + "files": [ + "./build/**/*" + ], + "exports": { + ".": { + "import": "./build/esm/_index.mjs", + "types": "./build/types/_index.d.mts" + } + }, + "scripts": { + "boot": "pnpm run build && ./docker/scripts/boot.sh && docker logs -f vitruvius", + "clean": "rm -rf reports coverage build dist lib .eslintcache", + "compile": "tsc -p tsconfig.build.json", + "build": "pnpm clean && pnpm compile", + "eslint:check": "eslint", + "eslint:fix": "eslint --fix", + "test:unit": "tsx --test **/*.spec.mts", + "test:unit:only": "tsx --test --test-only **/*.spec.mts", + "test:unit:stryker": "pnpm test:unit", + "test:mutation": "stryker run", + "node:coverage": "NODE_V8_COVERAGE=./reports/node-coverage tsx --experimental-test-coverage --test **/*.spec.mts", + "c8:coverage": "c8 --exclude mock --exclude test tsx --test-reporter spec --test **/*.spec.mts", + "ts:check": "tsc -p tsconfig.json --noEmit", + "spell:check": "cspell -c ../../.vscode/cspell.json .", + "ci:lint": "pnpm eslint:check", + "ci:ts:check": "pnpm ts:check", + "ci:spell:check": "pnpm spell:check", + "ci:test:unit": "pnpm test:unit", + "ci:full": "pnpm ci:lint && pnpm ci:ts:check && pnpm ci:spell:check && pnpm ci:test:unit", + "ci:publish": "pnpm publish --access public --no-git-checks", + "ci:publish:dry": "pnpm publish --access public --dry-run --no-git-checks", + "ci:build": "pnpm build" + }, + "dependencies": { + "@vitruvius-labs/ts-predicate": "workspace:^", + "@vitruvius-labs/toolbox": "workspace:^" + } +} diff --git a/packages/ddd/src/_index.mts b/packages/ddd/src/_index.mts new file mode 100644 index 00000000..cc0a5ef3 --- /dev/null +++ b/packages/ddd/src/_index.mts @@ -0,0 +1,2 @@ +export * from "./base/_index.mjs"; +export * from "./blank-record/_index.mjs"; diff --git a/packages/ddd/src/base/_index.mts b/packages/ddd/src/base/_index.mts new file mode 100644 index 00000000..3955a381 --- /dev/null +++ b/packages/ddd/src/base/_index.mts @@ -0,0 +1,2 @@ +export * from "./auxiliary/_index.mjs"; +export * from "./primary/_index.mjs"; diff --git a/packages/ddd/src/base/auxiliary/_index.mts b/packages/ddd/src/base/auxiliary/_index.mts new file mode 100644 index 00000000..0a649a04 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/_index.mts @@ -0,0 +1,4 @@ +export * from "./meta-property/_index.mjs"; +export * from "./property-definition/_index.mjs"; +export * from "./property/_index.mjs"; +export * from "./type-definition/_index.mjs"; diff --git a/packages/ddd/src/base/auxiliary/meta-property/_index.mts b/packages/ddd/src/base/auxiliary/meta-property/_index.mts new file mode 100644 index 00000000..f3048ac5 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/meta-property/_index.mts @@ -0,0 +1,3 @@ +export type * from "./definition/_index.mjs"; +export * from "./base-meta-property.mjs"; +export * from "./predefined-meta-property.mjs"; diff --git a/packages/ddd/src/base/auxiliary/meta-property/base-meta-property.mts b/packages/ddd/src/base/auxiliary/meta-property/base-meta-property.mts new file mode 100644 index 00000000..c2b1e210 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/meta-property/base-meta-property.mts @@ -0,0 +1,55 @@ +import type { BasePropertyDefinition } from "../property-definition/base-property-definition.mjs"; +import type { BaseProperty } from "../property/base-property.mjs"; +import type { BasePropertyInstantiationInterface } from "../property/definition/interface/base-property-instantiation.interface.mjs"; +import type { BaseTypeDefinition } from "../type-definition/base-type-definition.mjs"; + +abstract class BaseMetaProperty, P extends BaseProperty = BaseProperty> +{ + public abstract getPropertyDefinition(): Promise>; + + protected abstract createProperty(parameters: BasePropertyInstantiationInterface): P; + + public async createNewProperty(value: T): Promise

+ { + // @ts-expect-error -- We have to assume this is the correct definition + const definition: PD = await this.getPropertyDefinition(); + + const property: P = this.createProperty({ + definition: definition, + value: value, + existing: false, + }); + + return property; + } + + public async createExistingProperty(value: unknown): Promise

+ { + // @ts-expect-error -- We have to assume this is the correct definition + const property_definition: PD = await this.getPropertyDefinition(); + + const type_definition: BaseTypeDefinition = property_definition.getTypeDefinition(); + + const normalized_value: T = await this.normalize(value, type_definition); + + await type_definition.verify(normalized_value); + + const property: P = this.createProperty({ + definition: property_definition, + value: normalized_value, + existing: true, + }); + + return property; + } + + // eslint-disable-next-line @ts/class-methods-use-this, @ts/require-await + public async normalize(value: unknown, type_definition: BaseTypeDefinition): Promise + { + type_definition.assertType(value); + + return value; + } +} + +export { BaseMetaProperty }; diff --git a/packages/ddd/src/base/auxiliary/meta-property/definition/_index.mts b/packages/ddd/src/base/auxiliary/meta-property/definition/_index.mts new file mode 100644 index 00000000..343d0f1c --- /dev/null +++ b/packages/ddd/src/base/auxiliary/meta-property/definition/_index.mts @@ -0,0 +1 @@ +export type * from "./type/_index.mjs"; diff --git a/packages/ddd/src/base/auxiliary/meta-property/definition/type/_index.mts b/packages/ddd/src/base/auxiliary/meta-property/definition/type/_index.mts new file mode 100644 index 00000000..9bee720c --- /dev/null +++ b/packages/ddd/src/base/auxiliary/meta-property/definition/type/_index.mts @@ -0,0 +1 @@ +export type * from "./base-meta-property.type.mjs"; diff --git a/packages/ddd/src/base/auxiliary/meta-property/definition/type/base-meta-property.type.mts b/packages/ddd/src/base/auxiliary/meta-property/definition/type/base-meta-property.type.mts new file mode 100644 index 00000000..0f02e81f --- /dev/null +++ b/packages/ddd/src/base/auxiliary/meta-property/definition/type/base-meta-property.type.mts @@ -0,0 +1,7 @@ +import type { BaseMetaProperty } from "../../base-meta-property.mjs"; +import type { BasePropertyType } from "../../../property/definition/type/base-property.type.mjs"; +import type { BasePropertyDefinitionType } from "../../../property-definition/definition/type/base-property-definition.type.mjs"; + +type BaseMetaPropertyType = BaseMetaProperty; + +export type { BaseMetaPropertyType }; diff --git a/packages/ddd/src/base/auxiliary/meta-property/predefined-meta-property.mts b/packages/ddd/src/base/auxiliary/meta-property/predefined-meta-property.mts new file mode 100644 index 00000000..9423d184 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/meta-property/predefined-meta-property.mts @@ -0,0 +1,24 @@ +import type { BasePropertyDefinition } from "../property-definition/base-property-definition.mjs"; +import type { BaseProperty } from "../property/base-property.mjs"; +import { BaseMetaProperty } from "./base-meta-property.mjs"; + +abstract class PredefinedMetaProperty, P extends BaseProperty = BaseProperty> extends BaseMetaProperty +{ + protected abstract getInitialValue(): Promise; + + public async createPredefinedProperty(): Promise

+ { + // @ts-expect-error -- We have to assume this is the correct definition + const definition: PD = await this.getPropertyDefinition(); + + const property: P = this.createProperty({ + definition: definition, + value: await this.getInitialValue(), + existing: false, + }); + + return property; + } +} + +export { PredefinedMetaProperty }; diff --git a/packages/ddd/src/base/auxiliary/property-definition/_index.mts b/packages/ddd/src/base/auxiliary/property-definition/_index.mts new file mode 100644 index 00000000..f2702397 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property-definition/_index.mts @@ -0,0 +1,3 @@ +export type * from "./definition/_index.mjs"; +export * from "./base-property-definition-instantiation-interface.mjs"; +export * from "./base-property-definition.mjs"; diff --git a/packages/ddd/src/base/auxiliary/property-definition/base-property-definition-instantiation-interface.mts b/packages/ddd/src/base/auxiliary/property-definition/base-property-definition-instantiation-interface.mts new file mode 100644 index 00000000..5a6eef59 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property-definition/base-property-definition-instantiation-interface.mts @@ -0,0 +1,9 @@ +import type { BaseTypeDefinition } from "../type-definition/base-type-definition.mjs"; + +interface BasePropertyDefinitionInstantiationInterface +{ + identifier: string; + typeDefinition: BaseTypeDefinition; +} + +export type { BasePropertyDefinitionInstantiationInterface }; diff --git a/packages/ddd/src/base/auxiliary/property-definition/base-property-definition.mts b/packages/ddd/src/base/auxiliary/property-definition/base-property-definition.mts new file mode 100644 index 00000000..cc194ee9 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property-definition/base-property-definition.mts @@ -0,0 +1,34 @@ +import type { NoValue } from "@vitruvius-labs/ts-predicate"; +import type { BaseTypeDefinition } from "../type-definition/base-type-definition.mjs"; +import type { BasePropertyDefinitionInstantiationInterface } from "./base-property-definition-instantiation-interface.mjs"; + +abstract class BasePropertyDefinition +{ + protected readonly identifier: string; + protected readonly typeDefinition: BaseTypeDefinition; + + public constructor(parameters: BasePropertyDefinitionInstantiationInterface) + { + this.identifier = parameters.identifier; + this.typeDefinition = parameters.typeDefinition; + } + + public getIdentifier(): string + { + return this.identifier; + } + + public getTypeDefinition(): BaseTypeDefinition + { + return this.typeDefinition; + } + + // @ts-expect-error -- Parameters for overrides + // eslint-disable-next-line @ts/no-unused-vars + public async verify(value: T, previous_value: T | typeof NoValue): Promise + { + await this.typeDefinition.verify(value); + } +} + +export { BasePropertyDefinition }; diff --git a/packages/ddd/src/base/auxiliary/property-definition/definition/_index.mts b/packages/ddd/src/base/auxiliary/property-definition/definition/_index.mts new file mode 100644 index 00000000..343d0f1c --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property-definition/definition/_index.mts @@ -0,0 +1 @@ +export type * from "./type/_index.mjs"; diff --git a/packages/ddd/src/base/auxiliary/property-definition/definition/type/_index.mts b/packages/ddd/src/base/auxiliary/property-definition/definition/type/_index.mts new file mode 100644 index 00000000..5177bb02 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property-definition/definition/type/_index.mts @@ -0,0 +1 @@ +export type * from "./base-property-definition.type.mjs"; diff --git a/packages/ddd/src/base/auxiliary/property-definition/definition/type/base-property-definition.type.mts b/packages/ddd/src/base/auxiliary/property-definition/definition/type/base-property-definition.type.mts new file mode 100644 index 00000000..e01a7cee --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property-definition/definition/type/base-property-definition.type.mts @@ -0,0 +1,5 @@ +import type { BasePropertyDefinition } from "../../base-property-definition.mjs"; + +type BasePropertyDefinitionType = BasePropertyDefinition; + +export type { BasePropertyDefinitionType }; diff --git a/packages/ddd/src/base/auxiliary/property/_index.mts b/packages/ddd/src/base/auxiliary/property/_index.mts new file mode 100644 index 00000000..9c085e5c --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property/_index.mts @@ -0,0 +1,3 @@ +export type * from "./definition/_index.mjs"; +export * from "./base-immutable-property.mjs"; +export * from "./base-property.mjs"; diff --git a/packages/ddd/src/base/auxiliary/property/base-immutable-property.mts b/packages/ddd/src/base/auxiliary/property/base-immutable-property.mts new file mode 100644 index 00000000..05c37b39 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property/base-immutable-property.mts @@ -0,0 +1,20 @@ +import type { BasePropertyDefinition } from "../property-definition/base-property-definition.mjs"; +import type { BasePropertyInstantiationInterface } from "./definition/interface/base-property-instantiation.interface.mjs"; +import { BaseProperty } from "./base-property.mjs"; + +class BaseImmutableProperty> extends BaseProperty +{ + public constructor(parameters: BasePropertyInstantiationInterface) + { + super(parameters); + + this.disableMutability(); + } + + public override setValue(): never + { + throw new Error(`Property "${this.getDefinition().getIdentifier()}" is immutable.`); + } +} + +export { BaseImmutableProperty }; diff --git a/packages/ddd/src/base/auxiliary/property/base-property.mts b/packages/ddd/src/base/auxiliary/property/base-property.mts new file mode 100644 index 00000000..92e635d8 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property/base-property.mts @@ -0,0 +1,81 @@ +import { NoValue } from "@vitruvius-labs/ts-predicate"; +import type { BasePropertyDefinition } from "../property-definition/base-property-definition.mjs"; +import type { BasePropertyInstantiationInterface } from "./definition/interface/base-property-instantiation.interface.mjs"; + +class BaseProperty> +{ + private readonly definition: PD; + private mutability: boolean; + private value: T; + private previousValue: T | typeof NoValue; + + public constructor(parameters: BasePropertyInstantiationInterface) + { + this.definition = parameters.definition; + this.mutability = true; + this.value = parameters.value; + this.previousValue = parameters.existing ? parameters.value : NoValue; + } + + public getDefinition(): PD + { + return this.definition; + } + + // eslint-disable-next-line @ts/require-await + public async getValue(): Promise + { + return this.value; + } + + public isMutable(): boolean + { + return this.mutability; + } + + public disableMutability(): void + { + this.mutability = false; + } + + public assertMutability(): void + { + if (!this.mutability) + { + throw new Error(`Property "${this.definition.getIdentifier()}" is immutable.`); + } + } + + public async setValue(value: T): Promise + { + this.assertMutability(); + + await this.definition.verify(value, this.value); + + this.value = value; + } + + public isMutated(): boolean + { + if (this.previousValue === NoValue) + { + return true; + } + + const is_equal: boolean = this.definition.getTypeDefinition().isEqual(this.previousValue, this.value); + + return !is_equal; + } + + public getPreviousValue(): T | typeof NoValue + { + return this.previousValue; + } + + public overwritePreviousValue(): void + { + this.previousValue = this.value; + } +} + +export { BaseProperty }; diff --git a/packages/ddd/src/base/auxiliary/property/definition/_index.mts b/packages/ddd/src/base/auxiliary/property/definition/_index.mts new file mode 100644 index 00000000..e554e8ed --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property/definition/_index.mts @@ -0,0 +1,2 @@ +export type * from "./interface/_index.mjs"; +export type * from "./type/_index.mjs"; diff --git a/packages/ddd/src/base/auxiliary/property/definition/interface/_index.mts b/packages/ddd/src/base/auxiliary/property/definition/interface/_index.mts new file mode 100644 index 00000000..33518fe3 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property/definition/interface/_index.mts @@ -0,0 +1 @@ +export type * from "./base-property-instantiation.interface.mjs"; diff --git a/packages/ddd/src/base/auxiliary/property/definition/interface/base-property-instantiation.interface.mts b/packages/ddd/src/base/auxiliary/property/definition/interface/base-property-instantiation.interface.mts new file mode 100644 index 00000000..8c5397ac --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property/definition/interface/base-property-instantiation.interface.mts @@ -0,0 +1,10 @@ +import type { BasePropertyDefinition } from "../../../property-definition/base-property-definition.mjs"; + +interface BasePropertyInstantiationInterface> +{ + definition: PD; + value: T; + existing: boolean; +} + +export type { BasePropertyInstantiationInterface }; diff --git a/packages/ddd/src/base/auxiliary/property/definition/type/_index.mts b/packages/ddd/src/base/auxiliary/property/definition/type/_index.mts new file mode 100644 index 00000000..5383f2b9 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property/definition/type/_index.mts @@ -0,0 +1 @@ +export type * from "./base-property.type.mjs"; diff --git a/packages/ddd/src/base/auxiliary/property/definition/type/base-property.type.mts b/packages/ddd/src/base/auxiliary/property/definition/type/base-property.type.mts new file mode 100644 index 00000000..e481d1d9 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/property/definition/type/base-property.type.mts @@ -0,0 +1,6 @@ +import type { BaseProperty } from "../../base-property.mjs"; +import type { BasePropertyDefinitionType } from "../../../property-definition/definition/type/base-property-definition.type.mjs"; + +type BasePropertyType = BaseProperty; + +export type { BasePropertyType }; diff --git a/packages/ddd/src/base/auxiliary/type-definition/_index.mts b/packages/ddd/src/base/auxiliary/type-definition/_index.mts new file mode 100644 index 00000000..8d929288 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/type-definition/_index.mts @@ -0,0 +1,4 @@ +export * from "./base-optional-type-definition.mjs"; +export * from "./base-required-type-definition.mjs"; +export * from "./base-type-definition.mjs"; +export * from "./nullable-type-definition.mjs"; diff --git a/packages/ddd/src/base/auxiliary/type-definition/base-optional-type-definition.mts b/packages/ddd/src/base/auxiliary/type-definition/base-optional-type-definition.mts new file mode 100644 index 00000000..d83851a6 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/type-definition/base-optional-type-definition.mts @@ -0,0 +1,12 @@ +import { BaseTypeDefinition } from "./base-type-definition.mjs"; + +abstract class BaseOptionalTypeDefinition extends BaseTypeDefinition +{ + // eslint-disable-next-line @ts/class-methods-use-this + public isOptional(): true + { + return true; + } +} + +export { BaseOptionalTypeDefinition }; diff --git a/packages/ddd/src/base/auxiliary/type-definition/base-required-type-definition.mts b/packages/ddd/src/base/auxiliary/type-definition/base-required-type-definition.mts new file mode 100644 index 00000000..59d2c8d0 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/type-definition/base-required-type-definition.mts @@ -0,0 +1,24 @@ +import { BaseTypeDefinition } from "./base-type-definition.mjs"; + +abstract class BaseRequiredTypeDefinition extends BaseTypeDefinition +{ + // eslint-disable-next-line @ts/class-methods-use-this + public isOptional(): false + { + return false; + } + + // eslint-disable-next-line @ts/class-methods-use-this + public getEmptyValue(): never + { + throw new Error("Not an optional type"); + } + + // eslint-disable-next-line @ts/class-methods-use-this + public isEmptyValue(): false + { + return false; + } +} + +export { BaseRequiredTypeDefinition }; diff --git a/packages/ddd/src/base/auxiliary/type-definition/base-type-definition.mts b/packages/ddd/src/base/auxiliary/type-definition/base-type-definition.mts new file mode 100644 index 00000000..c2ab9acb --- /dev/null +++ b/packages/ddd/src/base/auxiliary/type-definition/base-type-definition.mts @@ -0,0 +1,19 @@ +abstract class BaseTypeDefinition +{ + public abstract assertType(value: unknown): asserts value is T; + public abstract isOptional(): boolean; + public abstract getEmptyValue(): T; + public abstract isEmptyValue(value: T): boolean; + + // eslint-disable-next-line @ts/class-methods-use-this + public isEqual(value_a: T, value_b: T): boolean + { + return value_a === value_b; + } + + // @ts-expect-error -- Parameters for overrides + // eslint-disable-next-line @ts/class-methods-use-this, @ts/no-unused-vars, @ts/no-empty-function + public async verify(value: T): Promise {} +} + +export { BaseTypeDefinition }; diff --git a/packages/ddd/src/base/auxiliary/type-definition/nullable-type-definition.mts b/packages/ddd/src/base/auxiliary/type-definition/nullable-type-definition.mts new file mode 100644 index 00000000..04589e28 --- /dev/null +++ b/packages/ddd/src/base/auxiliary/type-definition/nullable-type-definition.mts @@ -0,0 +1,69 @@ +import type { BaseRequiredTypeDefinition } from "./base-required-type-definition.mjs"; +import { BaseTypeDefinition } from "./base-type-definition.mjs"; + +class NullableTypeDefinition extends BaseTypeDefinition +{ + protected readonly definition: BaseRequiredTypeDefinition; + + public constructor(definition: BaseRequiredTypeDefinition) + { + super(); + + this.definition = definition; + } + + public assertType(value: unknown): asserts value is T | null + { + if (value === null) + { + return; + } + + this.definition.assertType(value); + } + + // eslint-disable-next-line @ts/class-methods-use-this + public isOptional(): boolean + { + return true; + } + + // eslint-disable-next-line @ts/class-methods-use-this + public getEmptyValue(): T | null + { + return null; + } + + // eslint-disable-next-line @ts/class-methods-use-this + public isEmptyValue(value: T | null): boolean + { + return value === null; + } + + public override isEqual(value_a: T | null, value_b: T | null): boolean + { + if (value_a === null && value_b === null) + { + return true; + } + + if (value_a === null || value_b === null) + { + return false; + } + + return this.definition.isEqual(value_a, value_b); + } + + public override async verify(value: T | null): Promise + { + if (value === null) + { + return; + } + + await this.definition.verify(value); + } +} + +export { NullableTypeDefinition }; diff --git a/packages/ddd/src/base/primary/_index.mts b/packages/ddd/src/base/primary/_index.mts new file mode 100644 index 00000000..5d4d3772 --- /dev/null +++ b/packages/ddd/src/base/primary/_index.mts @@ -0,0 +1,6 @@ +export type * from "./definition/_index.mjs"; +export * from "./base-factory.mjs"; +export * from "./base-meta-record.mjs"; +export * from "./base-read-repository.mjs"; +export * from "./base-record.mjs"; +export * from "./base-write-repository.mjs"; diff --git a/packages/ddd/src/base/primary/base-factory.mts b/packages/ddd/src/base/primary/base-factory.mts new file mode 100644 index 00000000..4ec62193 --- /dev/null +++ b/packages/ddd/src/base/primary/base-factory.mts @@ -0,0 +1,22 @@ +import type { BaseRecordConstructorType } from "./definition/type/base-record-constructor.type.mjs"; +import type { BaseRecord } from "./base-record.mjs"; + +abstract class BaseFactory +{ + protected readonly recordConstructor: BaseRecordConstructorType; + + public constructor(record_constructor: BaseRecordConstructorType) + { + this.recordConstructor = record_constructor; + } + + // eslint-disable-next-line @ts/require-await + public async create(parameters: I): Promise + { + const instance: M = new this.recordConstructor(parameters); + + return instance; + } +} + +export { BaseFactory }; diff --git a/packages/ddd/src/base/primary/base-meta-record.mts b/packages/ddd/src/base/primary/base-meta-record.mts new file mode 100644 index 00000000..f379c4f6 --- /dev/null +++ b/packages/ddd/src/base/primary/base-meta-record.mts @@ -0,0 +1,15 @@ +import type { BaseMetaPropertyType } from "../auxiliary/meta-property/definition/type/base-meta-property.type.mjs"; +import type { BaseRecord } from "./base-record.mjs"; +import type { BaseFactory } from "./base-factory.mjs"; +import type { BaseReadRepository } from "./base-read-repository.mjs"; +import type { BaseWriteRepository } from "./base-write-repository.mjs"; + +abstract class BaseMetaRecord +{ + public abstract getMetaProperties(): Array; + public abstract getFactory(): BaseFactory; + public abstract getReadRepository(): BaseReadRepository; + public abstract getWriteRepository(): BaseWriteRepository; +} + +export { BaseMetaRecord }; diff --git a/packages/ddd/src/base/primary/base-read-repository.mts b/packages/ddd/src/base/primary/base-read-repository.mts new file mode 100644 index 00000000..01c3ce72 --- /dev/null +++ b/packages/ddd/src/base/primary/base-read-repository.mts @@ -0,0 +1,35 @@ +import { type Nullable, isNullable } from "@vitruvius-labs/ts-predicate"; +import type { BaseRecord } from "./base-record.mjs"; + +abstract class BaseReadRepository +{ + protected abstract create(data: T): Promise; + + protected async createOptional(data: Nullable): Promise + { + if (isNullable(data)) + { + return undefined; + } + + const instance: M = await this.create(data); + + return instance; + } + + protected async createMany(data: Array): Promise> + { + const instances: Array = []; + + for (const item of data) + { + const instance: M = await this.create(item); + + instances.push(instance); + } + + return instances; + } +} + +export { BaseReadRepository }; diff --git a/packages/ddd/src/base/primary/base-record.mts b/packages/ddd/src/base/primary/base-record.mts new file mode 100644 index 00000000..6b524575 --- /dev/null +++ b/packages/ddd/src/base/primary/base-record.mts @@ -0,0 +1,52 @@ +import { isInstanceOf } from "@vitruvius-labs/ts-predicate"; +import type { BasePropertyType } from "../auxiliary/property/definition/type/base-property.type.mjs"; +import { BaseProperty } from "../auxiliary/property/base-property.mjs"; + +abstract class BaseRecord +{ + public abstract save(): Promise; + + // eslint-disable-next-line @ts/class-methods-use-this, @ts/no-empty-function + public async verify(): Promise {} + + public getProperty(identifier: string): BasePropertyType + { + for (const member of Object.values(this)) + { + if (isInstanceOf(member, BaseProperty) && member.getDefinition().getIdentifier() === identifier) + { + return member; + } + } + + throw new Error(`No property with identifier "${identifier}" found.`); + } + + public getAllProperties(): Array + { + const properties: Array = []; + + for (const member of Object.values(this)) + { + if (isInstanceOf(member, BaseProperty)) + { + properties.push(member); + } + } + + return properties; + } + + public * properties(): Generator + { + for (const member of Object.values(this)) + { + if (isInstanceOf(member, BaseProperty)) + { + yield member; + } + } + } +} + +export { BaseRecord }; diff --git a/packages/ddd/src/base/primary/base-write-repository.mts b/packages/ddd/src/base/primary/base-write-repository.mts new file mode 100644 index 00000000..ba487102 --- /dev/null +++ b/packages/ddd/src/base/primary/base-write-repository.mts @@ -0,0 +1,8 @@ +import type { BaseRecord } from "./base-record.mjs"; + +abstract class BaseWriteRepository +{ + public abstract saveRecord(instance: M): Promise; +} + +export { BaseWriteRepository }; diff --git a/packages/ddd/src/base/primary/definition/_index.mts b/packages/ddd/src/base/primary/definition/_index.mts new file mode 100644 index 00000000..343d0f1c --- /dev/null +++ b/packages/ddd/src/base/primary/definition/_index.mts @@ -0,0 +1 @@ +export type * from "./type/_index.mjs"; diff --git a/packages/ddd/src/base/primary/definition/type/_index.mts b/packages/ddd/src/base/primary/definition/type/_index.mts new file mode 100644 index 00000000..30ab735b --- /dev/null +++ b/packages/ddd/src/base/primary/definition/type/_index.mts @@ -0,0 +1,2 @@ +export type * from "./base-factory.type.mjs"; +export type * from "./base-record-constructor.type.mjs"; diff --git a/packages/ddd/src/base/primary/definition/type/base-factory.type.mts b/packages/ddd/src/base/primary/definition/type/base-factory.type.mts new file mode 100644 index 00000000..a39cf52b --- /dev/null +++ b/packages/ddd/src/base/primary/definition/type/base-factory.type.mts @@ -0,0 +1,6 @@ +import type { BaseFactory } from "../../base-factory.mjs"; +import type { BaseRecord } from "../../base-record.mjs"; + +type BaseFactoryType = BaseFactory; + +export type { BaseFactoryType }; diff --git a/packages/ddd/src/base/primary/definition/type/base-record-constructor.type.mts b/packages/ddd/src/base/primary/definition/type/base-record-constructor.type.mts new file mode 100644 index 00000000..db91c08c --- /dev/null +++ b/packages/ddd/src/base/primary/definition/type/base-record-constructor.type.mts @@ -0,0 +1,5 @@ +import type { BaseRecord } from "../../base-record.mjs"; + +type BaseRecordConstructorType = new (parameters: I) => M; + +export type { BaseRecordConstructorType }; diff --git a/packages/ddd/src/blank-record/_index.mts b/packages/ddd/src/blank-record/_index.mts new file mode 100644 index 00000000..13247b74 --- /dev/null +++ b/packages/ddd/src/blank-record/_index.mts @@ -0,0 +1,6 @@ +export * from "./blank-read-repository.mjs"; +export * from "./blank-record.mjs"; +export * from "./blank-write-repository.mjs"; +export * from "./definition/_index.mjs"; +export * from "./properties/_index.mjs"; +export * from "./write-repository-hooks.mjs"; diff --git a/packages/ddd/src/blank-record/blank-read-repository.mts b/packages/ddd/src/blank-record/blank-read-repository.mts new file mode 100644 index 00000000..20bb726e --- /dev/null +++ b/packages/ddd/src/blank-record/blank-read-repository.mts @@ -0,0 +1,38 @@ +import { type Nullable, isNullable } from "@vitruvius-labs/ts-predicate"; +import type { BlankRecord } from "./blank-record.mjs"; +import { BaseReadRepository } from "../base/primary/base-read-repository.mjs"; + +abstract class BlankReadRepository, T extends object> extends BaseReadRepository +{ + protected abstract fetchByUUID(uuid: string): Promise>; + + public async findByUUID(uuid: string): Promise + { + const result: Nullable = await this.fetchByUUID(uuid); + + if (isNullable(result)) + { + return undefined; + } + + const instance: M = await this.create(result); + + return instance; + } + + public async getByUUID(uuid: string): Promise + { + const result: Nullable = await this.fetchByUUID(uuid); + + if (isNullable(result)) + { + throw new Error(`Entity with UUID ${uuid} not found.`); + } + + const instance: M = await this.create(result); + + return instance; + } +} + +export { BlankReadRepository }; diff --git a/packages/ddd/src/blank-record/blank-record.mts b/packages/ddd/src/blank-record/blank-record.mts new file mode 100644 index 00000000..88039197 --- /dev/null +++ b/packages/ddd/src/blank-record/blank-record.mts @@ -0,0 +1,76 @@ +import type { RepositoryStatusProperty } from "./properties/repository-status/repository-status-property.mjs"; +import type { UUIDv4PropertyType } from "./properties/uuid-v4/definition/type/uuid-v4-property.type.mjs"; +import type { RepositoryDateProperty } from "./properties/repository-date/repository-date-property.mjs"; +import type { BlankRecordInstantiationInterface } from "./definition/interface/blank-record-instantiation.interface.mjs"; +import { BaseRecord } from "../base/primary/base-record.mjs"; +import { RepositoryActionEnum } from "./properties/repository-status/definition/enum/repository-action.enum.mjs"; +import type { BaseMetaRecord } from "../base/primary/base-meta-record.mjs"; + +abstract class BlankRecord> extends BaseRecord +{ + protected readonly metaRecord: BaseMetaRecord; + protected readonly status: RepositoryStatusProperty; + protected readonly uuid: UUIDv4PropertyType; + protected readonly registrationDate: RepositoryDateProperty; + protected readonly lastUpdateDate: RepositoryDateProperty; + + public constructor(parameters: BlankRecordInstantiationInterface) + { + super(); + + this.metaRecord = parameters.metaRecord; + this.status = parameters.status; + this.uuid = parameters.uuid; + this.registrationDate = parameters.registrationDate; + this.lastUpdateDate = parameters.lastUpdateDate; + } + + public getStatus(): RepositoryStatusProperty + { + return this.status; + } + + public getUUID(): UUIDv4PropertyType + { + return this.uuid; + } + + public getRegistrationDate(): RepositoryDateProperty + { + return this.registrationDate; + } + + public getLastUpdateDate(): RepositoryDateProperty + { + return this.lastUpdateDate; + } + + public async save(): Promise + { + // @ts-expect-error -- The concrete class should be final + await this.metaRecord.getWriteRepository().saveRecord(this); + } + + public async archive(): Promise + { + await this.status.setAction(RepositoryActionEnum.ARCHIVE); + + await this.save(); + } + + public async restore(): Promise + { + await this.status.setAction(RepositoryActionEnum.RESTORE); + + await this.save(); + } + + public async destroy(): Promise + { + await this.status.setAction(RepositoryActionEnum.DESTROY); + + await this.save(); + } +} + +export { BlankRecord }; diff --git a/packages/ddd/src/blank-record/blank-write-repository.mts b/packages/ddd/src/blank-record/blank-write-repository.mts new file mode 100644 index 00000000..403b3c10 --- /dev/null +++ b/packages/ddd/src/blank-record/blank-write-repository.mts @@ -0,0 +1,158 @@ +import type { BlankRecord } from "./blank-record.mjs"; +import type { WriteRepositoryHooks } from "./write-repository-hooks.mjs"; +import { BaseWriteRepository } from "../base/primary/base-write-repository.mjs"; +import { RepositoryActionEnum } from "./properties/repository-status/definition/enum/repository-action.enum.mjs"; + +abstract class BlankWriteRepository> extends BaseWriteRepository +{ + protected readonly hooks: WriteRepositoryHooks | undefined; + + public constructor(hooks?: WriteRepositoryHooks) + { + super(); + + this.hooks = hooks; + } + + protected abstract register(instance: M): Promise; + protected abstract update(instance: M): Promise; + protected abstract archive(instance: M): Promise; + protected abstract restore(instance: M): Promise; + protected abstract destroy(instance: M): Promise; + + public async saveRecord(instance: M): Promise + { + const action: RepositoryActionEnum = instance.getStatus().getAction(); + + switch (action) + { + case RepositoryActionEnum.REGISTER: + await this.registerRecord(instance); + break; + + case RepositoryActionEnum.UPDATE: + await this.updateRecord(instance); + break; + + case RepositoryActionEnum.ARCHIVE: + await this.archiveRecord(instance); + break; + + case RepositoryActionEnum.RESTORE: + await this.restoreRecord(instance); + break; + + case RepositoryActionEnum.DESTROY: + await this.destroyRecord(instance); + break; + + case RepositoryActionEnum.ERROR_ARCHIVED: + throw new Error("Impossible to save an archived record"); + + case RepositoryActionEnum.ERROR_DESTROYED: + throw new Error("Impossible to save a destroyed record"); + } + } + + protected async registerRecord(instance: M): Promise + { + if (this.hooks !== undefined) + { + await this.hooks.onPreRegister(instance); + } + + const date: Date = await this.register(instance); + + await instance.getRegistrationDate().setRepositoryValue(date); + await instance.getLastUpdateDate().setRepositoryValue(date); + await instance.getStatus().repositoryConfirmation(); + + for (const property of instance.properties()) + { + property.overwritePreviousValue(); + } + + if (this.hooks !== undefined) + { + await this.hooks.onPostRegister(instance); + } + } + + protected async updateRecord(instance: M): Promise + { + if (this.hooks !== undefined) + { + await this.hooks.onPreUpdate(instance); + } + + const date: Date = await this.update(instance); + + await instance.getLastUpdateDate().setRepositoryValue(date); + await instance.getStatus().repositoryConfirmation(); + + for (const property of instance.properties()) + { + property.overwritePreviousValue(); + } + + if (this.hooks !== undefined) + { + await this.hooks.onPostUpdate(instance); + } + } + + protected async archiveRecord(instance: M): Promise + { + if (this.hooks !== undefined) + { + await this.hooks.onPreArchive(instance); + } + + const date: Date = await this.archive(instance); + + await instance.getLastUpdateDate().setRepositoryValue(date); + await instance.getStatus().repositoryConfirmation(); + + if (this.hooks !== undefined) + { + await this.hooks.onPostArchive(instance); + } + } + + protected async restoreRecord(instance: M): Promise + { + if (this.hooks !== undefined) + { + await this.hooks.onPreRestore(instance); + } + + const date: Date = await this.restore(instance); + + await instance.getLastUpdateDate().setRepositoryValue(date); + await instance.getStatus().repositoryConfirmation(); + + if (this.hooks !== undefined) + { + await this.hooks.onPostRestore(instance); + } + } + + protected async destroyRecord(instance: M): Promise + { + if (this.hooks !== undefined) + { + await this.hooks.onPreDestroy(instance); + } + + await this.destroy(instance); + + await instance.getStatus().repositoryConfirmation(); + + if (this.hooks !== undefined) + { + await this.hooks.onPostDestroy(instance); + } + } +} + +export { BlankWriteRepository }; diff --git a/packages/ddd/src/blank-record/definition/_index.mts b/packages/ddd/src/blank-record/definition/_index.mts new file mode 100644 index 00000000..833204cb --- /dev/null +++ b/packages/ddd/src/blank-record/definition/_index.mts @@ -0,0 +1,2 @@ +export type * from "./interface/_index.mjs"; +export * from "./enum/_index.mjs"; diff --git a/packages/ddd/src/blank-record/definition/enum/_index.mts b/packages/ddd/src/blank-record/definition/enum/_index.mts new file mode 100644 index 00000000..d9dd967f --- /dev/null +++ b/packages/ddd/src/blank-record/definition/enum/_index.mts @@ -0,0 +1 @@ +export * from "./blank-record-property.enum.mjs"; diff --git a/packages/ddd/src/blank-record/definition/enum/blank-record-property.enum.mts b/packages/ddd/src/blank-record/definition/enum/blank-record-property.enum.mts new file mode 100644 index 00000000..d6c0db19 --- /dev/null +++ b/packages/ddd/src/blank-record/definition/enum/blank-record-property.enum.mts @@ -0,0 +1,9 @@ +const enum BlankRecordPropertyEnum +{ + UUID = "uuid", + REPOSITORY_STATUS = "repository-status", + REGISTRATION_DATE = "registration-date", + LAST_UPDATE_DATE = "last-update-date", +} + +export { BlankRecordPropertyEnum }; diff --git a/packages/ddd/src/blank-record/definition/interface/_index.mts b/packages/ddd/src/blank-record/definition/interface/_index.mts new file mode 100644 index 00000000..3c189183 --- /dev/null +++ b/packages/ddd/src/blank-record/definition/interface/_index.mts @@ -0,0 +1,2 @@ +export type * from "./blank-record-instantiation.interface.mjs"; +export type * from "./blank-record-recipe.interface.mjs"; diff --git a/packages/ddd/src/blank-record/definition/interface/blank-record-instantiation.interface.mts b/packages/ddd/src/blank-record/definition/interface/blank-record-instantiation.interface.mts new file mode 100644 index 00000000..335d47c3 --- /dev/null +++ b/packages/ddd/src/blank-record/definition/interface/blank-record-instantiation.interface.mts @@ -0,0 +1,16 @@ +import type { RepositoryStatusProperty } from "../../properties/repository-status/repository-status-property.mjs"; +import type { UUIDv4PropertyType } from "../../properties/uuid-v4/definition/type/uuid-v4-property.type.mjs"; +import type { RepositoryDateProperty } from "../../properties/repository-date/repository-date-property.mjs"; +import type { BlankRecord } from "../../blank-record.mjs"; +import type { BaseMetaRecord } from "../../../base/primary/base-meta-record.mjs"; + +interface BlankRecordInstantiationInterface> +{ + metaRecord: BaseMetaRecord; + status: RepositoryStatusProperty; + uuid: UUIDv4PropertyType; + registrationDate: RepositoryDateProperty; + lastUpdateDate: RepositoryDateProperty; +} + +export type { BlankRecordInstantiationInterface }; diff --git a/packages/ddd/src/blank-record/definition/interface/blank-record-recipe.interface.mts b/packages/ddd/src/blank-record/definition/interface/blank-record-recipe.interface.mts new file mode 100644 index 00000000..e60b8b00 --- /dev/null +++ b/packages/ddd/src/blank-record/definition/interface/blank-record-recipe.interface.mts @@ -0,0 +1,12 @@ +import type { UUID } from "node:crypto"; +import type { RepositoryStatusEnum } from "../../properties/repository-status/definition/enum/repository-status.enum.mjs"; + +interface BlankRecordRecipeInterface +{ + status: RepositoryStatusEnum; + uuid: UUID; + registrationDate: Date | null; + lastUpdateDate: Date | null; +} + +export type { BlankRecordRecipeInterface }; diff --git a/packages/ddd/src/blank-record/properties/_index.mts b/packages/ddd/src/blank-record/properties/_index.mts new file mode 100644 index 00000000..2ced7cbd --- /dev/null +++ b/packages/ddd/src/blank-record/properties/_index.mts @@ -0,0 +1,3 @@ +export * from "./repository-date/_index.mjs"; +export * from "./repository-status/_index.mjs"; +export * from "./uuid-v4/_index.mjs"; diff --git a/packages/ddd/src/blank-record/properties/repository-date/_index.mts b/packages/ddd/src/blank-record/properties/repository-date/_index.mts new file mode 100644 index 00000000..424458fa --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-date/_index.mts @@ -0,0 +1,6 @@ +export * from "./date-type-definition.mjs"; +export * from "./last-update-date-meta-property.mjs"; +export * from "./registration-date-meta-property.mjs"; +export * from "./repository-date-meta-property.mjs"; +export * from "./repository-date-property-definition.mjs"; +export * from "./repository-date-property.mjs"; diff --git a/packages/ddd/src/blank-record/properties/repository-date/date-type-definition.mts b/packages/ddd/src/blank-record/properties/repository-date/date-type-definition.mts new file mode 100644 index 00000000..bb1afd7c --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-date/date-type-definition.mts @@ -0,0 +1,25 @@ +import { ValidationError } from "@vitruvius-labs/ts-predicate"; +import { BaseRequiredTypeDefinition } from "../../../base/auxiliary/type-definition/base-required-type-definition.mjs"; + +class DateTypeDefinition extends BaseRequiredTypeDefinition +{ + // eslint-disable-next-line @ts/class-methods-use-this + public assertType(value: unknown): asserts value is Date + { + if (!(value instanceof Date)) + { + throw new ValidationError("Expected an instance of Date"); + } + } + + // eslint-disable-next-line @ts/class-methods-use-this, @ts/require-await + public override async verify(value: Date): Promise + { + if (Number.isNaN(value.getTime())) + { + throw new ValidationError("Invalid date"); + } + } +} + +export { DateTypeDefinition }; diff --git a/packages/ddd/src/blank-record/properties/repository-date/last-update-date-meta-property.mts b/packages/ddd/src/blank-record/properties/repository-date/last-update-date-meta-property.mts new file mode 100644 index 00000000..40b7ee63 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-date/last-update-date-meta-property.mts @@ -0,0 +1,13 @@ +import { BlankRecordPropertyEnum } from "../../definition/enum/blank-record-property.enum.mjs"; +import { RepositoryDateMetaProperty } from "./repository-date-meta-property.mjs"; + +class LastUpdateDateMetaProperty extends RepositoryDateMetaProperty +{ + // eslint-disable-next-line @ts/class-methods-use-this + public getIdentifier(): BlankRecordPropertyEnum + { + return BlankRecordPropertyEnum.LAST_UPDATE_DATE; + } +} + +export { LastUpdateDateMetaProperty }; diff --git a/packages/ddd/src/blank-record/properties/repository-date/registration-date-meta-property.mts b/packages/ddd/src/blank-record/properties/repository-date/registration-date-meta-property.mts new file mode 100644 index 00000000..d7b5fe3d --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-date/registration-date-meta-property.mts @@ -0,0 +1,13 @@ +import { BlankRecordPropertyEnum } from "../../definition/enum/blank-record-property.enum.mjs"; +import { RepositoryDateMetaProperty } from "./repository-date-meta-property.mjs"; + +class RegistrationDateMetaProperty extends RepositoryDateMetaProperty +{ + // eslint-disable-next-line @ts/class-methods-use-this + public getIdentifier(): BlankRecordPropertyEnum + { + return BlankRecordPropertyEnum.REGISTRATION_DATE; + } +} + +export { RegistrationDateMetaProperty }; diff --git a/packages/ddd/src/blank-record/properties/repository-date/repository-date-meta-property.mts b/packages/ddd/src/blank-record/properties/repository-date/repository-date-meta-property.mts new file mode 100644 index 00000000..6c93596c --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-date/repository-date-meta-property.mts @@ -0,0 +1,45 @@ +import { isNumber, isString } from "@vitruvius-labs/ts-predicate"; +import type { BasePropertyInstantiationInterface } from "../../../base/auxiliary/property/definition/interface/base-property-instantiation.interface.mjs"; +import type { BaseTypeDefinition } from "../../../base/auxiliary/type-definition/base-type-definition.mjs"; +import { RepositoryDatePropertyDefinition } from "./repository-date-property-definition.mjs"; +import { PredefinedMetaProperty } from "../../../base/auxiliary/meta-property/predefined-meta-property.mjs"; +import { RepositoryDateProperty } from "./repository-date-property.mjs"; +import { NullableTypeDefinition } from "../../../base/auxiliary/type-definition/nullable-type-definition.mjs"; +import { DateTypeDefinition } from "./date-type-definition.mjs"; + +abstract class RepositoryDateMetaProperty extends PredefinedMetaProperty +{ + protected abstract getIdentifier(): string; + + public override async normalize(value: unknown, type_definition: BaseTypeDefinition): Promise + { + if (isNumber(value) || isString(value)) + { + return new Date(value); + } + + return await super.normalize(value, type_definition); + } + + // eslint-disable-next-line @ts/require-await + public async getPropertyDefinition(): Promise + { + const type_definition: BaseTypeDefinition = new NullableTypeDefinition(new DateTypeDefinition()); + + return new RepositoryDatePropertyDefinition(this.getIdentifier(), type_definition); + } + + // eslint-disable-next-line @ts/class-methods-use-this + protected createProperty(parameters: BasePropertyInstantiationInterface): RepositoryDateProperty + { + return new RepositoryDateProperty(parameters); + } + + // eslint-disable-next-line @ts/class-methods-use-this, @ts/require-await + protected async getInitialValue(): Promise + { + return null; + } +} + +export { RepositoryDateMetaProperty }; diff --git a/packages/ddd/src/blank-record/properties/repository-date/repository-date-property-definition.mts b/packages/ddd/src/blank-record/properties/repository-date/repository-date-property-definition.mts new file mode 100644 index 00000000..3282a250 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-date/repository-date-property-definition.mts @@ -0,0 +1,15 @@ +import type { BaseTypeDefinition } from "../../../base/auxiliary/type-definition/base-type-definition.mjs"; +import { BasePropertyDefinition } from "../../../base/auxiliary/property-definition/base-property-definition.mjs"; + +class RepositoryDatePropertyDefinition extends BasePropertyDefinition +{ + public constructor(identifier: string, type_definition: BaseTypeDefinition) + { + super({ + identifier: identifier, + typeDefinition: type_definition, + }); + } +} + +export { RepositoryDatePropertyDefinition }; diff --git a/packages/ddd/src/blank-record/properties/repository-date/repository-date-property.mts b/packages/ddd/src/blank-record/properties/repository-date/repository-date-property.mts new file mode 100644 index 00000000..a213e23d --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-date/repository-date-property.mts @@ -0,0 +1,25 @@ +import { BaseProperty } from "../../../base/auxiliary/property/base-property.mjs"; +import type { RepositoryDatePropertyDefinition } from "./repository-date-property-definition.mjs"; + +class RepositoryDateProperty extends BaseProperty +{ + // eslint-disable-next-line @ts/class-methods-use-this + public override isMutable(): false + { + return false; + } + + public override setValue(): never + { + throw new Error(`Property "${this.getDefinition().getIdentifier()}" cannot be mutated directly.`); + } + + public async setRepositoryValue(value: Date): Promise + { + await super.setValue(value); + + this.overwritePreviousValue(); + } +} + +export { RepositoryDateProperty }; diff --git a/packages/ddd/src/blank-record/properties/repository-status/_index.mts b/packages/ddd/src/blank-record/properties/repository-status/_index.mts new file mode 100644 index 00000000..1b49ba6f --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/_index.mts @@ -0,0 +1,6 @@ +export * from "./definition/_index.mjs"; +export * from "./predicate/_index.mjs"; +export * from "./repository-status-meta-property.mjs"; +export * from "./repository-status-property-definition.mjs"; +export * from "./repository-status-property.mjs"; +export * from "./repository-status-type-definition.mjs"; diff --git a/packages/ddd/src/blank-record/properties/repository-status/definition/_index.mts b/packages/ddd/src/blank-record/properties/repository-status/definition/_index.mts new file mode 100644 index 00000000..8c185feb --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/definition/_index.mts @@ -0,0 +1,2 @@ +export type * from "./type/_index.mjs"; +export * from "./enum/_index.mjs"; diff --git a/packages/ddd/src/blank-record/properties/repository-status/definition/enum/_index.mts b/packages/ddd/src/blank-record/properties/repository-status/definition/enum/_index.mts new file mode 100644 index 00000000..cd94992f --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/definition/enum/_index.mts @@ -0,0 +1,2 @@ +export * from "./repository-action.enum.mjs"; +export * from "./repository-status.enum.mjs"; diff --git a/packages/ddd/src/blank-record/properties/repository-status/definition/enum/repository-action.enum.mts b/packages/ddd/src/blank-record/properties/repository-status/definition/enum/repository-action.enum.mts new file mode 100644 index 00000000..5e414ad4 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/definition/enum/repository-action.enum.mts @@ -0,0 +1,12 @@ +const enum RepositoryActionEnum +{ + REGISTER = "register", + UPDATE = "update", + ARCHIVE = "archive", + RESTORE = "restore", + DESTROY = "destroy", + ERROR_ARCHIVED = "error-restored", + ERROR_DESTROYED = "error-destroyed", +} + +export { RepositoryActionEnum }; diff --git a/packages/ddd/src/blank-record/properties/repository-status/definition/enum/repository-status.enum.mts b/packages/ddd/src/blank-record/properties/repository-status/definition/enum/repository-status.enum.mts new file mode 100644 index 00000000..da83cc33 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/definition/enum/repository-status.enum.mts @@ -0,0 +1,9 @@ +const enum RepositoryStatusEnum +{ + NEW = "new", + SAVED = "saved", + ARCHIVED = "archived", + DESTROYED = "destroyed", +} + +export { RepositoryStatusEnum }; diff --git a/packages/ddd/src/blank-record/properties/repository-status/definition/type/_index.mts b/packages/ddd/src/blank-record/properties/repository-status/definition/type/_index.mts new file mode 100644 index 00000000..3bed1079 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/definition/type/_index.mts @@ -0,0 +1 @@ +export type * from "./repository-status-property.type.mjs"; diff --git a/packages/ddd/src/blank-record/properties/repository-status/definition/type/repository-status-property.type.mts b/packages/ddd/src/blank-record/properties/repository-status/definition/type/repository-status-property.type.mts new file mode 100644 index 00000000..99fcfd9b --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/definition/type/repository-status-property.type.mts @@ -0,0 +1,7 @@ +import type { BaseProperty } from "../../../../../base/auxiliary/property/base-property.mjs"; +import type { RepositoryStatusEnum } from "../enum/repository-status.enum.mjs"; +import type { RepositoryStatusPropertyDefinition } from "../../repository-status-property-definition.mjs"; + +type RepositoryStatusPropertyType = BaseProperty; + +export type { RepositoryStatusPropertyType }; diff --git a/packages/ddd/src/blank-record/properties/repository-status/predicate/_index.mts b/packages/ddd/src/blank-record/properties/repository-status/predicate/_index.mts new file mode 100644 index 00000000..11569281 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/predicate/_index.mts @@ -0,0 +1 @@ +export * from "./assert-repository-status-enum.mjs"; diff --git a/packages/ddd/src/blank-record/properties/repository-status/predicate/assert-repository-status-enum.mts b/packages/ddd/src/blank-record/properties/repository-status/predicate/assert-repository-status-enum.mts new file mode 100644 index 00000000..96da70b8 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/predicate/assert-repository-status-enum.mts @@ -0,0 +1,14 @@ +import { assertEnumValue } from "@vitruvius-labs/ts-predicate"; +import { RepositoryStatusEnum } from "../definition/enum/repository-status.enum.mjs"; + +function assertRepositoryStatusEnum(value: unknown): asserts value is RepositoryStatusEnum +{ + assertEnumValue(value, [ + RepositoryStatusEnum.NEW, + RepositoryStatusEnum.SAVED, + RepositoryStatusEnum.ARCHIVED, + RepositoryStatusEnum.DESTROYED, + ]); +} + +export { assertRepositoryStatusEnum }; diff --git a/packages/ddd/src/blank-record/properties/repository-status/repository-status-meta-property.mts b/packages/ddd/src/blank-record/properties/repository-status/repository-status-meta-property.mts new file mode 100644 index 00000000..7401d84a --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/repository-status-meta-property.mts @@ -0,0 +1,28 @@ +import type { BasePropertyInstantiationInterface } from "../../../base/auxiliary/property/definition/interface/base-property-instantiation.interface.mjs"; +import { PredefinedMetaProperty } from "../../../base/auxiliary/meta-property/predefined-meta-property.mjs"; +import { RepositoryStatusEnum } from "./definition/enum/repository-status.enum.mjs"; +import { RepositoryStatusPropertyDefinition } from "./repository-status-property-definition.mjs"; +import { RepositoryStatusProperty } from "./repository-status-property.mjs"; + +class RepositoryStatusMetaProperty extends PredefinedMetaProperty +{ + // eslint-disable-next-line @ts/class-methods-use-this, @ts/require-await + public async getPropertyDefinition(): Promise + { + return new RepositoryStatusPropertyDefinition(); + } + + // eslint-disable-next-line @ts/class-methods-use-this + protected createProperty(parameters: BasePropertyInstantiationInterface): RepositoryStatusProperty + { + return new RepositoryStatusProperty(parameters); + } + + // eslint-disable-next-line @ts/class-methods-use-this, @ts/require-await + protected async getInitialValue(): Promise + { + return RepositoryStatusEnum.NEW; + } +} + +export { RepositoryStatusMetaProperty }; diff --git a/packages/ddd/src/blank-record/properties/repository-status/repository-status-property-definition.mts b/packages/ddd/src/blank-record/properties/repository-status/repository-status-property-definition.mts new file mode 100644 index 00000000..ab302ae0 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/repository-status-property-definition.mts @@ -0,0 +1,17 @@ +import type { RepositoryStatusEnum } from "./definition/enum/repository-status.enum.mjs"; +import { BasePropertyDefinition } from "../../../base/auxiliary/property-definition/base-property-definition.mjs"; +import { BlankRecordPropertyEnum } from "../../definition/enum/blank-record-property.enum.mjs"; +import { RepositoryStatusTypeDefinition } from "./repository-status-type-definition.mjs"; + +class RepositoryStatusPropertyDefinition extends BasePropertyDefinition +{ + public constructor() + { + super({ + identifier: BlankRecordPropertyEnum.REPOSITORY_STATUS, + typeDefinition: new RepositoryStatusTypeDefinition(), + }); + } +} + +export { RepositoryStatusPropertyDefinition }; diff --git a/packages/ddd/src/blank-record/properties/repository-status/repository-status-property.mts b/packages/ddd/src/blank-record/properties/repository-status/repository-status-property.mts new file mode 100644 index 00000000..9183e451 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/repository-status-property.mts @@ -0,0 +1,144 @@ +import type { RepositoryStatusPropertyDefinition } from "./repository-status-property-definition.mjs"; +import type { BasePropertyInstantiationInterface } from "../../../base/auxiliary/property/definition/interface/base-property-instantiation.interface.mjs"; +import { BaseProperty } from "../../../base/auxiliary/property/base-property.mjs"; +import { RepositoryStatusEnum } from "./definition/enum/repository-status.enum.mjs"; +import { RepositoryActionEnum } from "./definition/enum/repository-action.enum.mjs"; +import { NoValue } from "@vitruvius-labs/ts-predicate"; + +class RepositoryStatusProperty extends BaseProperty +{ + protected action: RepositoryActionEnum; + + public constructor(parameters: BasePropertyInstantiationInterface) + { + super(parameters); + + this.action = this.getDefaultAction(parameters.value); + } + + // eslint-disable-next-line @ts/class-methods-use-this + public override isMutable(): false + { + return false; + } + + public override setValue(): never + { + throw new Error(`Property "${this.getDefinition().getIdentifier()}" cannot be mutated directly.`); + } + + public async repositoryConfirmation(): Promise + { + const status: RepositoryStatusEnum | typeof NoValue = this.getNextStatus(this.action); + + if (status === NoValue) + { + return; + } + + await super.setValue(status); + + this.overwritePreviousValue(); + + this.action = this.getDefaultAction(status); + } + + public async setAction(action: RepositoryActionEnum): Promise + { + const status: RepositoryStatusEnum = await this.getValue(); + + const expected_status: RepositoryStatusEnum = this.getExpectedStatus(action); + + if (status !== expected_status) + { + throw new Error(`Cannot ${action} a record that is not ${expected_status}, record is ${status}.`); + } + + this.action = action; + } + + public async resetAction(): Promise + { + const status: RepositoryStatusEnum = await this.getValue(); + + this.action = this.getDefaultAction(status); + } + + public getAction(): RepositoryActionEnum + { + return this.action; + } + + // eslint-disable-next-line @ts/class-methods-use-this + private getDefaultAction(status: RepositoryStatusEnum): RepositoryActionEnum + { + switch (status) + { + case RepositoryStatusEnum.NEW: + return RepositoryActionEnum.REGISTER; + + case RepositoryStatusEnum.SAVED: + return RepositoryActionEnum.UPDATE; + + case RepositoryStatusEnum.ARCHIVED: + return RepositoryActionEnum.ERROR_ARCHIVED; + + case RepositoryStatusEnum.DESTROYED: + return RepositoryActionEnum.ERROR_DESTROYED; + } + } + + // eslint-disable-next-line @ts/class-methods-use-this + private getNextStatus(action: RepositoryActionEnum): RepositoryStatusEnum | typeof NoValue + { + switch (action) + { + case RepositoryActionEnum.REGISTER: + return RepositoryStatusEnum.SAVED; + + case RepositoryActionEnum.UPDATE: + return NoValue; + + case RepositoryActionEnum.ARCHIVE: + return RepositoryStatusEnum.ARCHIVED; + + case RepositoryActionEnum.RESTORE: + return RepositoryStatusEnum.SAVED; + + case RepositoryActionEnum.DESTROY: + return RepositoryStatusEnum.DESTROYED; + + case RepositoryActionEnum.ERROR_ARCHIVED: + case RepositoryActionEnum.ERROR_DESTROYED: + throw new Error("Impossible"); + } + } + + // eslint-disable-next-line @ts/class-methods-use-this + private getExpectedStatus(action: RepositoryActionEnum): RepositoryStatusEnum + { + switch (action) + { + case RepositoryActionEnum.REGISTER: + return RepositoryStatusEnum.NEW; + + case RepositoryActionEnum.UPDATE: + return RepositoryStatusEnum.SAVED; + + case RepositoryActionEnum.ARCHIVE: + return RepositoryStatusEnum.SAVED; + + case RepositoryActionEnum.RESTORE: + return RepositoryStatusEnum.ARCHIVED; + + case RepositoryActionEnum.DESTROY: + return RepositoryStatusEnum.SAVED; + + case RepositoryActionEnum.ERROR_ARCHIVED: + case RepositoryActionEnum.ERROR_DESTROYED: + throw new Error("Impossible"); + } + } +} + +export { RepositoryStatusProperty }; diff --git a/packages/ddd/src/blank-record/properties/repository-status/repository-status-type-definition.mts b/packages/ddd/src/blank-record/properties/repository-status/repository-status-type-definition.mts new file mode 100644 index 00000000..1e07d31b --- /dev/null +++ b/packages/ddd/src/blank-record/properties/repository-status/repository-status-type-definition.mts @@ -0,0 +1,14 @@ +import type { RepositoryStatusEnum } from "./definition/enum/repository-status.enum.mjs"; +import { BaseRequiredTypeDefinition } from "../../../base/auxiliary/type-definition/base-required-type-definition.mjs"; +import { assertRepositoryStatusEnum } from "./predicate/assert-repository-status-enum.mjs"; + +class RepositoryStatusTypeDefinition extends BaseRequiredTypeDefinition +{ + // eslint-disable-next-line @ts/class-methods-use-this + public assertType(value: unknown): asserts value is RepositoryStatusEnum + { + assertRepositoryStatusEnum(value); + } +} + +export { RepositoryStatusTypeDefinition }; diff --git a/packages/ddd/src/blank-record/properties/uuid-v4/_index.mts b/packages/ddd/src/blank-record/properties/uuid-v4/_index.mts new file mode 100644 index 00000000..71cc69ee --- /dev/null +++ b/packages/ddd/src/blank-record/properties/uuid-v4/_index.mts @@ -0,0 +1,5 @@ +export type * from "./definition/_index.mjs"; +export * from "./predicate/_index.mjs"; +export * from "./uuid-v4-meta-property.mjs"; +export * from "./uuid-v4-property-definition.mjs"; +export * from "./uuid-v4-type-definition.mjs"; diff --git a/packages/ddd/src/blank-record/properties/uuid-v4/definition/_index.mts b/packages/ddd/src/blank-record/properties/uuid-v4/definition/_index.mts new file mode 100644 index 00000000..343d0f1c --- /dev/null +++ b/packages/ddd/src/blank-record/properties/uuid-v4/definition/_index.mts @@ -0,0 +1 @@ +export type * from "./type/_index.mjs"; diff --git a/packages/ddd/src/blank-record/properties/uuid-v4/definition/type/_index.mts b/packages/ddd/src/blank-record/properties/uuid-v4/definition/type/_index.mts new file mode 100644 index 00000000..ac67799e --- /dev/null +++ b/packages/ddd/src/blank-record/properties/uuid-v4/definition/type/_index.mts @@ -0,0 +1 @@ +export type * from "./uuid-v4-property.type.mjs"; diff --git a/packages/ddd/src/blank-record/properties/uuid-v4/definition/type/uuid-v4-property.type.mts b/packages/ddd/src/blank-record/properties/uuid-v4/definition/type/uuid-v4-property.type.mts new file mode 100644 index 00000000..84bfa609 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/uuid-v4/definition/type/uuid-v4-property.type.mts @@ -0,0 +1,7 @@ +import type { UUID } from "node:crypto"; +import type { BaseImmutableProperty } from "../../../../../base/auxiliary/property/base-immutable-property.mjs"; +import type { UUIDv4PropertyDefinition } from "../../uuid-v4-property-definition.mjs"; + +type UUIDv4PropertyType = BaseImmutableProperty; + +export type { UUIDv4PropertyType }; diff --git a/packages/ddd/src/blank-record/properties/uuid-v4/predicate/_index.mts b/packages/ddd/src/blank-record/properties/uuid-v4/predicate/_index.mts new file mode 100644 index 00000000..c3c97f0c --- /dev/null +++ b/packages/ddd/src/blank-record/properties/uuid-v4/predicate/_index.mts @@ -0,0 +1 @@ +export * from "./assert-uuid-v4.mjs"; diff --git a/packages/ddd/src/blank-record/properties/uuid-v4/predicate/assert-uuid-v4.mts b/packages/ddd/src/blank-record/properties/uuid-v4/predicate/assert-uuid-v4.mts new file mode 100644 index 00000000..078c4d53 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/uuid-v4/predicate/assert-uuid-v4.mts @@ -0,0 +1,17 @@ +import type { UUID } from "node:crypto"; +import { ValidationError } from "@vitruvius-labs/ts-predicate"; + +function assertUUIDv4(value: unknown): asserts value is UUID +{ + if (typeof value !== "string") + { + throw new ValidationError("Value is not a UUIDv4."); + } + + if (!/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/.test(value)) + { + throw new ValidationError("Value is not a UUIDv4."); + } +} + +export { assertUUIDv4 }; diff --git a/packages/ddd/src/blank-record/properties/uuid-v4/uuid-v4-meta-property.mts b/packages/ddd/src/blank-record/properties/uuid-v4/uuid-v4-meta-property.mts new file mode 100644 index 00000000..14c5a1fe --- /dev/null +++ b/packages/ddd/src/blank-record/properties/uuid-v4/uuid-v4-meta-property.mts @@ -0,0 +1,28 @@ +import { type UUID, randomUUID } from "node:crypto"; +import type { BasePropertyInstantiationInterface } from "../../../base/auxiliary/property/definition/interface/base-property-instantiation.interface.mjs"; +import { PredefinedMetaProperty } from "../../../base/auxiliary/meta-property/predefined-meta-property.mjs"; +import { BaseImmutableProperty } from "../../../base/auxiliary/property/base-immutable-property.mjs"; +import { UUIDv4PropertyDefinition } from "./uuid-v4-property-definition.mjs"; + +class UUIDv4MetaProperty extends PredefinedMetaProperty> +{ + // eslint-disable-next-line @ts/class-methods-use-this, @ts/require-await + public async getPropertyDefinition(): Promise + { + return new UUIDv4PropertyDefinition(); + } + + // eslint-disable-next-line @ts/class-methods-use-this + protected createProperty(parameters: BasePropertyInstantiationInterface): BaseImmutableProperty + { + return new BaseImmutableProperty(parameters); + } + + // eslint-disable-next-line @ts/class-methods-use-this, @ts/require-await + protected async getInitialValue(): Promise + { + return randomUUID(); + } +} + +export { UUIDv4MetaProperty }; diff --git a/packages/ddd/src/blank-record/properties/uuid-v4/uuid-v4-property-definition.mts b/packages/ddd/src/blank-record/properties/uuid-v4/uuid-v4-property-definition.mts new file mode 100644 index 00000000..6c41ec0c --- /dev/null +++ b/packages/ddd/src/blank-record/properties/uuid-v4/uuid-v4-property-definition.mts @@ -0,0 +1,17 @@ +import type { UUID } from "node:crypto"; +import { BasePropertyDefinition } from "../../../base/auxiliary/property-definition/base-property-definition.mjs"; +import { UUIDv4TypeDefinition } from "./uuid-v4-type-definition.mjs"; +import { BlankRecordPropertyEnum } from "../../definition/enum/blank-record-property.enum.mjs"; + +class UUIDv4PropertyDefinition extends BasePropertyDefinition +{ + public constructor() + { + super({ + identifier: BlankRecordPropertyEnum.UUID, + typeDefinition: new UUIDv4TypeDefinition(), + }); + } +} + +export { UUIDv4PropertyDefinition }; diff --git a/packages/ddd/src/blank-record/properties/uuid-v4/uuid-v4-type-definition.mts b/packages/ddd/src/blank-record/properties/uuid-v4/uuid-v4-type-definition.mts new file mode 100644 index 00000000..ab4eaed3 --- /dev/null +++ b/packages/ddd/src/blank-record/properties/uuid-v4/uuid-v4-type-definition.mts @@ -0,0 +1,14 @@ +import type { UUID } from "node:crypto"; +import { BaseRequiredTypeDefinition } from "../../../base/auxiliary/type-definition/base-required-type-definition.mjs"; +import { assertUUIDv4 } from "./predicate/assert-uuid-v4.mjs"; + +class UUIDv4TypeDefinition extends BaseRequiredTypeDefinition +{ + // eslint-disable-next-line @ts/class-methods-use-this + public assertType(value: unknown): asserts value is UUID + { + assertUUIDv4(value); + } +} + +export { UUIDv4TypeDefinition }; diff --git a/packages/ddd/src/blank-record/write-repository-hooks.mts b/packages/ddd/src/blank-record/write-repository-hooks.mts new file mode 100644 index 00000000..e7ed4127 --- /dev/null +++ b/packages/ddd/src/blank-record/write-repository-hooks.mts @@ -0,0 +1,40 @@ +import type { BlankRecord } from "./blank-record.mjs"; + +/* eslint-disable @ts/no-empty-function */ +/* eslint-disable @ts/no-unused-vars */ +/* eslint-disable @ts/class-methods-use-this */ + +abstract class WriteRepositoryHooks> +{ + // @ts-expect-error -- Parameters for overrides + public async onPreRegister(instance: M): Promise {} + + // @ts-expect-error -- Parameters for overrides + public async onPostRegister(instance: M): Promise {} + + // @ts-expect-error -- Parameters for overrides + public async onPreUpdate(instance: M): Promise {} + + // @ts-expect-error -- Parameters for overrides + public async onPostUpdate(instance: M): Promise {} + + // @ts-expect-error -- Parameters for overrides + public async onPreRestore(instance: M): Promise {} + + // @ts-expect-error -- Parameters for overrides + public async onPostRestore(instance: M): Promise {} + + // @ts-expect-error -- Parameters for overrides + public async onPreArchive(instance: M): Promise {} + + // @ts-expect-error -- Parameters for overrides + public async onPostArchive(instance: M): Promise {} + + // @ts-expect-error -- Parameters for overrides + public async onPreDestroy(instance: M): Promise {} + + // @ts-expect-error -- Parameters for overrides + public async onPostDestroy(instance: M): Promise {} +} + +export { WriteRepositoryHooks }; diff --git a/packages/ddd/tsconfig.build.json b/packages/ddd/tsconfig.build.json new file mode 100644 index 00000000..093cc419 --- /dev/null +++ b/packages/ddd/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./build/esm", + "declarationDir": "./build/types", + "rootDir": "./src", + "declaration": true, + "declarationMap": false, + "removeComments": false, + "sourceMap": false, + "noEmit": false, + "listFiles": false, + "listEmittedFiles": false + }, + "include": [ + "./src" + ] +} diff --git a/packages/ddd/tsconfig.eslint.json b/packages/ddd/tsconfig.eslint.json new file mode 100644 index 00000000..e3fc6e6b --- /dev/null +++ b/packages/ddd/tsconfig.eslint.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": {}, + "include": [ + "./src", + "./test", + "./mock" + ] +} diff --git a/packages/ddd/tsconfig.json b/packages/ddd/tsconfig.json new file mode 100755 index 00000000..4066ce62 --- /dev/null +++ b/packages/ddd/tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.json", + "compilerOptions": { + "allowSyntheticDefaultImports": true + }, + "include": [ + "./src", + "./test", + "./mock" + ], + "exclude": [ + "./node_modules" + ] +} diff --git a/packages/ddd/tsconfig.mocha.json b/packages/ddd/tsconfig.mocha.json new file mode 100644 index 00000000..73373ac4 --- /dev/null +++ b/packages/ddd/tsconfig.mocha.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "include": [ + ".", + "./**/.*", + "./**/*.json" + ], + "ts-node": { + "files": true, + "swc": true + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c674449c..26343cc8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -110,6 +110,15 @@ importers: specifier: workspace:^ version: link:../ts-predicate + packages/ddd: + dependencies: + '@vitruvius-labs/toolbox': + specifier: workspace:^ + version: link:../toolbox + '@vitruvius-labs/ts-predicate': + specifier: workspace:^ + version: link:../ts-predicate + packages/functional: {} packages/mockingbird: @@ -967,36 +976,42 @@ packages: engines: {node: '>=10'} cpu: [arm64] os: [linux] + libc: [glibc] '@swc/core-linux-arm64-musl@1.15.24': resolution: {integrity: sha512-ZpF/pRe1guk6sKzQI9D1jAORtjTdNlyeXn9GDz8ophof/w2WhojRblvSDJaGe7rJjcPN8AaOkhwdRUh7q8oYIg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] + libc: [musl] '@swc/core-linux-ppc64-gnu@1.15.24': resolution: {integrity: sha512-QZEsZfisHTSJlmyChgDFNmKPb3W6Lhbfo/O76HhIngfEdnQNmukS38/VSe1feho+xkV5A5hETyCbx3sALBZKAQ==} engines: {node: '>=10'} cpu: [ppc64] os: [linux] + libc: [glibc] '@swc/core-linux-s390x-gnu@1.15.24': resolution: {integrity: sha512-DLdJKVsJgglqQrJBuoUYNmzm3leI7kUZhLbZGHv42onfKsGf6JDS3+bzCUQfte/XOqDjh/tmmn1DR/CF/tCJFw==} engines: {node: '>=10'} cpu: [s390x] os: [linux] + libc: [glibc] '@swc/core-linux-x64-gnu@1.15.24': resolution: {integrity: sha512-IpLYfposPA/XLxYOKpRfeccl1p5dDa3+okZDHHTchBkXEaVCnq5MADPmIWwIYj1tudt7hORsEHccG5no6IUQRw==} engines: {node: '>=10'} cpu: [x64] os: [linux] + libc: [glibc] '@swc/core-linux-x64-musl@1.15.24': resolution: {integrity: sha512-JHy3fMSc0t/EPWgo74+OK5TGr51aElnzqfUPaiRf2qJ/BfX5CUCfMiWVBuhI7qmVMBnk1jTRnL/xZnOSHDPLYg==} engines: {node: '>=10'} cpu: [x64] os: [linux] + libc: [musl] '@swc/core-win32-arm64-msvc@1.15.24': resolution: {integrity: sha512-Txj+qUH1z2bUd1P3JvwByfjKFti3cptlAxhWgmunBUUxy/IW3CXLZ6l6Gk4liANadKkU71nIU1X30Z5vpMT3BA==} diff --git a/vitruvius-labs-typescript.code-workspace b/vitruvius-labs-typescript.code-workspace index 6bcc31fa..93d89206 100644 --- a/vitruvius-labs-typescript.code-workspace +++ b/vitruvius-labs-typescript.code-workspace @@ -20,6 +20,10 @@ "name": "mockingbird", "path": "packages/mockingbird" }, + { + "name": "ddd", + "path": "packages/ddd" + }, { "name": "architectura", "path": "packages/architectura" @@ -69,6 +73,7 @@ "./packages/aws-s3", "./packages/aws-signature-v4", "./packages/aws-sqs", + "./packages/ddd", "./packages/functional", "./packages/mockingbird", "./packages/testing-ground",