@pattern-stack/codegen 0.6.0 → 0.6.1
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/CHANGELOG.md +12 -0
- package/package.json +6 -1
- package/src/config/case-converters.mjs +181 -0
- package/src/config/config-loader.mjs +34 -0
- package/src/config/locations.mjs +298 -0
- package/src/config/naming-config.mjs +173 -0
- package/src/config/paths.mjs +690 -0
- package/src/patterns/library/activity.pattern.ts +32 -0
- package/src/patterns/library/base.pattern.ts +28 -0
- package/src/patterns/library/index.ts +30 -0
- package/src/patterns/library/knowledge.pattern.ts +31 -0
- package/src/patterns/library/metadata.pattern.ts +31 -0
- package/src/patterns/library/synced.pattern.ts +34 -0
- package/src/patterns/pattern-definition.ts +280 -0
- package/src/patterns/registry.ts +365 -0
- package/src/schema/naming-config.schema.mjs +119 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ActivityPattern — replaces `family: activity`.
|
|
3
|
+
*
|
|
4
|
+
* Activity entities represent time-bounded interactions (calls, meetings,
|
|
5
|
+
* emails). The base repository/service expose date-range + opportunity +
|
|
6
|
+
* user-scoped lookups on top of the standard CRUD methods.
|
|
7
|
+
*
|
|
8
|
+
* Class names, import paths, and inherited-method strings match the
|
|
9
|
+
* legacy `FAMILY_MAP` entry verbatim so PATTERN-5's template swap produces
|
|
10
|
+
* byte-identical output.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { definePattern } from '../pattern-definition.js';
|
|
14
|
+
|
|
15
|
+
export const ActivityPattern = definePattern({
|
|
16
|
+
name: 'Activity',
|
|
17
|
+
extends: ['Base'],
|
|
18
|
+
repositoryClass: 'ActivityEntityRepository',
|
|
19
|
+
serviceClass: 'ActivityEntityService',
|
|
20
|
+
repositoryImport: '@shared/base-classes/activity-entity-repository',
|
|
21
|
+
serviceImport: '@shared/base-classes/activity-entity-service',
|
|
22
|
+
repositoryInheritedMethods: [
|
|
23
|
+
'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
|
|
24
|
+
'findByDateRange, findByUserId, findByOpportunityId, findRecentByOpportunityId',
|
|
25
|
+
],
|
|
26
|
+
serviceInheritedMethods: [
|
|
27
|
+
'findById, findByIds, list, count, exists, create, update, delete',
|
|
28
|
+
'findByDateRange, findByUserId, findByOpportunityId, findRecentByOpportunityId',
|
|
29
|
+
],
|
|
30
|
+
description:
|
|
31
|
+
'Time-bounded interaction entities — date-range + opportunity scoped lookups',
|
|
32
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BasePattern — identity pattern for the `extends` chain.
|
|
3
|
+
*
|
|
4
|
+
* Contributes no columns, no implied behaviors, and no config. Its only
|
|
5
|
+
* purpose is to anchor the inheritance hierarchy so every other pattern
|
|
6
|
+
* can declare `extends: ['Base']` and codegen can resolve that to a
|
|
7
|
+
* concrete `BaseRepository` / `BaseService` reference.
|
|
8
|
+
*
|
|
9
|
+
* Matches the existing `family: base` entry in
|
|
10
|
+
* `templates/entity/new/clean-lite-ps/prompt-extension.js` verbatim.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { definePattern } from '../pattern-definition.js';
|
|
14
|
+
|
|
15
|
+
export const BasePattern = definePattern({
|
|
16
|
+
name: 'Base',
|
|
17
|
+
repositoryClass: 'BaseRepository',
|
|
18
|
+
serviceClass: 'BaseService',
|
|
19
|
+
repositoryImport: '@shared/base-classes/base-repository',
|
|
20
|
+
serviceImport: '@shared/base-classes/base-service',
|
|
21
|
+
repositoryInheritedMethods: [
|
|
22
|
+
'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
|
|
23
|
+
],
|
|
24
|
+
serviceInheritedMethods: [
|
|
25
|
+
'findById, findByIds, list, count, exists, create, update, delete',
|
|
26
|
+
],
|
|
27
|
+
description: 'Identity pattern — base CRUD, no extra columns or methods',
|
|
28
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library pattern bootstrap — imports every shipped pattern and registers
|
|
3
|
+
* it with the shared library registry. Side-effect-only module: importing
|
|
4
|
+
* this barrel is what pre-registers `Base`, `Synced`, `Activity`,
|
|
5
|
+
* `Knowledge`, and `Metadata`.
|
|
6
|
+
*
|
|
7
|
+
* Adding a new library pattern is two edits: create the `*.pattern.ts`
|
|
8
|
+
* file and add the import+register pair below.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { registerLibraryPattern } from '../registry.js';
|
|
12
|
+
import { ActivityPattern } from './activity.pattern.js';
|
|
13
|
+
import { BasePattern } from './base.pattern.js';
|
|
14
|
+
import { KnowledgePattern } from './knowledge.pattern.js';
|
|
15
|
+
import { MetadataPattern } from './metadata.pattern.js';
|
|
16
|
+
import { SyncedPattern } from './synced.pattern.js';
|
|
17
|
+
|
|
18
|
+
registerLibraryPattern(BasePattern);
|
|
19
|
+
registerLibraryPattern(SyncedPattern);
|
|
20
|
+
registerLibraryPattern(ActivityPattern);
|
|
21
|
+
registerLibraryPattern(KnowledgePattern);
|
|
22
|
+
registerLibraryPattern(MetadataPattern);
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
ActivityPattern,
|
|
26
|
+
BasePattern,
|
|
27
|
+
KnowledgePattern,
|
|
28
|
+
MetadataPattern,
|
|
29
|
+
SyncedPattern,
|
|
30
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KnowledgePattern — replaces `family: knowledge`.
|
|
3
|
+
*
|
|
4
|
+
* Knowledge entities hold long-form content with a workflow status and
|
|
5
|
+
* semantic-search support (vectors, pending/approved states). The base
|
|
6
|
+
* classes expose `semanticSearch`, pending-by-opportunity lookups, and
|
|
7
|
+
* batch status updates.
|
|
8
|
+
*
|
|
9
|
+
* Class names, import paths, and inherited-method strings match the
|
|
10
|
+
* legacy `FAMILY_MAP` entry verbatim.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { definePattern } from '../pattern-definition.js';
|
|
14
|
+
|
|
15
|
+
export const KnowledgePattern = definePattern({
|
|
16
|
+
name: 'Knowledge',
|
|
17
|
+
extends: ['Base'],
|
|
18
|
+
repositoryClass: 'KnowledgeEntityRepository',
|
|
19
|
+
serviceClass: 'KnowledgeEntityService',
|
|
20
|
+
repositoryImport: '@shared/base-classes/knowledge-entity-repository',
|
|
21
|
+
serviceImport: '@shared/base-classes/knowledge-entity-service',
|
|
22
|
+
repositoryInheritedMethods: [
|
|
23
|
+
'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
|
|
24
|
+
'semanticSearch, findPendingByOpportunityId, updateStatus, updateStatusBatch',
|
|
25
|
+
],
|
|
26
|
+
serviceInheritedMethods: [
|
|
27
|
+
'findById, findByIds, list, count, exists, create, update, delete',
|
|
28
|
+
'semanticSearch, findPendingByOpportunityId, updateStatus, updateStatusBatch',
|
|
29
|
+
],
|
|
30
|
+
description: 'Knowledge entities — semantic search + workflow status',
|
|
31
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MetadataPattern — replaces `family: metadata`.
|
|
3
|
+
*
|
|
4
|
+
* Metadata entities represent history-tracked auxiliary rows attached to a
|
|
5
|
+
* parent entity (audit trails, custom-field values, change logs). The base
|
|
6
|
+
* classes expose entity-id + type scoped lookups and history listing.
|
|
7
|
+
*
|
|
8
|
+
* Class names, import paths, and inherited-method strings match the
|
|
9
|
+
* legacy `FAMILY_MAP` entry verbatim.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { definePattern } from '../pattern-definition.js';
|
|
13
|
+
|
|
14
|
+
export const MetadataPattern = definePattern({
|
|
15
|
+
name: 'Metadata',
|
|
16
|
+
extends: ['Base'],
|
|
17
|
+
repositoryClass: 'MetadataEntityRepository',
|
|
18
|
+
serviceClass: 'MetadataEntityService',
|
|
19
|
+
repositoryImport: '@shared/base-classes/metadata-entity-repository',
|
|
20
|
+
serviceImport: '@shared/base-classes/metadata-entity-service',
|
|
21
|
+
repositoryInheritedMethods: [
|
|
22
|
+
'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
|
|
23
|
+
'findByEntityIdAndType, listByEntityId, listHistoryByEntityId',
|
|
24
|
+
],
|
|
25
|
+
serviceInheritedMethods: [
|
|
26
|
+
'findById, findByIds, list, count, exists, create, update, delete',
|
|
27
|
+
'findByEntityIdAndType, listByEntityId, listHistoryByEntityId',
|
|
28
|
+
],
|
|
29
|
+
description:
|
|
30
|
+
'History-tracked metadata rows — entity-id + type scoped lookups',
|
|
31
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SyncedPattern — adds external-system sync columns and methods.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the legacy `family: synced` entry in
|
|
5
|
+
* `templates/entity/new/clean-lite-ps/prompt-extension.js`. Class names,
|
|
6
|
+
* import paths, and inherited-method comment lines are preserved verbatim
|
|
7
|
+
* so PATTERN-5's template swap produces byte-identical output for
|
|
8
|
+
* pre-existing `family: synced` fixtures.
|
|
9
|
+
*
|
|
10
|
+
* Implies `external_id_tracking` — the behavior that contributes the
|
|
11
|
+
* `external_id`, `provider`, and `provider_metadata` columns to the table.
|
|
12
|
+
* An entity declaring `pattern: Synced` need not re-declare the behavior.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { definePattern } from '../pattern-definition.js';
|
|
16
|
+
|
|
17
|
+
export const SyncedPattern = definePattern({
|
|
18
|
+
name: 'Synced',
|
|
19
|
+
extends: ['Base'],
|
|
20
|
+
repositoryClass: 'SyncedEntityRepository',
|
|
21
|
+
serviceClass: 'SyncedEntityService',
|
|
22
|
+
repositoryImport: '@shared/base-classes/synced-entity-repository',
|
|
23
|
+
serviceImport: '@shared/base-classes/synced-entity-service',
|
|
24
|
+
repositoryInheritedMethods: [
|
|
25
|
+
'findById, findByIds, list, count, exists, create, update, delete, upsertMany',
|
|
26
|
+
'findByExternalId, findAllByUserId, findVisibleByUserId, syncUpsert',
|
|
27
|
+
],
|
|
28
|
+
serviceInheritedMethods: [
|
|
29
|
+
'findById, findByIds, list, count, exists, create, update, delete',
|
|
30
|
+
'findByExternalId, findAllByUserId, findVisibleByUserId',
|
|
31
|
+
],
|
|
32
|
+
impliedBehaviors: ['external_id_tracking'],
|
|
33
|
+
description: 'External CRM/system sync columns and syncUpsert methods',
|
|
34
|
+
});
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Definition — pure metadata record returned by an identity function.
|
|
3
|
+
*
|
|
4
|
+
* `definePattern()` is the registration artifact for both library-shipped and
|
|
5
|
+
* app-defined patterns. It carries only names + import paths for the classes
|
|
6
|
+
* a generated entity should extend — never the class constructors themselves.
|
|
7
|
+
* This keeps the codegen pipeline free of TS class-evaluation cost and avoids
|
|
8
|
+
* `reflect-metadata`, which lets the Hygen subprocess cheaply rebuild the
|
|
9
|
+
* registry (see `src/cli/shared/hygen.ts` — the registry loads twice per
|
|
10
|
+
* `entity new` invocation).
|
|
11
|
+
*
|
|
12
|
+
* Two pattern kinds share this surface:
|
|
13
|
+
* - **domain** (default; ADR-031) — `PatternDefinition`. Contributes
|
|
14
|
+
* repository/service base classes, columns, behaviors to entities that
|
|
15
|
+
* declare `pattern:`/`patterns:` in YAML.
|
|
16
|
+
* - **orchestration** (ADR-032) — `OrchestrationPatternDefinition`. Declares
|
|
17
|
+
* a DI registry + optional dispatcher scaffold. Not entity-attached;
|
|
18
|
+
* codegen emits a NestJS module under `src/orchestration/` instead.
|
|
19
|
+
*
|
|
20
|
+
* See `docs/adrs/ADR-031-app-defined-patterns.md` §"Decision 1" for the
|
|
21
|
+
* domain binding surface and `docs/adrs/ADR-032-orchestration-patterns.md`
|
|
22
|
+
* for the orchestration kind.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import type { ZodSchema } from 'zod';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A column a pattern contributes to every entity that declares it.
|
|
29
|
+
*
|
|
30
|
+
* Column-level conflicts between patterns, between a pattern and an
|
|
31
|
+
* entity-declared field, or between a pattern and a behavior-contributed
|
|
32
|
+
* field are codegen-time hard errors; see
|
|
33
|
+
* `src/patterns/validate-composition.ts`.
|
|
34
|
+
*/
|
|
35
|
+
export interface PatternColumnContribution {
|
|
36
|
+
/** snake_case column name — matches the database column */
|
|
37
|
+
name: string;
|
|
38
|
+
/** Drizzle column type string, e.g. "varchar(255)" or "text" */
|
|
39
|
+
type: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Discriminator for the two pattern shapes. Default is `"domain"` to preserve
|
|
44
|
+
* Phase 1 (ADR-031) behaviour — every existing PatternDefinition without a
|
|
45
|
+
* `kind` field continues to register as a domain pattern.
|
|
46
|
+
*/
|
|
47
|
+
export type PatternKind = 'domain' | 'orchestration';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The full pattern metadata record. Every `definePattern({...})` call
|
|
51
|
+
* returns a value of this shape; the library and consumer registries
|
|
52
|
+
* store these and look them up by `name`.
|
|
53
|
+
*/
|
|
54
|
+
export interface PatternDefinition<TConfig = unknown> {
|
|
55
|
+
/** Unique name used in YAML — e.g. `pattern: Synced` */
|
|
56
|
+
name: string;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* ADR-032: defaults to `"domain"`. Phase 3 adds `"orchestration"` as a
|
|
60
|
+
* disjoint shape (see `OrchestrationPatternDefinition`). Domain
|
|
61
|
+
* `PatternDefinition` instances must omit this field or set it to
|
|
62
|
+
* `"domain"`; the loader routes orchestration values to a separate map.
|
|
63
|
+
*/
|
|
64
|
+
kind?: 'domain';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Built-in patterns this extends, by name. Phase 1 supports single-depth
|
|
68
|
+
* chains only — a pattern may `extends: ['Synced']` but the transitive
|
|
69
|
+
* chain is not yet resolved. Multi-depth inheritance is deferred until
|
|
70
|
+
* a real consumer asks.
|
|
71
|
+
*/
|
|
72
|
+
extends?: string[];
|
|
73
|
+
|
|
74
|
+
/** Constructor name codegen emits in the generated repo's `extends` clause */
|
|
75
|
+
repositoryClass?: string;
|
|
76
|
+
/** Constructor name codegen emits in the generated service's `extends` clause */
|
|
77
|
+
serviceClass?: string;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Fully-qualified TypeScript path alias the consumer's tsconfig resolves.
|
|
81
|
+
* Library patterns use the consumer-installed runtime base class path
|
|
82
|
+
* (e.g. `@shared/base-classes/synced-entity-repository`); app patterns
|
|
83
|
+
* use whatever alias the consumer has configured (e.g. `@/patterns/...`).
|
|
84
|
+
*/
|
|
85
|
+
repositoryImport?: string;
|
|
86
|
+
/** Same as `repositoryImport` but for the service base class */
|
|
87
|
+
serviceImport?: string;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Documentation-only method-signature strings emitted as comments in the
|
|
91
|
+
* generated repo. Exist purely so app authors reading the generated file
|
|
92
|
+
* see what their concrete class inherits without jumping to the base.
|
|
93
|
+
*/
|
|
94
|
+
repositoryInheritedMethods?: string[];
|
|
95
|
+
/** Same as `repositoryInheritedMethods` but for the service base class */
|
|
96
|
+
serviceInheritedMethods?: string[];
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Columns this pattern adds to every entity that declares it. Used by
|
|
100
|
+
* the composition validator to detect column-name collisions.
|
|
101
|
+
*/
|
|
102
|
+
columns?: PatternColumnContribution[];
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Behaviors this pattern implicitly enables. Entity YAML need not
|
|
106
|
+
* re-declare them; duplicates across patterns are silent-deduped.
|
|
107
|
+
*/
|
|
108
|
+
impliedBehaviors?: string[];
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Zod schema that validates the per-entity `config:` block for this
|
|
112
|
+
* pattern at parse time. When absent, entities may not supply a `config:`
|
|
113
|
+
* entry for this pattern and codegen emits no `patternConfig` property.
|
|
114
|
+
*/
|
|
115
|
+
configSchema?: ZodSchema<TConfig>;
|
|
116
|
+
|
|
117
|
+
/** One-line description for codegen help output and error messages */
|
|
118
|
+
description?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Identity function that returns its argument unchanged. The body is trivial
|
|
123
|
+
* on purpose — the whole point is to give TypeScript a hook for generic
|
|
124
|
+
* inference on `TConfig` while leaving the runtime value a plain object
|
|
125
|
+
* registered by the codegen loader.
|
|
126
|
+
*/
|
|
127
|
+
export function definePattern<TConfig = unknown>(
|
|
128
|
+
def: PatternDefinition<TConfig>,
|
|
129
|
+
): PatternDefinition<TConfig> {
|
|
130
|
+
return def;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Shape check for values produced by `import()`ing an app pattern file.
|
|
135
|
+
* The registry loader runs this on every exported value it finds; only
|
|
136
|
+
* values that pass are registered.
|
|
137
|
+
*
|
|
138
|
+
* We keep this deliberately loose — a `name` string is the whole
|
|
139
|
+
* requirement — because a pattern that contributes neither columns nor
|
|
140
|
+
* class references is still a *valid* identity pattern (e.g. `BasePattern`
|
|
141
|
+
* exists to anchor the `extends` chain without contributing anything).
|
|
142
|
+
* Stricter shape rules belong in the registry's "at-least-one-contribution"
|
|
143
|
+
* check, not here.
|
|
144
|
+
*
|
|
145
|
+
* This function is intentionally **kind-agnostic** — both
|
|
146
|
+
* `PatternDefinition` (domain) and `OrchestrationPatternDefinition`
|
|
147
|
+
* (orchestration) pass. The discriminator routing happens in the loader
|
|
148
|
+
* via `isOrchestrationPattern()`/`isDomainPattern()`.
|
|
149
|
+
*/
|
|
150
|
+
export function isPatternDefinition(val: unknown): val is PatternDefinition {
|
|
151
|
+
return (
|
|
152
|
+
typeof val === 'object' &&
|
|
153
|
+
val !== null &&
|
|
154
|
+
'name' in val &&
|
|
155
|
+
typeof (val as { name: unknown }).name === 'string'
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ============================================================================
|
|
160
|
+
// Orchestration kind (ADR-032)
|
|
161
|
+
// ============================================================================
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* One registry's declarative shape. ADR-032 §"The Proposal".
|
|
165
|
+
*
|
|
166
|
+
* Phase 3-1 records this; Phase 3-2 codegen reads it to emit token files,
|
|
167
|
+
* provider blocks, and dispatcher overload signatures. Phase 3-1 validates
|
|
168
|
+
* only what is statically checkable from this record alone — see
|
|
169
|
+
* `validate-orchestration.ts` for the rules and their deferral notes.
|
|
170
|
+
*/
|
|
171
|
+
export interface OrchestrationRegistrySpec {
|
|
172
|
+
/**
|
|
173
|
+
* Identifier for co-keyed sibling registries (ADR-032 Phase 3-2/3, O-1).
|
|
174
|
+
*
|
|
175
|
+
* The PRIMARY registry never carries a `name` — its tokens / methods are
|
|
176
|
+
* derived from the pattern name alone (`${PATTERN_CONST}_REGISTRY`,
|
|
177
|
+
* `select(...)`). Each co-keyed sibling MUST carry an explicit `name`
|
|
178
|
+
* which the emitter uppercases for the token suffix and PascalCases for
|
|
179
|
+
* the dispatcher method suffix:
|
|
180
|
+
*
|
|
181
|
+
* `coKeyedRegistries: [{ name: 'auth', valueType: 'IAuthStrategy' ... }]`
|
|
182
|
+
* ⇒ `CRM_PORTS_AUTH_REGISTRY` token + `selectAuth(...)` method.
|
|
183
|
+
*
|
|
184
|
+
* No auto-stripping of "I" prefix or "Strategy/Port/Adapter/Provider"
|
|
185
|
+
* suffixes — authors pick what reads right.
|
|
186
|
+
*/
|
|
187
|
+
name?: string;
|
|
188
|
+
/**
|
|
189
|
+
* Type alias the consumer's tsconfig resolves (e.g. `"CrmAdapterDomain"`).
|
|
190
|
+
* Phase 3-1 stores this string verbatim. Resolution that the path actually
|
|
191
|
+
* imports a concrete TS enum is deferred to Phase 3-2 emission, where
|
|
192
|
+
* codegen will need to read the consumer's source tree.
|
|
193
|
+
*/
|
|
194
|
+
keyType: string;
|
|
195
|
+
/**
|
|
196
|
+
* Module specifier the emitter writes into `import type { keyType } from
|
|
197
|
+
* '<keyTypeImport>'`. Required at Phase 3-2 emission; the generator emits
|
|
198
|
+
* `pattern_missing_import_path` if absent. (ADR-032 Phase 3-2 §3.4 / O-3.)
|
|
199
|
+
*/
|
|
200
|
+
keyTypeImport?: string;
|
|
201
|
+
/** Same shape as `keyType` — the registry's value-type interface ref. */
|
|
202
|
+
valueType: string;
|
|
203
|
+
/** Module specifier for `valueType` import. See `keyTypeImport`. */
|
|
204
|
+
valueTypeImport?: string;
|
|
205
|
+
entries: ReadonlyArray<{
|
|
206
|
+
/** Stable string key — must be unique within this registry. */
|
|
207
|
+
key: string;
|
|
208
|
+
/**
|
|
209
|
+
* Concrete provider class name (NOT a DI token string). Codegen will
|
|
210
|
+
* import this and use it as the constructor injectable.
|
|
211
|
+
* Phase 3-1 records it; Phase 3-2 verifies it resolves.
|
|
212
|
+
*/
|
|
213
|
+
provider: string;
|
|
214
|
+
/** Module specifier for `provider` import. See `keyTypeImport`. */
|
|
215
|
+
providerImport?: string;
|
|
216
|
+
}>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Orchestration pattern — declarative DI registry + optional dispatcher
|
|
221
|
+
* scaffold. ADR-032 §"The Proposal" + Decisions 1-8.
|
|
222
|
+
*
|
|
223
|
+
* Disjoint from `PatternDefinition` (domain): no columns, no
|
|
224
|
+
* repository/service base class, no entity-level patternConfig. Composition
|
|
225
|
+
* with domain patterns happens only at the DI layer in the consumer's
|
|
226
|
+
* generated code, not in entity YAML.
|
|
227
|
+
*/
|
|
228
|
+
export interface OrchestrationPatternDefinition {
|
|
229
|
+
name: string;
|
|
230
|
+
kind: 'orchestration';
|
|
231
|
+
/** Primary registry (always present). */
|
|
232
|
+
registry: OrchestrationRegistrySpec;
|
|
233
|
+
/**
|
|
234
|
+
* Sibling registries that share the primary registry's key space.
|
|
235
|
+
* ADR-032 Decision 2 — co-keyed groups are a first-class field.
|
|
236
|
+
* Validator enforces matching `keyType` across the group.
|
|
237
|
+
*/
|
|
238
|
+
coKeyedRegistries?: ReadonlyArray<OrchestrationRegistrySpec>;
|
|
239
|
+
/** Optional dispatcher scaffold spec (ADR-032 Decision 4 + 5). */
|
|
240
|
+
dispatcher?: {
|
|
241
|
+
/** Class name to emit (e.g. `"CrmPortsDispatcher"`). */
|
|
242
|
+
className: string;
|
|
243
|
+
/**
|
|
244
|
+
* Method name the consumer overrides in their subclass to fill the
|
|
245
|
+
* assembly body (ADR-032 Decision 5).
|
|
246
|
+
*/
|
|
247
|
+
assemblySlot: string;
|
|
248
|
+
};
|
|
249
|
+
/** One-line description for help output and error messages. */
|
|
250
|
+
description?: string;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Union for callers that need to handle both shapes. */
|
|
254
|
+
export type AnyPatternDefinition =
|
|
255
|
+
| PatternDefinition
|
|
256
|
+
| OrchestrationPatternDefinition;
|
|
257
|
+
|
|
258
|
+
export function isOrchestrationPattern(
|
|
259
|
+
def: AnyPatternDefinition,
|
|
260
|
+
): def is OrchestrationPatternDefinition {
|
|
261
|
+
return (def as { kind?: PatternKind }).kind === 'orchestration';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function isDomainPattern(
|
|
265
|
+
def: AnyPatternDefinition,
|
|
266
|
+
): def is PatternDefinition {
|
|
267
|
+
return !isOrchestrationPattern(def);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Identity function that returns its argument unchanged — orchestration
|
|
272
|
+
* counterpart to `definePattern()`. The body is trivial on purpose; the
|
|
273
|
+
* point is to give TypeScript a hook so consumer fixtures get full
|
|
274
|
+
* compile-time checking against `OrchestrationPatternDefinition`.
|
|
275
|
+
*/
|
|
276
|
+
export function defineOrchestrationPattern(
|
|
277
|
+
def: OrchestrationPatternDefinition,
|
|
278
|
+
): OrchestrationPatternDefinition {
|
|
279
|
+
return def;
|
|
280
|
+
}
|