@ultimat3/cli 27.3.0 → 27.4.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/package.json +28 -28
- package/src/app-layout.ts +46 -0
- package/src/boundary-findings.ts +2 -0
- package/src/error-codes.ts +2 -0
- package/src/mcp-errors.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/cli",
|
|
3
|
-
"version": "27.
|
|
3
|
+
"version": "27.4.0",
|
|
4
4
|
"description": "The `x` binary: new, dev, build, verify, generate, db, mcp, doctor, deploy",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,33 +39,33 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@babel/core": "^8.0.1",
|
|
42
|
-
"@ultimat3/action": "27.
|
|
43
|
-
"@ultimat3/admin": "27.
|
|
44
|
-
"@ultimat3/ai": "27.
|
|
45
|
-
"@ultimat3/auth": "27.
|
|
46
|
-
"@ultimat3/cache": "27.
|
|
47
|
-
"@ultimat3/core": "27.
|
|
48
|
-
"@ultimat3/db": "27.
|
|
49
|
-
"@ultimat3/entity": "27.
|
|
50
|
-
"@ultimat3/flags": "27.
|
|
51
|
-
"@ultimat3/http": "27.
|
|
52
|
-
"@ultimat3/i18n": "27.
|
|
53
|
-
"@ultimat3/jobs": "27.
|
|
54
|
-
"@ultimat3/mail": "27.
|
|
55
|
-
"@ultimat3/manifest": "27.
|
|
56
|
-
"@ultimat3/mcp": "27.
|
|
57
|
-
"@ultimat3/money": "27.
|
|
58
|
-
"@ultimat3/notify": "27.
|
|
59
|
-
"@ultimat3/policy": "27.
|
|
60
|
-
"@ultimat3/pwa": "27.
|
|
61
|
-
"@ultimat3/query": "27.
|
|
62
|
-
"@ultimat3/realtime": "27.
|
|
63
|
-
"@ultimat3/render": "27.
|
|
64
|
-
"@ultimat3/schema": "27.
|
|
65
|
-
"@ultimat3/seo": "27.
|
|
66
|
-
"@ultimat3/storage": "27.
|
|
67
|
-
"@ultimat3/testing": "27.
|
|
68
|
-
"@ultimat3/time": "27.
|
|
42
|
+
"@ultimat3/action": "27.4.0",
|
|
43
|
+
"@ultimat3/admin": "27.4.0",
|
|
44
|
+
"@ultimat3/ai": "27.4.0",
|
|
45
|
+
"@ultimat3/auth": "27.4.0",
|
|
46
|
+
"@ultimat3/cache": "27.4.0",
|
|
47
|
+
"@ultimat3/core": "27.4.0",
|
|
48
|
+
"@ultimat3/db": "27.4.0",
|
|
49
|
+
"@ultimat3/entity": "27.4.0",
|
|
50
|
+
"@ultimat3/flags": "27.4.0",
|
|
51
|
+
"@ultimat3/http": "27.4.0",
|
|
52
|
+
"@ultimat3/i18n": "27.4.0",
|
|
53
|
+
"@ultimat3/jobs": "27.4.0",
|
|
54
|
+
"@ultimat3/mail": "27.4.0",
|
|
55
|
+
"@ultimat3/manifest": "27.4.0",
|
|
56
|
+
"@ultimat3/mcp": "27.4.0",
|
|
57
|
+
"@ultimat3/money": "27.4.0",
|
|
58
|
+
"@ultimat3/notify": "27.4.0",
|
|
59
|
+
"@ultimat3/policy": "27.4.0",
|
|
60
|
+
"@ultimat3/pwa": "27.4.0",
|
|
61
|
+
"@ultimat3/query": "27.4.0",
|
|
62
|
+
"@ultimat3/realtime": "27.4.0",
|
|
63
|
+
"@ultimat3/render": "27.4.0",
|
|
64
|
+
"@ultimat3/schema": "27.4.0",
|
|
65
|
+
"@ultimat3/seo": "27.4.0",
|
|
66
|
+
"@ultimat3/storage": "27.4.0",
|
|
67
|
+
"@ultimat3/testing": "27.4.0",
|
|
68
|
+
"@ultimat3/time": "27.4.0",
|
|
69
69
|
"babel-preset-solid": "^1.9.15"
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// The one feature layout, made a build error: a primitive module `actions.ts` (or `live`,
|
|
2
|
+
// `queries`, `jobs`, `tasks`) beside the directory of the same name is refused. `x g` writes the
|
|
3
|
+
// directory form — `<slice>/actions/<name>.ts`, one primitive per file — and an app that also keeps
|
|
4
|
+
// `actions.ts` has two homes for one kind of thing, where `./actions` resolves to the file and never
|
|
5
|
+
// the directory. A component module beside its folder (`ui.tsx` + `ui/`) is not a primitive's home,
|
|
6
|
+
// and `x g resource` writes exactly that pair.
|
|
7
|
+
|
|
8
|
+
import { ERROR_DOCS_URL } from '@ultimat3/core';
|
|
9
|
+
import type { SourceFile } from './app-boundaries';
|
|
10
|
+
import type { Finding } from './output';
|
|
11
|
+
|
|
12
|
+
/** The directories `x g` writes primitives into — the only names the refusal reads. */
|
|
13
|
+
export const PRIMITIVE_DIRS: ReadonlySet<string> = new Set([
|
|
14
|
+
'actions',
|
|
15
|
+
'live',
|
|
16
|
+
'queries',
|
|
17
|
+
'jobs',
|
|
18
|
+
'tasks',
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
const MODULE = /^(?<stem>.+)\.(?:ts|tsx)$/;
|
|
22
|
+
|
|
23
|
+
/** Every primitive module `X.ts` among `files` beside a directory `X/` holding another of them. */
|
|
24
|
+
export function siblingModuleFindings(files: readonly SourceFile[]): readonly Finding[] {
|
|
25
|
+
const directories = new Set<string>();
|
|
26
|
+
for (const { path } of files) {
|
|
27
|
+
const parts = path.split('/');
|
|
28
|
+
for (let depth = 1; depth < parts.length; depth += 1) {
|
|
29
|
+
directories.add(parts.slice(0, depth).join('/'));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return files.flatMap(({ path }): Finding[] => {
|
|
33
|
+
const stem = MODULE.exec(path)?.groups?.['stem'];
|
|
34
|
+
const name = stem?.split('/').at(-1) ?? '';
|
|
35
|
+
if (stem === undefined || !PRIMITIVE_DIRS.has(name) || !directories.has(stem)) return [];
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
code: 'X_LAYOUT_SIBLING_MODULE',
|
|
39
|
+
cause: `${path} sits beside the directory ${stem}/ — \`./${name}\` resolves to the file, and the directory form is the one layout`,
|
|
40
|
+
fix: `x verify --only boundaries --json # move each declaration in ${path} to ${stem}/<its-export-in-kebab-case>.ts, then delete ${path}`,
|
|
41
|
+
docs: ERROR_DOCS_URL,
|
|
42
|
+
at: path,
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
});
|
|
46
|
+
}
|
package/src/boundary-findings.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { SourceFile } from './app-boundaries';
|
|
7
7
|
import { appImportGraph, checkImportRules, readAppSources } from './app-boundaries';
|
|
8
|
+
import { siblingModuleFindings } from './app-layout';
|
|
8
9
|
import { appTransportFindings } from './app-transport';
|
|
9
10
|
import { planBoundaryCuts } from './boundary-cuts';
|
|
10
11
|
import type { Finding } from './output';
|
|
@@ -44,5 +45,6 @@ export async function appBoundaryFindings(
|
|
|
44
45
|
return [
|
|
45
46
|
...withCutEdits(checkImportRules(files), appImportGraph(files)),
|
|
46
47
|
...(await appTransportFindings(root, files)),
|
|
48
|
+
...siblingModuleFindings(files),
|
|
47
49
|
];
|
|
48
50
|
}
|
package/src/error-codes.ts
CHANGED
|
@@ -232,6 +232,7 @@ export const CLI_OWNED_ERROR_CODES = [
|
|
|
232
232
|
'X_PACKAGE_TEST_ONLY_SHIPPED',
|
|
233
233
|
'X_ISLAND_MODULE_DUPLICATED',
|
|
234
234
|
'X_BACKFILL_SLICE_ENTITY',
|
|
235
|
+
'X_LAYOUT_SIBLING_MODULE',
|
|
235
236
|
] as const;
|
|
236
237
|
|
|
237
238
|
/**
|
|
@@ -420,6 +421,7 @@ export const CLI_ERROR_TITLES: Readonly<Record<CliOwnedErrorCode, string>> = {
|
|
|
420
421
|
X_PACKAGE_TEST_ONLY_SHIPPED: 'a test-only module ships in the tarball',
|
|
421
422
|
X_ISLAND_MODULE_DUPLICATED: 'An island chunk bundles one module more than once',
|
|
422
423
|
X_BACKFILL_SLICE_ENTITY: 'the slice holds no entity a generated backfill can sweep',
|
|
424
|
+
X_LAYOUT_SIBLING_MODULE: 'a primitive module beside its directory',
|
|
423
425
|
};
|
|
424
426
|
|
|
425
427
|
// One unconditional call, so a second package claiming one of the CLI's codes throws
|
package/src/mcp-errors.ts
CHANGED
|
@@ -302,6 +302,8 @@ const CLI_FIXES: Readonly<Record<CliErrorCode, string>> = {
|
|
|
302
302
|
'gh issue create --repo developerz-ai/ultimate --title "X_ISLAND_MODULE_DUPLICATED" # paste this error\'s --json as the body: the framework bundled one of its own modules twice',
|
|
303
303
|
X_BACKFILL_SLICE_ENTITY:
|
|
304
304
|
'x errors explain X_BACKFILL_SLICE_ENTITY --json # the refusal itself names the x g job line for this slice',
|
|
305
|
+
X_LAYOUT_SIBLING_MODULE:
|
|
306
|
+
'x verify --only boundaries --json # then move each declaration in X.ts to X/<name>.ts, one primitive per file',
|
|
305
307
|
};
|
|
306
308
|
|
|
307
309
|
const isCliCode = (code: string): code is CliErrorCode =>
|