Showing preview only (361K chars total). Download the full file or copy to clipboard to get everything.
Repository: 4lessandrodev/types-ddd
Branch: main
Commit: dba35bb7b257
Files: 128
Total size: 329.6 KB
Directory structure:
gitextract_7wdim7d5/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── dependabot.yml
│ └── workflows/
│ └── main.yml
├── .gitignore
├── .husky/
│ ├── pre-commit
│ └── pre-push
├── .prettierignore
├── .prettierrc
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── check-dev-deps.sh
├── docs/
│ └── README.md
├── global-setup.ts
├── jest.config.ts
├── lerna.json
├── package.json
├── packages/
│ ├── cnpj/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ ├── cnpj.value-object.util.spec.ts
│ │ │ └── is-valid-cpf-digit.util.spec.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── util.ts
│ ├── cpf/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ ├── cpf.value-object.util.spec.ts
│ │ │ └── is-valid-cpf-digit.util.spec.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── util.ts
│ ├── date/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ └── date.value-object.spec.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ ├── types.ts
│ │ └── util.ts
│ ├── email/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ ├── email-validator.spec.ts
│ │ │ └── email.value-object.spec.ts
│ │ ├── email.validator.util.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── logger/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ └── logger.spec.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── money/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ └── money.spec.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── password/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ ├── password.spec.ts
│ │ │ └── util.spec.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── utils.ts
│ ├── patterns/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ └── specification.value-object.spec.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── phone/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ ├── home-phone.value-object.spec.ts
│ │ │ ├── mobile-phone.value-object.spec.ts
│ │ │ └── phone.value-object.spec.ts
│ │ ├── ddd.list.ts
│ │ ├── home.value-object.ts
│ │ ├── index.ts
│ │ ├── mobile.value-object.ts
│ │ ├── package.json
│ │ ├── phone.value-object.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── type-ddd/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── username/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ └── user-name-value-object.util.spec.ts
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── zip-code/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── zip-code.value-object.spec.ts
│ ├── index.ts
│ ├── package.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── util.ts
├── scripts/
│ ├── login.sh
│ └── make-user.sh
├── tsconfig.json
└── update-peer-dependency.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
================================================
FILE: .github/workflows/main.yml
================================================
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
# Runs a single command using the runners shell
- name: Install dependencies
run: yarn install
# Runs a set of commands using the runners shell
- name: Run all tests
run: yarn test
# Runs a set of commands using the runners shell
- name: Run typescript check
run: yarn tsc --noEmit
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
build
.vscode/
packages/*/*.js
packages/*/*.d.ts
packages/*/tsconfig.build.tsbuildinfo
.npmrc
.nx/
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run check:types
npx lint-staged
================================================
FILE: .husky/pre-push
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run test
================================================
FILE: .prettierignore
================================================
# Ignore everything:
/*
/__snapshots__/*
*.ts.snap
# Except:
!/lib
!/example
!/tests
!jest.config.ts
!package.json
!tsconfig.json
!tsconfig.lib.json
!webpack.config.ts
================================================
FILE: .prettierrc
================================================
{
"useTabs": true,
"arrowParens": "always",
"singleQuote": true,
"semi": true,
"bracketSpacing": true,
"endOfLine": "lf",
"tabWidth": 4
}
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased
---
## Released
---
### [4.1.0] - 2024-12-15
#### Feat
- Atualizando lib core para nova versão
- rich-domain v1.25.0
---
### [4.0.5] - 2024-11-28
### Fix
- update rich-domain lib to check nullish type: now 'create' return a possibly null value in Result instance.
---
### [4.0.4] - 2024-09-26
### Fix
- Corrected `"files"` in `package.json` to include `utils.js` and `utils.d.ts`, resolving module not found errors during compilation in email and password.
---
### [4.0.2] - 2024-06-26
### Fix
- update: update rich-domain to v1.23.3
---
### [4.0.0] - 2024-05-31
### Refactor (Break Change)
- refactor: change lib to individual packages
---
### [3.9.0] - 2024-04-28
### Update (Break Change)
- Update core to v1.23.0
- check [Core Changelog](https://github.com/4lessandrodev/rich-domain/blob/main/CHANGELOG.md)
---
## Released
### [3.8.3] - 2024-04-13
### Update
- Update core
- Added support to multi context name
- Details [Commit](https://github.com/4lessandrodev/rich-domain/commit/00db292f0604469c8bf2f2fddf6460901a084cc6)
```ts
// Example Usage
const context = Context.events();
const handler = (event) => console.log(event);
// Subscribing to events under different contexts
context.subscribe('Context-A:SIGNUP', handler);
context.subscribe('Context-B:SIGNUP', handler);
context.subscribe('Context-C:SIGNUP', handler);
context.subscribe('Context-B:NOTIFY', handler);
context.subscribe('Context-B:SEND-EMAIL', handler);
// Dispatching events to specific contexts
// Dispatches the SIGNUP event to Context-B
context.dispatchEvent('Context-B:SIGNUP');
// Dispatches the SIGNUP event to all contexts
context.dispatchEvent('*:SIGNUP');
// Dispatches all events to all contexts. Not recommended
context.dispatchEvent('*:*');
// Dispatches all events under Context-B
context.dispatchEvent('Context-B:*');
```
---
### [3.8.2] - 2024-04-12
### Update
- Update core
- Added support to global events [ChangeLog](https://github.com/4lessandrodev/rich-domain/pull/139)
---
### [3.8.1] - 2024-03-18
### Update
- Update core
- Fix logger messages
---
### [3.8.0] - 2024-03-18
### Update
- update deps core to v1.20.0 see [changes](https://github.com/4lessandrodev/rich-domain/blob/main/CHANGELOG.md)
---
### [3.7.2] - 2024-03-15
### Update
- update deps
- added support to nodejs v21
---
### [3.7.1] - 2023-12-15
### Update
- update deps
---
### [3.7.0] - 2023-09-30
### Update
- Update core
- update deps
- rich-domain: update lib core to 1.19.0
- remove support for deprecated history method
- improve performance and save memory usage
---
### [3.6.4] - 2023-08-24
### Update
- Update core
- update deps
- rich-domain: update lib core to 1.18.4
---
### [3.6.3] - 2023-07-30
### Update
- Update core
- rich-domain: update lib core to 1.18.3 #272 #282
---
### [3.6.2] - 2023-07-09
### Update
- Update core
- rich-domain: update lib core to 1.18.2 #272
---
---
### [3.6.1] - 2023-06-30
### Update
- Update core
- rich-domain: update lib core to 1.18.1
---
### [3.6.0] - 2023-04-21
### Update
- Update core
- rich-domain: update lib core to 1.18.0 [More](https://github.com/4lessandrodev/rich-domain/blob/main/CHANGELOG.md)
---
### [3.5.3] - 2023-02-18
### Update
- Update core
- rich-domain: update lib core to 1.17.3
---
### [3.5.2] - 2023-02-18
### Changed
- validation to `url.value-object` use URL default validation and remove regex. by @ArturHamannRonconi
### Added
- added separator as optional param to `getInitials`method from `user-name.value-object`
---
### [3.5.1] - 2023-01-27
### Update
- Update core
- rich-domain: update lib core to 1.17.1
---
### [3.5.0] - 2023-01-21
### Update
- Update core
### Breaking Change
- rich-domain: update lib core to 1.17.0 check on [pull request 33](https://github.com/4lessandrodev/rich-domain/pull/33)
```ts
// Example using set now
const changed = user.set("name").to(age);
console.log(changed);
> true
```
```ts
// Example using clone now
const copy = user.clone();
console.log(copy.get("name").get("value"))
> "Jane Doe"
```
---
### [3.4.7] - 2023-01-19
### Update
- rich-domain: update lib core to 1.16.2
---
### [3.4.6] - 2023-01-18
### Update
- rich-domain: update lib core to 1.16.1
---
### [3.4.5] - 2023-01-14
### Changed
- date.value-object: rename method from `isEqual` to `isEqualDate`
### Update
- rich-domain: update lib core to 1.16.0
- Entity: added method isEqual to compare current instance with another one.
- ValueObject: added method isEqual to compare current instance with another one. [Issue 27](https://github.com/4lessandrodev/rich-domain/issues/27)
---
### [3.4.4] - 2023-01-12
### Added
- custom-string.value-object: By: [VinnyLima](https://github.com/VinnyLima)
- removeSpecialChars and onlyNumbers: [Issue 223](https://github.com/4lessandrodev/types-ddd/issues/223)
- email.value-object: added MESSAGE as customizable value
---
### [3.4.3] - 2023-01-05
### Updated
- rich-domain: update lib core to 1.15.2
---
### [3.4.2] - 2023-01-03
### Fix
- node version: update requirements. node version required >=16 and < 19
---
### [3.4.1] - 2023-01-03
### Fix
- user-name.value-object: remove empty spaces. By: [VinnyLima](https://github.com/VinnyLima)
---
### [3.4.0] - 2022-12-25
### Update
- rich-domain: update lib core to 1.15.0
- value-objects: added MESSAGE attribute to instance
Now its possible to customize error message
Example:
```ts
// custom-user-name.ts
import { UserNameValueObject } from 'types-ddd';
Reflect.set(UserNameValueObject, "MIN_LENGTH", 3);
Reflect.set(UserNameValueObject, "MAX_LENGTH", 20);
Reflect.set(UserNameValueObject, "MESSAGE", "Username must be a maximum of 3 and a minimum of 20 characters");
const CustomName = UserNameValueObject;
export CustomName; // > import this to create your user name
export default CustomName;
```
---
### [3.3.7] - 2022-11-27
### Update
- rich-domain: update lib core to 1.14.6
---
### [3.3.6] - 2022-11-25
### Update
- rich-domain: update lib core to 1.14.5
---
### [3.3.5] - 2022-11-22
### Update
- rich-domain: update lib core to 1.14.4
---
### [3.3.4] - 2022-11-22
### Update
- rich-domain: update lib core to 1.14.3
---
### [3.3.3] - 2022-11-17
### Changed
- Ok and Fail: ensure export from lib
---
### [3.3.2] - 2022-11-07
### Changed
- chore: deps - update deps
---
### [3.3.1] - 2022-11-03
### Fixed
- value-objects: calc validation
---
### [3.3.0] - 2022-10-05
### Changed
- value-objects: implement customization for value objects
---
### [3.2.2] - 2022-10-03
### Changed
- result: implement freeze result instance
- password: define protected props as MAX_LENGTH and MIN_LENGTH
- update deps: rich-domain
---
### [3.2.1] - 2022-09-26
### Changed
- update deps: rich-domain
- refactor: Fail
- refactor: Ok
- refactor: Result.Ok
- refactor: Result.fail
Change generic types order for `Result.fail` and `Result.Ok`
Now each method has your own order
Example:
```ts
// for implementation:
IResult<Payload, Error, MetaData>;
// before the generic order was the same for both method.
// now for
Result.Ok
// the generic order is
Result.Ok<Payload, MetaData, Error>(payload metaData);
// for
Result.fail
//the generic order is
Result.fail<Error, MetaData, Payload>(error, metaData);
```
Changes made on Ok
```ts
import { Ok } from 'rich-domain';
// simple use case for success. no arg required
return Ok();
// arg required
return Ok<string>('my payload');
// arg and metaData required
interface MetaData {
arg: string;
}
return Ok<string, MetaData>('payload', { arg: 'sample' });
```
Changes made on Fail
```ts
import { Fail } from 'rich-domain';
// simple use case for success. no arg required
return Fail();
// arg required
return Fail<string>('my payload');
// arg and metaData required
interface MetaData {
arg: string;
}
return Fail<string, MetaData>('payload', { arg: 'sample' });
```
---
### [3.2.0] - 2022-09-26
### Added
- update deps: rich-domain
- feat: implement function Fail
- feat: implement function Ok
---
### [3.1.5] - 2022-09-26
### Fixed
- EmailValueObject: remove regex and added function validation
---
### [3.1.4] - 2022-09-20
### Update
deps: update dependencies
- rich-domain to v1.12.0
---
### [3.1.3] - 2022-09-03
### Update
deps: update dependencies
- rich-domain to v1.11.2
- typescript to 4.8.2
---
### [3.1.2] - 2022-08-14
### Update
docs: update readme and documentation
deps: update dependencies
### Added
ci: install dependabot to check deps
---
### [3.1.1] - 2022-08-14
### Update
docs: update readme and documentation
---
### [3.1.0] - 2022-08-10
### Changed
- deps: update dependencies rich-domain to version 1.11.0
Change order validation args in value objects
```ts
// from
validation<Key extends keyof Props>(key: Key, value: Props[Key]): boolean {};
// to
validation<Key extends keyof Props>(value: Props[Key], key: Key): boolean {};
```
---
### [3.0.2] - 2022-08-07
### Update
- deps: update dependencies rich-domain to version 1.10.0
---
### [3.0.1-beta.0] - 2022-08-05
### Update
- deps: update dependencies rich-domain to version 1.9.0
---
### [3.0.0-beta.0] - 2022-08-05
### Update
- deps: update dependencies
---
### [3.0.0-beta] - 2022-08-04
### Change
- change core (**breaking changes**). using now rich-domain lib [npm rich-domain](https://www.npmjs.com/package/rich-domain)
---
### [2.12.1] - 2022-07-18
### Update
- deps: update dependencies
---
### [2.12.0] - 2022-04-18
### Changes
- TSProxy: change context param from function to instance of class [pull request](https://github.com/4lessandrodev/types-ddd/pull/144)
- deps: update dependencies
---
### [2.11.0] - 2022-04-02
### Added
- TSProxy: added abstract class as proxy implementation [pull request](https://github.com/4lessandrodev/types-ddd/pull/142)
---
### [2.10.3] - 2022-03-28
### Changes
- logger: make instance a singleton
---
### [2.10.2] - 2022-03-28
### Changes
- logger: update configs
### Update
- deps: update dependencies
---
### [2.10.1] - 2022-03-23
### Update
- deps: update dependencies
---
### [2.10.0] - 2022-02-27
### Fixed
- toObject: return string when there is a domainId as value-object attribute
---
### [2.9.13] - 2022-02-14
### Fixed
- toObject: added support to convert a simple object on entity
---
### [2.9.11] ~ [2.9.12] - 2022-02-13
### Changed
- toObject: added support to convert a value object inside another one
---
### [2.9.9] ~ [2.9.10] - 2022-02-13
### Changed
- update dependencies
- update documentation
---
### [2.9.8] - 2022-02-09
### Fixed
- toObject: fix adding support for string, boolean and numbers to domain entity attributes on call toObject method.
- create: ensure all domain entity implements create method
### Added
- clone: added method to clone a domain entity
---
### [2.9.7] - 2022-01-31
### Added
- logs deactivation: now its possible deactivate all logs;
```sh
NODE_ENV=production # automatically turn off all logs
TYPES_DDD_LOGS=off # manual turn off logs
TYPES_DDD_LOGS=error # show only errors log
TYPES_DDD_LOGS=info # show only info log
TYPES_DDD_LOGS=warn # show only warn log
```
---
### [2.9.6] - 2022-01-30
### Fixed
- toObject: ensure to convert a moderately complex value object
---
### [2.9.5] - 2022-01-30
### Added
- toObject: update types on entity.toObject method
### Changed
- update and change some documentation and examples
- mark IMapper interface as deprecated tool. Use TMapper instead
---
### [2.9.4] - 2022-01-29
### Added
- Imports: Create shortcuts for imports : Issue #114
---
### [2.9.3] - 2022-01-21
### Fixed
- AutoMapper: get string value when prop is DomainId or ShortDomainId
---
### [2.9.1] ~ [2.9.2] - 2022-01-21
### Changed
- DomainId: added clone method to create a new id from an instance
- ShortDomainId: added clone method to create a new id from an instance
### Added
- Available AutoMapper to convert Entity, Aggregate and ValueObject from domain instance to a persistence model
---
### [2.9.0] - 2022-01-21
### Changed
- DomainId and ShortDomainId: added property isNew to identify if is a new id
- Entity, Aggregate and ValueObject: added method toObject to convert domain instance to a persistence model
### Added
- Available AutoMapper to convert Entity, Aggregate and ValueObject from domain instance to a persistence model
---
### [2.8.8] - 2021-12-29
---
### Added
- Entity: hasSomeTypes method to validate different types from instance keys
### Changed
- Entity: isSome method > new accepted type: 'null'
- Entity: isAll method > new accepted type: 'null'
### [2.8.7] - 2021-12-28
---
### Added
- State: addManyState<T, E> method add many results to state and return unique keys
- State: getStateByKeys<T, E> method get many results by keys
### [2.8.6]- 2021-12-26
---
### Added
- Entity: toObject<T, E> method transform instance in persistence object
### [2.8.5] - 2021-12-25
---
### Added
- Entity: added method checkProps to entity instance
### [2.8.4] - 2021-12-24
---
### Changed
- State: define exists method as protected
### Added
- State: added callback on state
### [2.8.3] - 2021-12-23
---
### Changed
- State: define exists method as protected
### [2.8.2] - 2021-12-22
---
### Changed
- Mapper: rename to State
- Mapper: added exists method
### [2.8.1] - 2021-12-22
---
### Changed
- Mapper: added logger if state key does not exits
### [2.8.0] - 2021-12-22
---
### Changed
- static method on domain entities
- buildFromDto > change to build
- buildFromModel > change to build
- buildToModel > change to build
- IMapper2 > change to TMapper
### [2.7.15] - 2021-12-21
---
### Added
- static method on domain entities
- buildFromDto
- buildFromModel
- buildToModel
### [2.7.14] - 2021-12-21
---
### Added
- abstract class Mapper with state management methods
- IMappers interface with new methods
### [2.7.12] - [2.7.13] - 2021-12-14
---
### Changed
- DomainId and ShortDomainId: make both compatible
### [2.7.11] - 2021-12-14
---
### Changed
- BaseDomainEntity: ID accept DomainId or ShortDomainId
- Entity: getHashCode - now returns uid value base value added to ID
- Breaking change - Remove methods from DomainId:
- toShort()
- shortUid
### [2.7.10] - 2021-12-14
---
### Fix
- ShortDomainId: export resource
### [2.7.9] - 2021-12-14
---
### Added
- ShortDomainId: default short domain id - 16 bit
### [2.7.8] - 2021-11-22
---
### Fixed
- PasswordValueObject: validate if instance value already is encrypted.
### [2.7.7] - 2021-11-22
---
### Changed
- DimensionValueObject: now update methods returns updated instance.
- PasswordValueObject: now encrypt method returns updated instance.
- UserNameValueObject: now capitalize method returns updated instance.
- WeightValueObject: now update methods returns updated instance.
### [2.7.6] - 2021-11-21
---
### Fixed
- util: change regex to validate email (includes dot as valid char).
### [2.7.5] - 2021-10-11
---
### Changed
- entities and aggregates: getHashCode > combination of class name and id. Now using short uid.
### Fixed
- lib: publish only dist to keep lib small
### [2.7.4] - 2021-10-09
---
### Fixed
- removeUndefinedKeysFromObject: do not remove dates
### [2.7.3] - 2021-10-08
---
### Changed
- DateValueObject: added comparators methods
### [2.7.2] - 2021-10-08
---
### Changed
- DateValueObject: added validation on create a new instance
### [2.7.1] - 2021-10-08
---
### Added
- DateValueObject
### [2.7.0] - 2021-10-06
---
### Changed
- DomainId > change getters method
### [2.6.2] ~ [2.6.4] - 2021-10-06
---
### Changed
- DomainId > added toShort method
- DomainId > toShort method. Now you can choose length
### [2.6.1] - 2021-09-30
---
### Changed
- getUndefinedKeysAsObject > added new option to return as value
### [2.6.0] - 2021-09-29
---
### Changed
- CurrencyValueObject > added functions to compare values
- getUndefinedKeysAsObject > added option to get path as string
### [2.5.7] ~ 2.5.10 - 2021-09-25
---
### Added
- removeUndefinedKeysFromObject
### [2.5.6] - 2021-09-23
---
### Changed
- dist > update build
### [2.5.5] - 2021-09-24
---
### Changed
- getUndefinedKeysAsObject > define value to be applied
### [2.5.4] - 2021-09-24
---
### Changed
- DimensionValueObject > validate unit before create value object
- WeightValueObject > validate unit before create value object
### [2.5.3] - 2021-09-22
---
### Changed
- DimensionEntity > changed to value object: DimensionValueObject
- WeightEntity > changed to value object: WeightValueObject
### [2.5.2] - 2021-09-21
---
### Changed
- dist: remove unused files on dist
### [2.5.1] - 2021-09-20
---
### Changed
- Lib utils: Imports path
### Added
- WeightUnitValueObject
- UnitOfMeasureValueObject
- DimensionEntity
- WeightEntity
### [2.5.0] - 2021-09-18
---
### Changed
- PinValueObject: Define pin props as optional
- Rename folder: from src to lib
### Added
- CPFValueObject
- CNPJValueObject
- CustomStringValueObject
- CustomNumberValueObject
- HEXColorValueObject: Ensure don't generate light color like white
- RGBColorValueObject: Ensure don't generate light color like white
### [2.4.2] ~ [2.4.10] - 2021-09-09
---
### Fixed
- Update dependencies
### [2.4.1] - 2021-09-09
---
### Fixed
- UrlValueObject: export value object
### [2.4.0] - 2021-09-07
---
### Changed
- PinValueObject: util value object
### [2.3.6] - 2021-08-29
---
### Changed
- CurrencyValueObject: docs - identify max safe number
### [2.3.5] - 2021-08-29
---
### Changed
- Result - Change default generic type on `combine` method to `unknown` instead `any`
### [2.3.4] - 2021-08-29
---
### Changed
- ChangesObserver - Fix added possibility to get all added results `getAllAddedResults`
### [2.3.3] - 2021-08-28
---
### Changed
- Result - Fix possibility to return a void instance. Create a specific method `Result.success`
### [2.3.2] - 2021-08-28
---
### Changed
- Result - Fix possibility to return a void instance
### [2.3.1]- 2021-08-28
---
### Changed
- Result - added an internationalization error message
### [2.3.0] - 2021-08-27
---
### Added
- StatusCodeEnum
### Changed
- Result - provide an enum as string declaration instead number
### [2.2.3] - 2021-08-24
---
### Added
- SpecificationComposite
### Changed
- IBaseRepository - rename params and doc comments
### [2.2.2] - 2021-08-19
---
### Fixed
- colorGenerator
### [2.2.1] - 2021-08-17
---
### Fixed
- index (exports)
### [2.2.0] - 2021-08-17
---
### Added
- getUndefinedKeysAsArray
- getUndefinedKeysAsObject
### [2.1.0] - 2021-08-14
---
### Changed
- CurrencyValueObject
### Added
- ChangesObserver
### [2.0.4] - 2021-08-13
---
### Fixed
- Result
### [2.0.3] - 2021-08-13
---
### Fixed
- Result: new approach
### [2.0.2] - 2021-08-12
---
### Changed
- Dynamic types to Filter on IBaseRepository
### [2.0.1] - 2021-08-12
- Entity
### [2.0.0] - 2021-08-12
---
### Changed
- DomainId
- AggregateRoot: new approach
- Entity: new approach
- Filter
### Added
- OrderStatusValueObject
### Fixed
- AggregateRoot
### [1.5.1] - 2021-08-11
---
### Changed
- BirthdayValueObject
### [1.5.0] - 2021-08-11
---
### Added
- colorConverter
- colorGenerator
### Changed
- RGBColorValueObject
- HEXColorValueObject
### [1.4.1] - 2021-08-11
---
### Added
- RGBColorValueObject
- HEXColorValueObject
- PostalCodeValueObject
- UrlValueObject
### Changed
- Result
### [1.3.1] - 2021-08-10
---
### Fixed
- TrackingCodeValueObject
### [1.3.0] - 2021-08-10
---
### Changed
- Result StatusCode
### Added
- Logger
- HomePhoneValueObject
- MobilePhoneValueObject
- DomainId
- TrackingCodeValueObject
### [1.2.0] - 2021-08-09
---
### Added
- PasswordValueObject
- passwordGenerator
- CurrencyValueObject
- EmailValueObject
- UserNameValueObject
- BirthdayValueObject
### [1.1.0] - 2021-07-28
---
### Added
- Dynamic types to Filter
- Types validation to IBaseRepository
### [1.0.3] - 2021-07-16
---
### Fixed
- Define Node crash version on package.json
### [1.0.2] - 2021-07-09
---
### Changed
- Update documentation
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.
Please note we have a code of conduct, please follow it in all your interactions with the project.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Alessandro dev.
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.
================================================
FILE: Makefile
================================================
# Define the targets and the commands to be executed
.PHONY: startVerdaccio stopVerdaccio publishVerdaccio addUser login build graph publish update
# Start Verdaccio Docker container (local NPM registry)
# This will install 'expect', pull the latest Verdaccio image, and run it.
startVerdaccio:
# Install the 'expect' package needed for some automation scripts
sudo apt-get update && sudo apt-get install expect
# Pull the latest nightly version of the Verdaccio image
docker pull verdaccio/verdaccio:nightly-master
# Run Verdaccio in detached mode with port 4873 exposed
docker run -it -d --rm --name lib_verdaccio -p 4873:4873 verdaccio/verdaccio:nightly-master
# Clear any existing NPM configuration
echo "" > ./.npmrc
# Set the registry to the local Verdaccio instance for project-specific use
npm config set registry http://localhost:4873/ --location project
# Stop the running Verdaccio Docker container
stopVerdaccio:
# Stop the Verdaccio container with the name 'lib_verdaccio'
docker stop lib_verdaccio
# Add a new user to the local NPM registry (Verdaccio)
# This will execute the script that interacts with the registry to add a user
addUser:
./scripts/make-user.sh
# Log in to the local NPM registry (Verdaccio)
# This will execute the login script to authenticate the user with Verdaccio
login:
./scripts/login.sh
# Build the project and its dependencies using npm
# This includes running the general build process and the Lerna build process
build:
npm run build && npm run build:lerna
# Build and publish packages to the local Verdaccio registry
# This builds all packages using Yarn and Lerna and publishes them to the local registry
publishVerdaccio:
yarn build
yarn build:lerna
yarn lerna exec "npm publish --registry http://localhost:4873"
# Generate and visualize the dependency graph of the project using Nx
# This will show a graphical representation of the dependencies in the monorepo
graph:
yarn nx graph
# Publish packages using Lerna
# This command publishes packages to the specified registry using Lerna
publish:
yarn lerna publish
# Update a specific peer dependency across all packages in the monorepo
# Example usage: make update lib=rich-domain v=1.1.0
# This will update the specified peer dependency (e.g., 'rich-domain') to the given version (e.g., '1.1.0') in all package.json files in the ./packages directory
# update peer dependency in all packages once
update:
./update-peer-dependency.sh $(lib) $(v)
================================================
FILE: README.md
================================================
# @type-ddd/core
> Now with individual packages
This package provide utils file and interfaces to assistant build a complex application as domain driving design and nodeJS with typescript.
---
<a href="https://www.npmjs.com/package/types-ddd" rel="nofollow" class="keychainify-checked">
<img src="https://badgen.net/github/checks/4lessandrodev/types-ddd/main"
alt="checks"
style="max-width: 100%;">
</a>
<a href="https://www.npmjs.com/package/types-ddd" rel="nofollow" class="keychainify-checked">
<img src="https://badgen.net/github/stars/4lessandrodev/types-ddd"
alt="stars"
style="max-width: 100%;">
</a>
<a href="https://www.npmjs.com/package/types-ddd" rel="nofollow" class="keychainify-checked">
<img src="https://badgen.net/github/commits/4lessandrodev/types-ddd/main"
alt="commits"
style="max-width: 100%;">
</a>
<a href="https://www.npmjs.com/package/types-ddd" rel="nofollow" class="keychainify-checked">
<img src="https://badgen.net/github/last-commit/4lessandrodev/types-ddd/main"
alt="last commit"
style="max-width: 100%;">
</a>
<a href="https://www.npmjs.com/package/types-ddd" rel="nofollow" class="keychainify-checked">
<img src="https://badgen.net/github/license/4lessandrodev/types-ddd"
alt="license"
style="max-width: 100%;">
</a>
<a href="https://www.npmjs.com/package/types-ddd" rel="nofollow" class="keychainify-checked">
<img src="https://badgen.net/github/dependabot/4lessandrodev/types-ddd"
alt="dependabot"
style="max-width: 100%;">
</a>
<a href="https://www.npmjs.com/package/types-ddd" rel="nofollow" class="keychainify-checked">
<img src="https://badgen.net/github/tag/4lessandrodev/types-ddd"
alt="tags"
style="max-width: 100%;">
</a>
<a href="https://www.npmjs.com/package/types-ddd" rel="nofollow" class="keychainify-checked">
<img src="https://badgen.net/github/closed-issues/4lessandrodev/types-ddd"
alt="issues"
style="max-width: 100%;">
</a>
---
## Install core
Install full available packages
```sh
$ npm i @type-ddd/core
# or
$ yarn add @type-ddd/core
```
## Individual
Alternatively you can install individual packages
- `@type-ddd/cpf` [Docs](./packages/cpf)
- `@type-ddd/cnpj` [Docs](./packages/cnpj)
- `@type-ddd/date` [Docs](./packages/date)
- `@type-ddd/email` [Docs](./packages/email)
- `@type-ddd/password` [Docs](./packages/password)
- `@type-ddd/patterns` [Docs](./packages/patterns)
- `@type-ddd/phone` [Docs](./packages/phone)
- `@type-ddd/username` [Docs](./packages/username)
- `@type-ddd/zip-code` [Docs](./packages/zip-code)
- `@type-ddd/money` [Docs](./packages/money)
---
<img src="./readme/cover.png" alt="image" width="100%">
---
## 1. Ubiquitous language:
- Language and terms agreed upon by both business users and developers, within a bounded context
- Entities with the same name in a different context can have different behavior and data
- Bounded context helps in single responsibility for domain models
## 2. Rich domain model:
- Models (entities, value objects, aggregates) with rich behavior are preferred over anemic domain models (entities without behavior, which only keep data and represent the DB tables)
- Due to single responsibility principle (a class or method should have only one reason to change), non-cohesive behavior should be delegated to other classes (or even handled inside domain services) when necessary
- Model methods can also delegate the task to domain services by raising domain events
## 3. Thin domain service working on rich domain models:
- Domain services should not hold state (application services are not domain services, they are on the outer layer close to the UI layer, and can hold application/task state)
- Domain services have very little behavior and only which does not fit cohesively in any domain model
- Domain services sit in the core domain layer along with entities, value objects, aggregates and domain events, and expose domain models in their interfaces
## 4. Layers in a DDD application:
- Core domain layer (domain services, entities, value objects, aggregates and domain events)
- Core domain layer is surrounded by the UI/Application layer and Infrastructure layer
- UI/Application layer (UI and application service facade with messaging, JSON, XML capabilities, session, etc.)
- Infrastructure layer (persistence, file system, network, mail, logging, etc.)
## 5. Entities:
- Live longer than the application, should endure restarts, and are persisted and read from data sources (DB, file system, network, etc.)
- Have an id (preferably a GUID rather than a DB generated int because business transactions do not rely on persistence, can be persisted after other operations carried out in model's behavior)
- Have entity semantics (equality and `GetHashCode()` defined by class name + id)
- Behavior in an entity mostly orchestrates value objects for a use case
- Entity class should not have public property setters, setting a property should be a behavior method
- Entities should not have bidirectional relations (depending on the bounded context, either an egg can have a chicken or a chicken can have eggs, but not both)
- Entity relations should not reflect the complete set of DB foreign key relationships, should be bare down to the minimum for performing the behavior inside the bounded context
- Entity relations should not hold a reference to another entity class, it can only keep the id of another entity
- If a business transaction needs a reference to other entities in relation, aggregates should be used instead (aggregates can hold a reference to other aggregate roots, which are entity classes by definition)
## 6. Value objects:
- Are only identified by their values, not by their ids (for example money is a value object as long as we are not tracking individual banknotes, if we need to track individual banknotes then it should be a banknote entity)
- Can be used to measure or describe things (name, description, amount, height, date, time, range, address, etc.)
- You can combine other value types that usually go together into a new value object type, like address (city, street, country, postal code) or ...range, or ...type
- Prefer to put the behavior on value objects rather than on entities because value objects are immutable and do not have side effects (like changing their state or changing the state of any entity)
- Can be part of an entity
- Have value semantics (equality and `GetHashCode()` defined by property values)
- Should be immutable, behaviors should not change the state of a value object, but can rather create a new value object (should act similar to C# strings, structs, ints, and other value types)
- Can be persisted but only as part of an entity, not individually
## 7. Factories:
- Create, build aggregates and entities:
- Static Create...() factory method on a model class is used to guard against the construction of an invalid or incomplete model
- The model class should not have a public default constructor (however if it is to be persisted, for Entity Framework to work, it can have a protected or private default constructor)
## 8. Aggregates:
- Encapsulate and are composed of entity classes and value objects that change together in a business transaction
- Aggregates are a transactional graph of model objects
- Aggregate root should be an entity, an aggregate can even be a single entity
- Aggregate can keep a reference to other aggregate roots, but not to other entity classes which are not aggregate roots themselves
- Aggregate should not keep a reference to other aggregate root entity classes if those other entities do not change together with this aggregate root entity
- Aggregate can also keep the id of another entity, but keeping too many foreign key ids is a code smell (why?)
- If deleting an entity has a cascade effect on the other entities referenced by class in the object graph, these entities are part of the same aggregate, if not, they should not be inside this aggregate
## 9. Repositories:
- Persist and read aggregates to/from DB or file system
- Should have an interface close to a collection but should allow only the necessary operations needed for this aggregate (for example an aggregate might not need to be allowed to get updated or deleted)
- Should not be generic (should be specific for the aggregate type)
- Can have specific query methods if needed (like `FindByName()` etc.)
- Do not use lazy loading, instead use eager loading (use Include(...) in Entity Framework), else you can face "N+1 problem"s and excessive number of queries sent to DB
- Can have specific methods that only load some of the columns from a table
- Repository add/update/remove operation should commit to DB by itself (call Entity Framework ...Context.SaveChanges() at the end), because aggregate operations should be ACID transactions
- Repository interface sits inside Core domain layer, but implementations are inside Infrastructure layer
- Repositories are not used inside the domain models (entities, value objects, aggregates)
## 10. Shared kernel:
- Is where cross-cutting concerns or common types shared by all bounded contexts sit (like entity abstract base type, value object abstract base type, common value objects, authorization, etc.)
## 11. Domain events:
- Can be raised when a state change occurs in an entity
- Decouple models from each other
- Only used when an event needs to be handled inside a different model than the one raising this event, or handled inside a domain service or even an application service
- Are immutable classes, that represent past, named in the past tense, and cannot change (...Changed, ...Happened, etc.)
- Should include the time that this event was raised, as well as any other useful info for handling the event, as well as the id of the entity which raised the event
- Should not have behavior
- Domain events are subscribed to with a callback (lambda), or using pub sub interfaces, on a singleton or static event message bus
- Domain events implemented this way can be subscribed to and handled in the aggregate root of the entity which raised the event, or in domain services, or even in UI/Application layer
- Domain events are raised synchronously, if an asynchronous task needs to be carried out, it can be done inside the event handler (async-await pattern)
- Outside applications can also be triggered by using a message queue or an enterprise service bus (ESB) inside the domain event handler
## 12. Anti-corruption layer:
- Used to translate models from outside systems or legacy apps to models inside the bounded context and vice versa, and also to ease the communication with legacy services
- Can use service facades and model adapters
---
## Individual Packages
Install individual package
- `@type-ddd/cpf` [Docs](./packages/cpf)
- `@type-ddd/cnpj` [Docs](./packages/cnpj)
- `@type-ddd/date` [Docs](./packages/date)
- `@type-ddd/email` [Docs](./packages/email)
- `@type-ddd/password` [Docs](./packages/password)
- `@type-ddd/patterns` [Docs](./packages/patterns)
- `@type-ddd/phone` [Docs](./packages/phone)
- `@type-ddd/username` [Docs](./packages/username)
- `@type-ddd/zip-code` [Docs](./packages/zip-code)
- `@type-ddd/money` [Docs](./packages/money)
---
### Value Object
> A value object is a small, simple object that represents a single value or characteristic, such as a monetary amount or a date. It is characterized by having no identity of its own, meaning it is equal to another value object if its values are equal, regardless of its reference. Value objects are often used in domain-driven design to represent simple entities in the system.
#### Create a value object with business rules.
```ts
import { ValueObject, Ok, Fail, Result } from '@type-ddd/core';
interface Props {
amount: number;
}
// simple example as monetary value object business behavior
export default class Money extends ValueObject<Props> {
// private constructor. Avoid public new.
private constructor(props: Props) {
super(props);
}
// any business rule behavior. Check.
public isGt(x: Money): boolean {
const { number: Check } = this.validator;
const xValue = x.get('amount');
const currentValue = this.get('amount');
return Check(xValue).isGreaterThan(currentValue);
}
// any business rule behavior. Calc.
public sum(x: Money): Money {
const { number: Calc } = this.util;
const value = x.get('amount');
const current = this.get('amount');
const amount = Calc(current).sum(value);
return new Money({ amount });
}
// any business rule behavior. Calc.
public subtract(x: Money): Money {
const { number: Calc } = this.util;
const value = x.get('amount');
const current = this.get('amount');
const amount = Calc(current).subtract(value);
return new Money({ amount });
}
// any business rule to validate state.
public static isValidProps({ amount }: Props): boolean {
const { number: Check } = this.validator;
return Check(amount).isPositive();
}
// shortcut to create a zero value
public static zero(): Money {
return new Money({ amount: 0 });
}
// factory method to create an instance and validate value.
public static create(amount: number): Result<Money | null> {
const isValid = this.isValidProps({ amount });
if(!isValid) return Fail("Invalid amount for money");
return Ok(new Money({ amount }));
}
}
```
How to use value object instance
```ts
// operation result
const resA = Money.create(500);
// check if provided a valid value
console.log(resA.isOk());
// > true
// money instance
const moneyA = resA.value() as Money;
moneyA.get("amount");
// 500
// using methods
moneyA.isGt(Money.zero());
// > true
const moneyB = Money.create(100).value() as Money;
const moneyC = moneyA.sum(moneyB);
const value = moneyC.get('amount');
console.log(value);
// > 600
```
---
### Entity
> An entity in domain-driven design is an object that represents a concept in the real world and has a unique identity and attributes. It is a fundamental building block used to model complex business domains.
#### Create an entity with business rules.
```ts
import { Entity, Ok, Fail, Result, UID } from '@type-ddd/core';
interface Props {
id?: UID;
total: Money;
discount: Money;
fees: Money;
}
// simple example as payment entity using money value object
export default class Payment extends Entity<Props> {
// private constructor
private constructor(props: Props){
super(props);
}
// any business rule behavior. Update total.
public applyFees(fees: Money): Payment {
const props = this.props;
const total = props.total.sum(fees);
return new Payment({ ...props, total, fees });
}
// any business rule behavior. Discount must be less or equal total.
public applyDiscount(discount: Money): Payment {
const props = this.props;
const total = props.total.subtract(discount);
return new Payment({ ...props, total, discount });
}
// factory method to create a instance. Value must be positive.
public static create(props: Props): Result<Payment> {
return Ok(new Payment(props));
}
}
```
How to use entity instance
```ts
// operation result
const total = Money.create(500).value() as Money;
const discount = Money.zero();
const fees = Money.zero();
// create a payment
const payment = Payment.create({ total, discount, fees }).value() as Payment;
// create fee and discount
const fee = Money.create(17.50).value();
const disc = Money.create(170.50).value();
// apply fee and discount
const result = payment.applyFees(fee).applyDiscount(disc);
// get object from domain entity
console.log(result.toObject());
{
"id": "d7fc98f5-9711-4ad8-aa16-70cb8a52244a",
"total": {
"amount": 347
},
"discount": {
"amount": 170.50
},
"fees": {
"amount": 17.50
},
"createdAt":"2023-01-30T23:11:17.815Z",
"updatedAt":"2023-01-30T23:11:17.815Z"
}
```
### Aggregate
Encapsulate and are composed of entity classes and value objects that change together in a business transaction
#### Create an aggregate to compose your context.
In my example, let's use the context of payment. All payment transactions are encapsulated by an order (payment order) that represents a user's purchasing context.
```ts
import { Aggregate, Ok, Fail, Result, UID, EventHandler } from '@type-ddd/core';
// Entities and VO that encapsulate context.
interface Props {
id?: UID;
payment: Payment;
items: List<Item>;
status: OrderStatus;
customer: Customer;
}
// Simple example of an order aggregate encapsulating entities and
// value objects for context.
export default class Order extends Aggregate<Props> {
// Private constructor to ensure instances creation through static methods.
private constructor(props: Props){
super(props);
}
// Static method to begin a new order.
// Takes a customer as parameter and returns an instance of Order.
public static begin(customer: Customer): Order {
// Initialize the status of the order as "begin".
const status = OrderStatus.begin();
// Initialize the list of items as empty.
const items: List<Item> = List.empty();
// Initialize the payment as zero, since the order hasn't been paid yet.
const payment = Payment.none();
// Create a new instance of Order with the provided parameters.
const order = new Order({ status, payment, items, customer });
// Add an event to indicate that the order has begun.
order.addEvent('ORDER_HAS_BEGUN', (order) => {
// Perform some important operation when the order begins.
console.log('Do something important...');
});
// Alternatively, add an event by creating an
// instance of a class that extends EventHandler.
order.addEvent(new OrderBeganEventHandler());
// Return the created order instance.
return order;
}
// Method to add an item to the order.
// Takes an item as parameter and returns the Order instance.
addItem(item: Item): Order {
// Add the item to the order's items list.
this.props.items.add(item);
// Sum item price to payment amount
this.props.payment.sum(item.price);
// Return the Order instance itself to allow chained calls.
return this;
}
// Method to perform the payment of the order.
// Takes a payment object as parameter.
pay(payment: Payment): Order {
// Set the status of the order to "paid".
this.props.status = OrderStatus.paid();
// Set the provided payment object.
this.props.payment = payment;
// Add an event to indicate that the order has been paid.
// Assuming OrderPaidEvent is a class representing
// the event of order payment.
this.addEvent(new OrderPaidEventHandler());
return this;
}
// Static method to create an instance of Order.
// Returns a Result, which can be Ok (success) or Fail (failure).
// The value of the Result is an instance of Order,
// if creation is successful.
public static create(props: Props): Result<Order> {
return Ok(new Order(props));
}
}
```
#### How to use events
Event Handler
```ts
import { Context, EventHandler } from '@type-ddd/core';
class OrderCreatedEvent extends EventHandler<Order> {
constructor() {
super({ eventName: 'OrderCreated' });
}
dispatch(order: Order): void {
// dispatch event to another context
order.context().dispatchEvent('Context:Event', order.toObject());
};
}
```
Aggregates domain events
```ts
order.addEvent('Event', (...args) => {
console.log(args);
});
// Or add an EventHandler instance
order.addEvent(new OrderCreatedEvent());
order.dispatchEvent('OrderBegun');
// dispatch with args
order.dispatchEvent('Event', { info: 'custom_args' });
// OR call all added events
await order.dispatchAll();
```
#### How to subscribe to a global event
```ts
import { Context } from '@type-ddd/core';
const context = Context.events();
context.subscribe('Context:Event', (event) => {
const [model] = event.detail;
console.log(model);
});
// dispatch an event to a context with args
context.dispatchEvent('Context:Event', { name: 'Jane' });
// Dispatching events to specific contexts
// Dispatches the SIGNUP event to Context-X
context.dispatchEvent('Context-X:Signup');
// Dispatches the SIGNUP event to all contexts
context.dispatchEvent('*:Signup');
// Dispatches all events to all contexts. Not recommended
context.dispatchEvent('*:*');
// Dispatches all events under Context-Y
context.dispatchEvent('Context-Y:*');
```
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
Versions of types-ddd are currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 4.x.x | :white_check_mark: |
| 3.9.x | :white_check_mark: |
| <= 3.8 | :x: |
## Reporting a Vulnerability
If you find some vulnerability please report as issue.
Every month security updates are published as 0.0.x version.
================================================
FILE: check-dev-deps.sh
================================================
#!/bin/sh
DEV_DEPS="$(cat package.json | grep -A 100 "devDependencies" | grep -B 100 "\}\," | \
awk "NR>1" | sed -e "s/},//" | tr -d '":.^0-9,')";
for dep in "$(echo $DEV_DEPS)"; do
echo $dep | sed -e 's/ /\n/g' > deps;
done;
while IFS= read -r line; do
yarn list --depth 0 | grep $line@
done < ./deps;
rm -rf ./deps
================================================
FILE: docs/README.md
================================================
# types-ddd documentation
## About the lib
The library was created to support developers in developing domain-rich applications.
Full documentation.
Version 3.x
This lib use as core [rich-domain](https://www.npmjs.com/package/rich-domain)
### Simple App Example
A simple app example available on [link](https://github.com/4lessandrodev/ddd-app)
---
## Documentation
### Folders
Folders structure suggestion
Divided by
- Domain layer
- Application layer
- Infra layer
```shell
$ tree
.
├── package.json
├── README.md
└── src
├── configs
│ └── env
│
├── shared
│ └── infra
│ └── server
│
└── modules
│
└── [module-name]
│
│── domain
│ ├── value-objects
│ ├── entities
│ ├── aggregates
│ ├── events
│ ├── subscriptions
│ ├── adapter
│ ├── repository-interface
│ └── domain-services
│
├── application
│ └── use-cases
│
└── infra
├── models
└── repository
```
---
### Result
What is Result:
`Result` is a class that encapsulates the result of an operation and stores the success or failure state without throws the application.
#### Interface and Generic Types
- A = `Payload` optional default `void`
- B = `Error` optional default `string`
- C = `MetaData` optional default `{}`
```ts
IResult<A, B, C>;
```
Alternative shortcuts
```ts
import { Ok, Fail } from 'types-ddd';
// Success use case
return Ok();
// OR
return Ok<string>('success message');
// Failure use case
return Fail('error message here');
```
Example how to use generic types.
First let's create our interfaces to use as generic type.
- The type of data to be retrieved can be any type you want.
```tS
// Payload type
interface IData { data: string };
// Error type
interface IError { message: string };
// MetaData type
interface IMeta { arg: number };
```
Now let's implement a function that return the result below
```ts
IResult<IData, IError, IMeta>;
```
So let's implement that on a simple function.
```ts
const isEven = (value: number): Result<IData, IError, IMeta> => {
const isPairValue = value % 2 === 0;
const metaData: IMeta = { arg: value };
if (isPairValue) {
// success payload
const payload: IData = { data: `${value} is even` };
// return success
return Ok(payload, metaData);
}
// failure payload
const error: IError = { message: `${value} is not even` };
// return failure
return Fail(error, metaData);
};
```
Here we have a function that returns success if the value informed is even and returns failure if it is odd.
Success Case
```ts
const result = isEven(42);
console.log(result.isOk());
> true
console.log(result.value());
> 'Object { data: "42 is even" }'
console.log(result.metaData());
> 'Object { arg: 42 }'
console.log(result.error());
> null
```
Failure Case
```ts
const result = isEven(43);
console.log(result.isFail());
> true
console.log(result.error());
> 'Object { message: "43 is not even" }'
console.log(result.metaData());
> 'Object { arg: 43 }'
console.log(result.value());
> null
```
#### Void
The most simple void success example.<br>
Let's see the same example using void.
```ts
const checkEven = (value: number): Result<void> => {
const isPair = value % 2 === 0;
// success case
if(isPair) return Ok();
// failure case
return Fail('not enven');
}
```
Using the function as success example
```ts
const result: Result<void> = checkEven(42);
console.log(result.isOk());
> true
console.log(result.isFail());
> false
console.log(result.error());
> null
console.log(result.value());
> null
console.log(result.metaData());
> 'Object {}'
```
Fail example
```ts
const result: Result<void> = checkEven(43);
console.log(result.isFail());
> true
console.log(result.isOk());
> false
console.log(result.error());
> "not even"
console.log(result.value());
> null
console.log(result.metaData());
> 'Object {}'
```
#### toObject method
you can get a summarized object with the properties of an instance of a `Result`
```ts
console.log(result.toObject());
> Object
`{
"data": null,
"error": "not even",
"isFail": true,
"isOk": false,
"metaData": Object {}
}`
```
#### Hooks
In the instances of a Result there are two hooks that allow the execution of a command according to the state.
```ts
class Command implements ICommand<void, void> {
execute(): void {
console.log("running command ...");
}
}
const myCommand = new Command();
const result = Result.Ok();
result.execute(myCommand).on('Ok');
> "running command ..."
```
You might also want to pass arguments to the command
```ts
class Command implements ICommand<string, void> {
execute(error: string): void {
console.log(error);
}
}
const myCommand = new Command();
const result = Result.fail('something went wrong');
result.execute(myCommand).withData(result.error()).on('fail');
> "something went wrong"
```
#### Combine
You can use the static `combine` function of `Result` to check many instances if any are failed it will return the instance with error state.
Success example
```ts
const resultA = Result.Ok();
const resultB = Result.Ok();
const resultC = Result.Ok();
const result = Result.combine([resultA, resultB, resultC]);
console.log(result.isOk());
> true
```
Failure example
```ts
const resultA = Result.Ok();
const resultB = Result.fail('oops err');
const resultC = Result.Ok();
const result = Result.combine([resultA, resultB, resultC]);
// OR you can use Combine function
const result = Combine([resultA, resultB, resultC]);
console.log(result.isOk());
> false
console.log(result.error());
> 'oops err'
```
---
### ID
What is ID:
A symbol which uniquely identifies an object or record.<br>
In this Lib all IDs are generated by domain and uses uuid v4.
#### Create New
Create a new uuid.
```ts
// ID main
const id = ID.create();
// OR Id as function
const id = Id();
// OR id instance
const id = id.create();
console.log(id.value());
> "eb9c563c-719d-4872-b303-0a82921351f7"
```
#### Short New
Create a short id
```ts
const id = ID.short();
console.log(id.value());
> "EB9C563DB4872BF7"
```
#### Create Existing
Create a id with provided value
```ts
const id = ID.create('this-is-my-id-01');
console.log(id.value());
> "this-is-my-id-01"
```
#### Compare ids
The id instance has a method to compare two ids.
```ts
const idA = ID.short('this-is-my-id-01');
const idB = ID.short('this-is-my-id-02');
console.log(idA.equal(idB));
> false
console.log(idB.equal(idB));
> true
```
#### IsNew
Check if id instance is has a new value
```ts
const idA = ID.create('this-is-my-id-01');
const idB = ID.create();
console.log(idA.isNew());
> false
console.log(idB.isNew());
> true
```
#### Type for ID
Define type for ID
```ts
import { UID, ID } from 'types-ddd';
// UID type
let id: UID;
// ID value
id = ID.create();
```
---
### ValueObject
What is value object:
- Are only identified by their values, not by their ids (for example money is a value object as long as we are not tracking individual banknotes, if we need to track individual banknotes then it should be a banknote entity)
- Can be used to measure or describe things (name, description, amount, height, date, time, range, address, etc.)
- You can combine other value types that usually go together into a new value object type, like address (city, street, country, postal code) or ...range, or ...type
- Prefer to put the behavior on value objects rather than on entities because value objects are immutable and do not have side effects (like changing their state or changing the state of any entity)
- Can be part of an entity
- Should be immutable, behaviors should not change the state of a value object, but can rather create a new value object (should act similar to C# strings, structs, ints, and other value types)
- Can be persisted but only as part of an entity, not individually.
#### Simple Value Object.
Value objects extend to `ValueObject` class have private constructor and public static method called `create`.<br>
The `create` method receives the props which by default is an object with the key `value`.
the value object below is a base example without any kind of validation
```ts
import { IResult, Result, ValueObject } from "types-ddd";
export interface NameProps {
value: string;
}
export class Name extends ValueObject<NameProps>{
private constructor(props: NameProps) {
super(props);
}
public static create(value: string): IResult<Name> {
return Ok(new Name({ value }));
}
}
export default Name;
```
Now that we have defined our value object class, we can create an instance.<br>
The `create` method returns an instance of Name encapsulated by the `Result`, so it is important to always assess whether the result is a success before getting the value.
```ts
const result = Name.create('Jane');
console.log(result.isOk());
> true
const name = result.value();
console.log(name.get('value'));
> "Jane"
```
Once we have an instance of a value object, we can use some methods that the library makes available.
By default setters are enabled
```ts
// Prefer to create new instance instead of changing value to value object. Do not use set to value object.
name.set('value').to('John');
console.log(name.get('value'));
> "John"
```
When you use the `set` or `change` function to modify the state, each change is saved in a history
```ts
console.log(name.history().count());
> 2
// back to old value on history
name.history().back();
console.log(name.get('value'));
> "Jane"
```
> **We don't advise you to use state change of a value object. Create a new one instead of changing its state. However the library will leave that up to you to decide.**
To disable the setters of a value object use the parameters below in the super.<br>
This property disables the set function of the value object.
```ts
constructor(props: NameProps){
super(props, { disableSetters: true })
}
```
Now when trying to change the value using `set` or `change` it will not be modified.
```ts
console.log(name.get('value'));
> "John"
name.set('value').to('James');
console.log(name.get('value'));
> "John"
```
#### Using validation
Validation before create instance.<br>
A validator instance is available in the "Value Object" domain class.
```ts
import { Ok, Fail, Result, ValueObject } from "types-ddd";
export interface NameProps {
value: string;
}
export class Name extends ValueObject<NameProps>{
private constructor(props: NameProps) {
super(props);
}
public static isValidProps({ value }: NameProps): boolean {
const { string } = this.validator;
return string(value).hasLengthBetweenOrEqual(3, 30);
}
public static create(value: string): Result<Name> {
const message = 'name must have length min 3 and max 30 char';
if (!this.isValidProps({ value })) return Fail(message);
return Ok(new Name({ value }));
}
}
export default Name;
```
Now when you try to instantiate a name, the value will be checked and if it doesn't meet the validation requirements, a `Result` will be returned with an error state.
```ts
const empty = '';
const result = Name.create(empty);
console.log(result.isFail());
> true
console.log(result.error());
> "name must have length min 3 and max 30 char"
console.log(result.value());
> null
```
#### Validation before set
The `isValidProps` Method validates properties when creating a new instance, but which method validates before modifying a value?
For this there is the method `validation`
The validation method takes two arguments, the first the `key` of props and the second the `value`.
So when calling the `set` or `change` function, this method will be called automatically to validate the value, if it doesn't pass the validation, the value is not changed.
> There must be a validation for each "props" key
```ts
validation<Key extends keyof Props>(value: Props[Key], key: Key): boolean {
const { number } = this.validator;
const options: IPropsValidation<Props> = {
value: (value: number) => number.isBetween(0, 130),
}
return options[key](value);
};
```
In case your value object has only one attribute you can simply use the already created static validation method.<br>
Let's see a complete example as below
```ts
import { Result, Result, ValueObject } from "types-ddd";
export interface NameProps {
value: string;
}
export class Name extends ValueObject<NameProps>{
private constructor(props: NameProps) {
super(props);
}
validation(value: string): boolean {
return Name.isValidProps({ value });
}
public static isValidProps({ value }: NameProps): boolean {
const { string } = this.validator;
return string(value).hasLengthBetween(3, 30);
}
public static create(value: string): IResult<Name> {
const message = 'name must have length min 3 and max 30 char';
if (!this.isValidProps({ value })) return Fail(message);
return Ok(new Name({ value }));
}
}
export default Name;
```
Let's test the instance and the validation method.<br>
Value is not modified if it does not pass validation.
```ts
const result = Name.create('Jane');
console.log(result.isOk());
> true
const name = result.value();
console.log(name.get('value'));
> "Jane"
const empty = '';
name.set('value').to(empty);
console.log(name.get('value'));
> "Jane"
name.set('value').to("Jack");
console.log(name.get('value'));
> "Jack"
```
#### toObject
This method transforms a complex object into a simple object or value.<br>
This method is useful for cases where you have value objects inside other value objects
```ts
const street = Street.create('Dom Juan').value();
const number = Number.create(42).value();
const result = Address.create({ street, number });
const address = result.value();
console.log(address.toObject());
> Object
`{
"street": "Dom Juan",
"number": 42,
}`
```
#### Clone
This method creates a new instance with the same properties as the current value object.
```ts
const result = Name.create('Sammy');
const originalName = result.value();
console.log(originalName.value());
> "Sammy"
const clone = originalName.clone();
console.log(clone.isOk());
> true
const clonedName = clone.value();
console.log(clonedName.value());
> "Sammy"
```
Clone being a new instance does not change the properties of the original value object
```ts
clonedName.change('value', 'Jones');
console.log(clonedName.value());
> "Jones"
console.log(originalName.value());
> "Sammy"
```
#### createMany
Sometimes you will need to create many instances of different value objects and for that there is static method available `createMany` on value objects, entity and aggregate.
```ts
const itemPrice = Class<PriceProps>(ProductPrice, { value: price });
const itemName = Class<NameProps>(ProductName, { value: name });
const itemQtd = Class<QtdProps>(ProductQtd, { value: qtd });
const { data, result } = ValueObject.createMany([ itemPrice, itemName, itemQtd ]);
// you check if all value objects are ok
if (result.isFail()) return Result.fail(result.error());
// you can get instances from iterator data. In the same order as the array
const price = data.next().value() as ProductPrice; // index 0
const name = data.next().value() as ProductName; // index 1
const quantity = data.next().value() as ProductQtd; // index 2
const product = Product.create({ name, price, quantity });
```
---
### Entity
What is value object:
- Live longer than the application, should endure restarts, and are persisted and read from data sources (DB, file system, network, etc.)
- Have an id (preferably a GUID rather than a DB generated int because business transactions do not rely on persistence, can be persisted after other operations carried out in model's behavior)
- Have entity semantics (equality and `GetHashCode()` defined by class name + id)
- Behavior in an entity mostly orchestrates value objects for a use case
- Entity class should not have public property setters, setting a property should be a behavior method
- Entities should not have bidirectional relations (depending on the bounded context, either an egg can have a chicken or a chicken can have eggs, but not both)
- Entity relations should not reflect the complete set of DB foreign key relationships, should be bare down to the minimum for performing the behavior inside the bounded context
- Entity relations should not hold a reference to another entity class, it can only keep the id of another entity
- If a business transaction needs a reference to other entities in relation, aggregates should be used instead (aggregates can hold a reference to other aggregate roots, which are entity classes by definition)
#### Simple Entity
Entities extend to `Entity` class, have private constructor and public static method called `create`.
The `create` method receives the props which by default is an object with the key `id`.
the entity below is a base example without any kind of validation
```ts
interface UserProps { id?: UID, name: Name, age: Age };
export class User extends Entity<UserProps>{
private constructor(props: UserProps){
super(props)
}
public static create(props: UserProps): IResult<User> {
return Result.Ok(new User(props));
}
}
export default User;
```
`id` is a reserved word and must have the type `UID` or `string`.
All attributes for an entity must be value object except id.
```ts
const nameAttr = Name.create('James');
const ageAttr = Age.create(21);
// always check if value objects are success
const voResult = Combine([ nameAttr, ageAttr ])
console.log(voResult.isOk());
> true
const name = nameAttr.value();
const age = ageAttr.value();
// if you don't provide a value for the id it will be generated automatically
const result = User.create({ name, age });
console.log(result.isOk());
> true
```
#### toObject
when you extend entity class you get some methods from domain class, one of them is `toObject` method.<br>
In the entity, this method aims to transform a domain class into a simple object, that is, all value objects are transformed into simple attributes.
```ts
const user = result.value();
console.log(user.toObject());
> Object
`{
age: 21,
name: "James",
createdAt: "2022-08-13T03:51:25.738Z",
updatedAt: "2022-08-13T03:51:25.738Z"
id: "0709220f-7c2f-41e2-b535-151926286893",
}`
```
#### with id value
you can create an instance by entering an id
```ts
const name = nameAttr.value();
const id = ID.create('a3a5ea9d-7c57-4743-8a9b-5315fad365d0');
const result = User.create({ id, age, name });
console.log(result.isOk());
> true
const user = result.value();
console.log(user.toObject());
> Object
`{
age: 21,
name: "James",
createdAt: "2022-08-13T03:51:25.738Z",
updatedAt: "2022-08-13T03:51:25.738Z"
id: "a3a5ea9d-7c57-4743-8a9b-5315fad365d0",
}`
```
#### isNew
Check if instance is a new entity.<br> if you provide do not provide an id the entity will be considered as a new created entity instance.
```ts
// no id provided
const newUserResult = User.create({ name, age });
cons newUser = newUserResult.value();
console.log(newUser.isNew());
> true
// id provided
const userResult = User.create({ id, name, age });
cons user = userResult.value();
console.log(user.isNew());
> false
```
#### isValidProps
Validating props before create an instance.<br> Here you can apply your business validation.
```ts
public static isValidProps({ name, age }: UserProps): boolean {
// your business validation
const isValidName = doSomeBusinessValidation(name);
const isValidAge = doSomeBusinessValidation(age);
return isValidName && isValidAge;
}
```
Let's apply our props validation method to our entity class
```ts
interface UserProps { id?: UID, name: Name, age: Age };
export class User extends Entity<UserProps>{
private constructor(props: UserProps){
super(props)
}
public static isValidProps({ name, age }: UserProps): boolean {
// your business validation
const isValidName = doSomeBusinessValidation(name);
const isValidAge = doSomeBusinessValidation(age);
return isValidName && isValidAge;
}
public static create(props: UserProps): IResult<User> {
const isValidRules = User.isValidProps(props);
if(!isValidRules) return Result.fail('invalid props');
return Result.Ok(new User(props));
}
}
export default User;
```
#### change
in entities you can easily change an attribute with `change` or `set` method
```ts
const result = Name.create('Larry');
const newName = result.value();
user.change("name", newName);
console.log(user.get("name").value());
> "Larry"
```
#### Validation before change
The `isValidProps` Method validates properties when creating a new instance, but which method validates before modifying a value?<br>
For this there is the method `validation`
The validation method takes two arguments, the first the `key` of props and the second the `value`.
So when calling the `set` or `change` function, this method will be called automatically to validate the value, if it doesn't pass the validation, the value is not changed.
> There must be a validation for each "props" key
```ts
validation<Key extends keyof Props>(value: Props[Key], key: Key): boolean {
const options: IPropsValidation<Props> = {
name: (value: Name) => doSomeBusinessValidation(value),
age: (value: Age) => doSomeBusinessValidation(value)
}
return options[key](value);
};
```
Let's apply our validation method to our entity.<br> Now if the validation does not pass the value will not be changed.
```ts
interface UserProps { id?: UID, name: Name, age: Age };
export class User extends Entity<UserProps>{
private constructor(props: UserProps){
super(props)
}
validation<Key extends keyof Props>(value: Props[Key], key: Key): boolean {
const options: IPropsValidation<Props> = {
name: (value: Name) => doSomeBusinessValidation(value),
age: (value: Age) => doSomeBusinessValidation(value)
}
return options[key](value);
};
public static isValidProps({ name, age }: UserProps): boolean {
// your business validation
const isValidName = doSomeBusinessValidation(name);
const isValidAge = doSomeBusinessValidation(age);
return isValidName && isValidAge;
}
public static create(props: UserProps): IResult<User> {
const isValidRules = User.isValidProps(props);
if(!isValidRules) return Result.fail('invalid props');
return Result.Ok(new User(props));
}
}
export default User;
```
#### disableSetters
To disable the setters of an entity use the parameters below in the super.<br>
This property disables the set function of the entity.
```ts
constructor(props: NameProps){
super(props, { disableSetters: true })
}
```
#### clone entity
you can clone an entity and get a new instance
```ts
const result = User.create({ id, age, name });
console.log(result.isOk());
> true
const user = result.value();
const clonedUser = user.clone();
const newUser = clonedUser.value();
const newNameResult = Name.create('Luke');
const newName = newNameResult.value();
clonedUser.set('name').to(newName);
console.log(user.get('name').value());
> "James"
console.log(clonedUser.get('name').value());
> "Luke"
```
#### compare entities
You can compare two entities.
`compare` just check props values and id value. `deepEqual` check props values, id, types and history.
```ts
const isEqual = user1.isEqual(user2);
console.log(isEqual);
> false
```
#### history
Each operation to change any entity state property generates a history.<br>
At any time you can return to a previous state
```ts
const result = User.create({ name, age });
const user = result.value();
console.log(user.toObject());
> Object
`{
age: 21,
name: "James",
createdAt: "2022-08-13T03:51:25.738Z",
updatedAt: "2022-08-13T03:51:25.738Z",
id: "0709220f-7c2f-41e2-b535-151926286893"
}`
// first history is initial props on create an instance
console.log(user.history().count());
> 1
user.set('name').to(newName);
console.log(user.toObject());
> Object
`{
age: 21,
name: "Luke",
createdAt: "2022-08-13T03:51:25.738Z",
updatedAt: "2022-08-13T03:52:25.738Z",
id: "0709220f-7c2f-41e2-b535-151926286893"
}`
// On change name create a new history
console.log(user.history().count());
> 2
// back to history 1
user.history().back();
console.log(user.toObject());
> Object
`{
age: 21,
name: "James",
createdAt: "2022-08-13T03:51:25.738Z",
updatedAt: "2022-08-13T03:51:25.738Z",
id: "0709220f-7c2f-41e2-b535-151926286893"
}`
```
---
### Aggregate
What is aggregate:
- Encapsulate and are composed of entity classes and value objects that change together in a business transaction
- Aggregates are a transactional graph of model objects
- Aggregate root should be an entity, an aggregate can even be a single entity
- Aggregate can keep a reference to other aggregate roots, but not to other entity classes which are not aggregate roots themselves
- Aggregate should not keep a reference to other aggregate root entity classes if those other entities do not change together with this aggregate root entity
- Aggregate can also keep the id of another entity, but keeping too many foreign key ids is a code smell (why?)
- If deleting an entity has a cascade effect on the other entities referenced by class in the object graph, these entities are part of the same aggregate, if not, they should not be inside this aggregate
The aggregate has the same methods already mentioned in the entity.
And in addition to the entity methods, there is another one that is responsible for managing the `domain's events`.
#### Simple Aggregate
```ts
export interface ProductProps {
id?: UID;
name: ProductName;
price: ProductPrice;
createdAt?: Date;
updatedAt?: Date;
}
// extends to Aggregate
export class Product extends Aggregate<ProductProps>{
private constructor(props: ProductProps) {
super(props);
}
public static create(props: ProductProps): IResult<Product> {
return Result.Ok(new Product(props));
}
}
export default Product;
```
#### Domain Event
Let's create an aggregate instance and see how to add domain event to it.
```ts
export class ProductCreatedEvent implements IHandle<Product>{
public eventName: string;
constructor() {
this.eventName = 'ProductCreated';
}
dispatch(event: IDomainEvent<Product>): void {
// your action here
const { aggregate } = event;
console.log(`EVENT DISPATCH: ${aggregate.hashCode().value()}`);
}
}
export default ProductCreatedEvent;
```
Now let's add the event to a product instance.<br>
Events are stored in memory and are deleted after being triggered.
```ts
const result = Product.create({ name, price });
const product = result.value();
const event = new ProductCreatedEvent();
product.addEvent(event);
```
Now we can dispatch the event whenever we want.
```ts
product.dispatch("ProductCreated");
> "EVENT DISPATCH: [Aggregate@Product]:6039756f-d3bc-452e-932a-ec89ff536dda"
```
---
### Adapter
How to adapt the data from persistence to domain or from domain to persistence.
```ts
// from domain to data layer
class MyAdapterToInfra implements IAdapter<DomainUser, DataUser>{
build(target: DomainUser): Result<DataUser> {
// ...
}
}
// from data layer to domain
class MyAdapterToDomain implements IAdapter<DataUser, DomainUser>{
build(target: DataUser): Result<DomainUser> {
// ...
}
}
// You can use adapter instance in toObject function
const myAdapter = new MyAdapterToInfra();
const dataUser = domainUser.toObject<DataUser>(myAdapter);
```
---
### Utils
Some util tools available
#### Ready to use
- ✔ EmailValueObject
- ✔ UserNameValueObject
- ✔ BirthdayValueObject
- ✔ CurrencyValueObject
- ✔ PasswordValueObject
- ✔ HomePhoneValueObject
- ✔ MobilePhoneValueObject
- ✔ TrackingCodeValueObject
- ✔ RGBColorValueObject
- ✔ HEXColorValueObject
- ✔ PostalCodeValueObject
- ✔ UrlValueObject
- ✔ OrderStatusValueObject
- ✔ PinValueObject
- ✔ CPFValueObject
- ✔ CNPJValueObject
- ✔ CustomStringValueObject
- ✔ CustomNumberValueObject
- ✔ WeightUnitValueObject
- ✔ UnitOfMeasureValueObject
- ✔ DimensionValueObject
- ✔ WeightValueObject
- ✔ DateValueObject
- ✔ getUndefinedKeysAsArray
- ✔ getUndefinedKeysAsObject
- ✔ removeUndefinedKeysFromObject
- ✔ SpecificationComposite
- ✔ FactoryMethod
- ✔ TSProxy
---
#### Password
Just import and use
```ts
const passOrError = PasswordValueObject.create('my-strength-pass');
console.log(passOrError.isOk());
> true
const pass = passOrError.value();
pass.encrypt();
console.log(pass.value());
> "$2a$12$AdLoTarjC5wnc1tAUc3j1.RczGxxImH0mG6dZkS5zPaGrTi/EmPWG"
console.log(pass.isEncrypted());
> true
const passMatch = pass.compare('my-strength-pass');
console.log(passMatch);
> true
console.log(PasswordValueObject.random(12).value());
> "WtS65$@!A6by"
```
you can define a custom range for password length and error message
```ts
Reflect.set(PasswordValueObject, "MIN_LENGTH", 10);
Reflect.set(PasswordValueObject, "MAX_LENGTH", 20);
Reflect.set(PasswordValueObject, "MESSAGE", "Password must be between 10 and 20 characters");
```
#### Date
Just import and use
```ts
const currentDate = new Date();
const myDate = DateValueObject.create(currentDate).value();
console.log(myDate.value());
> "2021-10-11T14:45:04.758Z"
console.log(myDate.format("DD-MM-YYYY"));
> "11-10-2021"
myDate.addDays(3);
console.log(myDate.value());
> "2021-10-14T14:45:04.758Z"
const isWeekend = myDate.isWeekend();
console.log(isWeekend);
> false
myDate.addHours(7);
const isAfter = myDate.isAfter(currentDate);
console.log(isAfter);
> true
```
#### Currency
Just import and use
```ts
const voOrErr = CurrencyValueObject.create({ currency: 'BRL', value: 0.50 });
const myCurrency = voOrErr.value();
console.log(myCurrency.value());
> 0.5
myCurrency.add(0.50); // 1
myCurrency.multiplyBy(50); // 50
myCurrency.divideBy(2); // 25
myCurrency.subtractBy(5); // 20
myCurrency.add(80); // 100
myCurrency.addPercent(2); // 102
myCurrency.subtractBy(2); // 100
myCurrency.subtractPercent(30); // 70
console.log(myCurrency.value());
> 70
console.log(myCurrency.getCoin());
> "R$ 70.00"
// OR chain
const result = myCurrency.add(10).addPercent(21).multiplyBy(3).subtractBy(50);
```
#### Weight units
Just import and use
```ts
const result = WeightValueObject.create({ value: 1000, unit: "TON" });
console.log(result.isOk());
> true
const weight = result.value();
console.log(weight.unit);
> "TON"
console.log(weight.weight.value());
> 1000
// Convert instance value and unit to KG
weight.toKG();
console.log(weight.unit);
> "KG"
console.log(weight.weight.value());
> 1
```
#### Email
Just import and use
```ts
const result = EmailValueObject.create('dany@mailer.com');
console.log(result.isOk());
> true
const email = result.value();
console.log(email.value());
> "dany@mailer.com"
console.log(email.getNick());
> "dany"
console.log(email.getDomain());
> "mailer.com"
```
you can block same domain or define only specific domains
```ts
Reflect.set(EmailValueObject, "BLOCKED_DOMAINS", ["microsoft.com", "leak.com"]);
Reflect.set(EmailValueObject, "VALID_DOMAINS", ["gmail.com", "hotmail.com"]);
// now only two domains are accepted gmail and hotmail
```
#### Name
Just import and use
```ts
const result = UserNameValueObject.create('jannie lan spark');
console.log(result.isOk());
> true
const name = result.value();
console.log(name.value());
> "Jannie Lan Spark"
console.log(name.getLastName());
> "Spark"
console.log(name.getMiddleName());
> "Lan"
console.log(name.getFirstName());
> "Jannie"
console.log(name.getInitials());
> "J.L.S"
```
#### BirthDay
Just import and use
```ts
const year2000 = new Date(2000, 1, 1);
const result = BirthdayValueObject.create(year2000);
console.log(result.isOk());
> true
const birthDay = result.value();
console.log(birthDay.value());
> "2000-02-01T02:00:00.000Z"
console.log(birthDay.isAgeGreaterThan(18));
> true
console.log(birthDay.getAgeAsYearsOld());
> 22
```
#### Custom string
```ts
// my-custom-string.ts
import { CustomStringValueObject } from 'types-ddd';
Reflect.set(CustomStringValueObject, 'VALIDATOR', (value: string) => typeof value === 'string');
Reflect.set(CustomStringValueObject, 'MESSAGE', "my custom error message");
const MyCustomString = CustomStringValueObject;
export MyCustomString;
export default MyCustomString;
```
#### Custom number
```ts
// my-custom-number.ts
import { CustomNumberValueObject } from 'types-ddd';
Reflect.set(CustomNumberValueObject, 'VALIDATOR', (value: string) => typeof value === 'number');
Reflect.set(CustomNumberValueObject, 'MESSAGE', "my custom error message");
const MyCustomNumber = CustomNumberValueObject;
export MyCustomNumber;
export default MyCustomNumber;
```
================================================
FILE: global-setup.ts
================================================
process.env.TZ = 'UTC';
================================================
FILE: jest.config.ts
================================================
import './global-setup';
module.exports = {
roots: ['<rootDir>'],
collectCoverage: false,
coverageDirectory: 'coverage',
testEnvironment: 'node',
transform: {
'.+\\.ts$': 'ts-jest',
},
moduleNameMapper: {
'@type-ddd': '<rootDir>/lib/index',
},
};
================================================
FILE: lerna.json
================================================
{
"packages": [
"packages/*"
],
"version": "independent"
}
================================================
FILE: package.json
================================================
{
"name": "type-ddd",
"version": "4.1.0",
"description": "This package provide utils file and interfaces to assistant build a complex application with domain driving design",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "ISC",
"author": "Alessandro Dev",
"private": true,
"workspaces": [
"packages/*"
],
"engines": {
"node": ">=16.x <=22"
},
"keywords": [
"Complexity",
"NodeJS",
"Business Logic",
"DDD",
"Domain Driving Design",
"Typescript",
"DDD-Utils",
"Base Entity",
"Base Aggregate",
"Base Value Object",
"Use Cases",
"Domain Events",
"Clean Architecture"
],
"scripts": {
"prebuild": "rimraf ./dist && npm run check:circular-deps",
"build": "tsc -b -v packages",
"build:lerna": "lerna run build",
"test:prod": "NODE_ENV=production jest --silent --runInBand",
"test:dev": "jest --silent --runInBand",
"test:cov": "TYPES_DDD_LOGS=off jest --silent --runInBand --coverage",
"test": "TYPES_DDD_LOGS=off jest --silent --runInBand --coverage",
"test:verbose": "jest --runInBand",
"check:circular-deps": "madge --circular --extensions ts ./packages",
"prepublish:lib": "rimraf ./dist && npm run check:circular-deps",
"publish:lib": "npm publish",
"format:all": "npx prettier --write .",
"check:types": "tsc -v packages --noEmit"
},
"repository": {
"type": "git",
"url": "git+https://github.com/4lessandrodev/type-ddd.git"
},
"bugs": {
"url": "https://github.com/4lessandrodev/type-ddd/issues"
},
"homepage": "https://github.com/4lessandrodev/type-ddd/tree/main",
"peerDependencies": {
"rich-domain": "^1.25.0"
},
"devDependencies": {
"@types/jest": "^27.0.1",
"@types/node": "^22.5.2",
"@types/pino": "^7.0.5",
"husky": "^9.0.6",
"jest": "^27.5.1",
"lerna": "^8.1.3",
"lint-staged": "^15.0.1",
"madge": "^8.0.0",
"prettier": "^3.0.0",
"rich-domain": "1.26.0",
"rimraf": "^5.0.5",
"ts-jest": "^27.1.4",
"ts-node": "^10.7.0",
"typescript": "^5.1.6"
},
"files": [
"dist/*",
"package.json",
"packages/*/*.js",
"packages/*/*.d.ts",
"README.md"
],
"lint-staged": {
"*": [
"npm run format:all",
"npx prettier --ignore-unknown --check"
],
"*.{ts,js}": [
"npm run check:circular-deps"
]
}
}
================================================
FILE: packages/cnpj/CHANGELOG.md
================================================
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.0.4-alpha.0](https://github.com/4lessandrodev/type-ddd/compare/@type-ddd/cnpj@0.0.2...@type-ddd/cnpj@0.0.4-alpha.0) (2024-12-16)
## 4.0.5 (2024-11-28)
### Bug Fixes
* change type create method return null [#194](https://github.com/4lessandrodev/type-ddd/issues/194) ([2cd03bf](https://github.com/4lessandrodev/type-ddd/commit/2cd03bf34387f4889a0a292ba350f2c0cfc753b7))
## 4.0.3 (2024-07-26)
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased
---
## Released
---
### [0.0.3] - 2024-11-28
### Fix
- update rich-domain lib to check nullish type: now 'create' return a possibly null value in Result instance.
### [0.0.2] - 2024-05-31
### Docs
- Doc: update documentation
---
### [0.0.1] - 2024-05-31
### Base
- Create base value object as single pack
================================================
FILE: packages/cnpj/README.md
================================================
# `@type-ddd/cnpj`
> The @type-ddd/cnpj library provides TypeScript type definitions for handling CNPJ (Cadastro Nacional da Pessoa Jurídica) in Domain-Driven Design contexts. It facilitates the validation and manipulation of CNPJ numbers, ensuring they adhere to the Brazilian legal standards.
---
## Installation
Install `rich-domain` and `@type-ddd/cnpj` with your favorite package manager
```sh
npm i rich-domain @type-ddd/cnpj
# OR
yarn add rich-domain @type-ddd/cnpj
```
## Usage
Don't worry about removing special characters; they are automatically stripped from all instances.
```ts
import { CNPJ } from '@type-ddd/cnpj'
// Instance of CNPJ or throws an error if provide an invalid value
const cnpj = CNPJ.init('54097792000193');
// OR
// Result of CNPJ (Check Result pattern docs)
const result = CNPJ.create('54097792000193');
result.isOk(); // true
// cnpj instance or null if provide an invalid value
const cnpj = result.value();
```
## Compare values or instances
Method to compare two instances or values.
```ts
// value as string
const isEqual = cnpj.compare('54097792000194')
// Output: false
// OR
// value as instance of CNPJ
const isEqual = cnpj.compare(cnpj2)
// Output: false
```
## Check string is valid cnpj
Don't worry about removing special characters; they are automatically stripped from all instances.
```ts
const result = CNPJ.isValid('54097792000193');
// Output: true
```
## Special chars
If you need the value with the mask, you can use the `toPattern` method:
```ts
cnpj.toPattern();
// Output: 54.097.792/0001-93
```
Or if you need to apply mask from a string value you may use `addMask` method
```ts
CNPJ.addMask('54097792000193');
// Output: 54.097.792/0001-93
```
================================================
FILE: packages/cnpj/__tests__/cnpj.value-object.util.spec.ts
================================================
import CNPJValueObject from '../index';
describe('CNPJ Value Object', () => {
describe('Creation and Definition', () => {
it('should be able to create a CNPJ value object', () => {
const valueObject = CNPJValueObject.create;
expect(valueObject).toBeDefined();
});
it('should create a valid CNPJ with special characters removed', () => {
const valueObject = CNPJValueObject.create('43.909.299/0001-04');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('43909299000104');
});
it('should create a valid CNPJ with numbers only', () => {
const valueObject = CNPJValueObject.create('60105617000101');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('60105617000101');
});
it('should initialize an instance without error', () => {
const init = () => CNPJValueObject.init('27729251000168');
expect(init).not.toThrowError();
});
it('should throw an error when initializing with an invalid value', () => {
const init = () => CNPJValueObject.init('invalid');
expect(init).toThrowError();
});
});
describe('Validation', () => {
it('should return true for a valid CNPJ', () => {
const isValid = CNPJValueObject.isValid('43.909.299/0001-04');
expect(isValid).toBeTruthy();
});
it('should return false for an invalid CNPJ', () => {
const isValid = CNPJValueObject.isValid('invalid');
expect(isValid).toBeFalsy();
});
it('should fail for an invalid CNPJ', () => {
const valueObject = CNPJValueObject.create('53.462.048/0000-99');
expect(valueObject.isFail()).toBeTruthy();
});
it('should fail for an invalid CNPJ (digit sum)', () => {
const valueObject = CNPJValueObject.create('93.118.559/0001-1');
expect(valueObject.isFail()).toBeTruthy();
});
it('should fail for an invalid CNPJ (digit sum)', () => {
const valueObject = CNPJValueObject.create('93.118.559/0001-100');
expect(valueObject.isFail()).toBeTruthy();
});
});
describe('Formatting', () => {
it('should format a CNPJ with special characters', () => {
const valueObject =
CNPJValueObject.create('20.798.751/0001-02').value();
expect(valueObject?.toPattern()).toBe('20.798.751/0001-02');
});
it('should format a CNPJ with special characters', () => {
const valueObject =
CNPJValueObject.create('65.389.009/0001-81').value();
expect(valueObject?.toPattern()).toBe('65.389.009/0001-81');
});
it('should format a CNPJ with special characters', () => {
const valueObject =
CNPJValueObject.create('02.470.431/0001-47').value();
expect(valueObject?.toPattern()).toBe('02.470.431/0001-47');
});
it('should format a CNPJ with special characters and remove them later', () => {
const valueObject =
CNPJValueObject.create('62.412.404/0001-40').value();
expect(valueObject?.toPattern()).toBe('62.412.404/0001-40');
});
});
describe('Comparison', () => {
it('should correctly compare the value in the instance with the provided value', () => {
const validCNPJ = '22.606.062/0001-84';
const valueObject = CNPJValueObject.create(validCNPJ).value();
// Compare with invalid CNPJ
let isEqual = valueObject?.compare('invalid');
expect(isEqual).toBeFalsy();
// Compare with different valid CNPJ
isEqual = valueObject?.compare('22.606.062/0001-20');
expect(isEqual).toBeFalsy();
// Compare with the same valid CNPJ
isEqual = valueObject?.compare(validCNPJ);
expect(isEqual).toBeTruthy();
// Compare with a valid CNPJ with different format
isEqual = valueObject?.compare('22606062000155');
expect(isEqual).toBeFalsy();
// Compare with a valid CNPJ with the same value but different format
isEqual = valueObject?.compare('22606062000184');
expect(isEqual).toBeTruthy();
// Compare with the same valid CNPJ
isEqual = valueObject?.compare('22.606.062/0001-84');
expect(isEqual).toBeTruthy();
});
it('should compare two instances', () => {
const instanceA = CNPJValueObject.init('22.606.062/0001-84');
const instanceB = CNPJValueObject.init('22.606.062/0001-84');
expect(instanceA.compare(instanceB)).toBeTruthy();
});
it('should return false when comparing with null', () => {
const instanceA = CNPJValueObject.init('22.606.062/0001-84');
expect(instanceA.compare(null as any)).toBeFalsy();
});
});
describe('Special Character Removal', () => {
it('should remove special characters from a string', () => {
const value = CNPJValueObject.removeSpecialChars('93.118.559/0001-10');
expect(value).toBe('93118559000110');
});
it('should remove special characters from a string', () => {
const value = CNPJValueObject.addMask('93118559000110');
expect(value).toBe('93.118.559/0001-10');
});
});
});
================================================
FILE: packages/cnpj/__tests__/is-valid-cpf-digit.util.spec.ts
================================================
import { formatValueToCnpjPattern, isValidCnpjDigit, removeSpecialCharsFromCnpj } from '../util';
describe('is-valid-cnpj-digits', () => {
it('should be defined', () => {
const isValidCnpjDigitFn = isValidCnpjDigit;
expect(isValidCnpjDigitFn).toBeDefined();
});
it('should return false if provide a value less than 11 char', () => {
const isValidCnpjDigitFn = isValidCnpjDigit('506595900001');
expect(isValidCnpjDigitFn).toBe(false);
});
it('should return false if provide word instead numbers', () => {
const isValidCnpjDigitFn = isValidCnpjDigit('invalid_val');
expect(isValidCnpjDigitFn).toBe(false);
});
it('should return false if provide a value with invalid sum', () => {
const isValidCnpjDigitFn = isValidCnpjDigit('39.060.118/0001-41');
expect(isValidCnpjDigitFn).toBe(false);
});
it('should return false if provide a value with invalid sum', () => {
const isValidCnpjDigitFn = isValidCnpjDigit('50659590000140');
expect(isValidCnpjDigitFn).toBe(false);
});
it('should return true and validate with success', () => {
const isValidCnpjDigitFn = isValidCnpjDigit('50659590000137');
expect(isValidCnpjDigitFn).toBe(true);
});
it('should return true and validate with success', () => {
const isValidCnpjDigitFn = isValidCnpjDigit('05.718.081/0001-83');
expect(isValidCnpjDigitFn).toBe(true);
});
it('should return true and validate with success', () => {
const isValidCnpjDigitFn = isValidCnpjDigit('39.434.735/0001-69');
expect(isValidCnpjDigitFn).toBe(true);
});
it('should return true and validate with success', () => {
const isValidCnpjDigitFn = isValidCnpjDigit('47.091.193/0001-05');
expect(isValidCnpjDigitFn).toBe(true);
});
it('should format cnpj from pattern to only numbers', () => {
const isValidCnpjDigitFn =
removeSpecialCharsFromCnpj('39.604.284/0001-60');
expect(isValidCnpjDigitFn).toBe('39604284000160');
});
it('should format cnpj from pattern to only numbers', () => {
const isValidCnpjDigitFn = removeSpecialCharsFromCnpj('39604284000160');
expect(isValidCnpjDigitFn).toBe('39604284000160');
});
it('should format cnpj from pattern to only numbers', () => {
const isValidCnpjDigitFn =
removeSpecialCharsFromCnpj('val.cnpj.str-d0');
expect(isValidCnpjDigitFn).toBe('valcnpjstrd0');
});
it('should format cnpj from only numbers to pattern', () => {
const isValidCnpjDigitFn = formatValueToCnpjPattern('39604284000160');
expect(isValidCnpjDigitFn).toBe('39.604.284/0001-60');
});
it('should format cnpj from only numbers to pattern', () => {
const isValidCnpjDigitFn = formatValueToCnpjPattern('39604284000160');
expect(isValidCnpjDigitFn).toBe('39.604.284/0001-60');
});
it('should format cnpj from only numbers to pattern', () => {
const isValidCnpjDigitFn = formatValueToCnpjPattern('vacnppj00strd0');
expect(isValidCnpjDigitFn).toBe('va.cnp.pj0/0str-d0');
});
});
================================================
FILE: packages/cnpj/index.ts
================================================
import { Result, ValueObject } from 'rich-domain';
import isValidCnpjDigit, { formatValueToCnpjPattern, removeSpecialCharsFromCnpj } from './util';
const regexCnpj =
/^([0-9]{2})[\.]([0-9]{3})[\.]((?!\2)[0-9]{3})[\/]([0-9]{4})[-]([0-9]{2})$|^[0-9]{14}$/;
export class CNPJ extends ValueObject<string> {
protected static readonly REGEX = regexCnpj;
protected static readonly MESSAGE: string = 'Invalid value for cnpj';
private constructor(props: string) {
super(props);
}
/**
* @description return a cnpj value (only numbers).
* @example example "22398345000188".
* @summary If you want cnpj as pattern use `formatToCnpjPattern` before get value.
*/
value(): string {
return this.props;
}
/**
* @description add hyphen and dot to cnpj value.
* @example before "22398345000188"
* @example after "22.398.345/0001-88"
*/
toPattern(): string {
return formatValueToCnpjPattern(this.props);
}
/**
* @description add hyphen and dot to cnpj value.
* @example before "22398345000188"
* @example after "22.398.345/0001-88"
*/
public static addMask(cnpj: string): string {
return formatValueToCnpjPattern(cnpj);
}
/**
* @description remove hyphen and dot from cnpj value.
* @example before "22.398.345/0001-88"
* @example after "22398345000188"
*/
public static removeSpecialChars(cnpj: string): string {
return removeSpecialCharsFromCnpj(cnpj);
}
/**
*
* @param cnpj value as string only number or pattern.
* @returns true if cnpj match with instance value and false if not.
* @example param "22398345000188"
* @example param "22.398.345/0001-88"
*/
compare(cnpj: string | CNPJ): boolean {
if (typeof cnpj === 'string') {
const formattedCnpj = removeSpecialCharsFromCnpj(cnpj);
const instanceValue = this.props;
return instanceValue === formattedCnpj;
}
if (cnpj instanceof CNPJ) {
return cnpj.isEqual(this);
}
return false;
}
/**
* @description check if cnpj value is a valid pattern and has a valid digit sum.
* @param value cnpj as string
* @returns true if value is valid and false if not.
* @example "22.398.345/0001-88"
* @example "22398345000188"
*/
public static isValidProps(value: string): boolean {
const isValidPattern = CNPJ.REGEX.test(value);
const isValidDigits = isValidCnpjDigit(value);
return isValidDigits && isValidPattern;
}
/**
* @description check if cnpj value is a valid pattern and has a valid digit sum.
* @param value cnpj as string
* @returns true if value is valid and false if not.
* @example "22.398.345/0001-88"
* @example "22398345000188"
*/
public static isValid(value: string): boolean {
return this.isValidProps(value);
}
/**
*
* @param value value as string
* @returns instance of CNPJ or throw an error
*/
public static init(value: string): CNPJ {
const isValidValue = CNPJ.isValidProps(value);
if (!isValidValue) throw new Error(CNPJ.MESSAGE);
return new CNPJ(value);
}
/**
* @description create a cnpj value object
* @param value cnpj numbers as string
* @returns instance of Result with cnpj value
* @example "22.398.345/0001-88"
* @example "22398345000188"
* @summary fails if provide an invalid pattern or a cnpj with invalid digit sum
*/
public static create(value: string): Result<CNPJ | null> {
const isValidValue = CNPJ.isValidProps(value);
if (!isValidValue) {
return Result.fail(CNPJ.MESSAGE);
}
return Result.Ok(new CNPJ(removeSpecialCharsFromCnpj(value)));
}
}
export default CNPJ;
================================================
FILE: packages/cnpj/package.json
================================================
{
"name": "@type-ddd/cnpj",
"description": "Library that provides TypeScript type definitions for handling CNPJ (Cadastro Nacional da Pessoa Jurídica) in Domain-Driven Design contexts. It facilitates the validation and manipulation of CNPJ numbers, ensuring they adhere to the Brazilian legal standards.",
"version": "0.1.0",
"main": "index.js",
"types": "index.d.ts",
"author": "Alessandro Dev",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"keywords": [
"Complexity",
"Business Logic",
"DDD",
"Domain Driving Design",
"DDD-Utils",
"Base Value Object",
"Domain Events",
"Clean Architecture",
"Validation",
"Formatting",
"Value Object",
"Utility",
"Security",
"Standards",
"Brazil",
"CNPJ",
"Cadastro Nacional Pessoa Jurídica",
"Pessoa Jurídica"
],
"scripts": {
"build": "tsc"
},
"peerDependencies": {
"rich-domain": "^1.25.0"
},
"files": [
"index.js",
"index.d.ts",
"util.d.ts",
"util.js"
],
"repository": {
"type": "git",
"url": "git+https://github.com/4lessandrodev/type-ddd.git"
},
"bugs": {
"url": "https://github.com/4lessandrodev/type-ddd/issues"
},
"homepage": "https://github.com/4lessandrodev/type-ddd/tree/main/packages/cnpj",
"gitHead": "4cb9159bde8d6fc951e9d902feed2ad25da49fa4"
}
================================================
FILE: packages/cnpj/tsconfig.build.json
================================================
{
"extends": "../tsconfig.build.json",
"compilerOptions": {
"outDir": ".",
"rootDir": ".",
"paths": {}
},
"exclude": [
"node_modules",
"dist",
"__tests__/**/*",
"*.spec.ts"
],
"references": []
}
================================================
FILE: packages/cnpj/tsconfig.json
================================================
{
"extends": "../tsconfig.build.json",
"compilerOptions": {
"types": [
"node"
]
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.build.json"
}
]
}
================================================
FILE: packages/cnpj/util.ts
================================================
interface CnpjDigits {
penultimateDigit: number;
ultimateDigit: number;
}
const removeSpecialCharsFromCnpjRegex = /[\.]|[-]|[\/]/g;
export const formatValueToCnpjPattern = (cnpj: string): string => {
const cnpjValue = removeSpecialCharsFromCnpj(cnpj);
let formattedValue: string = '';
let index: number = 0;
while (formattedValue.length < 18 && index < 17) {
if (index === 2 || index === 5) {
formattedValue += '.';
} else if (index === 8) {
formattedValue += '/';
} else if (index === 12) {
formattedValue += '-';
}
formattedValue += cnpjValue[index];
index++;
}
return formattedValue;
};
export const removeSpecialCharsFromCnpj = (cnpj: string): string => {
return cnpj.replace(removeSpecialCharsFromCnpjRegex, '');
};
const getCnpjDigitsNumbers = (cnpj: string): CnpjDigits => {
const lastTwoNumbers = cnpj.slice(cnpj.length - 2);
const penultimateDigit = parseInt(lastTwoNumbers[0]);
const ultimateDigit = parseInt(lastTwoNumbers[1]);
return {
penultimateDigit,
ultimateDigit,
};
};
const transformCnpjInArrNumber = (cnpj: string): number[] => {
var arr: number[] = [];
let index = 0;
while (index < 12) {
arr.push(parseInt(cnpj[index]));
index++;
}
return arr;
};
const calculateCnpjDigits = (cnpjNumbers: number[]): CnpjDigits => {
const factor = 11;
let index = cnpjNumbers.length - 1;
let startAuxValue = 2;
let totalForDigit = 0;
while (index >= 0) {
totalForDigit = totalForDigit + cnpjNumbers[index] * startAuxValue;
startAuxValue = startAuxValue === 9 ? 2 : startAuxValue + 1;
index--;
}
const calcPDigit = totalForDigit % factor;
const resultPDigit = factor - calcPDigit;
const zeroIfPGreaterThanNine = resultPDigit >= 9 ? 0 : resultPDigit;
const penultimateDigit = zeroIfPGreaterThanNine;
cnpjNumbers.push(penultimateDigit);
index = cnpjNumbers.length - 1;
startAuxValue = 2;
totalForDigit = 0;
while (index >= 0) {
totalForDigit = totalForDigit + cnpjNumbers[index] * startAuxValue;
startAuxValue = startAuxValue === 9 ? 2 : startAuxValue + 1;
index--;
}
const calcUDigit = totalForDigit % factor;
const resultUDigit = factor - calcUDigit;
const zeroIfGreaterThanNine = resultUDigit > 9 ? 0 : resultUDigit;
const ultimateDigit = zeroIfGreaterThanNine;
return {
penultimateDigit,
ultimateDigit,
};
};
export const isValidCnpjDigit = (cnpj: string): boolean => {
const onlyNumbers = removeSpecialCharsFromCnpj(cnpj);
if (onlyNumbers.length !== 14) {
return false;
}
const digits = getCnpjDigitsNumbers(onlyNumbers);
const arrNumbers = transformCnpjInArrNumber(onlyNumbers);
const validDigits = calculateCnpjDigits(arrNumbers);
return (
digits.penultimateDigit === validDigits.penultimateDigit &&
digits.ultimateDigit === validDigits.ultimateDigit
);
};
export default isValidCnpjDigit;
================================================
FILE: packages/cpf/CHANGELOG.md
================================================
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.0.4-alpha.0](https://github.com/4lessandrodev/type-ddd/compare/@type-ddd/cpf@0.0.2...@type-ddd/cpf@0.0.4-alpha.0) (2024-12-16)
## 4.0.5 (2024-11-28)
### Bug Fixes
* change type create method return null [#194](https://github.com/4lessandrodev/type-ddd/issues/194) ([2cd03bf](https://github.com/4lessandrodev/type-ddd/commit/2cd03bf34387f4889a0a292ba350f2c0cfc753b7))
## 4.0.3 (2024-07-26)
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased
---
## Released
---
### [0.0.3] - 2024-11-28
### Fix
- update rich-domain lib to check nullish type: now 'create' return a possibly null value in Result instance.
### [0.0.2] - 2024-05-31
### Docs
- Doc: update documentation
---
### [0.0.1] - 2024-05-14
### Base
- Create base value object as single pack
================================================
FILE: packages/cpf/README.md
================================================
# `@type-ddd/cpf`
> The @type-ddd/cpf library provides TypeScript type definitions for handling CPF (Cadastro de Pessoa Física) in Domain-Driven Design contexts. It facilitates the validation and manipulation of CPF numbers, ensuring they adhere to the Brazilian legal standards.
---
## Installation
Install `rich-domain` and `@type-ddd/cpf` with your favorite package manager
```sh
npm i rich-domain @type-ddd/cpf
# OR
yarn add rich-domain @type-ddd/cpf
```
## Usage
Don't worry about removing special characters; they are automatically stripped from all instances.
```ts
import { CPF } from '@type-ddd/cpf'
// Instance of cpf or throws an error if provide an invalid value
const cpf = CPF.init('54097792000193');
// OR
// Result of cpf (Check Result pattern docs)
const result = CPF.create('54097792000193');
result.isOk(); // true
// cpf instance or null if provide an invalid value
const cpf = result.value();
```
## Compare values or instances
Method to compare two instances or values.
```ts
// value as string
const isEqual = cpf.compare('54097792000194')
// Output: false
// OR
// value as instance of cpf
const isEqual = cpf.compare(cpf2)
// Output: false
```
## Check string is valid cpf
Don't worry about removing special characters; they are automatically stripped from all instances.
```ts
const result = CPF.isValid('54097792000193');
// Output: true
```
## Special chars
If you need the value with the mask, you can use the `toPattern` method:
```ts
cpf.toPattern();
// Output: 54.097.792/0001-93
```
Or if you need to apply mask from a string value you may use `addMask` method
```ts
CPF.addMask('54097792000193');
// Output: 54.097.792/0001-93
```
================================================
FILE: packages/cpf/__tests__/cpf.value-object.util.spec.ts
================================================
import CPFValueObject from "../index";
describe('cpf.value-object', () => {
it('should be defined', () => {
const valueObject = CPFValueObject.create;
expect(valueObject).toBeDefined();
});
it('should create a valid cpf with special chars and remove special chars on get value', () => {
const valueObject = CPFValueObject.create('667.324.914-58');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('66732491458');
});
it('should create a valid cpf with special chars and remove special chars on get value', () => {
const valueObject = CPFValueObject.create('934.665.143-12');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('93466514312');
});
it('should create a valid cpf with special chars and remove special chars on get value', () => {
const valueObject = CPFValueObject.create('690.574.738-60');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('69057473860');
});
it('should create a valid cpf with special chars and remove special chars on get value', () => {
const valueObject = CPFValueObject.create('324.123.359-66');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('32412335966');
});
it('should create a valid cpf with special chars and remove special chars on get value', () => {
const valueObject = CPFValueObject.create('673.761.543-02');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('67376154302');
});
it('should create a valid cpf with special chars and remove special chars on get value', () => {
const valueObject = CPFValueObject.create('024.815.901-12');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('02481590112');
});
it('should create a valid cpf with special chars and remove special chars on get value', () => {
const valueObject = CPFValueObject.create('754.179.880-06');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('75417988006');
});
it('should format a cpf to add special chars', () => {
const valueObject = CPFValueObject.create('667.324.914-58').value();
expect(valueObject?.toPattern()).toBe('667.324.914-58');
});
it('should format a cpf to add special chars', () => {
const valueObject = CPFValueObject.create('578.363.883-87').value();
expect(valueObject?.toPattern()).toBe('578.363.883-87');
});
it('should format a cpf to add special chars', () => {
const valueObject = CPFValueObject.create('844.676.543-80').value();
expect(valueObject?.toPattern()).toBe('844.676.543-80');
});
it('should format a cpf to add special chars and remove it later', () => {
const valueObject = CPFValueObject.create('667.324.914-58').value();
expect(valueObject?.toPattern()).toBe('667.324.914-58');
expect(valueObject?.value()).toBe('66732491458');
});
it('should compare value on instance and provided value', () => {
const valueObject = CPFValueObject.create('549.777.281-14').value();
let isEqual = valueObject?.compare('invalid');
expect(isEqual).toBeFalsy();
isEqual = valueObject?.compare('549.777.281-15');
expect(isEqual).toBeFalsy();
isEqual = valueObject?.compare('549.777.281-14');
expect(isEqual).toBeTruthy();
isEqual = valueObject?.compare('54977728314');
expect(isEqual).toBeFalsy();
isEqual = valueObject?.compare('54977728114');
expect(isEqual).toBeTruthy();
isEqual = valueObject?.compare('54977728114');
expect(isEqual).toBeTruthy();
isEqual = valueObject?.compare('549.777.281-14');
expect(isEqual).toBeTruthy();
});
it('should create a valid cpf only numbers', () => {
const valueObject = CPFValueObject.create('53534317661');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('53534317661');
expect(CPFValueObject.isValid('53534317661')).toBeTruthy();
});
it('should fail if provide an invalid value', () => {
const valueObject = CPFValueObject.create('754.466.282-920');
expect(valueObject.isFail()).toBeTruthy();
});
it('should fail if provide an invalid value (digit sum)', () => {
const valueObject = CPFValueObject.create('754.466.282-01');
expect(valueObject.isFail()).toBeTruthy();
});
it('should fail if provide an invalid value (digit sum)', () => {
const valueObject = CPFValueObject.create('75446628201');
expect(valueObject.isFail()).toBeTruthy();
});
it('should create a valid cpf only numbers', () => {
const valueObject = CPFValueObject.create('53534317661');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('53534317661');
});
it('should create a valid cpf only numbers', () => {
const valueObject = CPFValueObject.create('98614591039');
expect(valueObject.isOk()).toBeTruthy();
expect(valueObject.value()?.value()).toBe('98614591039');
});
it('should init an instance with success', () => {
const init = () => CPFValueObject.init('53534317661');
expect(init).not.toThrowError();
});
it('should throw an error on init an instance with invalid value', () => {
const init = () => CPFValueObject.init('invalid');
expect(init).toThrowError();
});
it('should add mask with success', () => {
const result = CPFValueObject.addMask('53534317661');
expect(result).toBe('535.343.176-61');
});
it('should remove mask with success', () => {
const result = CPFValueObject.removeSpecialChars('535.343.176-61');
expect(result).toBe('53534317661');
});
it('should compare cpf instances with success', () => {
const cpfA = CPFValueObject.init('535.343.176-61');
const cpfB = CPFValueObject.init('53534317661');
const cpfC = CPFValueObject.init('89926097014');
expect(cpfA.compare(cpfB)).toBeTruthy();
expect(cpfA.compare(cpfC)).toBeFalsy();
expect(cpfC.compare(123 as any)).toBeFalsy();
});
});
================================================
FILE: packages/cpf/__tests__/is-valid-cpf-digit.util.spec.ts
================================================
import isValidCpfDigit, { formatValueToCpfPattern, removeSpecialCharsFromCpf } from "../util";
describe('is-valid-cpf-digits', () => {
it('should be defined', () => {
const isValidCpfDigitFn = isValidCpfDigit;
expect(isValidCpfDigitFn).toBeDefined();
});
it('should return false if provide a value less than 11 char', () => {
const isValidCpfDigitFn = isValidCpfDigit('727254778');
expect(isValidCpfDigitFn).toBe(false);
});
it('should return false if provide word instead numbers', () => {
const isValidCpfDigitFn = isValidCpfDigit('invalid_val');
expect(isValidCpfDigitFn).toBe(false);
});
it('should return false if provide a value with invalid sum', () => {
const isValidCpfDigitFn = isValidCpfDigit('766.682.694-01');
expect(isValidCpfDigitFn).toBe(false);
});
it('should return false if provide a value with invalid sum', () => {
const isValidCpfDigitFn = isValidCpfDigit('76668269401');
expect(isValidCpfDigitFn).toBe(false);
});
it('should return true and validate with success', () => {
const isValidCpfDigitFn = isValidCpfDigit('76668269400');
expect(isValidCpfDigitFn).toBe(true);
});
it('should return true and validate with success', () => {
const isValidCpfDigitFn = isValidCpfDigit('730.208.487-41');
expect(isValidCpfDigitFn).toBe(true);
});
it('should return true and validate with success', () => {
const isValidCpfDigitFn = isValidCpfDigit('641.482.734-79');
expect(isValidCpfDigitFn).toBe(true);
});
it('should return true and validate with success', () => {
const isValidCpfDigitFn = isValidCpfDigit('48153676474');
expect(isValidCpfDigitFn).toBe(true);
});
it('should format cpf from pattern to only numbers', () => {
const isValidCpfDigitFn = removeSpecialCharsFromCpf('641.482.734-79');
expect(isValidCpfDigitFn).toBe('64148273479');
});
it('should format cpf from pattern to only numbers', () => {
const isValidCpfDigitFn = removeSpecialCharsFromCpf('64148273479');
expect(isValidCpfDigitFn).toBe('64148273479');
});
it('should format cpf from pattern to only numbers', () => {
const isValidCpfDigitFn = removeSpecialCharsFromCpf('val.cpf.str-d0');
expect(isValidCpfDigitFn).toBe('valcpfstrd0');
});
it('should format cpf from only numbers to pattern', () => {
const isValidCpfDigitFn = formatValueToCpfPattern('64148273479');
expect(isValidCpfDigitFn).toBe('641.482.734-79');
});
it('should format cpf from only numbers to pattern', () => {
const isValidCpfDigitFn = formatValueToCpfPattern('64148273479');
expect(isValidCpfDigitFn).toBe('641.482.734-79');
});
it('should format cpf from only numbers to pattern', () => {
const isValidCpfDigitFn = formatValueToCpfPattern('valcpfstrd0');
expect(isValidCpfDigitFn).toBe('val.cpf.str-d0');
});
it('should to be valid cpf', () => {
const isValid = isValidCpfDigit('15173713097');
expect(isValid).toBeTruthy();
});
});
================================================
FILE: packages/cpf/index.ts
================================================
import { Result, ValueObject } from "rich-domain";
import isValidCpfDigit, { formatValueToCpfPattern } from "./util";
export class CPF extends ValueObject<string> {
protected static readonly REGEX = /^([0-9]{3})[\.]((?!\1)[0-9]{3})[\.]([0-9]{3})[-]([0-9]{2})$|^[0-9]{11}$/;
protected static readonly MESSAGE: string = 'Invalid value for cpf';
private constructor(value: string) {
super(value);
}
/**
* @description return a cpf value (only numbers).
* @example example "52734865211".
* @summary If you want cpf as pattern use `formatToCpfPattern` before get value.
*/
value(): string {
return this.props;
}
/**
* @description add hyphen and dot to cpf value.
* @example before "52734865211"
* @example after "527.348.652-11"
*/
toPattern(): string {
return formatValueToCpfPattern(this.props);
}
/**
* @description add hyphen and dot to cpf value.
* @example before "52734865211"
* @example after "527.348.652-11"
*/
public static addMask(cpf: string): string {
return formatValueToCpfPattern(cpf);
}
/**
* @description remove hyphen and dot from cpf value.
* @example before "527.348.652-11"
* @example after "52734865211"
*/
public static removeSpecialChars(cpf: string): string {
return this.util.string(cpf).removeSpecialChars();
}
/**
*
* @param cpf value as string only number or pattern Or instance of CPF.
* @returns true if cpf match with instance value and false if not.
* @example param "52734865211"
* @example param "527.348.652-11"
*/
compare(cpf: string | CPF): boolean {
if (typeof cpf === 'string') {
const valueA = this.util.string(cpf).removeSpecialChars();
const valueB = this.util
.string(this.props)
.removeSpecialChars();
return valueA === valueB;
}
if (cpf instanceof CPF) return cpf.isEqual(this);
return false;
}
/**
* @description check if cpf value is a valid pattern and has a valid digit sum.
* @param value cpf as string
* @returns true if value is valid and false if not.
* @example "527.348.652-11"
* @example "72725477824"
*/
public static isValidProps(value: string): boolean {
const isValidPattern = CPF.REGEX.test(value);
const isValidDigits = isValidCpfDigit(value);
return isValidDigits && isValidPattern;
}
/**
* @description check if cpf value is a valid pattern and has a valid digit sum.
* @param value cpf as string
* @returns true if value is valid and false if not.
* @example "527.348.652-11"
* @example "72725477824"
*/
public static isValid(value: string): boolean {
return this.isValidProps(value);
}
/**
*
* @param value value as string
* @returns instance of CPF or throw an error
*/
public static init(value: string): CPF {
const isValidValue = CPF.isValidProps(value);
if (!isValidValue) throw new Error(CPF.MESSAGE);
return new CPF(this.util.string(value).removeSpecialChars());
}
/**
* @description create a cpf value object
* @param value cpf numbers as string
* @returns instance of Result with cpf value
* @example "527.348.652-11"
* @example "72725477824"
* @summary fails if provide an invalid pattern or a cpf with invalid digit sum
*/
public static create(value: string): Result<CPF | null> {
const isValidValue = CPF.isValidProps(value);
if (!isValidValue) {
return Result.fail(CPF.MESSAGE);
}
return Result.Ok(new CPF(this.util.string(value).removeSpecialChars()));
}
}
export default CPF;
================================================
FILE: packages/cpf/package.json
================================================
{
"name": "@type-ddd/cpf",
"description": "This package provides TypeScript type definitions for handling CPF (Cadastro de Pessoa Física) in Domain-Driven Design contexts",
"version": "0.1.0",
"main": "index.js",
"types": "index.d.ts",
"author": "Alessandro Dev",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"keywords": [
"Complexity",
"Business Logic",
"DDD",
"Domain Driving Design",
"DDD-Utils",
"Base Value Object",
"Domain Events",
"CPF",
"Validation",
"Formatting",
"Value Object",
"Utility",
"Standards",
"Brazil",
"Cadastro de Pessoa Física"
],
"scripts": {
"build": "tsc"
},
"peerDependencies": {
"rich-domain": "^1.25.0"
},
"files": [
"index.js",
"index.d.ts",
"util.d.ts",
"util.js"
],
"repository": {
"type": "git",
"url": "git+https://github.com/4lessandrodev/type-ddd.git"
},
"bugs": {
"url": "https://github.com/4lessandrodev/type-ddd/issues"
},
"homepage": "https://github.com/4lessandrodev/type-ddd/tree/main/packages/cpf",
"gitHead": "4cb9159bde8d6fc951e9d902feed2ad25da49fa4"
}
================================================
FILE: packages/cpf/tsconfig.build.json
================================================
{
"extends": "../tsconfig.build.json",
"compilerOptions": {
"outDir": ".",
"rootDir": ".",
"paths": {}
},
"exclude": [
"node_modules",
"dist",
"__tests__/**/*",
"*.spec.ts"
],
"references": []
}
================================================
FILE: packages/cpf/tsconfig.json
================================================
{
"extends": "../tsconfig.build.json",
"compilerOptions": {
"types": [
"node"
]
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.build.json"
}
]
}
================================================
FILE: packages/cpf/util.ts
================================================
interface CpfDigits {
penultimateDigit: number;
ultimateDigit: number;
}
const removeSpecialCharsFromCpfRegex = /[\.]|[-]/g;
export const formatValueToCpfPattern = (cpf: string): string => {
const cpfValue = removeSpecialCharsFromCpf(cpf);
let formattedValue: string = '';
let index: number = 0;
while (formattedValue.length < 14 && index < 11) {
if (index === 3 || index === 6) {
formattedValue += '.';
} else if (index === 9) {
formattedValue += '-';
}
formattedValue += cpfValue[index];
index++;
}
return formattedValue;
};
export const removeSpecialCharsFromCpf = (cpf: string): string => {
return cpf.replace(removeSpecialCharsFromCpfRegex, '');
};
const getCpfDigitsNumbers = (cpf: string): CpfDigits => {
const lastTwoNumbers = cpf.slice(cpf.length - 2);
const penultimateDigit = parseInt(lastTwoNumbers[0]);
const ultimateDigit = parseInt(lastTwoNumbers[1]);
return {
penultimateDigit,
ultimateDigit,
};
};
const transformCpfInArrNumber = (cpf: string): number[] => {
var arr: number[] = [];
let index = 0;
while (index < 9) {
arr.push(parseInt(cpf[index]));
index++;
}
return arr;
};
export const calculateCpfDigits = (cpfNumbers: number[]): CpfDigits => {
const factor = 11;
let index = 0;
let startAuxValue = 10;
let totalForDigit = 0;
while (index < 9) {
totalForDigit = totalForDigit + cpfNumbers[index] * startAuxValue;
startAuxValue--;
index++;
}
const calcPDigit = totalForDigit % factor;
const resultPDigit = factor - calcPDigit;
const zeroIfPGreaterThanNine = resultPDigit > 9 ? 0 : resultPDigit;
const penultimateDigit = zeroIfPGreaterThanNine;
index = 0;
startAuxValue = 11;
totalForDigit = 0;
cpfNumbers.push(penultimateDigit);
while (index < 10) {
totalForDigit = totalForDigit + cpfNumbers[index] * startAuxValue;
startAuxValue--;
index++;
}
const calcUDigit = totalForDigit % factor;
const resultUDigit = factor - calcUDigit;
const zeroIfGreaterThanNine = resultUDigit > 9 ? 0 : resultUDigit;
const ultimateDigit = zeroIfGreaterThanNine;
return {
penultimateDigit,
ultimateDigit,
};
};
export const isValidCpfDigit = (cpf: string): boolean => {
const onlyNumbers = removeSpecialCharsFromCpf(cpf);
if (onlyNumbers.length !== 11) {
return false;
}
const digits = getCpfDigitsNumbers(onlyNumbers);
const arrNumbers = transformCpfInArrNumber(onlyNumbers);
const validDigits = calculateCpfDigits(arrNumbers);
return (
digits.penultimateDigit === validDigits.penultimateDigit &&
digits.ultimateDigit === validDigits.ultimateDigit
);
};
export default isValidCpfDigit;
================================================
FILE: packages/date/CHANGELOG.md
================================================
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.0.4-alpha.0](https://github.com/4lessandrodev/type-ddd/compare/@type-ddd/date@0.0.2...@type-ddd/date@0.0.4-alpha.0) (2024-12-16)
## 4.0.5 (2024-11-28)
### Bug Fixes
* change type create method return null [#194](https://github.com/4lessandrodev/type-ddd/issues/194) ([2cd03bf](https://github.com/4lessandrodev/type-ddd/commit/2cd03bf34387f4889a0a292ba350f2c0cfc753b7))
## 4.0.3 (2024-07-26)
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased
---
## Released
---
### [0.0.3] - 2024-11-28
### Fix
- update rich-domain lib to check nullish type: now 'create' return a possibly null value in Result instance.
### [0.0.2] - 2024-05-31
### Docs
- Doc: update documentation
---
### [0.0.1] - 2024-05-31
### Base
- Create base value object as single pack
================================================
FILE: packages/date/README.md
================================================
# `@type-ddd/date`
> The @type-ddd/date library provides a class Dates for handling date and time operations in TypeScript. It offers various methods for manipulating dates, calculating differences, formatting dates, and checking validity. This library aims to simplify date and time management in Domain-Driven Design contexts.
---
## Installation
Install `rich-domain` and `@type-ddd/date` with your favorite package manager:
```sh
npm i rich-domain @type-ddd/date
# OR
yarn add rich-domain @type-ddd/date
```
## Usage
```ts
import { Dates } from '@type-ddd/dates';
// Check if is valid value
const isValid = Dates.isValid('2020-02-31');
// false
// Initialize Dates instance with current date and time
const date = Dates.init();
// OR
// Create Dates instance from provided date or timestamp
const result = Dates.create('2024-05-24');
// Add days, months, hours, minutes, weeks, or years
const newDate = date.addDays(5).addMonths(2);
// Format date according to various patterns
const formattedDate = date.format('DD/MM/YYYY hh:mm:ss');
// Check if date is weekday or weekend
const isWeekday = date.isWeekday();
const isWeekend = date.isWeekend();
```
================================================
FILE: packages/date/__tests__/date.value-object.spec.ts
================================================
import Dates from '../index';
describe('Date', () => {
describe('create', () => {
it('should create an instance with success', () => {
const instance = Dates.create();
expect(instance.value()).toBeInstanceOf(Dates);
});
it('should create an instance with success', () => {
const instance = Dates.create('2020-12-25');
expect(instance.value()).toBeInstanceOf(Dates);
});
it('should create an instance with success', () => {
const instance = Dates.create(Date.now());
expect(instance.value()).toBeInstanceOf(Dates);
});
it('should create an instance with success', () => {
const instance = Dates.create(new Date(2020, 1, 1, 1, 1, 1));
expect(instance.value()?.value().toISOString()).toBe('2020-02-01T01:01:01.000Z');
});
it('should return result fail if provide an invalid value', () => {
const instance = Dates.create({} as any);
expect(instance.isFail()).toBeTruthy();
});
it('should return result fail if provide an invalid date string', () => {
const instance = Dates.create('invalid');
expect(instance.isFail()).toBeTruthy();
});
it('should init a valid v.o', () => {
const instance = Dates.init();
expect(instance).toBeInstanceOf(Dates);
});
it('should init a date with provided value', () => {
const instance = Dates.init(new Date(2020, 1, 1, 1, 1, 1));
expect(instance.value().toISOString()).toBe('2020-02-01T01:01:01.000Z');
});
it('should throw an error if provide an invalid value', () => {
const build = () => Dates.init({} as any);
expect(build).toThrowError();
});
it('should throw if provide an invalid date string', () => {
const build = () => Dates.init('invalid');
expect(build).toThrowError();
});
it('should throw if provide an invalid date string', () => {
const build = () => Dates.init('2020-12-25');
expect(build).not.toThrowError();
});
it('should throw if provide an invalid date string', () => {
const build = () => Dates.init(Date.now());
expect(build).not.toThrowError();
});
});
describe('addDays', () => {
it('should add 1 day with success', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.addDays(1).value().toISOString()).toBe('2020-01-02T08:00:00.000Z');
});
it('should add 7 days with success', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.addDays(7).value().toISOString()).toBe('2020-01-08T08:00:00.000Z');
});
});
describe('addMonths', () => {
it('should add 1 month with success', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.addMonths(1).value().toISOString()).toBe('2020-01-31T08:00:00.000Z');
});
it('should add 7 months with success', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.addMonths(7).value().toISOString()).toBe('2020-07-29T08:00:00.000Z');
});
});
describe('addHours', () => {
it('should add 1 hour with success', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.addHours(1).value().toISOString()).toBe('2020-01-01T09:00:00.000Z');
});
it('should add 7 hours with success', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.addHours(7).value().toISOString()).toBe('2020-01-01T15:00:00.000Z');
});
});
describe('addMinutes', () => {
it('should add 1 minute with success', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.addMinutes(1).value().toISOString()).toBe('2020-01-01T08:01:00.000Z');
});
it('should add 7 minutes with success', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.addMinutes(7).value().toISOString()).toBe('2020-01-01T08:07:00.000Z');
});
});
describe('addWeeks', () => {
it('should add 1 week with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.addWeeks(1);
expect(date2.value().toISOString()).toBe('2020-01-08T08:00:00.000Z');
expect(date2.differenceInDays(date1)).toBe(7);
});
it('should add 7 weeks with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.addWeeks(7);
expect(date2.value().toISOString()).toBe('2020-02-19T08:00:00.000Z');
expect(date2.differenceInDays(date1)).toBe(49);
});
});
describe('addYears', () => {
it('should add 1 year with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.addYears(1);
expect(date2.value().toISOString()).toBe('2020-12-31T08:00:00.000Z');
expect(date2.differenceInDays(date1)).toBe(365);
});
it('should add 7 years with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.addYears(7);
expect(date2.value().toISOString()).toBe('2026-12-30T08:00:00.000Z');
expect(date2.differenceInDays(date1)).toBe(2555);
});
});
describe('subtractDays', () => {
it('should subtract 1 day with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractDays(1);
expect(date2.value().toISOString()).toBe('2019-12-31T08:00:00.000Z');
expect(date1.differenceInDays(date2)).toBe(1);
});
it('should subtract 7 days with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractDays(7);
expect(date2.value().toISOString()).toBe('2019-12-25T08:00:00.000Z');
expect(date1.differenceInDays(date2)).toBe(7);
});
});
describe('subtractMonths', () => {
it('should subtract 1 month with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractMonths(1);
expect(date2.value().toISOString()).toBe('2019-12-02T08:00:00.000Z');
expect(date1.differenceInDays(date2)).toBe(30);
});
it('should subtract 7 months with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractMonths(7);
expect(date2.value().toISOString()).toBe('2019-06-05T08:00:00.000Z');
expect(date1.differenceInDays(date2)).toBe(210);
});
});
describe('subtractHours', () => {
it('should subtract 1 hour with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractHours(1);
expect(date2.value().toISOString()).toBe('2020-01-01T07:00:00.000Z');
expect(date1.differenceInHours(date2)).toBe(1);
});
it('should subtract 7 hours with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractHours(7);
expect(date2.value().toISOString()).toBe('2020-01-01T01:00:00.000Z');
expect(date1.differenceInHours(date2)).toBe(7);
});
});
describe('subtractMinutes', () => {
it('should subtract 1 minute with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractMinutes(1);
expect(date2.value().toISOString()).toBe('2020-01-01T07:59:00.000Z');
expect(date1.differenceInMinutes(date2)).toBe(1);
});
it('should subtract 7 minutes with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractMinutes(7);
expect(date2.value().toISOString()).toBe('2020-01-01T07:53:00.000Z');
expect(date1.differenceInMinutes(date2)).toBe(7);
});
});
describe('subtractWeeks', () => {
it('should subtract 1 week with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractWeeks(1);
expect(date2.value().toISOString()).toBe('2019-12-25T08:00:00.000Z');
expect(date1.differenceInDays(date2)).toBe(7);
});
it('should subtract 7 weeks with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractWeeks(7);
expect(date2.value().toISOString()).toBe('2019-11-13T08:00:00.000Z');
expect(date1.differenceInDays(date2)).toBe(49);
});
});
describe('subtractYears', () => {
it('should subtract 1 year with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractYears(1);
expect(date2.value().toISOString()).toBe('2019-01-01T08:00:00.000Z');
expect(date1.differenceInDays(date2)).toBe(365);
});
it('should subtract 7 years with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractYears(7);
expect(date2.value().toISOString()).toBe('2013-01-02T08:00:00.000Z');
expect(date1.differenceInDays(date2)).toBe(2555);
});
});
describe('differenceInHours', () => {
it('should subtract 1 hour with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractHours(1);
expect(date2.value().toISOString()).toBe('2020-01-01T07:00:00.000Z');
expect(date1.differenceInHours(date2)).toBe(1);
});
it('should subtract 7 hours with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractHours(7);
expect(date2.value().toISOString()).toBe('2020-01-01T01:00:00.000Z');
expect(date1.differenceInHours(date2)).toBe(7);
});
it('should return 0 if provide null', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.differenceInHours(null as any)).toBe(0);
})
});
describe('differenceInMinutes', () => {
it('should subtract 1 minute with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractMinutes(1);
expect(date2.value().toISOString()).toBe('2020-01-01T07:59:00.000Z');
expect(date1.differenceInMinutes(date2)).toBe(1);
});
it('should subtract 7 minutes with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractMinutes(7);
expect(date2.value().toISOString()).toBe('2020-01-01T07:53:00.000Z');
expect(date1.differenceInMinutes(date2)).toBe(7);
});
it('should return 0 if provide null value', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.differenceInMinutes(null as any)).toBe(0);
});
});
describe('differenceInMonths', () => {
it('should subtract 1 month with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractMonths(1);
expect(date2.value().toISOString()).toBe('2019-12-02T08:00:00.000Z');
expect(date1.differenceInMonths(date2)).toBe(0.97);
});
it('should subtract 7 months with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractMonths(7);
expect(date2.value().toISOString()).toBe('2019-06-05T08:00:00.000Z');
expect(date1.differenceInMonths(date2)).toBe(6.77);
});
it('should return 0 if provide null value', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.differenceInMonths(null as any)).toBe(0);
});
});
describe('differenceInYears', () => {
it('should subtract 1 year with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractYears(1);
expect(date2.value().toISOString()).toBe('2019-01-01T08:00:00.000Z');
expect(date1.differenceInYears(date2)).toBe(1);
});
it('should subtract 7 years with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractYears(7);
expect(date2.value().toISOString()).toBe('2013-01-02T08:00:00.000Z');
expect(date1.differenceInYears(date2)).toBe(6.98);
});
it('should return 0 if provide null value', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.differenceInYears(null as any)).toBe(0);
});
});
describe('differenceInWeeks', () => {
it('should subtract 1 week with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractWeeks(1);
expect(date2.value().toISOString()).toBe('2019-12-25T08:00:00.000Z');
expect(date1.differenceInWeeks(date2)).toBe(1);
});
it('should subtract 7 weeks with success', () => {
const date1 = Dates.init(new Date('2020-01-01T08:00:00'));
const date2 = date1.subtractWeeks(7);
expect(date2.value().toISOString()).toBe('2019-11-13T08:00:00.000Z');
expect(date1.differenceInWeeks(date2)).toBe(7);
});
it('should return 0 if provide null value', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.differenceInWeeks(null as any)).toBe(0);
});
});
describe('differenceInDays', () => {
it('should return 0 if provide null value', () => {
const date = Dates.init(new Date('2020-01-01T08:00:00'));
expect(date.differenceInDays(null as any)).toBe(0);
});
});
describe('validation', () => {
it('should return true if is valid date', () => {
const isValid = Dates.isValidProps(new Date());
expect(isValid).toBeTruthy();
});
it('should return false if is valid date', () => {
const isValid = Dates.isValid(new Date());
expect(isValid).toBeTruthy();
});
it('should return false if is not valid date', () => {
const isValid = Dates.isValid({} as any);
expect(isValid).toBeFalsy();
});
it('should return false if is not valid date', () => {
const isValid = Dates.isValidProps({} as any);
expect(isValid).toBeFalsy();
});
});
describe('weekend', () => {
it('should return true if is weekend', () => {
const date = new Date('2024-05-18T00:00:00.000Z');
const isWeekend = Dates.init(date).isWeekend();
expect(isWeekend).toBeTruthy();
expect(Dates.isWeekend(date));
});
it('should return true if is weekend', () => {
const date = new Date('2024-05-19T00:00:00.000Z');
const isWeekend = Dates.init(date).isWeekend();
expect(isWeekend).toBeTruthy();
expect(Dates.isWeekend(date)).toBeTruthy();
});
it('should return false if is not weekend', () => {
const date = new Date('2024-05-20T00:00:00.000Z');
const isWeekend = Dates.init(date).isWeekend();
expect(isWeekend).toBeFalsy();
expect(Dates.isWeekend(date)).toBeFalsy();
});
});
describe('weekDay', () => {
it('should return false if is weekend', () => {
const date = new Date('2024-05-18T00:00:00.000Z');
const isWeekend = Dates.init(date).isWeekday();
expect(isWeekend).toBeFalsy();
expect(Dates.isWeekday(date)).toBeFalsy();
});
it('should return false if is weekend', () => {
const date = new Date('2024-05-19T00:00:00.000Z');
const isWeekend = Dates.init(date).isWeekday();
expect(isWeekend).toBeFalsy();
expect(Dates.isWeekday(date)).toBeFalsy();
});
it('should return true if is not weekend', () => {
const date = new Date('2024-05-20T00:00:00.000Z');
const isWeekend = Dates.init(date).isWeekday();
expect(isWeekend).toBeTruthy();
expect(Dates.isWeekday(date)).toBeTruthy();
});
});
describe('isAfter', () => {
it('should return true if date2 is after date1', () => {
const date1 = Dates.init(new Date('2024-05-18T00:00:00.000Z'));
const date2 = date1.addDays(1);
expect(date2.isAfter(date1)).toBeTruthy();
});
it('should return false if date2 is not after date1', () => {
const date1 = Dates.init(new Date('2024-05-18T00:00:00.000Z'));
const date2 = date1.subtractDays(1);
expect(date2.isAfter(date1)).toBeFalsy();
});
it('should return false if provide an invalid value', () => {
const date = Dates.init(new Date('2024-05-18T00:00:00.000Z'));
expect(date.isAfter(null as any)).toBeFalsy();
});
});
describe('isBefore', () => {
it('should return true if date2 is before date1', () => {
const date1 = Dates.init(new Date('2024-05-18T00:00:00.000Z'));
const date2 = date1.addDays(1);
expect(date2.isBefore(date1)).toBeFalsy();
});
it('should return false if date2 is not before date1', () => {
const date1 = Dates.init(new Date('2024-05-18T00:00:00.000Z'));
const date2 = date1.subtractDays(1);
expect(date2.isBefore(date1)).toBeTruthy();
});
it('should return false if provide an invalid value', () => {
const date = Dates.init(new Date('2024-05-18T00:00:00.000Z'));
expect(date.isBefore(null as any)).toBeFalsy();
});
});
describe('isEqualDate', () => {
it('should return true if date2 is equal date1', () => {
const date1 = Dates.init(new Date('2024-05-18T00:00:00.000Z'));
expect(date1.isEqualDate(date1)).toBeTruthy();
});
it('should return false if date2 is not equal date1', () => {
const date1 = Dates.init(new Date('2024-05-18T00:00:00.000Z'));
const date2 = date1.subtractDays(1);
expect(date2.isEqualDate(date1)).toBeFalsy();
});
it('should return false if provide an invalid value', () => {
const date = Dates.init(new Date('2024-05-18T00:00:00.000Z'));
expect(date.isEqualDate(null as any)).toBeFalsy();
});
});
describe('format', () => {
it('should format to DD-MM-YY', () => {
const date = new Date('2024-12-25T00:00:00.000Z');
const format = Dates.init(date).format('DD-MM-YY');
expect(format).toBe('25-12-24');
});
it('should format to DD-MM-YY HH:mm:ss', () => {
const date = new Date('2024-12-25T08:10:00.000Z');
const format = Dates.init(date).format('DD-MM-YY HH:mm:ss');
expect(format).toBe('25-12-24 08:10:00');
});
it('should format to DD-MM-YY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD-MM-YY hh:mm:ss');
expect(format).toBe('25-12-24 04:10:00 PM');
});
it('should format to DD-MM-YYYY', () => {
const date = new Date('2024-12-25T00:00:00.000Z');
const format = Dates.init(date).format('DD-MM-YYYY');
expect(format).toBe('25-12-2024');
});
it('should format to DD-MM-YYYY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD-MM-YYYY HH:mm:ss');
expect(format).toBe('25-12-2024 16:10:00');
});
it('should format to DD-MM-YYYY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD-MM-YYYY hh:mm:ss');
expect(format).toBe('25-12-2024 04:10:00 PM');
});
it('should format to DD/MM/YY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD/MM/YY');
expect(format).toBe('25/12/24');
});
it('should format to DD/MM/YY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD/MM/YY');
expect(format).toBe('25/12/24');
});
it('should format to DD/MM/YY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD/MM/YY HH:mm:ss');
expect(format).toBe('25/12/24 16:10:00');
});
it('should format to DD/MM/YY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD/MM/YY hh:mm:ss');
expect(format).toBe('25/12/24 04:10:00 PM');
});
it('should format to DD/MM/YYYY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD/MM/YYYY');
expect(format).toBe('25/12/2024');
});
it('should format to DD/MM/YYYY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD/MM/YYYY HH:mm:ss');
expect(format).toBe('25/12/2024 16:10:00');
});
it('should format to DD/MM/YYYY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD/MM/YYYY hh:mm:ss');
expect(format).toBe('25/12/2024 04:10:00 PM');
});
it('should format to MM-DD-YY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM-DD-YY');
expect(format).toBe('12-25-24');
});
it('should format to MM-DD-YY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM-DD-YY HH:mm:ss');
expect(format).toBe('12-25-24 16:10:00');
});
it('should format to MM-DD-YY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM-DD-YY hh:mm:ss');
expect(format).toBe('12-25-24 04:10:00 PM');
});
it('should format to MM-DD-YYYY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM-DD-YYYY');
expect(format).toBe('12-25-2024');
});
it('should format to MM-DD-YYYY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM-DD-YYYY HH:mm:ss');
expect(format).toBe('12-25-2024 16:10:00');
});
it('should format to MM-DD-YYYY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM-DD-YYYY hh:mm:ss');
expect(format).toBe('12-25-2024 04:10:00 PM');
});
it('should format to MM/DD/YY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM/DD/YY');
expect(format).toBe('12/25/24');
});
it('should format to MM/DD/YY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM/DD/YY HH:mm:ss');
expect(format).toBe('12/25/24 16:10:00');
});
it('should format to MM/DD/YY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM/DD/YY hh:mm:ss');
expect(format).toBe('12/25/24 04:10:00 PM');
});
it('should format to MM/DD/YYYY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM/DD/YYYY');
expect(format).toBe('12/25/2024');
});
it('should format to MM/DD/YYYY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM/DD/YYYY HH:mm:ss');
expect(format).toBe('12/25/2024 16:10:00');
});
it('should format to MM/DD/YYYY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM/DD/YYYY hh:mm:ss');
expect(format).toBe('12/25/2024 04:10:00 PM');
});
it('should format to YYYY-MM-DD', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('YYYY-MM-DD');
expect(format).toBe('2024-12-25');
});
it('should format to YYYY-MM-DD HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('YYYY-MM-DD HH:mm:ss');
expect(format).toBe('2024-12-25 16:10:00');
});
it('should format to YYYY-MM-DD hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('YYYY-MM-DD hh:mm:ss');
expect(format).toBe('2024-12-25 04:10:00 PM');
});
it('should format to DD.MM.YYYY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD.MM.YY');
expect(format).toBe('25.12.24');
});
it('should format to MM.DD.YYYY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM.DD.YYYY');
expect(format).toBe('12.25.2024');
});
it('should format to DD.MM.YY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD.MM.YY');
expect(format).toBe('25.12.24');
});
it('should format to MM.DD.YY', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM.DD.YY');
expect(format).toBe('12.25.24');
});
it('should format to YYYY.MM.DD', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('YYYY.MM.DD');
expect(format).toBe('2024.12.25');
});
it('should format to DD.MM.YYYY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD.MM.YYYY hh:mm:ss');
expect(format).toBe('25.12.2024 04:10:00 PM');
});
it('should format to DD.MM.YYYY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD.MM.YYYY HH:mm:ss');
expect(format).toBe('25.12.2024 16:10:00');
});
it('should format to DD.MM.YY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD.MM.YY hh:mm:ss');
expect(format).toBe('25.12.24 04:10:00 PM');
});
it('should format to DD.MM.YY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('DD.MM.YY HH:mm:ss');
expect(format).toBe('25.12.24 16:10:00');
});
it('should format to MM.DD.YYYY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM.DD.YYYY hh:mm:ss');
expect(format).toBe('12.25.2024 04:10:00 PM');
});
it('should format to MM.DD.YYYY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM.DD.YYYY HH:mm:ss');
expect(format).toBe('12.25.2024 16:10:00');
});
it('should format to MM.DD.YY hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM.DD.YY hh:mm:ss');
expect(format).toBe('12.25.24 04:10:00 PM');
});
it('should format to MM.DD.YY HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('MM.DD.YY HH:mm:ss');
expect(format).toBe('12.25.24 16:10:00');
});
it('should format to YYYY.MM.DD hh:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('YYYY.MM.DD hh:mm:ss');
expect(format).toBe('2024.12.25 04:10:00 PM');
});
it('should format to YYYY.MM.DD HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('YYYY.MM.DD HH:mm:ss');
expect(format).toBe('2024.12.25 16:10:00');
});
it('should format to YYYY-MM.DD HH:mm:ss', () => {
const date = new Date('2024-12-25T16:10:00.000Z');
const format = Dates.init(date).format('YYYY_MM_DD' as any);
expect(format).toBe('2024-12-25');
});
});
});
================================================
FILE: packages/date/index.ts
================================================
import { Result, ValueObject } from 'rich-domain';
import { TimeZones } from './types';
const timeFormat12h = { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: true };
const timeFormat24h = { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
function commonDateFormat(timeFormat = null) {
const date = { year: 'numeric', month: '2-digit', day: '2-digit' };
const time = timeFormat ? { ...timeFormat } : null;
return { date, time };
}
const DateFormats = {
// only dates
'DD-MM-YYYY': commonDateFormat(),
'MM-DD-YYYY': commonDateFormat(),
'DD-MM-YY': commonDateFormat(),
'MM-DD-YY': commonDateFormat(),
'MM/DD/YYYY': commonDateFormat(),
'MM/DD/YY': commonDateFormat(),
'DD/MM/YYYY': commonDateFormat(),
'DD/MM/YY': commonDateFormat(),
'YYYY-MM-DD': commonDateFormat(),
'YYYY/MM/DD': commonDateFormat(),
// separate with dot
'DD.MM.YYYY': commonDateFormat(),
'MM.DD.YYYY': commonDateFormat(),
'DD.MM.YY': commonDateFormat(),
'MM.DD.YY': commonDateFormat(),
'YYYY.MM.DD': commonDateFormat(),
'DD.MM.YYYY hh:mm:ss': commonDateFormat(timeFormat12h),
'DD.MM.YY hh:mm:ss': commonDateFormat(timeFormat12h),
'MM.DD.YYYY hh:mm:ss': commonDateFormat(timeFormat12h),
'MM.DD.YY hh:mm:ss': commonDateFormat(timeFormat12h),
'DD.MM.YYYY HH:mm:ss': commonDateFormat(timeFormat24h),
'DD.MM.YY HH:mm:ss': commonDateFormat(timeFormat24h),
'MM.DD.YYYY HH:mm:ss': commonDateFormat(timeFormat24h),
'MM.DD.YY HH:mm:ss': commonDateFormat(timeFormat24h),
'YYYY.MM.DD hh:mm:ss': commonDateFormat(timeFormat12h),
'YYYY.MM.DD HH:mm:ss': commonDateFormat(timeFormat24h),
// with time 12h
'DD/MM/YYYY hh:mm:ss': commonDateFormat(timeFormat12h),
'DD-MM-YYYY hh:mm:ss': commonDateFormat(timeFormat12h),
'DD/MM/YY hh:mm:ss': commonDateFormat(timeFormat12h),
'DD-MM-YY hh:mm:ss': commonDateFormat(timeFormat12h),
'MM/DD/YYYY hh:mm:ss': commonDateFormat(timeFormat12h),
'MM-DD-YYYY hh:mm:ss': commonDateFormat(timeFormat12h),
'MM/DD/YY hh:mm:ss': commonDateFormat(timeFormat12h),
'MM-DD-YY hh:mm:ss': commonDateFormat(timeFormat12h),
// with time 24h
'DD/MM/YYYY HH:mm:ss': commonDateFormat(timeFormat24h),
'DD-MM-YYYY HH:mm:ss': commonDateFormat(timeFormat24h),
'DD/MM/YY HH:mm:ss': commonDateFormat(timeFormat24h),
'DD-MM-YY HH:mm:ss': commonDateFormat(timeFormat24h),
'MM/DD/YYYY HH:mm:ss': commonDateFormat(timeFormat24h),
'MM-DD-YYYY HH:mm:ss': commonDateFormat(timeFormat24h),
'MM/DD/YY HH:mm:ss': commonDateFormat(timeFormat24h),
'MM-DD-YY HH:mm:ss': commonDateFormat(timeFormat24h),
// manual
'YYYY/MM/DD hh:mm:ss': commonDateFormat(timeFormat12h),
'YYYY-MM-DD hh:mm:ss': commonDateFormat(timeFormat12h),
'YYYY/MM/DD HH:mm:ss': commonDateFormat(timeFormat24h),
'YYYY-MM-DD HH:mm:ss': commonDateFormat(timeFormat24h),
};
type DateFormat = keyof typeof DateFormats;
type FormatParams = { date: Intl.DateTimeFormatOptions; time: Intl.DateTimeFormatOptions | null };
export class Dates extends ValueObject<Date> {
protected static readonly MESSAGE: string = 'Invalid date value';
private readonly ONE_DAY: number = 86400000;
private readonly ONE_HOUR: number = 3600000;
private readonly ONE_MINUTE: number = 60000;
private readonly ONE_MONTH: number = 2678400000;
private readonly ONE_WEEK: number = 604800000;
private readonly ONE_YEAR: number = 31622400000;
private constructor(props: Date) {
super(props);
}
/**
* @returns instance value as Date
*/
value(): Date {
return this.props;
}
/**
*
* @param days as number to be added
* @returns instance of Dates with updated value
*/
addDays(days: number): Dates {
return new Dates(this.util.date(this.props).add(days).days());
}
/**
*
* @param months as number to be added
* @returns instance of Dates with updated value
*/
addMonths(months: number): Dates {
return new Dates(this.util.date(this.props).add(months).months());
}
/**
*
* @param hours as number to be added
* @returns instance of Dates with updated value
*/
addHours(hours: number): Dates {
return new Dates(this.util.date(this.props).add(hours).hours());
}
/**
*
* @param minutes as number to be added
* @returns instance of Dates with updated value
*/
addMinutes(minutes: number): Dates {
return new Dates(this.util.date(this.props).add(minutes).minutes());
}
/**
*
* @param weeks as number to be added
* @returns instance of Dates with updated value
*/
addWeeks(weeks: number): Dates {
return new Dates(this.util.date(this.props).add(weeks).weeks());
}
/**
*
* @param years as number to be added
* @returns instance of Dates with updated value
*/
addYears(years: number): Dates {
return new Dates(this.util.date(this.props).add(years).years());
}
/**
*
* @param days as number to be subtracted
* @returns instance of Dates with updated value
*/
subtractDays(days: number): Dates {
return new Dates(this.util.date(this.props).remove(days).days());
}
/**
*
* @param months as number to be subtracted
* @returns instance of Dates with updated value
*/
subtractMonths(months: number): Dates {
return new Dates(this.util.date(this.props).remove(months).months());
}
/**
*
* @param hours as number to be subtracted
* @returns instance of Dates with updated value
*/
subtractHours(hours: number): Dates {
return new Dates(this.util.date(this.props).remove(hours).hours());
}
/**
*
* @param minutes as number to be subtracted
* @returns instance of Dates with updated value
*/
subtractMinutes(minutes: number): Dates {
return new Dates(this.util.date(this.props).remove(minutes).minutes());
}
/**
*
* @param weeks as number to be subtracted
* @returns instance of Dates with updated value
*/
subtractWeeks(weeks: number): Dates {
return new Dates(this.util.date(this.props).remove(weeks).weeks());
}
/**
*
* @param years as number to be subtracted
* @returns instance of Dates with updated value
*/
subtractYears(years: number): Dates {
return new Dates(this.util.date(this.props).remove(years).years());
}
/**
*
* @param date as Date to be compared or instance of Dates
* @returns result as number of days
* @summary returns positive result if instance value is greater than provided value
*/
differenceInDays(date: Date | Dates): number {
if (date instanceof Date) {
const currentDays = this.props.getTime() / this.ONE_DAY;
const dateTime = date.getTime() / this.ONE_DAY;
const calc = (currentDays * 100 - dateTime * 100) / 100;
return parseFloat(calc.toFixed(2));
}
if (date instanceof Dates) {
return this.differenceInDays(date.value());
}
return 0;
}
/**
*
* @param date to be compared or instance of Dates
* @returns result as number of hours
* @summary returns positive result if instance value is greater than provided value
*/
differenceInHours(date: Date | Dates): number {
if (date instanceof Date) {
const currentDays = this.props.getTime() / this.ONE_HOUR;
const dateTime = date.getTime() / this.ONE_HOUR;
const calc = (currentDays * 100 - dateTime * 100) / 100;
return parseFloat(calc.toFixed(2));
}
if (date instanceof Dates) {
return this.differenceInHours(date.value());
}
return 0;
}
/**
*
* @param date to be compared or instance of Dates
* @returns result as number of minutes
* @summary returns positive result if instance value is greater than provided value
*/
differenceInMinutes(date: Date | Dates): number {
if (date instanceof Date) {
const currentDays = this.props.getTime() / this.ONE_MINUTE;
const dateTime = date.getTime() / this.ONE_MINUTE;
const calc = (currentDays * 100 - dateTime * 100) / 100;
return parseFloat(calc.toFixed(2));
}
if (date instanceof Dates) {
return this.differenceInMinutes(date.value());
}
return 0;
}
/**
*
* @param date to be compared or Dates
* @returns result as number of months
* @summary returns positive result if instance value is greater than provided value
*/
differenceInMonths(date: Date | Dates): number {
if (date instanceof Date) {
const currentDays = this.props.getTime() / this.ONE_MONTH;
const dateTime = date.getTime() / this.ONE_MONTH;
const calc = (currentDays * 100 - dateTime * 100) / 100;
return parseFloat(calc.toFixed(2));
}
if (date instanceof Dates) {
return this.differenceInMonths(date.value());
}
return 0;
}
/**
*
* @param date to be compared or Dates
* @returns result as number of years
* @summary returns positive result if instance value is greater than provided value
*/
differenceInYears(date: Date | Dates): number {
if (date instanceof Date) {
const currentDays = this.props.getTime() / this.ONE_YEAR;
const dateTime = date.getTime() / this.ONE_YEAR;
const calc = (currentDays * 100 - dateTime * 100) / 100;
return parseFloat(calc.toFixed(2));
}
if (date instanceof Dates) {
return this.differenceInYears(date.value());
}
return 0;
}
/**
*
* @param date to be compared or instance of Dates
* @returns result as number of weeks
* @summary returns positive result if instance value is greater than provided value
*/
differenceInWeeks(date: Date | Dates): number {
if (date instanceof Date) {
const currentDays = this.props.getTime() / this.ONE_WEEK;
const dateTime = date.getTime() / this.ONE_WEEK;
const calc = (currentDays * 100 - dateTime * 100) / 100;
return parseFloat(calc.toFixed(2));
}
if (date instanceof Dates) {
return this.differenceInWeeks(date.value());
}
return 0;
}
/**
*
* @param format pattern to be applied
* @param timeZone as TimeZone name as string to be considered
* @returns formatted date as string
*/
format(format: DateFormat, timeZone?: TimeZones): string {
const firstChars = format.slice(0, 2);
const dateLocale = firstChars === 'DD' ? 'pt-BR' : 'en-US';
const formatDate = (date: Date, formats: FormatParams) => {
const locale = (format.slice(0, 2) === 'YY') ? 'sv-SE' : undefined;
const year = (format.includes('YYYY')) ? 'numeric' : '2-digit';
const dateFn = new Intl.DateTimeFormat(locale || dateLocale, {
month: '2-digit',
day: '2-digit',
...(formats?.date ?? {}),
year,
timeZone
}).format(date);
if (formats?.time) {
const timeFn = new Intl.DateTimeFormat(dateLocale, {
...formats.time,
timeZone
}).format(date);
return { dateFn, timeFn }
}
return { dateFn, timeFn: '' };
};
const result = formatDate(this.props, DateFormats[format] as FormatParams);
const applySeparator = (date: string): string => {
if (format.includes('/')) return date.replace(/-/g, '/');
if (format.includes('-')) return date.replace(/\//g, '-');
if (format.includes('.')) return date.replace(/\/|\-/g, '.');
return date;
}
return applySeparator(`${result.dateFn} ${result.timeFn}`.trim());
}
/**
* @param value as Date or date string
* @returns true if provided value is instance of date, and false if not.
*/
public static isValidProps(value: Date | string | number): boolean {
if(typeof value === 'number') {
const date = new Date(value);
return date instanceof Date;
}
if (typeof value === 'string') {
const date = new Date(value);
return (
!isNaN(date as unknown as number) &&
date.toISOString().slice(0, 10) === value &&
!isNaN(date.getFullYear())
);
}
return this.validator.isDate(value);
}
/**
* @param value as Date or date string or number as timestamp
* @returns true if provided value is instance of date, and false if not.
*/
public static isValid(value: Date | string): boolean {
return this.isValidProps(value);
}
/**
*
* @returns true if date day is week day [Monday-Friday]
*/
isWeekday(): boolean {
return !this.isWeekend()
}
/**
*
* @returns true if date value day is weekend day [Saturday-Sunday]
*/
isWeekend(): boolean {
return this.validator.date(this.props).isWeekend();
}
/**
*
* @param date value as date.
* @returns true if date day is week day [Monday-Friday]
*/
public static isWeekday(date: Date): boolean {
return !this.isWeekend(date);
}
/**
*
* @param date value as date.
* @returns true if date day is weekend day [Sat-Sunday]
*/
public static isWeekend(date: Date): boolean {
return this.validator.date(date).isWeekend();
}
/**
*
* @param date as Date or instance of Dates
* @returns true or false. True if instance date is greater than provided value
* @example
*
* const date = new Date("1989-05-31 11:42:00");
*
* const vo = Dates.create(date).value();
*
* const isAfter = vo.isAfter(new Date());
*
* console.log(isAfter);
*
* > false
*
* ...
*/
isAfter(date: Date | Dates): boolean {
if (date instanceof Date) {
return this.validator.date(this.props).isAfterThan(date);
}
if (date instanceof Dates) {
return this.isAfter(date.value());
}
return false;
}
/**
*
* @param date as Date or instance of Dates
* @returns true or false. True if instance date is less than provided value
* @example
*
* const date = new Date("1989-05-31 11:42:00");
*
* const vo = Dates.create(date).value();
*
* const isBefore = vo.isBefore(new Date());
*
* console.log(isBefore);
*
* > true
*
* ...
*/
isBefore(date: Date | Dates): boolean {
if (date instanceof Date) {
return this.validator.date(this.props).isBeforeThan(date);
}
if (date instanceof Dates) {
return this.isBefore(date.value());
}
return false;
}
/**
*
* @param date as Date or instance of Dates
* @returns true or false. True if instance date is equal to provided value
*/
isEqualDate(date: Date | Dates): boolean {
if (date instanceof Date) {
const time = date.getTime();
const instanceTime = this.props.getTime();
return this.validator.number(time).isEqualTo(instanceTime);
}
if (date instanceof Dates) {
return this.isEqualDate(date.value());
}
return false;
}
/**
*
* @param value value as Date or date string or number as timestamp
* @returns instance of Dates or throw an error
*/
public static init(value?: Date | string | number): Dates {
if (!value) return new Dates(new Date());
const isValidValue = Dates.isValidProps(value);
if (!isValidValue) throw new Error(Dates.MESSAGE);
if (value instanceof Date) return new Dates(value);
return new Dates(new Date(value));
}
/**
*
* @param value value as Date or date string or number as timestamp
* @returns Result of Dates
*/
public static create(date?: Date | string | number): Result<Dates | null> {
const value = date ?? new Date();
const isValid = Dates.isValidProps(value);
if (!isValid) return Result.fail(Dates.MESSAGE);
if (value instanceof Date) return Result.Ok(new Dates(value));
return Result.Ok(new Dates(new Date(value)));
}
}
export default Dates;
================================================
FILE: packages/date/package.json
================================================
{
"name": "@type-ddd/date",
"description": "Library that provides TypeScript type definitions for handling Dates in Domain-Driven Design contexts. It facilitates the validation and manipulation of Dates.",
"version": "0.1.0",
"main": "index.js",
"types": "index.d.ts",
"author": "Alessandro Dev",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"keywords": [
"Complexity",
"Business Logic",
"DDD",
"Domain Driving Design",
"DDD-Utils",
"Base Value Object",
"Clean Architecture",
"Date",
"Validation",
"Formatting",
"Value Object",
"Utility",
"Security",
"Standards"
],
"scripts": {
"build": "tsc"
},
"peerDependencies": {
"rich-domain": "^1.25.0"
},
"files": [
"index.js",
"index.d.ts",
"util.js",
"util.d.ts",
"types.js",
"types.d.ts"
],
"repository": {
"type": "git",
"url": "git+https://github.com/4lessandrodev/type-ddd.git"
},
"bugs": {
"url": "https://github.com/4lessandrodev/type-ddd/issues"
},
"homepage": "https://github.com/4lessandrodev/type-ddd/tree/main/packages/date",
"gitHead": "4cb9159bde8d6fc951e9d902feed2ad25da49fa4"
}
================================================
FILE: packages/date/tsconfig.build.json
================================================
{
"extends": "../tsconfig.build.json",
"compilerOptions": {
"outDir": ".",
"rootDir": ".",
"paths": {}
},
"exclude": [
"node_modules",
"dist",
"__tests__/**/*",
"*.spec.ts"
],
"references": []
}
================================================
FILE: packages/date/tsconfig.json
================================================
{
"extends": "../tsconfig.build.json",
"compilerOptions": {
"types": [
"node"
]
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.build.json"
}
]
}
================================================
FILE: packages/date/types.ts
================================================
export type TimeZones =
"Africa/Abidjan" |
"Africa/Accra" |
"Africa/Addis_Ababa" |
"Africa/Algiers" |
"Africa/Asmara" |
"Africa/Asmera" |
"Africa/Bamako" |
"Africa/Bangui" |
"Africa/Banjul" |
"Africa/Bissau" |
"Africa/Blantyre" |
"Africa/Brazzaville" |
"Africa/Bujumbura" |
"Africa/Cairo" |
"Africa/Casablanca" |
"Africa/Ceuta" |
"Africa/Conakry" |
"Africa/Dakar" |
"Africa/Dar_es_Salaam" |
"Africa/Djibouti" |
"Africa/Douala" |
"Africa/El_Aaiun" |
"Africa/Freetown" |
"Africa/Gaborone" |
"Africa/Harare" |
"Africa/Johannesburg" |
"Africa/Juba" |
"Africa/Kampala" |
"Africa/Khartoum" |
"Africa/Kigali" |
"Africa/Kinshasa" |
"Africa/Lagos" |
"Africa/Libreville" |
"Africa/Lome" |
"Africa/Luanda" |
"Africa/Lubumbashi" |
"Africa/Lusaka" |
"Africa/Malabo" |
"Africa/Maputo" |
"Africa/Maseru" |
"Africa/Mbabane" |
"Africa/Mogadishu" |
"Africa/Monrovia" |
"Africa/Nairobi" |
"Africa/Ndjamena" |
"Africa/Niamey" |
"Africa/Nouakchott" |
"Africa/Ouagadougou" |
"Africa/Porto-Novo" |
"Africa/Sao_Tome" |
"Africa/Timbuktu" |
"Africa/Tripoli" |
"Africa/Tunis" |
"Africa/Windhoek" |
"America/Adak" |
"America/Anchorage" |
"America/Anguilla" |
"America/Antigua" |
"America/Araguaina" |
"America/Argentina/Buenos_Aires" |
"America/Argentina/Catamarca" |
"America/Argentina/ComodRivadavia" |
"America/Argentina/Cordoba" |
"America/Argentina/Jujuy" |
"America/Argentina/La_Rioja" |
"America/Argentina/Mendoza" |
"America/Argentina/Rio_Gallegos" |
"America/Argentina/Salta" |
"America/Argentina/San_Juan" |
"America/Argentina/San_Luis" |
"America/Argentina/Tucuman" |
"America/Argentina/Ushuaia" |
"America/Aruba" |
"America/Asuncion" |
"America/Atikokan" |
"America/Atka" |
"America/Bahia" |
"America/Bahia_Banderas" |
"America/Barbados" |
"America/Belem" |
"America/Belize" |
"America/Blanc-Sablon" |
"America/Boa_Vista" |
"America/Bogota" |
"America/Boise" |
"America/Buenos_Aires" |
"America/Cambridge_Bay" |
"America/Campo_Grande" |
"America/Cancun" |
"America/Caracas" |
"America/Catamarca" |
"America/Cayenne" |
"America/Cayman" |
"America/Chicago" |
"America/Chihuahua" |
"America/Coral_Harbour" |
"America/Cordoba" |
"America/Costa_Rica" |
"America/Creston" |
"America/Cuiaba" |
"America/Curacao" |
"America/Danmarkshavn" |
"America/Dawson" |
"America/Dawson_Creek" |
"America/Denver" |
"America/Detroit" |
"America/Dominica" |
"America/Edmonton" |
"America/Eirunepe" |
"America/El_Salvador" |
"America/Ensenada" |
"America/Fort_Nelson" |
"America/Fort_Wayne" |
"America/Fortaleza" |
"America/Glace_Bay" |
"America/Godthab" |
"America/Goose_Bay" |
"America/Grand_Turk" |
"America/Grenada" |
"America/Guadeloupe" |
"America/Guatemala" |
"America/Guayaquil" |
"America/Guyana" |
"America/Halifax" |
"America/Havana" |
"America/Hermosillo" |
"America/Indiana/Indianapolis" |
"America/Indiana/Knox" |
"America/Indiana/Marengo" |
"America/Indiana/Petersburg" |
"America/Indiana/Tell_City" |
"America/Indiana/Vevay" |
"America/Indiana/Vincennes" |
"America/Indiana/Winamac" |
"America/Indianapolis" |
"America/Inuvik" |
"America/Iqaluit" |
"America/Jamaica" |
"America/Jujuy" |
"America/Juneau" |
"America/Kentucky/Louisville" |
"America/Kentucky/Monticello" |
"America/Knox_IN" |
"America/Kralendijk" |
"America/La_Paz" |
"America/Lima" |
"America/Los_Angeles" |
"America/Louisville" |
"America/Lower_Princes" |
"America/Maceio" |
"America/Managua" |
"America/Manaus" |
"America/Marigot" |
"America/Martinique" |
"America/Matamoros" |
"America/Mazatlan" |
"America/Mendoza" |
"America/Menominee" |
"America/Merida" |
"America/Metlakatla" |
"America/Mexico_City" |
"America/Miquelon" |
"America/Moncton" |
"America/Monterrey" |
"America/Montevideo" |
"America/Montreal" |
"America/Montserrat" |
"America/Nassau" |
"America/New_York" |
"America/Nipigon" |
"America/Nome" |
"America/Noronha" |
"America/North_Dakota/Beulah" |
"America/North_Dakota/Center" |
"America/North_Dakota/New_Salem" |
"America/Ojinaga" |
"America/Panama" |
"America/Pangnirtung" |
"America/Paramaribo" |
"America/Phoenix" |
"America/Port-au-Prince" |
"America/Port_of_Spain" |
"America/Porto_Acre" |
"America/Porto_Velho" |
"America/Puerto_Rico" |
"America/Punta_Arenas" |
"America/Rainy_River" |
"America/Rankin_Inlet" |
"America/Recife" |
"America/Regina" |
"America/Resolute" |
"America/Rio_Branco" |
"America/Rosario" |
"America/Santa_Isabel" |
"America/Santarem" |
"America/Santiago" |
"America/Santo_Domingo" |
"America/Sao_Paulo" |
"America/Scoresbysund" |
"America/Shiprock" |
"America/Sitka" |
"America/St_Barthelemy" |
"America/St_Johns" |
"America/St_Kitts" |
"America/St_Lucia" |
"America/St_Thomas" |
"America/St_Vincent" |
"America/Swift_Current" |
"America/Tegucigalpa" |
"America/Thule" |
"America/Thunder_Bay" |
"America/Tijuana" |
"America/Toronto" |
"America/Tortola" |
"America/Vancouver" |
"America/Virgin" |
"America/Whitehorse" |
"America/Winnipeg" |
"America/Yakutat" |
"America/Yellowknife" |
"Antarctica/Casey" |
"Antarctica/Davis" |
"Antarctica/DumontDUrville" |
"Antarctica/Macquarie" |
"Antarctica/Mawson" |
"Antarctica/McMurdo" |
"Antarctica/Palmer" |
"Antarctica/Rothera" |
"Antarctica/South_Pole" |
"Antarctica/Syowa" |
"Antarctica/Troll" |
"Antarctica/Vostok" |
"Arctic/Longyearbyen" |
"Asia/Aden" |
"Asia/Almaty" |
"Asia/Amman" |
"Asia/Anadyr" |
"Asia/Aqtau" |
"Asia/Aqtobe" |
"Asia/Ashgabat" |
"Asia/Ashkhabad" |
"Asia/Atyrau" |
"Asia/Baghdad" |
"Asia/Bahrain" |
"Asia/Baku" |
"Asia/Bangkok" |
"Asia/Barnaul" |
"Asia/Beirut" |
"Asia/Bishkek" |
"Asia/Brunei" |
"Asia/Calcutta" |
"Asia/Chita" |
"Asia/Choibalsan" |
"Asia/Chongqing" |
"Asia/Chungking" |
"Asia/Colombo" |
"Asia/Dacca" |
"Asia/Damascus" |
"Asia/Dhaka" |
"Asia/Dili" |
"Asia/Dubai" |
"Asia/Dushanbe" |
"Asia/Famagusta" |
"Asia/Gaza" |
"Asia/Harbin" |
"Asia/Hebron" |
"Asia/Ho_Chi_Minh" |
"Asia/Hong_Kong" |
"Asia/Hovd" |
"Asia/Irkutsk" |
"Asia/Istanbul" |
"Asia/Jakarta" |
"Asia/Jayapura" |
"Asia/Jerusalem" |
"Asia/Kabul" |
"Asia/Kamchatka" |
"Asia/Karachi" |
"Asia/Kashgar" |
"Asia/Kathmandu" |
"Asia/Katmandu" |
"Asia/Khandyga" |
"Asia/Kolkata" |
"Asia/Krasnoyarsk" |
"Asia/Kuala_Lumpur" |
"Asia/Kuching" |
"Asia/Kuwait" |
"Asia/Macao" |
"Asia/Macau" |
"Asia/Magadan" |
"Asia/Makassar" |
"Asia/Manila" |
"Asia/Muscat" |
"Asia/Nicosia" |
"Asia/Novokuznetsk" |
"Asia/Novosibirsk" |
"Asia/Omsk" |
"Asia/Oral" |
"Asia/Phnom_Penh" |
"Asia/Pontianak" |
"Asia/Pyongyang" |
"Asia/Qatar" |
"Asia/Qostanay" |
"Asia/Qyzylorda" |
"Asia/Rangoon" |
"Asia/Riyadh" |
"Asia/Saigon" |
"Asia/Sakhalin" |
"Asia/Samarkand" |
"Asia/Seoul" |
"Asia/Shanghai" |
"Asia/Singapore" |
"Asia/Srednekolymsk" |
"Asia/Taipei" |
"Asia/Tashkent" |
"Asia/Tbilisi" |
"Asia/Tehran" |
"Asia/Tel_Aviv" |
"Asia/Thimbu" |
"Asia/Thimphu" |
"Asia/Tokyo" |
"Asia/Tomsk" |
"Asia/Ujung_Pandang" |
"Asia/Ulaanbaatar" |
"Asia/Ulan_Bator" |
"Asia/Urumqi" |
"Asia/Ust-Nera" |
"Asia/Vientiane" |
"Asia/Vladivostok" |
"Asia/Yakutsk" |
"Asia/Yangon" |
"Asia/Yekaterinburg" |
"Asia/Yerevan" |
"Atlantic/Azores" |
"Atlantic/Bermuda" |
"Atlantic/Canary" |
"Atlantic/Cape_Verde" |
"Atlantic/Faeroe" |
"Atlantic/Faroe" |
"Atlantic/Jan_Mayen" |
"Atlantic/Madeira" |
"Atlantic/Reykjavik" |
"Atlantic/South_Georgia" |
"Atlantic/St_Helena" |
"Atlantic/Stanley" |
"Australia/ACT" |
"Australia/Adelaide" |
"Australia/Brisbane" |
"Australia/Broken_Hill" |
"Australia/Canberra" |
"Australia/Currie" |
"Australia/Darwin" |
"Australia/Eucla" |
"Australia/Hobart" |
"Australia/LHI" |
"Australia/Lindeman" |
"Australia/Lord_Howe" |
"Australia/Melbourne" |
"Australia/NSW" |
"Australia/North" |
"Australia/Perth" |
"Australia/Queensland" |
"Australia/South" |
"Australia/Sydney" |
"Australia/Tasmania" |
"Australia/Victoria" |
"Australia/West" |
"Australia/Yancowinna" |
"Brazil/Acre" |
"Brazil/DeNoronha" |
"Brazil/East" |
"Brazil/West" |
"CET" |
"CST6CDT" |
"Canada/Atlantic" |
"Canada/Central" |
"Canada/Eastern" |
"Canada/Mountain" |
"Canada/Newfoundland" |
"Canada/Pacific" |
"Canada/Saskatchewan" |
"Canada/Yukon" |
"Chile/Continental" |
"Chile/EasterIsland" |
"Cuba" |
"EET" |
"EST" |
"EST5EDT" |
"Egypt" |
"Eire" |
"Etc/GMT" |
"Etc/GMT+0" |
"Etc/GMT+1" |
"Etc/GMT+10" |
"Etc/GMT+11" |
"Etc/GMT+12" |
"Etc/GMT+2" |
"Etc/GMT+3" |
"Etc/GMT+4" |
"Etc/GMT+5" |
"Etc/GMT+6" |
"Etc/GMT+7" |
"Etc/GMT+8" |
"Etc/GMT+9" |
"Etc/GMT-0" |
"Etc/GMT-1" |
"Etc/GMT-10" |
"Etc/GMT-11" |
"Etc/GMT-12" |
"Etc/GMT-13" |
"Etc/GMT-14" |
"Etc/GMT-2" |
"Etc/GMT-3" |
"Etc/GMT-4" |
"Etc/GMT-5" |
"Etc/GMT-6" |
"Etc/GMT-7" |
"Etc/GMT-8" |
"Etc/GMT-9" |
"Etc/GMT0" |
"Etc/Greenwich" |
"Etc/UCT" |
"Etc/UTC" |
"Etc/Universal" |
"Etc/Zulu" |
"Europe/Amsterdam" |
"Europe/Andorra" |
"Europe/Astrakhan" |
"Europe/Athens" |
"Europe/Belfast" |
"Europe/Belgrade" |
"Europe/Berlin" |
"Europe/Bratislava" |
"Europe/Brussels" |
"Europe/Bucharest" |
"Europe/Budapest" |
"Europe/Busingen" |
"Europe/Chisinau" |
"Europe/Copenhagen" |
"Europe/Dublin" |
"Europe/Gibraltar" |
"Europe/Guernsey" |
"Europe/Helsinki" |
"Europe/Isle_of_Man" |
"Europe/Istanbul" |
"Europe/Jersey" |
"Europe/Kaliningrad" |
"Europe/Kiev" |
"Europe/Kirov" |
"Europe/Lisbon" |
"Europe/Ljubljana" |
"Europe/London" |
"Europe/Luxembourg" |
"Europe/Madrid" |
"Europe/Malta" |
"Europe/Mariehamn" |
"Europe/Minsk" |
"Europe/Monaco" |
"Europe/Moscow" |
"Europe/Nicosia" |
"Europe/Oslo" |
"Europe/Paris" |
"Europe/Podgorica" |
"Europe/Prague" |
"Europe/Riga" |
"Europe/Rome" |
"Europe/Samara" |
"Europe/San_Marino" |
"Europe/Sarajevo" |
"Europe/Saratov" |
"Europe/Simferopol" |
"Europe/Skopje" |
"Europe/Sofia" |
"Europe/Stockholm" |
"Europe/Tallinn" |
"Europe/Tirane" |
"Europe/Tiraspol" |
"Europe/Ulyanovsk" |
"Europe/Uzhgorod" |
"Europe/Vaduz" |
"Europe/Vatican" |
"Europe/Vienna" |
"Europe/Vilnius" |
"Europe/Volgograd" |
"Europe/Warsaw" |
"Europe/Zagreb" |
"Europe/Zaporozhye" |
"Europe/Zurich" |
"GB" |
"GB-Eire" |
"GMT" |
"GMT+0" |
"GMT-0" |
"GMT0" |
"Greenwich" |
"HST" |
"Hongkong" |
"Iceland" |
"Indian/Antananarivo" |
"Indian/Chagos" |
"Indian/Christmas" |
"Indian/Cocos" |
"Indian/Comoro" |
"Indian/Kerguelen" |
"Indian/Mahe" |
"Indian/Maldives" |
"Indian/Mauritius" |
"Indian/Mayotte" |
"Indian/Reunion" |
"Iran" |
"Israel" |
"Jamaica" |
"Japan" |
"Kwajalein" |
"Libya" |
"MET" |
"MST" |
"MST7MDT" |
"Mexico/BajaNorte" |
"Mexico/BajaSur" |
"Mexico/General" |
"NZ" |
"NZ-CHAT" |
"Navajo" |
"PRC" |
"PST8PDT" |
"Pacific/Apia" |
"Pacific/Auckland" |
"Pacific/Bougainville" |
"Pacific/Chatham" |
"Pacific/Chuuk" |
"Pacific/Easter" |
"Pacific/Efate" |
"Pacific/Enderbury" |
"Pacific/Fakaofo" |
"Pacific/Fiji" |
"Pacific/Funafuti" |
"Pacific/Galapagos" |
"Pacific/Gambier" |
"Pacific/Guadalcanal" |
"Pacific/Guam" |
"Pacific/Honolulu" |
"Pacific/Johnston" |
"Pacific/Kiritimati" |
"Pacific/Kosrae" |
"Pacific/Kwajalein" |
"Pacific/Majuro" |
"Pacific/Marquesas" |
"Pacific/Midway" |
"Pacific/Nauru" |
"Pacific/Niue" |
"Pacific/Norfolk" |
"Pacific/Noumea" |
"Pacific/Pago_Pago" |
"Pacific/Palau" |
"Pacific/Pitcairn" |
"Pacific/Pohnpei" |
"Pacific/Ponape" |
"Pacific/Port_Moresby" |
"Pacific/Rarotonga" |
"Pacific/Saipan" |
"Pacific/Samoa" |
"Pacific/Tahiti" |
"Pacific/Tarawa" |
"Pacific/Tongatapu" |
"Pacific/Truk" |
"Pacific/Wake" |
"Pacific/Wallis" |
"Pacific/Yap" |
"Poland" |
"Portugal" |
"ROC" |
"ROK" |
"Singapore" |
"Turkey" |
"UCT" |
"US/Alaska" |
"US/Aleutian" |
"US/Arizona" |
"US/Central" |
"US/East-Indiana" |
"US/Eastern" |
"US/Hawaii" |
"US/Indiana-Starke" |
"US/Michigan" |
"US/Mountain" |
"US/Pacific" |
"US/Samoa" |
"UTC" |
"Universal" |
"W-SU" |
"WET" |
"Zulu" |
"America/Rio_Branco" |
"America/Manaus" |
"America/Cuiaba" |
"America/Porto_Velho" |
"America/Boa_Vista" |
"America/Recife" |
"America/Fortaleza" |
"America/Noronha";
================================================
FILE: packages/date/util.ts
================================================
interface CpfDigits {
penultimateDigit: number;
ultimateDigit: number;
}
const removeSpecialCharsFromCpfRegex = /[\.]|[-]/g;
export const formatValueToCpfPattern = (cpf: string): string => {
const cpfValue = removeSpecialCharsFromCpf(cpf);
let formattedValue: string = '';
let index: number = 0;
while (formattedValue.length < 14 && index < 11) {
if (index === 3 || index === 6) {
formattedValue += '.';
} else if (index === 9) {
formattedValue += '-';
}
formattedValue += cpfValue[index];
index++;
}
return formattedValue;
};
export const removeSpecialCharsFromCpf = (cpf: string): string => {
return cpf.replace(removeSpecialCharsFromCpfRegex, '');
};
const getCpfDigitsNumbers = (cpf: string): CpfDigits => {
const lastTwoNumbers = cpf.slice(cpf.length - 2);
const penultimateDigit = parseInt(lastTwoNumbers[0]);
const ultimateDigit = parseInt(lastTwoNumbers[1]);
return {
penultimateDigit,
ultimateDigit,
};
};
const transformCpfInArrNumber = (cpf: string): number[] => {
var arr: number[] = [];
let index = 0;
while (index < 9) {
arr.push(parseInt(cpf[index]));
index++;
}
return arr;
};
export const calculateCpfDigits = (cpfNumbers: number[]): CpfDigits => {
const factor = 11;
let index = 0;
let startAuxValue = 10;
let totalForDigit = 0;
while (index < 9) {
totalForDigit = totalForDigit + cpfNumbers[index] * startAuxValue;
startAuxValue--;
index++;
}
const calcPDigit = totalForDigit % factor;
const resultPDigit = factor - calcPDigit;
const zeroIfPGreaterThanNine = resultPDigit > 9 ? 0 : resultPDigit;
const penultimateDigit = zeroIfPGreaterThanNine;
index = 0;
startAuxValue = 11;
totalForDigit = 0;
cpfNumbers.push(penultimateDigit);
while (index < 10) {
totalForDigit = totalForDigit + cpfNumbers[index] * startAuxValue;
startAuxValue--;
index++;
}
const calcUDigit = totalForDigit % factor;
const resultUDigit = factor - calcUDigit;
const zeroIfGreaterThanNine = resultUDigit > 9 ? 0 : resultUDigit;
const ultimateDigit = zeroIfGreaterThanNine;
return {
penultimateDigit,
ultimateDigit,
};
};
export const isValidCpfDigit = (cpf: string): boolean => {
const onlyNumbers = removeSpecialCharsFromCpf(cpf);
if (onlyNumbers.length !== 11) {
return false;
}
const digits = getCpfDigitsNumbers(onlyNumbers);
const arrNumbers = transformCpfInArrNumber(onlyNumbers);
const validDigits = calculateCpfDigits(arrNumbers);
return (
digits.penultimateDigit === validDigits.penultimateDigit &&
digits.ultimateDigit === validDigits.ultimateDigit
);
};
export default isValidCpfDigit;
================================================
FILE: packages/email/CHANGELOG.md
================================================
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.0.5-alpha.0](https://github.com/4lessandrodev/type-ddd/compare/@type-ddd/email@0.0.2...@type-ddd/email@0.0.5-alpha.0) (2024-12-16)
## 4.0.5 (2024-11-28)
### Bug Fixes
* change type create method return null [#194](https://github.com/4lessandrodev/type-ddd/issues/194) ([2cd03bf](https://github.com/4lessandrodev/type-ddd/commit/2cd03bf34387f4889a0a292ba350f2c0cfc753b7))
* solve error module not found [#449](https://github.com/4lessandrodev/type-ddd/issues/449) ([e9d14f6](https://github.com/4lessandrodev/type-ddd/commit/e9d14f694cafc9c2123cc31055a4561f460a82d3))
## 4.0.3 (2024-07-26)
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased
---
## Released
---
### [0.0.4] - 2024-11-28
### Fix
- update rich-domain lib to check nullish type: now 'create' return a possibly null value in Result instance.
---
### [0.0.3] - 2024-09-26
### Fix
- Corrected `"files"` in `package.json` to include `utils.js` and `utils.d.ts`, resolving module not found errors during compilation.
---
### [0.0.2] - 2024-05-31
### Docs
- Doc: update documentation
---
### [0.0.1] - 2024-05-31
### Base
- Create base value object as single pack
================================================
FILE: packages/email/README.md
================================================
# `@type-ddd/email`
> The @type-ddd/email module provides a class Email for handling email addresses in TypeScript. It includes methods for validating email addresses, extracting parts of the email (such as nickname and domain), and creating instances of validated emails.
## Installation
Install `rich-domain` and `@type-ddd/email` with your favorite package manager:
```sh
npm i rich-domain @type-ddd/email
#OR
yarn add rich-domain @type-ddd/email
```
## Usage
```ts
import { Email } from '@type-ddd/email';
// Check if is valid value
const isValid = Email.isValid('sample@domain.com');
// true
// Initialize Email instance with a valid email address
const email = Email.init('example@example.com');
// OR
// Create Email instance from provided email address
const result = Email.create('example@example.com');
// Get parts of the email address
const nickname = email.nick();
const domain = email.domain();
```
### Block some domains
If you want to block some specifics domains
```ts
import { Email } from '@type-ddd/email';
const list = ['hack.com'];
Reflect.set(Email, 'BLOCKED_DOMAINS', list);
const isValid = Email.isValid('user@hack.com');
// false
const isValid = Email.isValid('user@gmail.com');
// true
```
### Allowed some domains
If you want to allow only some specifics domains
```ts
import { Email } from '@type-ddd/email';
const list = ['my-company.com'];
Reflect.set(Email, 'VALID_DOMAINS', list);
const isValid = Email.isValid('user@my-company.com');
// true
const isValid = Email.isValid('user@gmail.com');
// false
```
================================================
FILE: packages/email/__tests__/email-validator.spec.ts
================================================
import IsValidEmail, { GetCharCode, IsSpecialChar } from '../email.validator.util';
describe('email-validator', () => {
it('should return 0 if is not string', () => {
const code = GetCharCode(3 as any);
expect(code).toBe(0);
});
it('should return true', () => {
const is = IsSpecialChar('+');
expect(is).toBeTruthy();
});
it('should to be false', () => {
const invalid = 'invalid-log-email-value'.repeat(10);
const long = invalid + '@gmail.com';
expect(IsValidEmail(long)).toBeFalsy();
});
it('should to be false', () => {
const invalid
gitextract_7wdim7d5/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── dependabot.yml │ └── workflows/ │ └── main.yml ├── .gitignore ├── .husky/ │ ├── pre-commit │ └── pre-push ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── check-dev-deps.sh ├── docs/ │ └── README.md ├── global-setup.ts ├── jest.config.ts ├── lerna.json ├── package.json ├── packages/ │ ├── cnpj/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── __tests__/ │ │ │ ├── cnpj.value-object.util.spec.ts │ │ │ └── is-valid-cpf-digit.util.spec.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ └── util.ts │ ├── cpf/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── __tests__/ │ │ │ ├── cpf.value-object.util.spec.ts │ │ │ └── is-valid-cpf-digit.util.spec.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ └── util.ts │ ├── date/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── __tests__/ │ │ │ └── date.value-object.spec.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── types.ts │ │ └── util.ts │ ├── email/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── __tests__/ │ │ │ ├── email-validator.spec.ts │ │ │ └── email.value-object.spec.ts │ │ ├── email.validator.util.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── logger/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── __tests__/ │ │ │ └── logger.spec.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── money/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── __tests__/ │ │ │ └── money.spec.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── password/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── __tests__/ │ │ │ ├── password.spec.ts │ │ │ └── util.spec.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ └── utils.ts │ ├── patterns/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── __tests__/ │ │ │ └── specification.value-object.spec.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── phone/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── __tests__/ │ │ │ ├── home-phone.value-object.spec.ts │ │ │ ├── mobile-phone.value-object.spec.ts │ │ │ └── phone.value-object.spec.ts │ │ ├── ddd.list.ts │ │ ├── home.value-object.ts │ │ ├── index.ts │ │ ├── mobile.value-object.ts │ │ ├── package.json │ │ ├── phone.value-object.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── type-ddd/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── username/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── __tests__/ │ │ │ └── user-name-value-object.util.spec.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── zip-code/ │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__/ │ │ └── zip-code.value-object.spec.ts │ ├── index.ts │ ├── package.json │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── util.ts ├── scripts/ │ ├── login.sh │ └── make-user.sh ├── tsconfig.json └── update-peer-dependency.sh
SYMBOL INDEX (235 symbols across 21 files)
FILE: packages/cnpj/index.ts
class CNPJ (line 8) | class CNPJ extends ValueObject<string> {
method constructor (line 12) | private constructor(props: string) {
method value (line 21) | value(): string {
method toPattern (line 30) | toPattern(): string {
method addMask (line 39) | public static addMask(cnpj: string): string {
method removeSpecialChars (line 48) | public static removeSpecialChars(cnpj: string): string {
method compare (line 59) | compare(cnpj: string | CNPJ): boolean {
method isValidProps (line 78) | public static isValidProps(value: string): boolean {
method isValid (line 91) | public static isValid(value: string): boolean {
method init (line 100) | public static init(value: string): CNPJ {
method create (line 114) | public static create(value: string): Result<CNPJ | null> {
FILE: packages/cnpj/util.ts
type CnpjDigits (line 1) | interface CnpjDigits {
FILE: packages/cpf/index.ts
class CPF (line 4) | class CPF extends ValueObject<string> {
method constructor (line 8) | private constructor(value: string) {
method value (line 17) | value(): string {
method toPattern (line 26) | toPattern(): string {
method addMask (line 35) | public static addMask(cpf: string): string {
method removeSpecialChars (line 44) | public static removeSpecialChars(cpf: string): string {
method compare (line 55) | compare(cpf: string | CPF): boolean {
method isValidProps (line 74) | public static isValidProps(value: string): boolean {
method isValid (line 87) | public static isValid(value: string): boolean {
method init (line 96) | public static init(value: string): CPF {
method create (line 110) | public static create(value: string): Result<CPF | null> {
FILE: packages/cpf/util.ts
type CpfDigits (line 1) | interface CpfDigits {
FILE: packages/date/index.ts
function commonDateFormat (line 7) | function commonDateFormat(timeFormat = null) {
type DateFormat (line 76) | type DateFormat = keyof typeof DateFormats;
type FormatParams (line 78) | type FormatParams = { date: Intl.DateTimeFormatOptions; time: Intl.DateT...
class Dates (line 80) | class Dates extends ValueObject<Date> {
method constructor (line 89) | private constructor(props: Date) {
method value (line 96) | value(): Date {
method addDays (line 105) | addDays(days: number): Dates {
method addMonths (line 114) | addMonths(months: number): Dates {
method addHours (line 123) | addHours(hours: number): Dates {
method addMinutes (line 132) | addMinutes(minutes: number): Dates {
method addWeeks (line 141) | addWeeks(weeks: number): Dates {
method addYears (line 150) | addYears(years: number): Dates {
method subtractDays (line 159) | subtractDays(days: number): Dates {
method subtractMonths (line 168) | subtractMonths(months: number): Dates {
method subtractHours (line 177) | subtractHours(hours: number): Dates {
method subtractMinutes (line 186) | subtractMinutes(minutes: number): Dates {
method subtractWeeks (line 195) | subtractWeeks(weeks: number): Dates {
method subtractYears (line 204) | subtractYears(years: number): Dates {
method differenceInDays (line 214) | differenceInDays(date: Date | Dates): number {
method differenceInHours (line 233) | differenceInHours(date: Date | Dates): number {
method differenceInMinutes (line 252) | differenceInMinutes(date: Date | Dates): number {
method differenceInMonths (line 271) | differenceInMonths(date: Date | Dates): number {
method differenceInYears (line 290) | differenceInYears(date: Date | Dates): number {
method differenceInWeeks (line 309) | differenceInWeeks(date: Date | Dates): number {
method format (line 328) | format(format: DateFormat, timeZone?: TimeZones): string {
method isValidProps (line 369) | public static isValidProps(value: Date | string | number): boolean {
method isValid (line 389) | public static isValid(value: Date | string): boolean {
method isWeekday (line 397) | isWeekday(): boolean {
method isWeekend (line 405) | isWeekend(): boolean {
method isWeekday (line 414) | public static isWeekday(date: Date): boolean {
method isWeekend (line 423) | public static isWeekend(date: Date): boolean {
method isAfter (line 445) | isAfter(date: Date | Dates): boolean {
method isBefore (line 473) | isBefore(date: Date | Dates): boolean {
method isEqualDate (line 488) | isEqualDate(date: Date | Dates): boolean {
method init (line 505) | public static init(value?: Date | string | number): Dates {
method create (line 518) | public static create(date?: Date | string | number): Result<Dates | nu...
FILE: packages/date/types.ts
type TimeZones (line 1) | type TimeZones =
FILE: packages/date/util.ts
type CpfDigits (line 1) | interface CpfDigits {
FILE: packages/email/index.ts
class Email (line 4) | class Email extends ValueObject<string> {
method constructor (line 9) | private constructor(props: string) {
method value (line 13) | value(): string {
method nick (line 17) | nick(): string {
method domain (line 21) | domain(): string {
method isValid (line 26) | public static isValid(value: string): boolean {
method isValidProps (line 42) | public static isValidProps(email: string): boolean {
method init (line 69) | public static init(value: string): Email {
method create (line 75) | public static create(value: string): Result<Email | null> {
FILE: packages/logger/index.ts
class DefaultLogger (line 4) | class DefaultLogger {
method instance (line 20) | public static instance() {
type LogsType (line 28) | type LogsType = 'error' | 'info' | 'warn';
FILE: packages/money/index.ts
type Currencies (line 3) | enum Currencies {
type Locales (line 26) | enum Locales {
type Locale (line 47) | type Locale = keyof typeof Locales;
type Currency (line 48) | type Currency = keyof typeof Currencies;
class Money (line 50) | class Money extends ValueObject<number> {
method constructor (line 53) | private constructor(prop: number) {
method value (line 57) | value(): number {
method isValid (line 66) | public static isValid(value: number): boolean {
method isValidProps (line 75) | public static isValidProps(value: number): boolean {
method coin (line 85) | coin(currency?: Currency | Currencies, locale?: Locale | Locales): str...
method sum (line 97) | sum(money: Money | number): Money {
method subtract (line 109) | subtract(money: Money | number): Money {
method multiply (line 121) | multiply(money: Money | number): Money {
method divide (line 133) | divide(money: Money | number): Money {
method addPercent (line 145) | addPercent(percent: number): Money {
method percent (line 158) | percent(percent: number): Money {
method isGt (line 170) | isGt(value: Money | number): boolean {
method isGte (line 181) | isGte(value: Money | number): boolean {
method isLt (line 192) | isLt(value: Money | number): boolean {
method isLte (line 203) | isLte(value: Money | number): boolean {
method isEq (line 214) | isEq(value: Money | number): boolean {
method isPos (line 224) | isPos(): boolean {
method isNeg (line 232) | isNeg(): boolean {
method isZero (line 240) | isZero(): boolean {
method makePos (line 248) | makePos(): Money {
method makeNeg (line 260) | makeNeg(): Money {
method zero (line 272) | public static zero(): Money {
method one (line 280) | public static one(): Money {
method ten (line 288) | public static ten(): Money {
method one_hundred (line 296) | public static one_hundred(): Money {
method one_thousand (line 304) | public static one_thousand(): Money {
method sum (line 314) | public static sum(a: number | Money, b: number | Money): number {
method divide (line 326) | public static divide(a: number | Money, b: number | Money): number {
method multiply (line 338) | public static multiply(a: number | Money, b: number | Money): number {
method subtract (line 350) | public static subtract(a: number | Money, b: number | Money): number {
method floor (line 360) | floor(): Money {
method ceil (line 369) | ceil(): Money {
method closure (line 379) | public static closure(initial: number) {
method interest (line 395) | interest(rate: number, periods: number): Money {
method max (line 413) | public static max(values: Money[]): Money {
method min (line 424) | public static min(values: Money[]): Money {
method compoundInterest (line 437) | compoundInterest(rate: number, periods: number): Money {
method random (line 458) | public static random(min: number, max: number): Money {
method average (line 470) | public static average(values: Money[]): Money {
method convertTo (line 482) | convertTo(exchangeRate: Money | number): Money {
method init (line 495) | public static init(value: number): Money {
method create (line 506) | public static create(value: number): Result<Money | null> {
FILE: packages/password/index.ts
class Password (line 6) | class Password extends ValueObject<string> {
method constructor (line 12) | private constructor(props: string) {
method value (line 19) | value(): string {
method compare (line 29) | public compare(plainText: string): boolean {
method isEncrypted (line 40) | public isEncrypted(): boolean {
method isEncrypted (line 51) | public static isEncrypted(value: string): boolean {
method random (line 61) | public static random(length?: ILength): Password {
method encrypt (line 71) | public encrypt(): Password {
method isValid (line 87) | public static isValid(value: string): boolean {
method isValidProps (line 96) | public static isValidProps(value: string): boolean {
method init (line 115) | public static init(value: string): Password {
method create (line 126) | static create(value: string): Result<Password | null> {
FILE: packages/password/utils.ts
type ILength (line 73) | type ILength = 8 | 10 | 12 | 14 | 16 | 18;
FILE: packages/patterns/__tests__/specification.value-object.spec.ts
type User (line 3) | type User = {
class AdultSpecification (line 9) | class AdultSpecification extends Specification<User> {
method isSatisfiedBy (line 10) | isSatisfiedBy(user: User): boolean {
class AdminSpecification (line 15) | class AdminSpecification extends Specification<User> {
method isSatisfiedBy (line 16) | isSatisfiedBy(user: User): boolean {
class CanAccessSpecification (line 21) | class CanAccessSpecification extends Specification<User> {
method isSatisfiedBy (line 22) | isSatisfiedBy(user: User): boolean {
FILE: packages/patterns/index.ts
type ISpecification (line 1) | interface ISpecification<T> {
method and (line 13) | and(other: ISpecification<T>): ISpecification<T> {
method or (line 17) | or(other: ISpecification<T>): ISpecification<T> {
method orNot (line 21) | orNot(other: ISpecification<T>): ISpecification<T> {
method andNot (line 25) | andNot(other: ISpecification<T>): ISpecification<T> {
method not (line 29) | not(): ISpecification<T> {
class AndSpecification (line 34) | class AndSpecification<T> extends Specification<T> {
method constructor (line 35) | constructor(
method isSatisfiedBy (line 42) | isSatisfiedBy(target: T): boolean {
class OrSpecification (line 49) | class OrSpecification<T> extends Specification<T> {
method constructor (line 50) | constructor(
method isSatisfiedBy (line 57) | isSatisfiedBy(target: T): boolean {
class OrNotSpecification (line 64) | class OrNotSpecification<T> extends Specification<T> {
method constructor (line 65) | constructor(
method isSatisfiedBy (line 72) | isSatisfiedBy(target: T): boolean {
class AndNotSpecification (line 80) | class AndNotSpecification<T> extends Specification<T> {
method constructor (line 81) | constructor(
method isSatisfiedBy (line 88) | isSatisfiedBy(target: T): boolean {
class NotSpecification (line 96) | class NotSpecification<T> extends Specification<T> {
method constructor (line 97) | constructor(private readonly other: ISpecification<T>) {
method isSatisfiedBy (line 101) | isSatisfiedBy(target: T): boolean {
FILE: packages/phone/ddd.list.ts
type ddd (line 78) | type ddd = keyof typeof UfForCode;
FILE: packages/phone/home.value-object.ts
class HomePhone (line 10) | class HomePhone extends ValueObject<string> {
method constructor (line 14) | private constructor(prop: string) {
method toPattern (line 18) | toPattern(): string {
method isMobile (line 22) | isMobile(): boolean {
method isHome (line 26) | isHome(): boolean {
method isValid (line 30) | public static isValid(value: string): boolean {
method isValidProps (line 39) | public static isValidProps(value: string): boolean {
method value (line 48) | value(): string {
method toCall (line 57) | toCall(): string {
method number (line 62) | number(): string {
method code (line 71) | code(): ddd {
method code (line 80) | public static code(phone: string): ddd {
method uf (line 85) | uf() {
method removeSpecialChars (line 90) | public static removeSpecialChars(cell: string): string {
method addMask (line 95) | public static addMask(cell: string): string {
method init (line 108) | public static init(value: string): HomePhone {
method create (line 121) | public static create(value: string): Result<HomePhone | null> {
FILE: packages/phone/mobile.value-object.ts
class MobilePhone (line 12) | class MobilePhone extends ValueObject<string> {
method constructor (line 16) | private constructor(prop: string) {
method toPattern (line 20) | toPattern(): string {
method isValidProps (line 29) | public static isValidProps(value: string): boolean {
method isValid (line 35) | public static isValid(value: string): boolean {
method isMobile (line 39) | isMobile(): boolean {
method isHome (line 43) | isHome(): boolean {
method value (line 50) | value(): string {
method toCall (line 59) | toCall(): string {
method number (line 64) | number(): string {
method code (line 73) | code(): ddd {
method code (line 82) | public static code(phone: string): ddd {
method uf (line 87) | uf() {
method removeSpecialChars (line 92) | public static removeSpecialChars(cell: string): string {
method addMask (line 97) | public static addMask(cell: string): string {
method init (line 110) | public static init(value: string): MobilePhone {
method create (line 123) | public static create(value: string): Result<MobilePhone | null> {
FILE: packages/phone/phone.value-object.ts
class Phone (line 6) | class Phone extends ValueObject<string> {
method ddd (line 23) | public static ddd(phone: string): ddd {
method removeSpecialChars (line 28) | public static removeSpecialChars(cell: string): string {
method addMask (line 33) | public static addMask(cell: string): string {
method isValid (line 38) | public static isValid(phone: string): boolean {
method isValidProps (line 42) | public static isValidProps(phone: string): boolean {
method isMobile (line 46) | public static isMobile(phone: string): boolean {
method isHome (line 50) | public static isHome(phone: string): boolean {
method init (line 59) | public static init(value: string): MobilePhone | HomePhone {
method create (line 73) | public static create(value: string): Result<MobilePhone | HomePhone | ...
FILE: packages/username/index.ts
class UserName (line 3) | class UserName extends ValueObject<string> {
method constructor (line 8) | private constructor(props: string) {
method value (line 15) | value(): string {
method capitalize (line 23) | public static capitalize(fullName: string): string {
method upperCase (line 45) | upperCase(): string {
method lowerCase (line 53) | lowerCase(): string {
method hasMiddleName (line 61) | hasMiddleName(): boolean {
method title (line 65) | title(title: string) {
method hasLastName (line 78) | hasLastName(): boolean {
method firstName (line 86) | firstName(title: string = ''): string {
method middleName (line 94) | middleName(): string {
method lastName (line 105) | lastName(): string {
method initials (line 117) | initials(separator = ''): string {
method init (line 132) | public static init(value: string): UserName {
method isValid (line 144) | public static isValid(value: string): boolean {
method isValidProps (line 153) | public static isValidProps(value: string): boolean {
method create (line 161) | public static create(value: string): Result<UserName | null> {
FILE: packages/zip-code/index.ts
class ZipCode (line 5) | class ZipCode extends ValueObject<string> {
method constructor (line 9) | private constructor(prop: string) {
method value (line 17) | value(): string {
method addMask (line 26) | public static addMask(zipCode: string): string {
method removeSpecialChars (line 37) | public static removeSpecialChars(zipCode: string): string {
method toPattern (line 45) | toPattern(): string {
method isValid (line 54) | public static isValid(value: string): boolean {
method isValidProps (line 63) | public static isValidProps(value: string): boolean {
method init (line 72) | public static init(value: string): ZipCode {
method create (line 80) | public static create(value: string): Result<ZipCode | null> {
FILE: packages/zip-code/util.ts
type CpfDigits (line 1) | interface CpfDigits {
Condensed preview — 128 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (372K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".github/dependabot.yml",
"chars": 503,
"preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
},
{
"path": ".github/workflows/main.yml",
"chars": 1234,
"preview": "# This is a basic workflow to help you get started with Actions\n\nname: CI\n\n# Controls when the action will run. \non:\n #"
},
{
"path": ".gitignore",
"chars": 1713,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs."
},
{
"path": ".husky/pre-commit",
"chars": 78,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpm run check:types\nnpx lint-staged\n"
},
{
"path": ".husky/pre-push",
"chars": 55,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpm run test\n"
},
{
"path": ".prettierignore",
"chars": 170,
"preview": "# Ignore everything:\n/*\n/__snapshots__/*\n*.ts.snap\n\n# Except:\n!/lib\n!/example\n!/tests\n!jest.config.ts\n!package.json\n!tsc"
},
{
"path": ".prettierrc",
"chars": 154,
"preview": "{\n\t\"useTabs\": true,\n\t\"arrowParens\": \"always\",\n\t\"singleQuote\": true,\n\t\"semi\": true,\n \"bracketSpacing\": true,\n \"endO"
},
{
"path": "CHANGELOG.md",
"chars": 20383,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n## Unreleased\n\n---\n\n## Released\n\n---\n"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5202,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "CONTRIBUTING.md",
"chars": 300,
"preview": "# Contributing\n\nWhen contributing to this repository, please first discuss the change you wish to make via issue, email,"
},
{
"path": "LICENSE",
"chars": 1072,
"preview": "MIT License\n\nCopyright (c) 2021 Alessandro dev.\n\nPermission is hereby granted, free of charge, to any person obtaining a"
},
{
"path": "Makefile",
"chars": 2466,
"preview": "# Define the targets and the commands to be executed\n.PHONY: startVerdaccio stopVerdaccio publishVerdaccio addUser login"
},
{
"path": "README.md",
"chars": 20976,
"preview": "\n# @type-ddd/core\n\n> Now with individual packages\n\nThis package provide utils file and interfaces to assistant build a c"
},
{
"path": "SECURITY.md",
"chars": 431,
"preview": "# Security Policy\n\n## Supported Versions\n\nVersions of types-ddd are currently being supported with security updates.\n\n| "
},
{
"path": "check-dev-deps.sh",
"chars": 324,
"preview": "#!/bin/sh\n\nDEV_DEPS=\"$(cat package.json | grep -A 100 \"devDependencies\" | grep -B 100 \"\\}\\,\" | \\\nawk \"NR>1\" | sed -e \"s/"
},
{
"path": "docs/README.md",
"chars": 33236,
"preview": "# types-ddd documentation\n\n## About the lib\n\nThe library was created to support developers in developing domain-rich app"
},
{
"path": "global-setup.ts",
"chars": 23,
"preview": "process.env.TZ = 'UTC';"
},
{
"path": "jest.config.ts",
"chars": 260,
"preview": "import './global-setup';\n\nmodule.exports = {\n\troots: ['<rootDir>'],\n\tcollectCoverage: false,\n\tcoverageDirectory: 'covera"
},
{
"path": "lerna.json",
"chars": 69,
"preview": "{\n \"packages\": [\n \"packages/*\"\n ],\n \"version\": \"independent\"\n}\n"
},
{
"path": "package.json",
"chars": 2248,
"preview": "{\n\t\"name\": \"type-ddd\",\n\t\"version\": \"4.1.0\",\n\t\"description\": \"This package provide utils file and interfaces to assistant"
},
{
"path": "packages/cnpj/CHANGELOG.md",
"chars": 993,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/cnpj/README.md",
"chars": 1747,
"preview": "# `@type-ddd/cnpj`\n\n> The @type-ddd/cnpj library provides TypeScript type definitions for handling CNPJ (Cadastro Nacion"
},
{
"path": "packages/cnpj/__tests__/cnpj.value-object.util.spec.ts",
"chars": 4801,
"preview": "import CNPJValueObject from '../index';\n\ndescribe('CNPJ Value Object', () => {\n\tdescribe('Creation and Definition', () ="
},
{
"path": "packages/cnpj/__tests__/is-valid-cpf-digit.util.spec.ts",
"chars": 2902,
"preview": "import { formatValueToCnpjPattern, isValidCnpjDigit, removeSpecialCharsFromCnpj } from '../util';\n\ndescribe('is-valid-cn"
},
{
"path": "packages/cnpj/index.ts",
"chars": 3519,
"preview": "import { Result, ValueObject } from 'rich-domain';\nimport isValidCnpjDigit, { formatValueToCnpjPattern, removeSpecialCha"
},
{
"path": "packages/cnpj/package.json",
"chars": 1366,
"preview": "{\n \"name\": \"@type-ddd/cnpj\",\n \"description\": \"Library that provides TypeScript type definitions for handling CNPJ (Cad"
},
{
"path": "packages/cnpj/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/cnpj/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/cnpj/util.ts",
"chars": 2822,
"preview": "interface CnpjDigits {\n\tpenultimateDigit: number;\n\tultimateDigit: number;\n}\n\nconst removeSpecialCharsFromCnpjRegex = /[\\"
},
{
"path": "packages/cpf/CHANGELOG.md",
"chars": 991,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/cpf/README.md",
"chars": 1712,
"preview": "# `@type-ddd/cpf`\n\n> The @type-ddd/cpf library provides TypeScript type definitions for handling CPF (Cadastro de Pessoa"
},
{
"path": "packages/cpf/__tests__/cpf.value-object.util.spec.ts",
"chars": 5891,
"preview": "import CPFValueObject from \"../index\";\n\ndescribe('cpf.value-object', () => {\n\tit('should be defined', () => {\n\t\tconst va"
},
{
"path": "packages/cpf/__tests__/is-valid-cpf-digit.util.spec.ts",
"chars": 2903,
"preview": "import isValidCpfDigit, { formatValueToCpfPattern, removeSpecialCharsFromCpf } from \"../util\";\n\ndescribe('is-valid-cpf-d"
},
{
"path": "packages/cpf/index.ts",
"chars": 3451,
"preview": "import { Result, ValueObject } from \"rich-domain\";\nimport isValidCpfDigit, { formatValueToCpfPattern } from \"./util\";\n\ne"
},
{
"path": "packages/cpf/package.json",
"chars": 1158,
"preview": "{\n \"name\": \"@type-ddd/cpf\",\n \"description\": \"This package provides TypeScript type definitions for handling CPF (Cadas"
},
{
"path": "packages/cpf/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/cpf/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/cpf/util.ts",
"chars": 2604,
"preview": "interface CpfDigits {\n\tpenultimateDigit: number;\n\tultimateDigit: number;\n}\n\nconst removeSpecialCharsFromCpfRegex = /[\\.]"
},
{
"path": "packages/date/CHANGELOG.md",
"chars": 993,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/date/README.md",
"chars": 1178,
"preview": "# `@type-ddd/date`\n\n> The @type-ddd/date library provides a class Dates for handling date and time operations in TypeScr"
},
{
"path": "packages/date/__tests__/date.value-object.spec.ts",
"chars": 26502,
"preview": "import Dates from '../index';\n\ndescribe('Date', () => {\n\n\tdescribe('create', () => {\n\t\tit('should create an instance wit"
},
{
"path": "packages/date/index.ts",
"chars": 14972,
"preview": "import { Result, ValueObject } from 'rich-domain';\nimport { TimeZones } from './types';\n\nconst timeFormat12h = { hour: '"
},
{
"path": "packages/date/package.json",
"chars": 1201,
"preview": "{\n \"name\": \"@type-ddd/date\",\n \"description\": \"Library that provides TypeScript type definitions for handling Dates in "
},
{
"path": "packages/date/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/date/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/date/types.ts",
"chars": 11574,
"preview": "export type TimeZones = \n\"Africa/Abidjan\" |\n\"Africa/Accra\" |\n\"Africa/Addis_Ababa\" |\n\"Africa/Algiers\" |\n\"Africa/Asmara\" |"
},
{
"path": "packages/date/util.ts",
"chars": 2604,
"preview": "interface CpfDigits {\n\tpenultimateDigit: number;\n\tultimateDigit: number;\n}\n\nconst removeSpecialCharsFromCpfRegex = /[\\.]"
},
{
"path": "packages/email/CHANGELOG.md",
"chars": 1370,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/email/README.md",
"chars": 1576,
"preview": "# `@type-ddd/email`\n\n> The @type-ddd/email module provides a class Email for handling email addresses in TypeScript. It "
},
{
"path": "packages/email/__tests__/email-validator.spec.ts",
"chars": 876,
"preview": "import IsValidEmail, { GetCharCode, IsSpecialChar } from '../email.validator.util';\n\ndescribe('email-validator', () => {"
},
{
"path": "packages/email/__tests__/email.value-object.spec.ts",
"chars": 8226,
"preview": "import EmailValueObject from '../index';\n\ndescribe('email-value-object.util', () => {\n\tit('should init an instance with "
},
{
"path": "packages/email/email.validator.util.ts",
"chars": 4177,
"preview": "/**\n * a-z = 97 - 122\n * 0-9 = 48 - 57\n * 95 = _\n * 45 = -\n * 64 = @\n * 46 = .\n */\nconst ValidChars = { min: 97, max: 12"
},
{
"path": "packages/email/index.ts",
"chars": 2177,
"preview": "import { Result, ValueObject } from 'rich-domain';\nimport IsValidEmail from './email.validator.util';\n\nexport class Emai"
},
{
"path": "packages/email/package.json",
"chars": 1203,
"preview": "{\n \"name\": \"@type-ddd/email\",\n \"description\": \"Library that provides TypeScript type definitions for handling Email in"
},
{
"path": "packages/email/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/email/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/logger/CHANGELOG.md",
"chars": 633,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/logger/README.md",
"chars": 119,
"preview": "# `types-ddd`\n\n> TODO: description\n\n## Usage\n\n```\nconst typesDdd = require('types-ddd');\n\n// TODO: DEMONSTRATE API\n```\n"
},
{
"path": "packages/logger/__tests__/logger.spec.ts",
"chars": 2331,
"preview": "import Logger, { checkEnv } from '../index';\n\ndescribe('Logger', () => {\n\tit('should log if is not production and log is"
},
{
"path": "packages/logger/index.ts",
"chars": 1389,
"preview": "import pino, { BaseLogger } from 'pino';\nimport { LoggerOptions } from 'pino';\n\nclass DefaultLogger {\n\tprotected static "
},
{
"path": "packages/logger/package.json",
"chars": 828,
"preview": "{\n \"name\": \"@type-ddd/logger\",\n \"description\": \"This package provide utils file and interfaces to assistant build a co"
},
{
"path": "packages/logger/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/logger/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/money/CHANGELOG.md",
"chars": 995,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/money/README.md",
"chars": 2016,
"preview": "# `@type-ddd/money`\n\n> The @type-ddd/money library provides TypeScript type definitions for handling Money in Domain-Dri"
},
{
"path": "packages/money/__tests__/money.spec.ts",
"chars": 14034,
"preview": "import { Money } from '../index';\n\ndescribe('money', () => {\n\tdescribe('create', () => {\n\t\tit('should create instance wi"
},
{
"path": "packages/money/index.ts",
"chars": 18505,
"preview": "import { Result, ValueObject } from 'rich-domain';\n\nexport enum Currencies {\n\tUSD = 'USD', // Dólar Americano\n\tEUR = 'EU"
},
{
"path": "packages/money/package.json",
"chars": 1101,
"preview": "{\n \"name\": \"@type-ddd/money\",\n \"description\": \"This package provides TypeScript type definitions for handling Money in"
},
{
"path": "packages/money/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/money/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/password/CHANGELOG.md",
"chars": 1567,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/password/README.md",
"chars": 1041,
"preview": "# `@type-ddd/password`\n\n> The @type-ddd/password module provides a class Password for handling password in TypeScript. I"
},
{
"path": "packages/password/__tests__/password.spec.ts",
"chars": 5485,
"preview": "import PasswordValueObject from '../index'\n\ndescribe('password.value-object', () => {\n\tdescribe('default', () => {\n\t\tit("
},
{
"path": "packages/password/__tests__/util.spec.ts",
"chars": 1092,
"preview": "import { passwordGenerator } from '../utils';\n\ndescribe('password-generator.util', () => {\n\tit('should be defined', () ="
},
{
"path": "packages/password/index.ts",
"chars": 3690,
"preview": "import { genSaltSync, hashSync, compareSync } from 'bcrypt';\nimport { Result, ValueObject } from 'rich-domain';\nimport p"
},
{
"path": "packages/password/package.json",
"chars": 1315,
"preview": "{\n \"name\": \"@type-ddd/password\",\n \"description\": \"Library that provides TypeScript type definitions for handling Passw"
},
{
"path": "packages/password/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/password/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/password/utils.ts",
"chars": 1224,
"preview": "const upper = [\n\t'A',\n\t'B',\n\t'C',\n\t'D',\n\t'E',\n\t'F',\n\t'G',\n\t'H',\n\t'I',\n\t'J',\n\t'K',\n\t'L',\n\t'M',\n\t'N',\n\t'O',\n\t'P',\n\t'Q',\n\t'"
},
{
"path": "packages/patterns/CHANGELOG.md",
"chars": 1001,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/patterns/README.md",
"chars": 119,
"preview": "# `types-ddd`\n\n> TODO: description\n\n## Usage\n\n```\nconst typesDdd = require('types-ddd');\n\n// TODO: DEMONSTRATE API\n```\n"
},
{
"path": "packages/patterns/__tests__/specification.value-object.spec.ts",
"chars": 4147,
"preview": "import { Specification } from '../index';\n\ntype User = {\n\tname: string;\n\tage: number;\n\tpermission: 'ADMIN' | 'CLIENT' | "
},
{
"path": "packages/patterns/index.ts",
"chars": 2473,
"preview": "export interface ISpecification<T> {\n\tisSatisfiedBy: (target: T) => boolean;\n\tand: (other: ISpecification<T>) => ISpecif"
},
{
"path": "packages/patterns/package.json",
"chars": 1165,
"preview": "{\n \"name\": \"@type-ddd/patterns\",\n \"description\": \"This package provide utils file and interfaces to assistant build a "
},
{
"path": "packages/patterns/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/patterns/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/phone/CHANGELOG.md",
"chars": 995,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/phone/README.md",
"chars": 2090,
"preview": "# `@type-ddd/phone`\n\n> The @type-ddd/phone library provides TypeScript type definitions for handling phone (Brazilian) i"
},
{
"path": "packages/phone/__tests__/home-phone.value-object.spec.ts",
"chars": 3163,
"preview": "import { HomePhone as HomePhoneValueObject } from '../home.value-object';\n\ndescribe('home-phone.value-object', () => {\n\t"
},
{
"path": "packages/phone/__tests__/mobile-phone.value-object.spec.ts",
"chars": 3362,
"preview": "import { MobilePhone as MobilePhoneValueObject } from '../mobile.value-object';\n\ndescribe('home-phone.value-object', () "
},
{
"path": "packages/phone/__tests__/phone.value-object.spec.ts",
"chars": 2157,
"preview": "import { Phone } from '../phone.value-object';\n\ndescribe('phone', () => {\n it('should create a valid phone', () => {\n"
},
{
"path": "packages/phone/ddd.list.ts",
"chars": 1898,
"preview": "export const AreaCodes = [\n 61, 62, 64, 65, 66, 67, // CENTRO OESTE\n 82, 71, 73, 74, 75, 77, 85, 88, 98, 99, 83, 8"
},
{
"path": "packages/phone/home.value-object.ts",
"chars": 3099,
"preview": "import { Result, ValueObject } from 'rich-domain';\nimport { AreaCodes, UfForCode, ddd } from './ddd.list';\nconst regexHa"
},
{
"path": "packages/phone/index.ts",
"chars": 114,
"preview": "export * from './home.value-object';\nexport * from './mobile.value-object';\nexport * from './phone.value-object';\n"
},
{
"path": "packages/phone/mobile.value-object.ts",
"chars": 3173,
"preview": "import { Result, ValueObject } from 'rich-domain';\nimport { UfForCode, ddd, AreaCodes } from './ddd.list';\n\nconst regexH"
},
{
"path": "packages/phone/package.json",
"chars": 1427,
"preview": "{\n \"name\": \"@type-ddd/phone\",\n \"description\": \"Library that provides TypeScript type definitions for handling Phone Nu"
},
{
"path": "packages/phone/phone.value-object.ts",
"chars": 2494,
"preview": "import { Result, ValueObject } from \"rich-domain\";\nimport MobilePhone from \"./mobile.value-object\";\nimport HomePhone fro"
},
{
"path": "packages/phone/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/phone/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/tsconfig.build.json",
"chars": 617,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"module\": \"commonjs\",\n \"declaration\": true,\n "
},
{
"path": "packages/tsconfig.json",
"chars": 909,
"preview": "{\n \"files\": [],\n \"references\": [\n {\n \"path\": \"./cnpj/tsconfig.build.json\"\n },\n {\n "
},
{
"path": "packages/type-ddd/CHANGELOG.md",
"chars": 1565,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/type-ddd/README.md",
"chars": 647,
"preview": "# `@type-ddd/core`\n\n> The @type-ddd/core library provides TypeScript type definitions for handling utils value object in"
},
{
"path": "packages/type-ddd/index.ts",
"chars": 367,
"preview": "export * from 'rich-domain';\nexport * from \"@type-ddd/cpf\";\nexport * from \"@type-ddd/cnpj\";\nexport * from \"@type-ddd/dat"
},
{
"path": "packages/type-ddd/package.json",
"chars": 1126,
"preview": "{\n \"name\": \"@type-ddd/core\",\n \"description\": \"This package provide utils file and interfaces to assistant build a comp"
},
{
"path": "packages/type-ddd/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/type-ddd/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/username/CHANGELOG.md",
"chars": 1001,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/username/README.md",
"chars": 1325,
"preview": "# `@type-ddd/username`\n\n> The @type-ddd/username library provides TypeScript type definitions for handling User Name in "
},
{
"path": "packages/username/__tests__/user-name-value-object.util.spec.ts",
"chars": 6037,
"preview": "import UserName from '../index'\n\ndescribe('user-name.value-object', () => {\n\tit('should be defined', () => {\n\t\tconst use"
},
{
"path": "packages/username/index.ts",
"chars": 4149,
"preview": "import { Result, ValueObject } from 'rich-domain';\n\nexport class UserName extends ValueObject<string> {\n\tprotected stati"
},
{
"path": "packages/username/package.json",
"chars": 1115,
"preview": "{\n \"name\": \"@type-ddd/username\",\n \"description\": \"This package provides TypeScript type definitions for handling User "
},
{
"path": "packages/username/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/username/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/zip-code/CHANGELOG.md",
"chars": 1001,
"preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nSee [Conventional Commits](https://co"
},
{
"path": "packages/zip-code/README.md",
"chars": 1461,
"preview": "# `@type-ddd/zip-code`\n\n> The @type-ddd/cpf library provides TypeScript type definitions for handling ZipCode in Domain-"
},
{
"path": "packages/zip-code/__tests__/zip-code.value-object.spec.ts",
"chars": 2140,
"preview": "import { ZipCode as ZipCodeValueObject } from '../index'\n\n\ndescribe('postal-code.value-object', () => {\n\tit('should be d"
},
{
"path": "packages/zip-code/index.ts",
"chars": 2285,
"preview": "import { Result, ValueObject } from 'rich-domain';\n\nconst regexHash = /^[0-9]{5}-[0-9]{3}$|^[0-9]{8}$/;\n\nclass ZipCode e"
},
{
"path": "packages/zip-code/package.json",
"chars": 1168,
"preview": "{\n \"name\": \"@type-ddd/zip-code\",\n \"description\": \"This package provides TypeScript type definitions for handling Brazi"
},
{
"path": "packages/zip-code/tsconfig.build.json",
"chars": 274,
"preview": "{\n \"extends\": \"../tsconfig.build.json\",\n \"compilerOptions\": {\n \"outDir\": \".\",\n \"rootDir\": \".\",\n "
},
{
"path": "packages/zip-code/tsconfig.json",
"chars": 187,
"preview": "{\n\t\"extends\": \"../tsconfig.build.json\",\n\t\"compilerOptions\": {\n\t\t\"types\": [\n\t\t\t\"node\"\n\t\t]\n\t},\n\t\"files\": [],\n\t\"include\": ["
},
{
"path": "packages/zip-code/util.ts",
"chars": 2604,
"preview": "interface CpfDigits {\n\tpenultimateDigit: number;\n\tultimateDigit: number;\n}\n\nconst removeSpecialCharsFromCpfRegex = /[\\.]"
},
{
"path": "scripts/login.sh",
"chars": 350,
"preview": "#!/usr/bin/expect -f\n\nset admin_user \"admin\"\nset admin_password \"admin\"\nset admin_email \"dev@localhost.com\"\nset registry"
},
{
"path": "scripts/make-user.sh",
"chars": 746,
"preview": "#!/bin/bash\n\nurl='http://localhost:4873/-/user/org.couchdb.user:admin'\n\nargs=(\n \"//localhost:4873/:_auth=YWRtaW46YWRt"
},
{
"path": "tsconfig.json",
"chars": 1011,
"preview": "{\n\t\"compilerOptions\": {\n\t\t\"module\": \"commonjs\",\n\t\t\"noImplicitAny\": false,\n\t\t\"noUnusedLocals\": false,\n\t\t\"removeComments\":"
},
{
"path": "update-peer-dependency.sh",
"chars": 2337,
"preview": "#!/bin/bash\n\n# Update a specific peer dependency across all packages in the monorepo\n\n# Usage example:\n# in root folder "
}
]
About this extraction
This page contains the full source code of the 4lessandrodev/types-ddd GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 128 files (329.6 KB), approximately 97.1k tokens, and a symbol index with 235 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.