@mettlecast/domain-cli 0.2.63 → 0.2.65
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
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
### fix(#4810): `build` no longer crashes on aggregate/flows registries
|
|
6
|
+
|
|
7
|
+
`runBuild` aggregated **every** `.mc/*-registry.json` file for `actions-types.d.ts`
|
|
8
|
+
generation, including the aggregate `domain-registry.json` and the
|
|
9
|
+
`flows-registry.json` (which have no `domain`/`actions` shape). This threw
|
|
10
|
+
`TypeError: Cannot read properties of undefined (reading 'id')` in
|
|
11
|
+
`buildActionsTypes`, breaking `mc-domain-module build` (and, downstream, the
|
|
12
|
+
`doctor` "Actions have types" check) on any scaffolded project that has flows
|
|
13
|
+
or an aggregate registry. The build now only aggregates per-domain registries
|
|
14
|
+
(those carrying `domain.id` and an `actions` array), and `buildActionsTypes`
|
|
15
|
+
defensively skips malformed registries.
|
|
16
|
+
|
|
5
17
|
### feat(#2132): Policy-as-data refactor (manifest v2)
|
|
6
18
|
|
|
7
19
|
**Manifest schema bumped to v2.** Each `ManifestFileEntry` now carries a `policy` field:
|
|
@@ -9,6 +9,13 @@
|
|
|
9
9
|
export function buildActionsTypes(registries) {
|
|
10
10
|
const overloads = [];
|
|
11
11
|
for (const reg of registries) {
|
|
12
|
+
// Skip registries that are not per-domain registries. The `.mc/` directory
|
|
13
|
+
// also contains aggregate (`domain-registry.json`) and flows
|
|
14
|
+
// (`flows-registry.json`) snapshots that share the `*-registry.json` suffix
|
|
15
|
+
// but have a different shape (no `domain` object / no `actions` array).
|
|
16
|
+
if (!reg?.domain?.id || !Array.isArray(reg.actions)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
12
19
|
for (const action of reg.actions) {
|
|
13
20
|
const fqn = `${reg.domain.id}.${action.id}`;
|
|
14
21
|
// Phase 1: use unknown for input/output
|
package/dist/commands/build.js
CHANGED
|
@@ -25,13 +25,29 @@ export async function runBuild(options) {
|
|
|
25
25
|
await writeFile(outFile, JSON.stringify(registry), 'utf8');
|
|
26
26
|
const apiExposedActions = registry.actions.filter(action => action.exposure.type === 'api').length;
|
|
27
27
|
cliLogger.info({ outFile, apiExposedActions, events: registry.events.length }, 'Registry written');
|
|
28
|
-
// Discover all sibling registries and emit aggregated types
|
|
28
|
+
// Discover all sibling per-domain registries and emit aggregated types.
|
|
29
|
+
// The `.mc/` directory also holds aggregate (`domain-registry.json`) and
|
|
30
|
+
// flows (`flows-registry.json`) snapshots that share the `*-registry.json`
|
|
31
|
+
// suffix but are not per-domain registries; only registries carrying a
|
|
32
|
+
// `domain.id` and an `actions` array are aggregated for type generation.
|
|
29
33
|
const registryDir = join(outFile, '..');
|
|
30
34
|
const registryFiles = (await readdir(registryDir)).filter(f => f.endsWith('-registry.json'));
|
|
31
35
|
const allRegistries = [];
|
|
32
36
|
for (const f of registryFiles) {
|
|
33
37
|
const raw = await readFile(join(registryDir, f), 'utf8');
|
|
34
|
-
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = JSON.parse(raw);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
cliLogger.warn(`Skipping unparseable registry file: ${f}`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const candidate = parsed;
|
|
47
|
+
if (!candidate?.domain?.id || !Array.isArray(candidate.actions)) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
allRegistries.push(candidate);
|
|
35
51
|
}
|
|
36
52
|
const typesContent = buildActionsTypes(allRegistries);
|
|
37
53
|
const typesFile = join(registryDir, 'actions-types.d.ts');
|
package/package.json
CHANGED
|
@@ -71,6 +71,35 @@ describe('buildActionsTypes', () => {
|
|
|
71
71
|
expect(output).toContain("call(actionId: 'payments.charge'");
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
+
it('skips aggregate/flows registries that lack a domain object', () => {
|
|
75
|
+
const auth: DomainRegistry = {
|
|
76
|
+
schemaVersion: '1',
|
|
77
|
+
domainRoot: '/test/auth',
|
|
78
|
+
domain: { id: 'auth', kind: 'domain', name: 'Auth', tenancy: 'required' },
|
|
79
|
+
webhooks: [],
|
|
80
|
+
subscribers: [],
|
|
81
|
+
schedules: [],
|
|
82
|
+
jobs: [],
|
|
83
|
+
actions: [
|
|
84
|
+
{ id: 'verify', kind: 'action', handlerFile: 'verify.ts', backendAccess: 'platform', exposure: { type: 'internal' }, idempotent: true },
|
|
85
|
+
],
|
|
86
|
+
integrations: [],
|
|
87
|
+
events: [],
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Aggregate registry (domain-registry.json): has actions but no domain.
|
|
91
|
+
const aggregate = { schemaVersion: '1', actions: [{ id: 'x' }] } as unknown as DomainRegistry;
|
|
92
|
+
// Flows registry (flows-registry.json): neither domain nor actions.
|
|
93
|
+
const flows = { schemaVersion: '1', flows: [] } as unknown as DomainRegistry;
|
|
94
|
+
|
|
95
|
+
expect(() => buildActionsTypes([auth, aggregate, flows])).not.toThrow();
|
|
96
|
+
|
|
97
|
+
const output = buildActionsTypes([auth, aggregate, flows]);
|
|
98
|
+
expect(output).toContain("call(actionId: 'auth.verify'");
|
|
99
|
+
// The aggregate registry's bare action must not leak in without a domain prefix.
|
|
100
|
+
expect(output).not.toContain('undefined.x');
|
|
101
|
+
});
|
|
102
|
+
|
|
74
103
|
it('returns valid TypeScript .d.ts syntax', () => {
|
|
75
104
|
const registry: DomainRegistry = {
|
|
76
105
|
schemaVersion: '1',
|
|
@@ -12,6 +12,13 @@ export function buildActionsTypes(registries: DomainRegistry[]): string {
|
|
|
12
12
|
const overloads: string[] = [];
|
|
13
13
|
|
|
14
14
|
for (const reg of registries) {
|
|
15
|
+
// Skip registries that are not per-domain registries. The `.mc/` directory
|
|
16
|
+
// also contains aggregate (`domain-registry.json`) and flows
|
|
17
|
+
// (`flows-registry.json`) snapshots that share the `*-registry.json` suffix
|
|
18
|
+
// but have a different shape (no `domain` object / no `actions` array).
|
|
19
|
+
if (!reg?.domain?.id || !Array.isArray(reg.actions)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
15
22
|
for (const action of reg.actions) {
|
|
16
23
|
const fqn = `${reg.domain.id}.${action.id}`;
|
|
17
24
|
// Phase 1: use unknown for input/output
|
package/src/commands/build.ts
CHANGED
|
@@ -43,13 +43,28 @@ export async function runBuild(options: BuildOptions): Promise<string> {
|
|
|
43
43
|
const apiExposedActions = registry.actions.filter(action => action.exposure.type === 'api').length;
|
|
44
44
|
cliLogger.info({ outFile, apiExposedActions, events: registry.events.length }, 'Registry written');
|
|
45
45
|
|
|
46
|
-
// Discover all sibling registries and emit aggregated types
|
|
46
|
+
// Discover all sibling per-domain registries and emit aggregated types.
|
|
47
|
+
// The `.mc/` directory also holds aggregate (`domain-registry.json`) and
|
|
48
|
+
// flows (`flows-registry.json`) snapshots that share the `*-registry.json`
|
|
49
|
+
// suffix but are not per-domain registries; only registries carrying a
|
|
50
|
+
// `domain.id` and an `actions` array are aggregated for type generation.
|
|
47
51
|
const registryDir = join(outFile, '..');
|
|
48
52
|
const registryFiles = (await readdir(registryDir)).filter(f => f.endsWith('-registry.json'));
|
|
49
53
|
const allRegistries: DomainRegistry[] = [];
|
|
50
54
|
for (const f of registryFiles) {
|
|
51
55
|
const raw = await readFile(join(registryDir, f), 'utf8');
|
|
52
|
-
|
|
56
|
+
let parsed: unknown;
|
|
57
|
+
try {
|
|
58
|
+
parsed = JSON.parse(raw);
|
|
59
|
+
} catch {
|
|
60
|
+
cliLogger.warn(`Skipping unparseable registry file: ${f}`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const candidate = parsed as Partial<DomainRegistry>;
|
|
64
|
+
if (!candidate?.domain?.id || !Array.isArray(candidate.actions)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
allRegistries.push(candidate as DomainRegistry);
|
|
53
68
|
}
|
|
54
69
|
const typesContent = buildActionsTypes(allRegistries);
|
|
55
70
|
const typesFile = join(registryDir, 'actions-types.d.ts');
|