@principal-ai/subsystems-core 0.42.0 → 0.43.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/dist/validation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,sBAAsB,EAGvB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,0BAA0B;IACzC,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,sBAAsB,EAGvB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,0BAA0B;IACzC,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAyBD;;;GAGG;AACH,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,sBAAsB,GAC1B,0BAA0B,EAAE,CAuE9B"}
|
package/dist/validation.js
CHANGED
|
@@ -16,6 +16,24 @@ exports.validateSubsystemModelCrossField = void 0;
|
|
|
16
16
|
function isUngrounded(c) {
|
|
17
17
|
return c.construct === 'external' || c.proposed === true;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* `file` is a path *inside the repo named by `purl`* — it resolves against that
|
|
21
|
+
* checkout, never against an installed artifact. `node_modules/` is installed,
|
|
22
|
+
* gitignored, and its layout depends on hoisting, so a claim anchored there can
|
|
23
|
+
* never resolve and is unverifiable by construction.
|
|
24
|
+
*
|
|
25
|
+
* Such a component is a third-party dependency: model it as `construct:
|
|
26
|
+
* 'external'` with `purl: 'pkg:npm/<package>'` and no file. Applies to externals
|
|
27
|
+
* too — they carry no file by design, so an install path there is dead weight
|
|
28
|
+
* that draws a link nothing can open. `proposed` is exempt like every other
|
|
29
|
+
* grounding rule: its file is a placeholder for something not placed yet.
|
|
30
|
+
*
|
|
31
|
+
* The same rule covers walkthrough step files — a seam into an external belongs
|
|
32
|
+
* at the call site in the caller, which *is* in the repo.
|
|
33
|
+
*/
|
|
34
|
+
function mentionsNodeModules(path) {
|
|
35
|
+
return /(^|\/)node_modules(\/|$)/.test(path);
|
|
36
|
+
}
|
|
19
37
|
/**
|
|
20
38
|
* Validate the cross-field rules of a subsystem model document. Returns an
|
|
21
39
|
* empty array when the document is consistent.
|
|
@@ -47,6 +65,12 @@ function validateSubsystemModelCrossField(doc) {
|
|
|
47
65
|
message: `component ${JSON.stringify(c.alias)}: module ${JSON.stringify(module)} is set but file is empty — a module frame needs a file to ground it (mark the component proposed if it is not placed yet).`,
|
|
48
66
|
});
|
|
49
67
|
}
|
|
68
|
+
if (file && c.proposed !== true && mentionsNodeModules(file)) {
|
|
69
|
+
problems.push({
|
|
70
|
+
path: `/components/${i}/file`,
|
|
71
|
+
message: `component ${JSON.stringify(c.alias)}: file ${JSON.stringify(file)} points into node_modules — installed artifacts are not part of the repo and cannot be verified. Model the dependency as construct "external" with purl "pkg:npm/<package>" and no file, or anchor the claim to the package's real source.`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
50
74
|
});
|
|
51
75
|
walkthroughs.forEach((w, wi) => {
|
|
52
76
|
(w.steps ?? []).forEach((step, si) => {
|
|
@@ -74,6 +98,12 @@ function validateSubsystemModelCrossField(doc) {
|
|
|
74
98
|
});
|
|
75
99
|
}
|
|
76
100
|
}
|
|
101
|
+
if (mentionsNodeModules(step.file)) {
|
|
102
|
+
problems.push({
|
|
103
|
+
path: `/walkthroughs/${wi}/steps/${si}/file`,
|
|
104
|
+
message: `walkthrough ${JSON.stringify(w.id)}: step ${si} file ${JSON.stringify(step.file)} points into node_modules — anchor the seam at the call site inside the repo instead.`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
77
107
|
});
|
|
78
108
|
});
|
|
79
109
|
return problems;
|
package/dist/validation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -138,7 +138,8 @@
|
|
|
138
138
|
},
|
|
139
139
|
"file": {
|
|
140
140
|
"type": "string",
|
|
141
|
-
"
|
|
141
|
+
"pattern": "^(?!.*(^|/)node_modules(/|$)).*$",
|
|
142
|
+
"description": "Repo-root-relative path of the source location this component lives in. Resolved against the checkout of the repo named by this component's `purl` (via the Alexandria registry). Empty string allowed for pure externals. `node_modules/` is rejected: installed artifacts are not part of the repo — model a third-party dependency as `construct: external` with `purl: pkg:npm/<package>` and no file."
|
|
142
143
|
},
|
|
143
144
|
"purl": {
|
|
144
145
|
"type": "string",
|
|
@@ -234,6 +235,7 @@
|
|
|
234
235
|
"file": {
|
|
235
236
|
"type": "string",
|
|
236
237
|
"minLength": 1,
|
|
238
|
+
"pattern": "^(?!.*(^|/)node_modules(/|$)).*$",
|
|
237
239
|
"description": "Repo-root-relative path where the seam fires for this walkthrough."
|
|
238
240
|
},
|
|
239
241
|
"line": {
|
package/src/validation.test.ts
CHANGED
|
@@ -100,4 +100,77 @@ describe('validateSubsystemModelCrossField', () => {
|
|
|
100
100
|
),
|
|
101
101
|
).toEqual([]);
|
|
102
102
|
});
|
|
103
|
+
|
|
104
|
+
test('flags a component file pointing into node_modules', () => {
|
|
105
|
+
const problems = validateSubsystemModelCrossField(
|
|
106
|
+
doc({
|
|
107
|
+
components: [
|
|
108
|
+
comp('dep', {
|
|
109
|
+
construct: 'store',
|
|
110
|
+
file: 'packages/react/node_modules/@pierre/diffs/dist/highlighter/shared_highlighter.js',
|
|
111
|
+
}),
|
|
112
|
+
comp('bare', { file: 'node_modules/left-pad/index.js' }),
|
|
113
|
+
// An external may carry such a path too — the path is the defect
|
|
114
|
+
// whichever construct claims it.
|
|
115
|
+
comp('ext', {
|
|
116
|
+
construct: 'external',
|
|
117
|
+
file: 'packages/react/node_modules/@pierre/diffs/dist/components/CodeView.js',
|
|
118
|
+
}),
|
|
119
|
+
],
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
expect(problems).toHaveLength(3);
|
|
123
|
+
expect(problems[0]!.path).toBe('/components/0/file');
|
|
124
|
+
expect(problems[0]!.message).toContain('node_modules');
|
|
125
|
+
expect(problems[1]!.path).toBe('/components/1/file');
|
|
126
|
+
expect(problems[2]!.path).toBe('/components/2/file');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('accepts node_modules only as an external/proposed component identity', () => {
|
|
130
|
+
expect(
|
|
131
|
+
validateSubsystemModelCrossField(
|
|
132
|
+
doc({
|
|
133
|
+
components: [
|
|
134
|
+
// A dependency modeled as a package: no file, npm purl.
|
|
135
|
+
comp('dep', {
|
|
136
|
+
construct: 'external',
|
|
137
|
+
file: '',
|
|
138
|
+
purl: 'pkg:npm/@pierre/diffs',
|
|
139
|
+
}),
|
|
140
|
+
// A planned in-repo component may sit where it will live.
|
|
141
|
+
comp('plan', { file: 'packages/x/node_modules/y/z.ts', proposed: true }),
|
|
142
|
+
// `node_modulesx` is an ordinary directory, not the install root.
|
|
143
|
+
comp('ok', { file: 'packages/node_modulesx/z.ts' }),
|
|
144
|
+
],
|
|
145
|
+
}),
|
|
146
|
+
),
|
|
147
|
+
).toEqual([]);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('flags a walkthrough step anchored in node_modules', () => {
|
|
151
|
+
const problems = validateSubsystemModelCrossField(
|
|
152
|
+
doc({
|
|
153
|
+
components: [comp('a')],
|
|
154
|
+
walkthroughs: [
|
|
155
|
+
{
|
|
156
|
+
id: 'w1',
|
|
157
|
+
title: 'flow',
|
|
158
|
+
steps: [
|
|
159
|
+
{
|
|
160
|
+
from: 'a',
|
|
161
|
+
to: 'a',
|
|
162
|
+
mechanism: 'calls',
|
|
163
|
+
file: 'packages/react/node_modules/@pierre/diffs/dist/index.js',
|
|
164
|
+
line: 1,
|
|
165
|
+
purl: 'pkg:npm/@pierre/diffs#packages/react/node_modules/@pierre/diffs/dist/index.js',
|
|
166
|
+
symbol: 'a',
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
}),
|
|
172
|
+
);
|
|
173
|
+
expect(problems).toHaveLength(1);
|
|
174
|
+
expect(problems[0]!.path).toBe('/walkthroughs/0/steps/0/file');
|
|
175
|
+
});
|
|
103
176
|
});
|
package/src/validation.ts
CHANGED
|
@@ -27,6 +27,25 @@ function isUngrounded(c: SubsystemComponent): boolean {
|
|
|
27
27
|
return c.construct === 'external' || c.proposed === true;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* `file` is a path *inside the repo named by `purl`* — it resolves against that
|
|
32
|
+
* checkout, never against an installed artifact. `node_modules/` is installed,
|
|
33
|
+
* gitignored, and its layout depends on hoisting, so a claim anchored there can
|
|
34
|
+
* never resolve and is unverifiable by construction.
|
|
35
|
+
*
|
|
36
|
+
* Such a component is a third-party dependency: model it as `construct:
|
|
37
|
+
* 'external'` with `purl: 'pkg:npm/<package>'` and no file. Applies to externals
|
|
38
|
+
* too — they carry no file by design, so an install path there is dead weight
|
|
39
|
+
* that draws a link nothing can open. `proposed` is exempt like every other
|
|
40
|
+
* grounding rule: its file is a placeholder for something not placed yet.
|
|
41
|
+
*
|
|
42
|
+
* The same rule covers walkthrough step files — a seam into an external belongs
|
|
43
|
+
* at the call site in the caller, which *is* in the repo.
|
|
44
|
+
*/
|
|
45
|
+
function mentionsNodeModules(path: string): boolean {
|
|
46
|
+
return /(^|\/)node_modules(\/|$)/.test(path);
|
|
47
|
+
}
|
|
48
|
+
|
|
30
49
|
/**
|
|
31
50
|
* Validate the cross-field rules of a subsystem model document. Returns an
|
|
32
51
|
* empty array when the document is consistent.
|
|
@@ -60,6 +79,12 @@ export function validateSubsystemModelCrossField(
|
|
|
60
79
|
message: `component ${JSON.stringify(c.alias)}: module ${JSON.stringify(module)} is set but file is empty — a module frame needs a file to ground it (mark the component proposed if it is not placed yet).`,
|
|
61
80
|
});
|
|
62
81
|
}
|
|
82
|
+
if (file && c.proposed !== true && mentionsNodeModules(file)) {
|
|
83
|
+
problems.push({
|
|
84
|
+
path: `/components/${i}/file`,
|
|
85
|
+
message: `component ${JSON.stringify(c.alias)}: file ${JSON.stringify(file)} points into node_modules — installed artifacts are not part of the repo and cannot be verified. Model the dependency as construct "external" with purl "pkg:npm/<package>" and no file, or anchor the claim to the package's real source.`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
63
88
|
});
|
|
64
89
|
|
|
65
90
|
walkthroughs.forEach((w: SubsystemWalkthrough, wi) => {
|
|
@@ -88,6 +113,12 @@ export function validateSubsystemModelCrossField(
|
|
|
88
113
|
});
|
|
89
114
|
}
|
|
90
115
|
}
|
|
116
|
+
if (mentionsNodeModules(step.file)) {
|
|
117
|
+
problems.push({
|
|
118
|
+
path: `/walkthroughs/${wi}/steps/${si}/file`,
|
|
119
|
+
message: `walkthrough ${JSON.stringify(w.id)}: step ${si} file ${JSON.stringify(step.file)} points into node_modules — anchor the seam at the call site inside the repo instead.`,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
91
122
|
});
|
|
92
123
|
});
|
|
93
124
|
|