ddd-ts-kit 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.fr.md +1595 -0
- package/README.md +424 -5
- package/dist/check/business-names/closing-parenthesis.d.ts +3 -0
- package/dist/check/business-names/closing-parenthesis.d.ts.map +1 -0
- package/dist/check/business-names/closing-parenthesis.js +16 -0
- package/dist/check/business-names/closing-parenthesis.js.map +1 -0
- package/dist/check/business-names/declared-bindings.d.ts +4 -0
- package/dist/check/business-names/declared-bindings.d.ts.map +1 -0
- package/dist/check/business-names/declared-bindings.js +41 -0
- package/dist/check/business-names/declared-bindings.js.map +1 -0
- package/dist/check/business-names/generic-words-of.d.ts +4 -0
- package/dist/check/business-names/generic-words-of.d.ts.map +1 -0
- package/dist/check/business-names/generic-words-of.js +7 -0
- package/dist/check/business-names/generic-words-of.js.map +1 -0
- package/dist/check/business-names/generic-words.d.ts +7 -0
- package/dist/check/business-names/generic-words.d.ts.map +1 -0
- package/dist/check/business-names/generic-words.js +26 -0
- package/dist/check/business-names/generic-words.js.map +1 -0
- package/dist/check/business-names/identifier-at.d.ts +3 -0
- package/dist/check/business-names/identifier-at.d.ts.map +1 -0
- package/dist/check/business-names/identifier-at.js +6 -0
- package/dist/check/business-names/identifier-at.js.map +1 -0
- package/dist/check/business-names/judges-names.d.ts +7 -0
- package/dist/check/business-names/judges-names.d.ts.map +1 -0
- package/dist/check/business-names/judges-names.js +14 -0
- package/dist/check/business-names/judges-names.js.map +1 -0
- package/dist/check/business-names/named-binding.d.ts +12 -0
- package/dist/check/business-names/named-binding.d.ts.map +1 -0
- package/dist/check/business-names/named-binding.js +2 -0
- package/dist/check/business-names/named-binding.js.map +1 -0
- package/dist/check/business-names/named-bindings.d.ts +4 -0
- package/dist/check/business-names/named-bindings.d.ts.map +1 -0
- package/dist/check/business-names/named-bindings.js +8 -0
- package/dist/check/business-names/named-bindings.js.map +1 -0
- package/dist/check/business-names/opening-bracket.d.ts +3 -0
- package/dist/check/business-names/opening-bracket.d.ts.map +1 -0
- package/dist/check/business-names/opening-bracket.js +16 -0
- package/dist/check/business-names/opening-bracket.js.map +1 -0
- package/dist/check/business-names/parameter-bindings.d.ts +4 -0
- package/dist/check/business-names/parameter-bindings.d.ts.map +1 -0
- package/dist/check/business-names/parameter-bindings.js +28 -0
- package/dist/check/business-names/parameter-bindings.js.map +1 -0
- package/dist/check/business-names/parameter-lists.d.ts +14 -0
- package/dist/check/business-names/parameter-lists.d.ts.map +1 -0
- package/dist/check/business-names/parameter-lists.js +109 -0
- package/dist/check/business-names/parameter-lists.js.map +1 -0
- package/dist/check/business-names/pattern-bindings.d.ts +7 -0
- package/dist/check/business-names/pattern-bindings.d.ts.map +1 -0
- package/dist/check/business-names/pattern-bindings.js +31 -0
- package/dist/check/business-names/pattern-bindings.js.map +1 -0
- package/dist/check/business-names/property-bindings.d.ts +7 -0
- package/dist/check/business-names/property-bindings.d.ts.map +1 -0
- package/dist/check/business-names/property-bindings.js +31 -0
- package/dist/check/business-names/property-bindings.js.map +1 -0
- package/dist/check/business-names/suggested-name.d.ts +8 -0
- package/dist/check/business-names/suggested-name.d.ts.map +1 -0
- package/dist/check/business-names/suggested-name.js +28 -0
- package/dist/check/business-names/suggested-name.js.map +1 -0
- package/dist/check/business-names/top-level-separator.d.ts +3 -0
- package/dist/check/business-names/top-level-separator.d.ts.map +1 -0
- package/dist/check/business-names/top-level-separator.js +23 -0
- package/dist/check/business-names/top-level-separator.js.map +1 -0
- package/dist/check/catalogue/architecture-rules.d.ts.map +1 -1
- package/dist/check/catalogue/architecture-rules.js +2 -0
- package/dist/check/catalogue/architecture-rules.js.map +1 -1
- package/dist/check/explain/tree-docs.d.ts.map +1 -1
- package/dist/check/explain/tree-docs.js +11 -0
- package/dist/check/explain/tree-docs.js.map +1 -1
- package/dist/check/fixtures/business-name-fixtures.d.ts +3 -0
- package/dist/check/fixtures/business-name-fixtures.d.ts.map +1 -0
- package/dist/check/fixtures/business-name-fixtures.js +98 -0
- package/dist/check/fixtures/business-name-fixtures.js.map +1 -0
- package/dist/check/fixtures/fixtures.d.ts.map +1 -1
- package/dist/check/fixtures/fixtures.js +2 -0
- package/dist/check/fixtures/fixtures.js.map +1 -1
- package/dist/check/rules/naming-business-name.d.ts +3 -0
- package/dist/check/rules/naming-business-name.d.ts.map +1 -0
- package/dist/check/rules/naming-business-name.js +41 -0
- package/dist/check/rules/naming-business-name.js.map +1 -0
- package/dist/config/config.d.ts +8 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/parse-business-names.d.ts +4 -0
- package/dist/config/parse-business-names.d.ts.map +1 -0
- package/dist/config/parse-business-names.js +14 -0
- package/dist/config/parse-business-names.js.map +1 -0
- package/dist/config/parse-config.d.ts.map +1 -1
- package/dist/config/parse-config.js +2 -0
- package/dist/config/parse-config.js.map +1 -1
- package/dist/config/serialize-config.js +9 -1
- package/dist/config/serialize-config.js.map +1 -1
- package/dist/generate/kinds/query-kind.d.ts.map +1 -1
- package/dist/generate/kinds/query-kind.js +2 -1
- package/dist/generate/kinds/query-kind.js.map +1 -1
- package/dist/generate/kinds/route/write-controller.d.ts.map +1 -1
- package/dist/generate/kinds/route/write-controller.js +2 -1
- package/dist/generate/kinds/route/write-controller.js.map +1 -1
- package/dist/generate/kinds/route-kind.d.ts.map +1 -1
- package/dist/generate/kinds/route-kind.js +2 -1
- package/dist/generate/kinds/route-kind.js.map +1 -1
- package/dist/generate/searched-elements.d.ts +13 -0
- package/dist/generate/searched-elements.d.ts.map +1 -0
- package/dist/generate/searched-elements.js +63 -0
- package/dist/generate/searched-elements.js.map +1 -0
- package/dist/init/manifest/versions.js +24 -24
- package/dist/naming/index.d.ts +2 -0
- package/dist/naming/index.d.ts.map +1 -1
- package/dist/naming/index.js +2 -0
- package/dist/naming/index.js.map +1 -1
- package/dist/naming/plural.d.ts +3 -0
- package/dist/naming/plural.d.ts.map +1 -0
- package/dist/naming/plural.js +8 -0
- package/dist/naming/plural.js.map +1 -0
- package/dist/naming/singular.d.ts +3 -0
- package/dist/naming/singular.d.ts.map +1 -0
- package/dist/naming/singular.js +11 -0
- package/dist/naming/singular.js.map +1 -0
- package/dist/naming/to-name.d.ts.map +1 -1
- package/dist/naming/to-name.js +1 -6
- package/dist/naming/to-name.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/templates/command/handler.declared.ts.tpl +3 -3
- package/templates/command/handler.ts.tpl +3 -3
- package/templates/controller/controller.search.ts.tpl +1 -1
- package/templates/init/rules/application.md.tpl +1 -1
- package/templates/init/rules/domain.md.tpl +1 -1
- package/templates/init/scripts/events.ts.tpl +6 -6
- package/templates/init/scripts/openapi.ts.tpl +2 -2
- package/templates/pipeline/package-scripts.json +1 -1
- package/templates/policy/policy.declared.ts.tpl +1 -1
- package/templates/policy/policy.ts.tpl +1 -1
- package/templates/presets/identity/context/infrastructure/persistence/drizzle-search-members-reader.repository.drizzle.ts.tpl +2 -2
- package/templates/presets/identity/context/presentation/http/organisations/search-members/search-members.controller.ts.tpl +2 -2
- package/templates/presets/identity/context/test/integration/search-members-reader.spec.ts.tpl +1 -1
- package/templates/query/search.reader.fake.ts.tpl +1 -1
- package/templates/query/search.reader.in-memory.ts.tpl +20 -18
- package/templates/route/route.search.ts.tpl +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { plural, singular, toName } from '../naming/index.js';
|
|
2
|
+
/** Names the templates of a search already give to something else, and the reserved words of the language. */
|
|
3
|
+
const TAKEN = new Set([
|
|
4
|
+
'page',
|
|
5
|
+
'scope',
|
|
6
|
+
'visible',
|
|
7
|
+
'start',
|
|
8
|
+
'last',
|
|
9
|
+
'id',
|
|
10
|
+
'cursor',
|
|
11
|
+
'left',
|
|
12
|
+
'right',
|
|
13
|
+
'toward',
|
|
14
|
+
'field',
|
|
15
|
+
'value',
|
|
16
|
+
'reader',
|
|
17
|
+
'query',
|
|
18
|
+
'queries',
|
|
19
|
+
'request',
|
|
20
|
+
'input',
|
|
21
|
+
'line',
|
|
22
|
+
'new',
|
|
23
|
+
'delete',
|
|
24
|
+
'class',
|
|
25
|
+
'function',
|
|
26
|
+
'default',
|
|
27
|
+
'case',
|
|
28
|
+
'in',
|
|
29
|
+
'of',
|
|
30
|
+
'this',
|
|
31
|
+
'void',
|
|
32
|
+
'with',
|
|
33
|
+
'do',
|
|
34
|
+
'if',
|
|
35
|
+
'for',
|
|
36
|
+
'return',
|
|
37
|
+
'switch',
|
|
38
|
+
'interface',
|
|
39
|
+
'package',
|
|
40
|
+
'static',
|
|
41
|
+
'let',
|
|
42
|
+
'yield',
|
|
43
|
+
'await',
|
|
44
|
+
]);
|
|
45
|
+
/**
|
|
46
|
+
* The business words of a search, for the bindings that hold its lines: `invoice` and `invoices`
|
|
47
|
+
* for `SearchInvoices`, never `item` and `items`. A name the template already uses is prefixed:
|
|
48
|
+
* `foundPage`, `foundPages`.
|
|
49
|
+
*/
|
|
50
|
+
export function searchedElements(name) {
|
|
51
|
+
const words = name.words.split(' ');
|
|
52
|
+
const things = words[0] === 'search' && words.length > 1 ? words.slice(1) : words;
|
|
53
|
+
const last = things.at(-1) ?? '';
|
|
54
|
+
const one = singular(last);
|
|
55
|
+
const lead = things.slice(0, -1);
|
|
56
|
+
const element = toName([...lead, one ?? last].join(' ')).camel;
|
|
57
|
+
const elements = toName([...lead, one === undefined ? plural(last) : last].join(' ')).camel;
|
|
58
|
+
if (TAKEN.has(element) || TAKEN.has(elements)) {
|
|
59
|
+
return { element: `found${toName(element).pascal}`, elements: `found${toName(elements).pascal}` };
|
|
60
|
+
}
|
|
61
|
+
return { element, elements };
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=searched-elements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searched-elements.js","sourceRoot":"","sources":["../../src/generate/searched-elements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAa,MAAM,oBAAoB,CAAC;AAQzE,8GAA8G;AAC9G,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC;IACpB,MAAM;IACN,OAAO;IACP,SAAS;IACT,OAAO;IACP,MAAM;IACN,IAAI;IACJ,QAAQ;IACR,MAAM;IACN,OAAO;IACP,QAAQ;IACR,OAAO;IACP,OAAO;IACP,QAAQ;IACR,OAAO;IACP,SAAS;IACT,SAAS;IACT,OAAO;IACP,MAAM;IACN,KAAK;IACL,QAAQ;IACR,OAAO;IACP,UAAU;IACV,SAAS;IACT,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,MAAM;IACN,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,QAAQ;IACR,QAAQ;IACR,WAAW;IACX,SAAS;IACT,QAAQ;IACR,KAAK;IACL,OAAO;IACP,OAAO;CACR,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAU;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAClF,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/D,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;IAE5F,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9C,OAAO,EAAE,OAAO,EAAE,QAAQ,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IACpG,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
/** Ranges written to the generated package.json; bumped together with the kit. */
|
|
2
2
|
export const VERSIONS = {
|
|
3
|
-
'@ddd-ts-kit/kernel': '^0.
|
|
4
|
-
'@ddd-ts-kit/validation': '^0.
|
|
5
|
-
'@ddd-ts-kit/http': '^0.
|
|
6
|
-
'@ddd-ts-kit/tracing': '^0.
|
|
7
|
-
'@ddd-ts-kit/di': '^0.
|
|
8
|
-
'@ddd-ts-kit/events': '^0.
|
|
9
|
-
'@ddd-ts-kit/testing': '^0.
|
|
10
|
-
'@ddd-ts-kit/drizzle': '^0.
|
|
11
|
-
'@ddd-ts-kit/knex': '^0.
|
|
12
|
-
'@ddd-ts-kit/prisma': '^0.
|
|
13
|
-
'@ddd-ts-kit/typeorm': '^0.
|
|
14
|
-
'@ddd-ts-kit/auth': '^0.
|
|
15
|
-
'@ddd-ts-kit/rabbitmq': '^0.
|
|
16
|
-
'@ddd-ts-kit/aspects': '^0.
|
|
17
|
-
'@ddd-ts-kit/audit': '^0.
|
|
18
|
-
'@ddd-ts-kit/redis': '^0.
|
|
19
|
-
'@ddd-ts-kit/kafka': '^0.
|
|
20
|
-
'@ddd-ts-kit/nats': '^0.
|
|
21
|
-
'@ddd-ts-kit/scheduling': '^0.
|
|
22
|
-
'@ddd-ts-kit/sentry': '^0.
|
|
23
|
-
'@ddd-ts-kit/s3': '^0.
|
|
3
|
+
'@ddd-ts-kit/kernel': '^0.17.0',
|
|
4
|
+
'@ddd-ts-kit/validation': '^0.17.0',
|
|
5
|
+
'@ddd-ts-kit/http': '^0.17.0',
|
|
6
|
+
'@ddd-ts-kit/tracing': '^0.17.0',
|
|
7
|
+
'@ddd-ts-kit/di': '^0.17.0',
|
|
8
|
+
'@ddd-ts-kit/events': '^0.17.0',
|
|
9
|
+
'@ddd-ts-kit/testing': '^0.17.0',
|
|
10
|
+
'@ddd-ts-kit/drizzle': '^0.17.0',
|
|
11
|
+
'@ddd-ts-kit/knex': '^0.17.0',
|
|
12
|
+
'@ddd-ts-kit/prisma': '^0.17.0',
|
|
13
|
+
'@ddd-ts-kit/typeorm': '^0.17.0',
|
|
14
|
+
'@ddd-ts-kit/auth': '^0.17.0',
|
|
15
|
+
'@ddd-ts-kit/rabbitmq': '^0.17.0',
|
|
16
|
+
'@ddd-ts-kit/aspects': '^0.17.0',
|
|
17
|
+
'@ddd-ts-kit/audit': '^0.17.0',
|
|
18
|
+
'@ddd-ts-kit/redis': '^0.17.0',
|
|
19
|
+
'@ddd-ts-kit/kafka': '^0.17.0',
|
|
20
|
+
'@ddd-ts-kit/nats': '^0.17.0',
|
|
21
|
+
'@ddd-ts-kit/scheduling': '^0.17.0',
|
|
22
|
+
'@ddd-ts-kit/sentry': '^0.17.0',
|
|
23
|
+
'@ddd-ts-kit/s3': '^0.17.0',
|
|
24
24
|
'@aws-sdk/client-s3': '^3.1150.0',
|
|
25
25
|
'@aws-sdk/lib-storage': '^3.1150.0',
|
|
26
26
|
'@aws-sdk/s3-request-presigner': '^3.1150.0',
|
|
27
27
|
busboy: '^1.6.0',
|
|
28
28
|
'@sentry/node': '^11.6.0',
|
|
29
|
-
'@ddd-ts-kit/pino': '^0.
|
|
29
|
+
'@ddd-ts-kit/pino': '^0.17.0',
|
|
30
30
|
pino: '^10.4.0',
|
|
31
31
|
'pino-pretty': '^13.2.0',
|
|
32
|
-
'@ddd-ts-kit/winston': '^0.
|
|
32
|
+
'@ddd-ts-kit/winston': '^0.17.0',
|
|
33
33
|
winston: '^3.19.0',
|
|
34
34
|
'@confluentinc/kafka-javascript': '^1.11.0',
|
|
35
35
|
'@nats-io/jetstream': '^3.4.0',
|
|
@@ -37,7 +37,7 @@ export const VERSIONS = {
|
|
|
37
37
|
ioredis: '^6.0.0',
|
|
38
38
|
jose: '^6.0.0',
|
|
39
39
|
amqplib: '^2.2.0',
|
|
40
|
-
'ddd-ts-kit': '^0.
|
|
40
|
+
'ddd-ts-kit': '^0.17.0',
|
|
41
41
|
tsx: '^4.23.15',
|
|
42
42
|
vitest: '^5.0.3',
|
|
43
43
|
zod: '^4.6.5',
|
package/dist/naming/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export { looksLikePastFact } from './looks-like-past-fact.js';
|
|
|
6
6
|
export type { Name } from './name.js';
|
|
7
7
|
export { parseConceptName } from './parse-concept-name.js';
|
|
8
8
|
export { parseContextName } from './parse-context-name.js';
|
|
9
|
+
export { plural } from './plural.js';
|
|
10
|
+
export { singular } from './singular.js';
|
|
9
11
|
export { splitWords } from './split-words.js';
|
|
10
12
|
export { toName } from './to-name.js';
|
|
11
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/naming/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/naming/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/naming/index.js
CHANGED
|
@@ -5,6 +5,8 @@ export { eventName } from './event-name.js';
|
|
|
5
5
|
export { looksLikePastFact } from './looks-like-past-fact.js';
|
|
6
6
|
export { parseConceptName } from './parse-concept-name.js';
|
|
7
7
|
export { parseContextName } from './parse-context-name.js';
|
|
8
|
+
export { plural } from './plural.js';
|
|
9
|
+
export { singular } from './singular.js';
|
|
8
10
|
export { splitWords } from './split-words.js';
|
|
9
11
|
export { toName } from './to-name.js';
|
|
10
12
|
//# sourceMappingURL=index.js.map
|
package/dist/naming/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/naming/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/naming/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plural.d.ts","sourceRoot":"","sources":["../../src/naming/plural.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM3C"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** The plural of an English word, as the generator writes table names: `purchase_orders`, `deliveries`. */
|
|
2
|
+
export function plural(word) {
|
|
3
|
+
if (/[^aeiou]y$/.test(word)) {
|
|
4
|
+
return `${word.slice(0, -1)}ies`;
|
|
5
|
+
}
|
|
6
|
+
return /(?:s|x|z|ch|sh)$/.test(word) ? `${word}es` : `${word}s`;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=plural.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plural.js","sourceRoot":"","sources":["../../src/naming/plural.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IACnC,CAAC;IAED,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singular.d.ts","sourceRoot":"","sources":["../../src/naming/singular.ts"],"names":[],"mappings":"AAAA,6HAA6H;AAC7H,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUzD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The singular of a plural English word (`reminders`, `deliveries`, `boxes`), or undefined when it does not read as one. */
|
|
2
|
+
export function singular(word) {
|
|
3
|
+
if (/[^aeiou]ies$/.test(word)) {
|
|
4
|
+
return `${word.slice(0, -3)}y`;
|
|
5
|
+
}
|
|
6
|
+
if (/(?:ss|x|z|ch|sh)es$/.test(word)) {
|
|
7
|
+
return word.slice(0, -2);
|
|
8
|
+
}
|
|
9
|
+
return /[^su]s$/.test(word) ? word.slice(0, -1) : undefined;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=singular.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"singular.js","sourceRoot":"","sources":["../../src/naming/singular.ts"],"names":[],"mappings":"AAAA,6HAA6H;AAC7H,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IACjC,CAAC;IAED,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-name.d.ts","sourceRoot":"","sources":["../../src/naming/to-name.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"to-name.d.ts","sourceRoot":"","sources":["../../src/naming/to-name.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAItC,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAgB1C"}
|
package/dist/naming/to-name.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { capitalize } from './capitalize.js';
|
|
2
|
+
import { plural as pluralize } from './plural.js';
|
|
2
3
|
import { splitWords } from './split-words.js';
|
|
3
4
|
export function toName(input) {
|
|
4
5
|
const words = splitWords(input);
|
|
@@ -16,10 +17,4 @@ export function toName(input) {
|
|
|
16
17
|
tableCamel: plural.map((word, index) => (index === 0 ? word : capitalize(word))).join(''),
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
|
-
function pluralize(word) {
|
|
20
|
-
if (/[^aeiou]y$/.test(word)) {
|
|
21
|
-
return `${word.slice(0, -1)}ies`;
|
|
22
|
-
}
|
|
23
|
-
return /(?:s|x|z|ch|sh)$/.test(word) ? `${word}es` : `${word}s`;
|
|
24
|
-
}
|
|
25
20
|
//# sourceMappingURL=to-name.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-name.js","sourceRoot":"","sources":["../../src/naming/to-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,UAAU,MAAM,CAAC,KAAa;IAClC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAEtE,OAAO;QACL,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACvD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;QACtB,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE;QACvC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;QACtB,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG;QACrD,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;KAC1F,CAAC;AACJ,CAAC
|
|
1
|
+
{"version":3,"file":"to-name.js","sourceRoot":"","sources":["../../src/naming/to-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,UAAU,MAAM,CAAC,KAAa;IAClC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAEtE,OAAO;QACL,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACvD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;QACtB,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE;QACvC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;QACtB,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG;QACrD,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;KAC1F,CAAC;AACJ,CAAC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.17.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.17.0';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ddd-ts-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Bootstrap, scaffold and verify DDD and Clean Architecture TypeScript projects.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,6 +29,6 @@
|
|
|
29
29
|
"clean:packages": "node dist/bin.js check --clean-only ../kernel/src ../validation/src ../http/src ../tracing/src ../di/src ../events/src ../testing/src ../s3/src ../drizzle/src ../knex/src ../prisma/src ../typeorm/src ../aspects/src ../rabbitmq/src ../auth/src ../audit/src ../redis/src ../scheduling/src ../pino/src ../winston/src ../sentry/src ../kafka/src ../nats/src ../eslint-plugin/src"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@ddd-ts-kit/kernel": "^0.
|
|
32
|
+
"@ddd-ts-kit/kernel": "^0.17.0"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -20,11 +20,11 @@ export class {{name.pascal}}Handler implements CommandHandler<{{name.pascal}}Com
|
|
|
20
20
|
{{#if refusals.classes}}
|
|
21
21
|
// TODO(domain): decide what "{{name.words}}" does to a {{aggregate.pascal}}: either it gives birth to one (keep `create`, pass the facts of `command` as value objects), or it changes an existing one (load it with `findById`, throw a not-found DomainError, call ONE intention method). The events it records leave with the save: nothing else to do here.
|
|
22
22
|
{{/if}}
|
|
23
|
-
const
|
|
23
|
+
const {{aggregate.camel}} = {{aggregate.pascal}}.create(this.ids.next(), this.clock.now());
|
|
24
24
|
|
|
25
|
-
await this.{{aggregate.camel}}Repository.save(
|
|
25
|
+
await this.{{aggregate.camel}}Repository.save({{aggregate.camel}});
|
|
26
26
|
|
|
27
|
-
return
|
|
27
|
+
return {{aggregate.camel}}.id;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
{{/if}}
|
|
@@ -18,11 +18,11 @@ export class {{name.pascal}}Handler implements CommandHandler<{{name.pascal}}Com
|
|
|
18
18
|
{{#if refusals.classes}}
|
|
19
19
|
// TODO(domain): decide what "{{name.words}}" does to a {{aggregate.pascal}}: either it gives birth to one (keep `create`, pass the facts of `command` as value objects), or it changes an existing one (load it with `findById`, throw a not-found DomainError, call ONE intention method). The events it records leave with the save: nothing else to do here.
|
|
20
20
|
{{/if}}
|
|
21
|
-
const
|
|
21
|
+
const {{aggregate.camel}} = {{aggregate.pascal}}.create(this.ids.next(), this.clock.now());
|
|
22
22
|
|
|
23
|
-
await this.{{aggregate.camel}}Repository.save(
|
|
23
|
+
await this.{{aggregate.camel}}Repository.save({{aggregate.camel}});
|
|
24
24
|
|
|
25
|
-
return
|
|
25
|
+
return {{aggregate.camel}}.id;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
{{/if}}
|
|
@@ -90,7 +90,7 @@ export class {{name.pascal}}Controller {
|
|
|
90
90
|
// TODO(domain): hand the criteria of the query over too, once the query schema declares them next to the page (a request mapper is their place); map each line to its public fields.
|
|
91
91
|
const page = await this.queries.execute(new {{route.input}}(request.actor, request.query));
|
|
92
92
|
|
|
93
|
-
return pageResponse(page, (
|
|
93
|
+
return pageResponse(page, ({{search.element}}) => ({ id: {{search.element}}.id }));
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
{{#if search.customSchema}}
|
|
@@ -6,7 +6,7 @@ paths:
|
|
|
6
6
|
# Application layer
|
|
7
7
|
|
|
8
8
|
- Imports `domain/`, `application/`, published languages, `@ddd-ts-kit/kernel` and `@ddd-ts-kit/auth` only (`layer/application-inward`): never `infrastructure/`, `presentation/`, a framework or a driver.
|
|
9
|
-
- One use case per folder, `commands/<use-case>/` or `queries/<use-case>/`: the message, its handler, a `*.result.ts` when the answer has a shape of its own. The handler has one public method, `execute` (`application/handler-shape`): it loads or builds, calls ONE intention method, `await repository.save(aggregate)`, and returns at most an identifier.
|
|
9
|
+
- One use case per folder, `commands/<use-case>/` or `queries/<use-case>/`: the message, its handler, a `*.result.ts` when the answer has a shape of its own. The handler has one public method, `execute` (`application/handler-shape`): it loads or builds, calls ONE intention method, `await repository.save(aggregate)`, and returns at most an identifier. Each binding is named after the concept it holds, `const invoice = await this.invoiceRepository.findById(command.invoiceId)`, `for (const reminder of dueReminders)`, never `target`, `data` or `item` (`naming/business-name`).
|
|
10
10
|
- Events leave with the save: never `pullEvents()`, never a `DomainEventDispatcher` here (`domain-events/dispatched-on-save`). A reaction is a class of `event-handlers/` or a publisher of `integration/publishers/`. It runs at the end of the unit of work, before the commit, and depends only on what the transaction covers (`domain-events/transactional-before-commit`): repositories, the integration event publisher, the command bus. An effect outside the database is a handler of the `after-commit` phase (`generate event-handler ... --phase after-commit`: at most once, never saves, sends a command) or, when it must not be lost, the handler of an integration event.
|
|
11
11
|
- Nothing here reads the event store: an event-sourced aggregate is loaded and saved through its repository port like any other (`event-sourcing/store-behind-repository`). A projection writes its read model and never a stream (`event-sourcing/projection-never-appends`); one fed by the store is declared `@Projection(name, { feed: 'event-store' })`.
|
|
12
12
|
{{#if style.declared}}
|
|
@@ -7,7 +7,7 @@ paths:
|
|
|
7
7
|
# Domain layer
|
|
8
8
|
|
|
9
9
|
- Imports only its own `domain/`, the shared kernel and `@ddd-ts-kit/kernel` (`layer/domain-purity`): no framework, no decorator, no validation library, no `node:` module.
|
|
10
|
-
- Names come from the glossary of `CONTEXT.md`, written first (`language/glossary-term`); no `Manager`, `Helper`, `Data`, `Info`, `Model` (`language/no-technical-name`).
|
|
10
|
+
- Names come from the glossary of `CONTEXT.md`, written first (`language/glossary-term`); no `Manager`, `Helper`, `Data`, `Info`, `Model` (`language/no-technical-name`). A variable, a parameter or a property is named after the concept it holds, `appointment`, `calendar`, never `target`, `data`, `item` (`naming/business-name`).
|
|
11
11
|
- Aggregate: private constructor, `create` for birth, `rehydrate` for loading, state changed by methods named after the business intention, no setter, no public mutable property (`domain/aggregate-encapsulation`). One aggregate per transaction; another one is referenced by its id.
|
|
12
12
|
- A status that moves through a lifecycle is declared once as a state machine, `state-machines/<aggregate>.state-machine.ts` (`generate state-machine <context> <Aggregate> --value .. --transition ..`): the aggregate writes its status only from `machine.next(..)` or `machine.start(..)` (`lifecycle/status-through-machine`), and a forbidden transition is refused by a static factory of its error class (`refused`).
|
|
13
13
|
- A value object is a class extending `ValueObject` of the kernel, one per `value-objects/*.vo.ts` (`npx ddd-ts-kit generate value-object <context> <Name>`): a private constructor, static factories that check its rules (`create`, or named after what they build: `of`, `between`), its operations as methods that return a new instance. It is immutable and compared with `equals` (`domain/value-object-immutability`). An email, an amount, a period are types, not strings.{{#if values.classes}} A value is never a function that builds a plain object, nor an exported object literal (`domain/value-object-class`): the class compares by value, checks its invariants once and is recognised with `instanceof` by a mapper. A function of `domain/` or of the shared kernel that answers a record of data, `({ amount, currency })`, is reported; one that answers value objects it builds (`Money.of(..)`), derives a record with a spread, or answers functions (a specification) is not, and a decision is a refusal thrown by a factory or a value object.{{/if}}
|
|
@@ -9,7 +9,7 @@ import { contractFormat, stringifyContract } from 'ddd-ts-kit/dist/contracts.js'
|
|
|
9
9
|
// published language; with --check, fails when a committed file differs. Both files are build
|
|
10
10
|
// products: never edit them by hand.
|
|
11
11
|
const contextsDirectory = '{{config.contextsDir}}';
|
|
12
|
-
const
|
|
12
|
+
const catalog = path.resolve('docs/integration');
|
|
13
13
|
const format = contractFormat();
|
|
14
14
|
const other = format === 'yaml' ? 'asyncapi.json' : 'asyncapi.yaml';
|
|
15
15
|
|
|
@@ -34,17 +34,17 @@ const generated: Readonly<Record<string, string>> = {
|
|
|
34
34
|
if (process.argv.includes('--check')) {
|
|
35
35
|
reportStale();
|
|
36
36
|
} else {
|
|
37
|
-
fs.mkdirSync(
|
|
38
|
-
fs.rmSync(path.join(
|
|
37
|
+
fs.mkdirSync(catalog, { recursive: true });
|
|
38
|
+
fs.rmSync(path.join(catalog, other), { force: true });
|
|
39
39
|
|
|
40
40
|
for (const [file, content] of Object.entries(generated)) {
|
|
41
|
-
fs.writeFileSync(path.join(
|
|
41
|
+
fs.writeFileSync(path.join(catalog, file), content);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function reportStale(): void {
|
|
46
46
|
for (const [file, content] of Object.entries(generated)) {
|
|
47
|
-
const committed = fs.existsSync(path.join(
|
|
47
|
+
const committed = fs.existsSync(path.join(catalog, file)) ? fs.readFileSync(path.join(catalog, file), 'utf8') : '';
|
|
48
48
|
|
|
49
49
|
if (committed.replaceAll('\r\n', '\n') !== content) {
|
|
50
50
|
console.error('docs/integration/' + file + ' is out of date: run the script `docs:generate` and commit it.');
|
|
@@ -52,7 +52,7 @@ function reportStale(): void {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
if (fs.existsSync(path.join(
|
|
55
|
+
if (fs.existsSync(path.join(catalog, other))) {
|
|
56
56
|
console.error('docs/integration/' + other + ' is in the other format: run the script `docs:generate`.');
|
|
57
57
|
process.exitCode = 1;
|
|
58
58
|
}
|
|
@@ -65,8 +65,8 @@ if (process.argv.includes('--check')) {
|
|
|
65
65
|
|
|
66
66
|
function reportDrift(): void {
|
|
67
67
|
for (const [file, content] of generated) {
|
|
68
|
-
const
|
|
69
|
-
const committed = fs.existsSync(
|
|
68
|
+
const contract = path.join(directory, file);
|
|
69
|
+
const committed = fs.existsSync(contract) ? fs.readFileSync(contract, 'utf8') : '';
|
|
70
70
|
|
|
71
71
|
if (committed.replaceAll('\r\n', '\n') !== content) {
|
|
72
72
|
console.error(`openapi/${file} is out of date: run the script \`docs:generate\` and commit it.`);
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@biomejs/biome": "2.5.15",
|
|
24
24
|
"@commitlint/cli": "^21.2.3",
|
|
25
25
|
"@commitlint/config-conventional": "^21.2.3",
|
|
26
|
-
"@ddd-ts-kit/eslint-plugin": "^0.
|
|
26
|
+
"@ddd-ts-kit/eslint-plugin": "^0.17.0",
|
|
27
27
|
"@eslint/js": "^10.0.1",
|
|
28
28
|
"@typescript/native": "npm:typescript@~7.0.2",
|
|
29
29
|
"@vitest/coverage-v8": "5.0.3",
|
|
@@ -2,7 +2,7 @@ import { isAuthenticated, type Actor } from '@ddd-ts-kit/kernel';
|
|
|
2
2
|
import { Policy } from '@ddd-ts-kit/kernel/declarations';
|
|
3
3
|
import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
|
|
4
4
|
|
|
5
|
-
// TODO(domain): give `allows` the aggregate "{{name.words}}" reads as its second parameter, add `appliesTo(subject: unknown): boolean { return subject instanceof <Aggregate>; }`, and decide from the actor, the loaded element and `now`; the handler asks it through `Access.authorize(command.actor, permission, element)
|
|
5
|
+
// TODO(domain): give `allows` the aggregate "{{name.words}}" reads as its second parameter, add `appliesTo(subject: unknown): boolean { return subject instanceof <Aggregate>; }`, and decide from the actor, the loaded element and `now`; the handler asks it through `Access.authorize(command.actor, permission, <aggregate>)`, the loaded aggregate named after its concept (`invoice`, never `element`).
|
|
6
6
|
@Policy({{context.camel}}Permissions['{{access.permission}}'], { name: '{{context.kebab}}.{{name.kebab}}' })
|
|
7
7
|
export class {{name.pascal}}Policy {
|
|
8
8
|
allows(actor: Actor): boolean {
|
|
@@ -2,7 +2,7 @@ import { definePolicy } from '@ddd-ts-kit/auth';
|
|
|
2
2
|
import { isAuthenticated } from '@ddd-ts-kit/kernel';
|
|
3
3
|
import { {{context.camel}}Permissions } from '{{alias.context}}/application/authorization/{{context.kebab}}.permissions{{alias.ext}}';
|
|
4
4
|
|
|
5
|
-
// TODO(domain): replace `unknown` by the aggregate "{{name.words}}" reads, add `appliesTo: (subject): subject is <Aggregate> => subject instanceof <Aggregate>`, and decide from the actor, the loaded element and `now`; the handler asks it through `Access.authorize(command.actor, permission, element)
|
|
5
|
+
// TODO(domain): replace `unknown` by the aggregate "{{name.words}}" reads, add `appliesTo: (subject): subject is <Aggregate> => subject instanceof <Aggregate>`, and decide from the actor, the loaded element and `now`; the handler asks it through `Access.authorize(command.actor, permission, <aggregate>)`, the loaded aggregate named after its concept (`invoice`, never `element`).
|
|
6
6
|
export const {{name.camel}} = definePolicy<unknown>({
|
|
7
7
|
name: '{{name.kebab}}',
|
|
8
8
|
permission: {{context.camel}}Permissions['{{access.permission}}'],
|
|
@@ -22,7 +22,7 @@ export class DrizzleSearchMembersReader implements SearchMembersReader {
|
|
|
22
22
|
const order = page.sort[0]?.direction === 'desc' ? desc : asc;
|
|
23
23
|
const current = and(eq(members.organisationId, organisationId), isNull(members.removedAt));
|
|
24
24
|
|
|
25
|
-
const
|
|
25
|
+
const currentMembers = await executorOf(this.database)
|
|
26
26
|
.select({
|
|
27
27
|
memberId: members.id,
|
|
28
28
|
accountId: members.accountId,
|
|
@@ -40,7 +40,7 @@ export class DrizzleSearchMembersReader implements SearchMembersReader {
|
|
|
40
40
|
|
|
41
41
|
const [counted] = await executorOf(this.database).select({ total: count() }).from(members).where(current);
|
|
42
42
|
|
|
43
|
-
return pageOf(
|
|
43
|
+
return pageOf(currentMembers, { total: counted?.total ?? 0 });
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -81,6 +81,6 @@ export class SearchMembersController {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function toLine(
|
|
85
|
-
return { ...
|
|
84
|
+
function toLine(member: SearchMembersItem): SearchMembersLine {
|
|
85
|
+
return { ...member, joinedAt: member.joinedAt.toISOString() };
|
|
86
86
|
}
|
package/templates/presets/identity/context/test/integration/search-members-reader.spec.ts.tpl
CHANGED
|
@@ -76,7 +76,7 @@ describe('{{#if orm.knex}}Knex{{/if}}{{#if orm.drizzle}}Drizzle{{/if}}SearchMemb
|
|
|
76
76
|
|
|
77
77
|
const page = await reader.search('acme', offsetPage({ limit: 1, offset: 1, sort: [{ field: 'joinedAt', direction: 'desc' }] }));
|
|
78
78
|
|
|
79
|
-
expect(page.items.map((
|
|
79
|
+
expect(page.items.map((member) => member.memberId)).toEqual(['member-1']);
|
|
80
80
|
expect(page.total).toBe(2);
|
|
81
81
|
});
|
|
82
82
|
});
|
|
@@ -2,7 +2,7 @@ import type { {{name.pascal}}Item } from '{{alias.context}}/application/queries/
|
|
|
2
2
|
import { InMemory{{name.pascal}}Reader } from '{{alias.context}}/infrastructure/persistence/{{reader.file}}{{alias.ext}}';
|
|
3
3
|
|
|
4
4
|
export class Fake{{name.pascal}}Reader extends InMemory{{name.pascal}}Reader {
|
|
5
|
-
constructor(protected override readonly
|
|
5
|
+
constructor(protected override readonly {{search.elements}}: readonly {{name.pascal}}Item[]) {
|
|
6
6
|
super();
|
|
7
7
|
}
|
|
8
8
|
}
|
|
@@ -17,38 +17,40 @@ import type {
|
|
|
17
17
|
@Repository({{name.pascal}}Reader)
|
|
18
18
|
{{/if}}
|
|
19
19
|
export class InMemory{{name.pascal}}Reader implements {{name.pascal}}Reader {
|
|
20
|
-
protected readonly
|
|
20
|
+
protected readonly {{search.elements}}: readonly {{name.pascal}}Item[] = [];
|
|
21
21
|
|
|
22
22
|
async search(scope: AccessScope, page: PageRequest<{{name.pascal}}Sort>): Promise<{{name.pascal}}Result> {
|
|
23
23
|
// TODO(domain): replace these canned lines by a real read of the projection; a production adapter (SQL view, search index) implements the same port next to this file, turns the scope into a condition of its statement, and orders by the sort field then by `id` in the same direction, so that a cursor resumes on a total order.
|
|
24
|
-
const visible = this.
|
|
24
|
+
const visible = this.{{search.elements}}.filter(isVisibleTo(scope)).sort(inOrder(page));
|
|
25
25
|
const start = page.kind === 'offset' ? page.offset : positionAfter(visible, page.cursor);
|
|
26
|
-
const
|
|
26
|
+
const {{search.elements}} = visible.slice(start, start + page.limit);
|
|
27
27
|
|
|
28
28
|
if (page.kind === 'offset') {
|
|
29
|
-
return pageOf(
|
|
29
|
+
return pageOf({{search.elements}}, { total: visible.length });
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const last =
|
|
32
|
+
const last = {{search.elements}}.at(-1);
|
|
33
33
|
|
|
34
34
|
return pageOf(
|
|
35
|
-
|
|
35
|
+
{{search.elements}},
|
|
36
36
|
last !== undefined && start + page.limit < visible.length ? { nextCursor: encodeCursor({ id: last.id }) } : {},
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
function isVisibleTo(scope: AccessScope): ({{search.element}}: {{name.pascal}}Item) => boolean {
|
|
42
|
+
return ({{search.element}}) => {
|
|
43
|
+
switch (scope.kind) {
|
|
44
|
+
case 'all':
|
|
45
|
+
return true;
|
|
46
|
+
case 'none':
|
|
47
|
+
return false;
|
|
48
|
+
case 'ids':
|
|
49
|
+
return scope.ids.includes({{search.element}}.id);
|
|
50
|
+
case 'filter':
|
|
51
|
+
return Object.entries(scope.filter).every(([field, value]) => Reflect.get({{search.element}}, field) === value);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
function inOrder(page: PageRequest<{{name.pascal}}Sort>): (left: {{name.pascal}}Item, right: {{name.pascal}}Item) => number {
|
|
@@ -68,5 +70,5 @@ function positionAfter(visible: readonly {{name.pascal}}Item[], cursor: string |
|
|
|
68
70
|
throw new InvalidPageRequestError('The cursor of a page is the nextCursor of a previous page');
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
return visible.findIndex((
|
|
73
|
+
return visible.findIndex(({{search.element}}) => {{search.element}}.id === id) + 1;
|
|
72
74
|
}
|
|
@@ -84,7 +84,7 @@ export function {{name.camel}}Route(queries: QueryBus): Route {
|
|
|
84
84
|
// TODO(domain): hand the criteria of the query over too, once the query schema declares them next to the page; map each line to its public fields.
|
|
85
85
|
const page = await queries.execute(new {{route.input}}(actorOf(input.context.principal), input.query));
|
|
86
86
|
|
|
87
|
-
return { status: 200, body: pageResponse(page, (
|
|
87
|
+
return { status: 200, body: pageResponse(page, ({{search.element}}) => ({ id: {{search.element}}.id })) };
|
|
88
88
|
},
|
|
89
89
|
);
|
|
90
90
|
}
|