@tactical-ddd/nx 0.0.1-alpha.0 → 0.0.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -0
- package/generators/shared-kernel/schema.d.ts +6 -0
- package/generators/shared-kernel/schema.json +24 -1
- package/generators/shared-kernel/shared-kernel.d.ts.map +1 -1
- package/generators/shared-kernel/shared-kernel.js +11 -3
- package/generators/shared-kernel/shared-kernel.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# @tactical-ddd/nx
|
|
2
|
+
|
|
3
|
+
`@tactical-ddd/nx` is a collection of Nx generators for scaffolding and enforcing Domain-Driven Design (DDD) architectures inside your Nx monorepo.
|
|
4
|
+
|
|
5
|
+
The suite is being built out incrementally. Generators currently available:
|
|
6
|
+
|
|
7
|
+
- [`shared-kernel`](#shared-kernel-generator) — scaffolds the Shared Kernel, the agnostic foundation reused by every other module.
|
|
8
|
+
|
|
9
|
+
> More generators (e.g. `domain`) are planned. This document covers the generators shipped today.
|
|
10
|
+
|
|
11
|
+
## Shared Kernel Generator
|
|
12
|
+
|
|
13
|
+
The `shared-kernel` generator is designed to automatically deploy the **Shared Kernel** within your Nx monorepo. This forms the absolute foundation of the entire application architecture, containing completely agnostic (not tied to any specific business domain) code that is reused across all other modules in the system.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
To run the generator, execute the following command in the root of your workspace:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
nx g @tactical-ddd/nx:shared-kernel
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The generator checks for the existence of libraries before creating them, making it safe to run multiple times to restore missing kernel layers.
|
|
24
|
+
|
|
25
|
+
## Options
|
|
26
|
+
|
|
27
|
+
| Option | Type | Default | Required | Description |
|
|
28
|
+
| ---------------- | -------- | ------------- | -------- | ------------------------------------------------------------------------------------------------- |
|
|
29
|
+
| `directory` | `string` | `libs/shared` | Yes | Root folder the shared kernel libraries are generated into. Keeping `libs/shared` is recommended. |
|
|
30
|
+
| `prefix` | `string` | — | No | Organization prefix used for the generated import paths, e.g. `@my-org`. |
|
|
31
|
+
| `linter` | `string` | — | Yes | Linter to configure for the generated libraries. One of `eslint`, `none`. |
|
|
32
|
+
| `unitTestRunner` | `string` | — | Yes | Unit test runner to set up. One of `jest`, `vitest`, `none`. |
|
|
33
|
+
| `bundler` | `string` | `none` | No | Bundler used to build the libraries. One of `none`, `swc`, `tsc`, `rollup`, `vite`, `esbuild`. |
|
|
34
|
+
|
|
35
|
+
When run interactively (or via Nx Console), the generator prompts for any required option that is not passed on the command line. Example with explicit flags:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
nx g @tactical-ddd/nx:shared-kernel \
|
|
39
|
+
--directory=libs/shared \
|
|
40
|
+
--prefix=@my-org \
|
|
41
|
+
--linter=eslint \
|
|
42
|
+
--unitTestRunner=jest \
|
|
43
|
+
--bundler=none
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
> Note: the `contracts` library is always generated without a unit test runner — it holds only compile-time types — regardless of the `unitTestRunner` value, which applies to `utils` and `infrastructure`.
|
|
47
|
+
|
|
48
|
+
## Architecture & Folder Structure
|
|
49
|
+
|
|
50
|
+
The generator creates three core libraries inside the `libs/shared/` directory:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
libs/shared/
|
|
54
|
+
├── contracts/ # Global types, DTOs, and interfaces (no implementation)
|
|
55
|
+
├── utils/ # Pure helpers and utility functions
|
|
56
|
+
└── infrastructure/ # API clients, storage adapters, and loggers
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 1. 📦 contracts
|
|
60
|
+
|
|
61
|
+
**Nx Tags:** `scope:shared`, `type:contracts`
|
|
62
|
+
|
|
63
|
+
This is the lowest and most abstract layer of the application. It establishes the "social contracts" between different parts of the system and the backend.
|
|
64
|
+
|
|
65
|
+
- **What's inside:** Global TypeScript interfaces, data types, validation schemas (Zod/Yup), API response shapes (DTOs), and global domain event structures.
|
|
66
|
+
- **Features:** This library is generated without a unit test runner (`unitTestRunner: 'none'`) because it contains strictly compile-time types and interfaces that carry no executable logic.
|
|
67
|
+
- **Import Rule:** It is strictly forbidden to import anything into this library from any other modules in the workspace.
|
|
68
|
+
|
|
69
|
+
### 2. 🛠 utils
|
|
70
|
+
|
|
71
|
+
**Nx Tags:** `scope:shared`, `type:utils`
|
|
72
|
+
|
|
73
|
+
A layer of general-purpose utilities designed to solve purely technical computation and data transformation tasks.
|
|
74
|
+
|
|
75
|
+
- **What's inside:** Pure functions for strings, arrays, date formatting, mathematical calculations, and custom domain-agnostic RxJS operators.
|
|
76
|
+
- **Features:** Functions in this library must not have side effects and must remain decoupled from the application state.
|
|
77
|
+
- **Import Rule:** Can only import types from `libs/shared/contracts`.
|
|
78
|
+
|
|
79
|
+
### 3. 🌐 infrastructure
|
|
80
|
+
|
|
81
|
+
**Nx Tags:** `scope:shared`, `type:infrastructure`
|
|
82
|
+
|
|
83
|
+
The I/O (Input/Output) implementation layer that integrates your system with the outside world. This is the technical heart of your application's infrastructure.
|
|
84
|
+
|
|
85
|
+
- **What's inside:**
|
|
86
|
+
- HTTP/WebSocket client configurations (Axios instances, Fetch wrappers).
|
|
87
|
+
- Base wrappers for browser storage (localStorage, IndexedDB).
|
|
88
|
+
- Third-party service integrations (Sentry clients for logging, analytics trackers).
|
|
89
|
+
- State management configurations (`@tanstack/query-core` / base cache providers).
|
|
90
|
+
- **Import Rule:** Can freely import contracts from `libs/shared/contracts` and pure helpers from `libs/shared/utils`.
|
|
91
|
+
|
|
92
|
+
## Module Boundaries & Isolation Rules
|
|
93
|
+
|
|
94
|
+
The generator automatically tags these projects with `scope:shared`. In your root linter configuration (`eslint.config.js`), strict boundaries are enforced for these tags:
|
|
95
|
+
|
|
96
|
+
1. **Isolation from Business Logic:** Code inside `libs/shared/*` never and under no circumstances can import code from business domains (`libs/auth/*`, `libs/payments/*`, etc.). The shared kernel is completely isolated from business logic.
|
|
97
|
+
2. **Linear Layer Dependencies:** A strict hierarchy is maintained within the kernel itself:
|
|
98
|
+
- `contracts` knows about no one.
|
|
99
|
+
- `utils` only knows about `contracts`.
|
|
100
|
+
- `infrastructure` knows about both `contracts` and `utils`.
|
|
@@ -9,7 +9,30 @@
|
|
|
9
9
|
"description": "Root folder for the shared kernel (it is recommended to keep libs/shared)",
|
|
10
10
|
"default": "libs/shared",
|
|
11
11
|
"x-prompt": "Which directory should the Shared Kernel be generated into?"
|
|
12
|
+
},
|
|
13
|
+
"prefix": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Organization prefix for the generated libraries, e.g. '@my-org'",
|
|
16
|
+
"x-prompt": "Which organization prefix should be used (e.g. @my-org)?"
|
|
17
|
+
},
|
|
18
|
+
"linter": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"enum": ["eslint", "none"],
|
|
21
|
+
"description": "Which linter to use?",
|
|
22
|
+
"x-prompt": "Which linter should be configured?"
|
|
23
|
+
},
|
|
24
|
+
"unitTestRunner": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": ["jest", "vitest", "none"],
|
|
27
|
+
"description": "Which test runner to use?",
|
|
28
|
+
"x-prompt": "Which unit test runner should be used?"
|
|
29
|
+
},
|
|
30
|
+
"bundler": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": ["none", "swc", "tsc", "rollup", "vite", "esbuild"],
|
|
33
|
+
"default": "none",
|
|
34
|
+
"description": "Which bundler to use?"
|
|
12
35
|
}
|
|
13
36
|
},
|
|
14
|
-
"required": ["directory"]
|
|
37
|
+
"required": ["directory", "linter", "unitTestRunner"]
|
|
15
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-kernel.d.ts","sourceRoot":"","sources":["../../../../../packages/nx/src/generators/shared-kernel/shared-kernel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAGpD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAG5D,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,2BAA2B,
|
|
1
|
+
{"version":3,"file":"shared-kernel.d.ts","sourceRoot":"","sources":["../../../../../packages/nx/src/generators/shared-kernel/shared-kernel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAGpD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAG5D,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,2BAA2B,iBA8DrC;AAED,eAAe,qBAAqB,CAAC"}
|
|
@@ -27,10 +27,12 @@ async function sharedKernelGenerator(tree, options) {
|
|
|
27
27
|
if (!tree.exists(contractsRoot)) {
|
|
28
28
|
console.log(`Creating contracts library at ${contractsRoot}...`);
|
|
29
29
|
await (0, _js.libraryGenerator)(tree, {
|
|
30
|
-
name: 'contracts',
|
|
30
|
+
name: options.prefix ? `${options.prefix}/shared-contracts` : 'shared-contracts',
|
|
31
31
|
directory: contractsRoot,
|
|
32
32
|
useProjectJson: false,
|
|
33
33
|
unitTestRunner: 'none',
|
|
34
|
+
bundler: options.bundler,
|
|
35
|
+
linter: options.linter,
|
|
34
36
|
tags: `${_types.LibraryScope.Shared},${_types.LibraryType.Contracts}`
|
|
35
37
|
});
|
|
36
38
|
} else {
|
|
@@ -39,9 +41,12 @@ async function sharedKernelGenerator(tree, options) {
|
|
|
39
41
|
if (!tree.exists(utilsRoot)) {
|
|
40
42
|
console.log(`Creating utils library at ${utilsRoot}...`);
|
|
41
43
|
await (0, _js.libraryGenerator)(tree, {
|
|
42
|
-
name: 'utils',
|
|
44
|
+
name: options.prefix ? `${options.prefix}/shared-utils` : 'shared-utils',
|
|
43
45
|
directory: utilsRoot,
|
|
44
46
|
useProjectJson: false,
|
|
47
|
+
unitTestRunner: options.unitTestRunner,
|
|
48
|
+
bundler: options.bundler,
|
|
49
|
+
linter: options.linter,
|
|
45
50
|
tags: `${_types.LibraryScope.Shared},${_types.LibraryType.Utils}`
|
|
46
51
|
});
|
|
47
52
|
} else {
|
|
@@ -50,9 +55,12 @@ async function sharedKernelGenerator(tree, options) {
|
|
|
50
55
|
if (!tree.exists(infrastructureRoot)) {
|
|
51
56
|
console.log(`Creating infrastructure library at ${infrastructureRoot}...`);
|
|
52
57
|
await (0, _js.libraryGenerator)(tree, {
|
|
53
|
-
name: 'infrastructure',
|
|
58
|
+
name: options.prefix ? `${options.prefix}/shared-infrastructure` : 'shared-infrastructure',
|
|
54
59
|
directory: infrastructureRoot,
|
|
55
60
|
useProjectJson: false,
|
|
61
|
+
unitTestRunner: options.unitTestRunner,
|
|
62
|
+
bundler: options.bundler,
|
|
63
|
+
linter: options.linter,
|
|
56
64
|
tags: `${_types.LibraryScope.Shared},${_types.LibraryType.Infrastructure}`
|
|
57
65
|
});
|
|
58
66
|
} else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../packages/nx/src/generators/shared-kernel/shared-kernel.ts"],"sourcesContent":["import { formatFiles, type Tree } from '@nx/devkit';\nimport { libraryGenerator } from '@nx/js';\n\nimport type { SharedKernelGeneratorSchema } from './schema';\nimport { LibraryScope, LibraryType } from '../../types';\n\nexport async function sharedKernelGenerator(\n tree: Tree,\n options: SharedKernelGeneratorSchema,\n) {\n const sharedDirectory = options.directory;\n const contractsRoot = `${sharedDirectory}/contracts`;\n const utilsRoot = `${sharedDirectory}/utils`;\n const infrastructureRoot = `${sharedDirectory}/infrastructure`;\n\n if (!tree.exists(contractsRoot)) {\n console.log(`Creating contracts library at ${contractsRoot}...`);\n\n await libraryGenerator(tree, {\n name: 'contracts',\n directory: contractsRoot,\n useProjectJson: false,\n unitTestRunner: 'none',\n tags: `${LibraryScope.Shared},${LibraryType.Contracts}`,\n });\n } else {\n console.log(`Contracts library already exists at ${contractsRoot}`);\n }\n\n if (!tree.exists(utilsRoot)) {\n console.log(`Creating utils library at ${utilsRoot}...`);\n\n await libraryGenerator(tree, {\n name: 'utils',\n directory: utilsRoot,\n useProjectJson: false,\n tags: `${LibraryScope.Shared},${LibraryType.Utils}`,\n });\n } else {\n console.log(`Utils library already exists at ${utilsRoot}`);\n }\n\n if (!tree.exists(infrastructureRoot)) {\n console.log(`Creating infrastructure library at ${infrastructureRoot}...`);\n\n await libraryGenerator(tree, {\n name: 'infrastructure',\n directory: infrastructureRoot,\n useProjectJson: false,\n tags: `${LibraryScope.Shared},${LibraryType.Infrastructure}`,\n });\n } else {\n console.log(\n `Infrastructure library already exists at ${infrastructureRoot}`,\n );\n }\n\n await formatFiles(tree);\n}\n\nexport default sharedKernelGenerator;\n"],"names":["sharedKernelGenerator","tree","options","sharedDirectory","directory","contractsRoot","utilsRoot","infrastructureRoot","exists","console","log","libraryGenerator","name","useProjectJson","unitTestRunner","tags","LibraryScope","Shared","LibraryType","Contracts","Utils","Infrastructure","formatFiles"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx/src/generators/shared-kernel/shared-kernel.ts"],"sourcesContent":["import { formatFiles, type Tree } from '@nx/devkit';\nimport { libraryGenerator } from '@nx/js';\n\nimport type { SharedKernelGeneratorSchema } from './schema';\nimport { LibraryScope, LibraryType } from '../../types';\n\nexport async function sharedKernelGenerator(\n tree: Tree,\n options: SharedKernelGeneratorSchema,\n) {\n const sharedDirectory = options.directory;\n const contractsRoot = `${sharedDirectory}/contracts`;\n const utilsRoot = `${sharedDirectory}/utils`;\n const infrastructureRoot = `${sharedDirectory}/infrastructure`;\n\n if (!tree.exists(contractsRoot)) {\n console.log(`Creating contracts library at ${contractsRoot}...`);\n\n await libraryGenerator(tree, {\n name: options.prefix\n ? `${options.prefix}/shared-contracts`\n : 'shared-contracts',\n directory: contractsRoot,\n useProjectJson: false,\n unitTestRunner: 'none',\n bundler: options.bundler,\n linter: options.linter,\n tags: `${LibraryScope.Shared},${LibraryType.Contracts}`,\n });\n } else {\n console.log(`Contracts library already exists at ${contractsRoot}`);\n }\n\n if (!tree.exists(utilsRoot)) {\n console.log(`Creating utils library at ${utilsRoot}...`);\n\n await libraryGenerator(tree, {\n name: options.prefix ? `${options.prefix}/shared-utils` : 'shared-utils',\n directory: utilsRoot,\n useProjectJson: false,\n unitTestRunner: options.unitTestRunner,\n bundler: options.bundler,\n linter: options.linter,\n tags: `${LibraryScope.Shared},${LibraryType.Utils}`,\n });\n } else {\n console.log(`Utils library already exists at ${utilsRoot}`);\n }\n\n if (!tree.exists(infrastructureRoot)) {\n console.log(`Creating infrastructure library at ${infrastructureRoot}...`);\n\n await libraryGenerator(tree, {\n name: options.prefix\n ? `${options.prefix}/shared-infrastructure`\n : 'shared-infrastructure',\n directory: infrastructureRoot,\n useProjectJson: false,\n unitTestRunner: options.unitTestRunner,\n bundler: options.bundler,\n linter: options.linter,\n tags: `${LibraryScope.Shared},${LibraryType.Infrastructure}`,\n });\n } else {\n console.log(\n `Infrastructure library already exists at ${infrastructureRoot}`,\n );\n }\n\n await formatFiles(tree);\n}\n\nexport default sharedKernelGenerator;\n"],"names":["sharedKernelGenerator","tree","options","sharedDirectory","directory","contractsRoot","utilsRoot","infrastructureRoot","exists","console","log","libraryGenerator","name","prefix","useProjectJson","unitTestRunner","bundler","linter","tags","LibraryScope","Shared","LibraryType","Contracts","Utils","Infrastructure","formatFiles"],"mappings":";;;;;;;;;;;QAwEA;eAAA;;QAlEsBA;eAAAA;;;wBANiB;oBACN;uBAGS;AAEnC,eAAeA,sBACpBC,IAAU,EACVC,OAAoC;IAEpC,MAAMC,kBAAkBD,QAAQE,SAAS;IACzC,MAAMC,gBAAgB,GAAGF,gBAAgB,UAAU,CAAC;IACpD,MAAMG,YAAY,GAAGH,gBAAgB,MAAM,CAAC;IAC5C,MAAMI,qBAAqB,GAAGJ,gBAAgB,eAAe,CAAC;IAE9D,IAAI,CAACF,KAAKO,MAAM,CAACH,gBAAgB;QAC/BI,QAAQC,GAAG,CAAC,CAAC,8BAA8B,EAAEL,cAAc,GAAG,CAAC;QAE/D,MAAMM,IAAAA,oBAAgB,EAACV,MAAM;YAC3BW,MAAMV,QAAQW,MAAM,GAChB,GAAGX,QAAQW,MAAM,CAAC,iBAAiB,CAAC,GACpC;YACJT,WAAWC;YACXS,gBAAgB;YAChBC,gBAAgB;YAChBC,SAASd,QAAQc,OAAO;YACxBC,QAAQf,QAAQe,MAAM;YACtBC,MAAM,GAAGC,mBAAY,CAACC,MAAM,CAAC,CAAC,EAAEC,kBAAW,CAACC,SAAS,EAAE;QACzD;IACF,OAAO;QACLb,QAAQC,GAAG,CAAC,CAAC,oCAAoC,EAAEL,eAAe;IACpE;IAEA,IAAI,CAACJ,KAAKO,MAAM,CAACF,YAAY;QAC3BG,QAAQC,GAAG,CAAC,CAAC,0BAA0B,EAAEJ,UAAU,GAAG,CAAC;QAEvD,MAAMK,IAAAA,oBAAgB,EAACV,MAAM;YAC3BW,MAAMV,QAAQW,MAAM,GAAG,GAAGX,QAAQW,MAAM,CAAC,aAAa,CAAC,GAAG;YAC1DT,WAAWE;YACXQ,gBAAgB;YAChBC,gBAAgBb,QAAQa,cAAc;YACtCC,SAASd,QAAQc,OAAO;YACxBC,QAAQf,QAAQe,MAAM;YACtBC,MAAM,GAAGC,mBAAY,CAACC,MAAM,CAAC,CAAC,EAAEC,kBAAW,CAACE,KAAK,EAAE;QACrD;IACF,OAAO;QACLd,QAAQC,GAAG,CAAC,CAAC,gCAAgC,EAAEJ,WAAW;IAC5D;IAEA,IAAI,CAACL,KAAKO,MAAM,CAACD,qBAAqB;QACpCE,QAAQC,GAAG,CAAC,CAAC,mCAAmC,EAAEH,mBAAmB,GAAG,CAAC;QAEzE,MAAMI,IAAAA,oBAAgB,EAACV,MAAM;YAC3BW,MAAMV,QAAQW,MAAM,GAChB,GAAGX,QAAQW,MAAM,CAAC,sBAAsB,CAAC,GACzC;YACJT,WAAWG;YACXO,gBAAgB;YAChBC,gBAAgBb,QAAQa,cAAc;YACtCC,SAASd,QAAQc,OAAO;YACxBC,QAAQf,QAAQe,MAAM;YACtBC,MAAM,GAAGC,mBAAY,CAACC,MAAM,CAAC,CAAC,EAAEC,kBAAW,CAACG,cAAc,EAAE;QAC9D;IACF,OAAO;QACLf,QAAQC,GAAG,CACT,CAAC,yCAAyC,EAAEH,oBAAoB;IAEpE;IAEA,MAAMkB,IAAAA,mBAAW,EAACxB;AACpB;MAEA,WAAeD"}
|
package/index.d.ts
CHANGED
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../packages/nx/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../packages/nx/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0CAA0C,CAAC;AACzD,cAAc,SAAS,CAAC"}
|
package/index.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
5
|
const _export_star = require("@swc/helpers/_/_export_star");
|
|
6
|
+
_export_star._(require("./generators/shared-kernel/shared-kernel"), exports);
|
|
6
7
|
_export_star._(require("./types"), exports);
|
|
7
8
|
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../packages/nx/src/index.ts"],"sourcesContent":["export * from './types';\n"],"names":[],"mappings":";;;;;uBAAc"}
|
|
1
|
+
{"version":3,"sources":["../../../packages/nx/src/index.ts"],"sourcesContent":["export * from './generators/shared-kernel/shared-kernel';\nexport * from './types';\n"],"names":[],"mappings":";;;;;uBAAc;uBACA"}
|