dxcomplete 0.2.1 → 0.2.2
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/.env.example +0 -7
- package/README.md +17 -45
- package/dist/cli.js +0 -22
- package/dist/validate.js +10 -26
- package/docs/model.md +3 -3
- package/docs/taxonomy.md +1 -1
- package/package.json +23 -23
- package/templates/process/README.md +1 -1
- package/dist/http/service.d.ts +0 -7
- package/dist/http/service.js +0 -725
- package/dist/mcp/docs.d.ts +0 -114
- package/dist/mcp/docs.js +0 -626
- package/dist/mcp/server.d.ts +0 -20
- package/dist/mcp/server.js +0 -3059
- package/dist/runtime/auth.d.ts +0 -162
- package/dist/runtime/auth.js +0 -394
- package/dist/runtime/check.d.ts +0 -7
- package/dist/runtime/check.js +0 -16
- package/dist/runtime/config.d.ts +0 -17
- package/dist/runtime/config.js +0 -93
- package/dist/runtime/mongo.d.ts +0 -9
- package/dist/runtime/mongo.js +0 -56
- package/dist/runtime/records.d.ts +0 -427
- package/dist/runtime/records.js +0 -2092
- package/scripts/check-env-surface.mjs +0 -136
- package/scripts/check-public-copy.mjs +0 -263
- package/scripts/check-service-boundary.mjs +0 -63
- package/scripts/runtime-work-order.mjs +0 -506
- package/scripts/smoke-mcp-http.mjs +0 -4026
- package/src/cli.ts +0 -268
- package/src/http/server.ts +0 -314
- package/src/http/service.ts +0 -934
- package/src/init.ts +0 -262
- package/src/install-manifest.ts +0 -144
- package/src/mcp/docs.ts +0 -777
- package/src/mcp/server.ts +0 -4580
- package/src/package-root.ts +0 -31
- package/src/runtime/actor.ts +0 -61
- package/src/runtime/auth.ts +0 -673
- package/src/runtime/check.ts +0 -18
- package/src/runtime/config.ts +0 -128
- package/src/runtime/mongo.ts +0 -89
- package/src/runtime/records.ts +0 -3205
- package/src/runtime/workspace.ts +0 -155
- package/src/upgrade.ts +0 -356
- package/src/validate.ts +0 -141
- package/src/version.ts +0 -16
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
6
|
-
|
|
7
|
-
const allowedEnvVars = new Map([
|
|
8
|
-
["DXC_MONGODB_URI", "secret"],
|
|
9
|
-
["DXC_DATABASE_NAME", "environment-specific value"],
|
|
10
|
-
["DXC_GOOGLE_CLIENT_ID", "provisioning detail"],
|
|
11
|
-
["DXC_GOOGLE_CLIENT_SECRET", "secret"],
|
|
12
|
-
["DXC_SERVICE_PROVISIONING_SECRET", "secret"],
|
|
13
|
-
["DXC_SERVICE_URL", "environment-specific value"],
|
|
14
|
-
["DXC_SERVICE_CLIENT_ID", "provisioning detail"],
|
|
15
|
-
["DXC_SERVICE_CLIENT_SECRET", "secret"]
|
|
16
|
-
]);
|
|
17
|
-
|
|
18
|
-
const allowedClassifications = new Set([
|
|
19
|
-
"secret",
|
|
20
|
-
"provisioning detail",
|
|
21
|
-
"environment-specific value"
|
|
22
|
-
]);
|
|
23
|
-
|
|
24
|
-
const scanTargets = [
|
|
25
|
-
".env.example",
|
|
26
|
-
"README.md",
|
|
27
|
-
"AGENTS.md",
|
|
28
|
-
"package.json",
|
|
29
|
-
"api",
|
|
30
|
-
"scripts",
|
|
31
|
-
"src",
|
|
32
|
-
"dist",
|
|
33
|
-
"docs",
|
|
34
|
-
"templates",
|
|
35
|
-
"website"
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
const docEnvPattern = /\b[A-Z][A-Z0-9]+(?:_[A-Z0-9]+)+\b/g;
|
|
39
|
-
const quotedEnvPattern = /["'`]([A-Z][A-Z0-9]+(?:_[A-Z0-9]+)+)["'`]/g;
|
|
40
|
-
const failures = [];
|
|
41
|
-
const references = new Map();
|
|
42
|
-
|
|
43
|
-
for (const [name, classification] of allowedEnvVars) {
|
|
44
|
-
if (!allowedClassifications.has(classification)) {
|
|
45
|
-
failures.push(`${name}: invalid env classification "${classification}".`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
for (const target of scanTargets) {
|
|
50
|
-
const targetPath = path.join(rootDir, target);
|
|
51
|
-
if (!existsSync(targetPath)) continue;
|
|
52
|
-
|
|
53
|
-
for (const filePath of listFiles(targetPath)) {
|
|
54
|
-
scanFile(filePath);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
for (const [name, locations] of references) {
|
|
59
|
-
if (!allowedEnvVars.has(name)) {
|
|
60
|
-
failures.push(
|
|
61
|
-
`${name}: referenced but not allowed. Add it to scripts/check-env-surface.mjs with classification secret, provisioning detail, or environment-specific value, or remove it. First seen at ${locations[0]}.`
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (failures.length > 0) {
|
|
67
|
-
console.error("Environment surface check failed:");
|
|
68
|
-
for (const failure of failures) {
|
|
69
|
-
console.error(`- ${failure}`);
|
|
70
|
-
}
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
console.log("Environment surface check passed.");
|
|
75
|
-
|
|
76
|
-
function listFiles(targetPath) {
|
|
77
|
-
const stats = statSync(targetPath);
|
|
78
|
-
if (stats.isFile()) {
|
|
79
|
-
return shouldScan(targetPath) ? [targetPath] : [];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const files = [];
|
|
83
|
-
for (const entry of readdirSync(targetPath)) {
|
|
84
|
-
if (entry === "node_modules" || entry === ".git") continue;
|
|
85
|
-
files.push(...listFiles(path.join(targetPath, entry)));
|
|
86
|
-
}
|
|
87
|
-
return files;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function shouldScan(filePath) {
|
|
91
|
-
const relativePath = path.relative(rootDir, filePath);
|
|
92
|
-
if (relativePath === "package-lock.json") return false;
|
|
93
|
-
|
|
94
|
-
return [
|
|
95
|
-
".example",
|
|
96
|
-
".html",
|
|
97
|
-
".js",
|
|
98
|
-
".json",
|
|
99
|
-
".md",
|
|
100
|
-
".mjs",
|
|
101
|
-
".ts",
|
|
102
|
-
".yml",
|
|
103
|
-
".yaml"
|
|
104
|
-
].some((extension) => filePath.endsWith(extension));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function scanFile(filePath) {
|
|
108
|
-
const relativePath = path.relative(rootDir, filePath);
|
|
109
|
-
const content = readFileSync(filePath, "utf8");
|
|
110
|
-
|
|
111
|
-
if (relativePath === ".env.example") {
|
|
112
|
-
for (const [index, line] of content.split(/\r?\n/).entries()) {
|
|
113
|
-
const trimmed = line.trim();
|
|
114
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
115
|
-
const key = trimmed.split("=", 1)[0]?.trim();
|
|
116
|
-
if (key) addReference(key, `${relativePath}:${index + 1}`);
|
|
117
|
-
}
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const isSourceLike = /\.(?:js|json|mjs|ts)$/.test(filePath);
|
|
122
|
-
const pattern = isSourceLike ? quotedEnvPattern : docEnvPattern;
|
|
123
|
-
pattern.lastIndex = 0;
|
|
124
|
-
|
|
125
|
-
for (const match of content.matchAll(pattern)) {
|
|
126
|
-
const name = isSourceLike ? match[1] : match[0];
|
|
127
|
-
addReference(name, relativePath);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function addReference(name, location) {
|
|
132
|
-
if (!references.has(name)) {
|
|
133
|
-
references.set(name, []);
|
|
134
|
-
}
|
|
135
|
-
references.get(name).push(location);
|
|
136
|
-
}
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
6
|
-
const websiteDir = path.join(rootDir, "website");
|
|
7
|
-
|
|
8
|
-
const forbiddenPatterns = [
|
|
9
|
-
{ pattern: /\bMermaid\b/i, reason: "Mermaid/source details are not public-facing copy." },
|
|
10
|
-
{ pattern: /\bflowchart\b/i, reason: "Diagram source terms are not public-facing copy." },
|
|
11
|
-
{ pattern: /\bdiagram source\b/i, reason: "Source labels are not public-facing copy." },
|
|
12
|
-
{ pattern: /\bCLI\b/, reason: "Command-line internals do not belong in public website copy." },
|
|
13
|
-
{ pattern: /\bMCP\b/, reason: "MCP internals do not belong in public website copy." },
|
|
14
|
-
{ pattern: /\bMongoDB\b/, reason: "Storage internals do not belong in public website copy." },
|
|
15
|
-
{ pattern: /\bnpx\b/i, reason: "Install command details do not belong in public website copy." },
|
|
16
|
-
{ pattern: /\bcommand-line\b/i, reason: "Command-line internals do not belong in public website copy." },
|
|
17
|
-
{ pattern: /\bbootstrap\b/i, reason: "Implementation language does not belong in public website copy." },
|
|
18
|
-
{ pattern: /\bscaffold(?:ing)?\b/i, reason: "Scaffold language is maintainer-facing." },
|
|
19
|
-
{ pattern: /\btemplates?\b/i, reason: "Template language is maintainer-facing." },
|
|
20
|
-
{ pattern: /\bpackage\b/i, reason: "Package details are maintainer-facing." },
|
|
21
|
-
{ pattern: /\bimplementation\b/i, reason: "Implementation mechanics are maintainer-facing." },
|
|
22
|
-
{ pattern: /\bconfiguration\b/i, reason: "Configuration mechanics are maintainer-facing." },
|
|
23
|
-
{ pattern: /\bdeveloper\b/i, reason: "Developer-facing language should stay out of public pages." },
|
|
24
|
-
{ pattern: /\binternal\b/i, reason: "Internal-facing language should stay out of public pages." },
|
|
25
|
-
{ pattern: /\bTODO\b/i, reason: "TODOs should not appear in public website copy." },
|
|
26
|
-
{ pattern: /\bdraft\b/i, reason: "Draft labels should not appear in public website copy." },
|
|
27
|
-
{ pattern: /\bplaceholder\b/i, reason: "Placeholder labels should not appear in public website copy." }
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
const htmlFiles = readdirSync(websiteDir)
|
|
31
|
-
.filter((file) => file.endsWith(".html"))
|
|
32
|
-
.sort();
|
|
33
|
-
|
|
34
|
-
const jsFiles = readdirSync(websiteDir)
|
|
35
|
-
.filter((file) => file.endsWith(".js"))
|
|
36
|
-
.sort();
|
|
37
|
-
|
|
38
|
-
const mdFiles = readdirSync(websiteDir)
|
|
39
|
-
.filter((file) => file.endsWith(".md"))
|
|
40
|
-
.sort();
|
|
41
|
-
|
|
42
|
-
const failures = [];
|
|
43
|
-
const alphabet = "abcdefghijklmnopqrstuvwxyz".split("");
|
|
44
|
-
|
|
45
|
-
function visibleTextFromHtml(html) {
|
|
46
|
-
return html
|
|
47
|
-
.replace(/<script[\s\S]*?<\/script>/gi, " ")
|
|
48
|
-
.replace(/<style[\s\S]*?<\/style>/gi, " ")
|
|
49
|
-
.replace(/<!--[\s\S]*?-->/g, " ")
|
|
50
|
-
.replace(/<[^>]+>/g, " ")
|
|
51
|
-
.replace(/ /g, " ")
|
|
52
|
-
.replace(/&/g, "&")
|
|
53
|
-
.replace(/</g, "<")
|
|
54
|
-
.replace(/>/g, ">")
|
|
55
|
-
.replace(/"/g, '"')
|
|
56
|
-
.replace(/'/g, "'")
|
|
57
|
-
.replace(/\s+/g, " ")
|
|
58
|
-
.trim();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function addCopyFailures(file, text) {
|
|
62
|
-
for (const { pattern, reason } of forbiddenPatterns) {
|
|
63
|
-
const match = text.match(pattern);
|
|
64
|
-
if (match) {
|
|
65
|
-
failures.push({
|
|
66
|
-
file,
|
|
67
|
-
reason,
|
|
68
|
-
detail: `matched "${match[0]}"`
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function addBrokenLinkFailures(file, html) {
|
|
75
|
-
const linkPattern = /href=["']\.\/([^"'#?]+\.html)(?:[?#][^"']*)?["']/g;
|
|
76
|
-
for (const match of html.matchAll(linkPattern)) {
|
|
77
|
-
const linkedFile = match[1];
|
|
78
|
-
const linkedPath = path.join(websiteDir, linkedFile);
|
|
79
|
-
if (!existsSync(linkedPath)) {
|
|
80
|
-
failures.push({
|
|
81
|
-
file,
|
|
82
|
-
reason: "Local website link points to a missing page.",
|
|
83
|
-
detail: linkedFile
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function plainText(html) {
|
|
90
|
-
return html
|
|
91
|
-
.replace(/<[^>]+>/g, " ")
|
|
92
|
-
.replace(/ /g, " ")
|
|
93
|
-
.replace(/&/g, "&")
|
|
94
|
-
.replace(/</g, "<")
|
|
95
|
-
.replace(/>/g, ">")
|
|
96
|
-
.replace(/"/g, '"')
|
|
97
|
-
.replace(/'/g, "'")
|
|
98
|
-
.replace(/\s+/g, " ")
|
|
99
|
-
.trim();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function normalizeTerm(term) {
|
|
103
|
-
return term.toLowerCase().replace(/\s+/g, " ").trim();
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function compareTerms(left, right) {
|
|
107
|
-
return left.localeCompare(right, "en", { sensitivity: "base" });
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function addGlossaryFailures() {
|
|
111
|
-
const glossaryFile = "glossary.html";
|
|
112
|
-
const glossarySource = readFileSync(path.join(websiteDir, glossaryFile), "utf8");
|
|
113
|
-
const sectionPattern = /<section id="([a-z])" class="glossary-letter">([\s\S]*?)<\/section>/g;
|
|
114
|
-
const sections = [...glossarySource.matchAll(sectionPattern)].map((match) => ({
|
|
115
|
-
id: match[1],
|
|
116
|
-
body: match[2]
|
|
117
|
-
}));
|
|
118
|
-
const sectionIds = sections.map((section) => section.id);
|
|
119
|
-
const expectedSectionIds = [...sectionIds].sort();
|
|
120
|
-
|
|
121
|
-
if (sectionIds.join("") !== expectedSectionIds.join("")) {
|
|
122
|
-
failures.push({
|
|
123
|
-
file: `website/${glossaryFile}`,
|
|
124
|
-
reason: "Glossary letter sections are not in alphabetical order.",
|
|
125
|
-
detail: sectionIds.join(", ")
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const terms = [];
|
|
130
|
-
for (const section of sections) {
|
|
131
|
-
const sectionTerms = [...section.body.matchAll(/<dt>([\s\S]*?)<span>/g)]
|
|
132
|
-
.map((match) => plainText(match[1]));
|
|
133
|
-
const expectedTerms = [...sectionTerms].sort(compareTerms);
|
|
134
|
-
|
|
135
|
-
if (sectionTerms.join("||") !== expectedTerms.join("||")) {
|
|
136
|
-
failures.push({
|
|
137
|
-
file: `website/${glossaryFile}`,
|
|
138
|
-
reason: `Glossary terms under ${section.id.toUpperCase()} are not alphabetical.`,
|
|
139
|
-
detail: sectionTerms.join(", ")
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
for (const term of sectionTerms) {
|
|
144
|
-
if (term.charAt(0).toLowerCase() !== section.id) {
|
|
145
|
-
failures.push({
|
|
146
|
-
file: `website/${glossaryFile}`,
|
|
147
|
-
reason: "Glossary term appears under the wrong letter.",
|
|
148
|
-
detail: `${term} under ${section.id.toUpperCase()}`
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
terms.push(term);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const normalizedTerms = terms.map(normalizeTerm);
|
|
156
|
-
const duplicateTerms = normalizedTerms.filter((term, index) => normalizedTerms.indexOf(term) !== index);
|
|
157
|
-
for (const term of [...new Set(duplicateTerms)]) {
|
|
158
|
-
failures.push({
|
|
159
|
-
file: `website/${glossaryFile}`,
|
|
160
|
-
reason: "Glossary contains a duplicate term.",
|
|
161
|
-
detail: term
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const linkLetters = [...glossarySource.matchAll(/<a href="#([a-z])">[A-Z]<\/a>/g)].map((match) => match[1]);
|
|
166
|
-
const disabledLetters = [...glossarySource.matchAll(/<span aria-disabled="true">([A-Z])<\/span>/g)].map((match) => match[1].toLowerCase());
|
|
167
|
-
const indexLetters = [...linkLetters, ...disabledLetters].sort();
|
|
168
|
-
|
|
169
|
-
if (indexLetters.join("") !== alphabet.join("")) {
|
|
170
|
-
failures.push({
|
|
171
|
-
file: `website/${glossaryFile}`,
|
|
172
|
-
reason: "Glossary A-Z index must include every letter exactly once.",
|
|
173
|
-
detail: indexLetters.join("")
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
for (const sectionId of sectionIds) {
|
|
178
|
-
if (!linkLetters.includes(sectionId)) {
|
|
179
|
-
failures.push({
|
|
180
|
-
file: `website/${glossaryFile}`,
|
|
181
|
-
reason: "Glossary A-Z index is missing a link for a populated letter.",
|
|
182
|
-
detail: sectionId.toUpperCase()
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
for (const disabledLetter of disabledLetters) {
|
|
188
|
-
if (sectionIds.includes(disabledLetter)) {
|
|
189
|
-
failures.push({
|
|
190
|
-
file: `website/${glossaryFile}`,
|
|
191
|
-
reason: "Glossary A-Z index disables a populated letter.",
|
|
192
|
-
detail: disabledLetter.toUpperCase()
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const glossarySet = new Set(normalizedTerms);
|
|
198
|
-
for (const requiredTerm of publicTermsThatNeedGlossaryEntries()) {
|
|
199
|
-
if (!glossarySet.has(normalizeTerm(requiredTerm))) {
|
|
200
|
-
failures.push({
|
|
201
|
-
file: `website/${glossaryFile}`,
|
|
202
|
-
reason: "Glossary is missing a term used by the public website.",
|
|
203
|
-
detail: requiredTerm
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function publicTermsThatNeedGlossaryEntries() {
|
|
210
|
-
const terms = new Set();
|
|
211
|
-
|
|
212
|
-
const recordsSource = readFileSync(path.join(websiteDir, "objects.html"), "utf8");
|
|
213
|
-
for (const match of recordsSource.matchAll(/<span class="record-name">([\s\S]*?)<\/span>/g)) {
|
|
214
|
-
terms.add(plainText(match[1]));
|
|
215
|
-
}
|
|
216
|
-
for (const match of recordsSource.matchAll(/<dt>([\s\S]*?)<\/dt>/g)) {
|
|
217
|
-
terms.add(plainText(match[1]));
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
for (const file of htmlFiles.filter((file) => file.startsWith("phase-"))) {
|
|
221
|
-
const source = readFileSync(path.join(websiteDir, file), "utf8");
|
|
222
|
-
const recordsMatch = source.match(/<h2>Records<\/h2>[\s\S]*?<ul>([\s\S]*?)<\/ul>/);
|
|
223
|
-
if (!recordsMatch) continue;
|
|
224
|
-
for (const match of recordsMatch[1].matchAll(/<li>([\s\S]*?)<\/li>/g)) {
|
|
225
|
-
terms.add(plainText(match[1]));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const rolesSource = readFileSync(path.join(websiteDir, "roles.html"), "utf8");
|
|
230
|
-
for (const match of rolesSource.matchAll(/<button[^>]*>([\s\S]*?)<\/button>/g)) {
|
|
231
|
-
terms.add(plainText(match[1]));
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return [...terms].sort(compareTerms);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
for (const file of htmlFiles) {
|
|
238
|
-
const source = readFileSync(path.join(websiteDir, file), "utf8");
|
|
239
|
-
addCopyFailures(`website/${file}`, visibleTextFromHtml(source));
|
|
240
|
-
addBrokenLinkFailures(`website/${file}`, source);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
for (const file of jsFiles) {
|
|
244
|
-
const source = readFileSync(path.join(websiteDir, file), "utf8");
|
|
245
|
-
addCopyFailures(`website/${file}`, source);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
for (const file of mdFiles) {
|
|
249
|
-
const source = readFileSync(path.join(websiteDir, file), "utf8");
|
|
250
|
-
addCopyFailures(`website/${file}`, source);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
addGlossaryFailures();
|
|
254
|
-
|
|
255
|
-
if (failures.length > 0) {
|
|
256
|
-
console.error("Public website copy check failed:");
|
|
257
|
-
for (const failure of failures) {
|
|
258
|
-
console.error(`- ${failure.file}: ${failure.reason} (${failure.detail})`);
|
|
259
|
-
}
|
|
260
|
-
process.exit(1);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
console.log("Public website copy check passed.");
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
6
|
-
const workspaceBoundaryFiles = [
|
|
7
|
-
"src/http/server.ts",
|
|
8
|
-
"api/mcp.js",
|
|
9
|
-
"api/dxcomplete.js",
|
|
10
|
-
"api/auth/callback/google.js",
|
|
11
|
-
"templates/next/pages/api/mcp.js",
|
|
12
|
-
"templates/next/pages/api/dxcomplete.js",
|
|
13
|
-
"templates/next/pages/api/dxcomplete/[...path].js",
|
|
14
|
-
"templates/next/pages/api/auth/callback/google.js"
|
|
15
|
-
];
|
|
16
|
-
const bannedPatterns = [
|
|
17
|
-
{
|
|
18
|
-
pattern: /\bconnectRuntime\b/,
|
|
19
|
-
reason: "workspace MCP code must not open the runtime database"
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
pattern: /runtime\/mongo|runtime\\mongo|\.\.\/runtime\/mongo|\.\.\/dist\/runtime\/mongo/,
|
|
23
|
-
reason: "workspace MCP code must not import the Mongo runtime"
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
pattern: /runtime\/records|runtime\\records|\.\.\/runtime\/records|\.\.\/dist\/runtime\/records/,
|
|
27
|
-
reason: "workspace MCP code must not import runtime record modules"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
pattern: /runtime\/auth|runtime\\auth|\.\.\/runtime\/auth|\.\.\/dist\/runtime\/auth/,
|
|
31
|
-
reason: "workspace MCP code must not import central auth/storage modules"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
pattern: /\bDXC_MONGODB_URI\b/,
|
|
35
|
-
reason: "workspace MCP code must not reference Mongo credentials"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
pattern: /\bDXC_GOOGLE_CLIENT_ID\b|\bDXC_GOOGLE_CLIENT_SECRET\b/,
|
|
39
|
-
reason: "workspace MCP code must not reference Google OAuth provisioning secrets"
|
|
40
|
-
}
|
|
41
|
-
];
|
|
42
|
-
const failures = [];
|
|
43
|
-
|
|
44
|
-
for (const relativePath of workspaceBoundaryFiles) {
|
|
45
|
-
const absolutePath = path.join(rootDir, relativePath);
|
|
46
|
-
const content = readFileSync(absolutePath, "utf8");
|
|
47
|
-
|
|
48
|
-
for (const { pattern, reason } of bannedPatterns) {
|
|
49
|
-
if (pattern.test(content)) {
|
|
50
|
-
failures.push(`${relativePath}: ${reason}.`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (failures.length > 0) {
|
|
56
|
-
console.error("Service boundary check failed:");
|
|
57
|
-
for (const failure of failures) {
|
|
58
|
-
console.error(`- ${failure}`);
|
|
59
|
-
}
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
console.log("Service boundary check passed.");
|