@beignet/cli 0.0.40 → 0.0.42
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 +19 -0
- package/README.md +139 -17
- package/dist/analysis/source-index.d.ts +38 -0
- package/dist/analysis/source-index.d.ts.map +1 -0
- package/dist/analysis/source-index.js +271 -0
- package/dist/analysis/source-index.js.map +1 -0
- package/dist/analysis/workspace.d.ts +16 -0
- package/dist/analysis/workspace.d.ts.map +1 -0
- package/dist/analysis/workspace.js +134 -0
- package/dist/analysis/workspace.js.map +1 -0
- package/dist/app-map-schema.d.ts +5 -0
- package/dist/app-map-schema.d.ts.map +1 -0
- package/dist/app-map-schema.js +26 -0
- package/dist/app-map-schema.js.map +1 -0
- package/dist/app-map.d.ts +96 -0
- package/dist/app-map.d.ts.map +1 -0
- package/dist/app-map.js +1141 -0
- package/dist/app-map.js.map +1 -0
- package/dist/check.d.ts +7 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +20 -5
- package/dist/check.js.map +1 -1
- package/dist/choices.d.ts +18 -0
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +35 -0
- package/dist/choices.js.map +1 -1
- package/dist/db.d.ts +49 -13
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +202 -21
- package/dist/db.js.map +1 -1
- package/dist/doctor-fixes.d.ts +64 -0
- package/dist/doctor-fixes.d.ts.map +1 -0
- package/dist/doctor-fixes.js +142 -0
- package/dist/doctor-fixes.js.map +1 -0
- package/dist/explain.d.ts +100 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +606 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +165 -21
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +33 -9
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +406 -119
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +10 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +5 -2
- package/dist/lib.js.map +1 -1
- package/dist/make/shared.js +3 -3
- package/dist/make/shared.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +63 -2
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +188 -12
- package/dist/mcp.js.map +1 -1
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +10 -0
- package/dist/preflight.js.map +1 -1
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +167 -13
- package/dist/provider-add.js.map +1 -1
- package/dist/provider-audit.d.ts +6 -0
- package/dist/provider-audit.d.ts.map +1 -1
- package/dist/provider-audit.js +63 -15
- package/dist/provider-audit.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +35 -6
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +13 -7
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +8 -1
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts +3 -0
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +55 -1
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shared.d.ts +2 -1
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +8 -5
- package/dist/templates/shared.js.map +1 -1
- package/package.json +3 -2
- package/skills/app-structure/SKILL.md +48 -6
- package/src/analysis/source-index.ts +395 -0
- package/src/analysis/workspace.ts +180 -0
- package/src/app-map-schema.ts +28 -0
- package/src/app-map.ts +1705 -0
- package/src/check.ts +27 -4
- package/src/choices.ts +57 -0
- package/src/db.ts +276 -28
- package/src/doctor-fixes.ts +252 -0
- package/src/explain.ts +894 -0
- package/src/index.ts +237 -31
- package/src/inspect.ts +567 -147
- package/src/lib.ts +64 -1
- package/src/make/shared.ts +3 -3
- package/src/make.ts +90 -2
- package/src/mcp.ts +292 -12
- package/src/preflight.ts +14 -0
- package/src/provider-add.ts +211 -12
- package/src/provider-audit.ts +127 -22
- package/src/templates/agents.ts +35 -6
- package/src/templates/base.ts +25 -6
- package/src/templates/index.ts +18 -1
- package/src/templates/server.ts +58 -1
- package/src/templates/shared.ts +15 -7
package/dist/inspect.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { parseProviderPackageMetadata } from "@beignet/core/providers";
|
|
4
4
|
import { createPainter } from "./ansi.js";
|
|
5
5
|
import { clientDirPath, clientFormsPath, clientIndexPath, defaultBeignetConfig, directoryPath, loadBeignetConfig, normalizePath, resolveConfig, } from "./config.js";
|
|
6
|
+
import { applyPlannedDoctorFixes, createDoctorFixPlan, planDoctorFixFileChange, publicDoctorFixPlan, } from "./doctor-fixes.js";
|
|
6
7
|
import { formatGithubAnnotation, } from "./github-annotations.js";
|
|
7
8
|
import { createListenersRegistrySource, listenersFilePath, providersFilePath, updateListenersRegistrySource, wireListenersProviderSource, } from "./make/shared.js";
|
|
8
9
|
import { detectedProviderVariants, diagnosticPackageJsonFile, formatProviderMetadataIssues, installedPackageNames, providerDoctorRulesForInstalledPackages, providerOperationalConfigFiles, providerRequiredEnvExists, providerRequiredTableExists, providerVariantRegistrationSeverity, readProviderListEntries, readProviderPackageMetadataSource, registrationTokenHint, serverProviderFiles, } from "./provider-audit.js";
|
|
9
10
|
import { appendToArrayExpression, appendToNamedArray, appendToOutboxRegistryArray, arrayInitializerInfo, identifiersFromArrayExpression, insertAfterImports, matchingDelimiterIndex, outboxRegistryValueInfo, } from "./registry-edits.js";
|
|
10
|
-
import { currentGeneratedPackageScripts, externalVersions, legacyGeneratedPackageScripts, } from "./templates/shared.js";
|
|
11
|
+
import { currentGeneratedPackageScripts, externalVersions, isLegacyGeneratedPackageScript, legacyGeneratedPackageScripts, } from "./templates/shared.js";
|
|
11
12
|
import { testSupportTemplateFiles } from "./templates/testing.js";
|
|
12
13
|
import { getCliVersion } from "./version.js";
|
|
13
14
|
export async function inspectApp(options = {}) {
|
|
@@ -33,7 +34,40 @@ export async function inspectApp(options = {}) {
|
|
|
33
34
|
fixes: [],
|
|
34
35
|
};
|
|
35
36
|
}
|
|
37
|
+
/** Plan every currently available low-risk doctor repair without writing. */
|
|
38
|
+
export async function planDoctorFixes(options = {}) {
|
|
39
|
+
const plan = publicDoctorFixPlan(await buildDoctorFixPlan(options));
|
|
40
|
+
const inspected = await inspectApp(options);
|
|
41
|
+
return {
|
|
42
|
+
...plan,
|
|
43
|
+
convention: inspected.convention,
|
|
44
|
+
diagnostics: inspected.diagnostics,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Apply a current doctor fix plan after verifying its plan ID. */
|
|
48
|
+
export async function applyDoctorFixPlan(options) {
|
|
49
|
+
const plan = await buildDoctorFixPlan(options);
|
|
50
|
+
if (plan.planId !== options.planId) {
|
|
51
|
+
throw new Error("Doctor fix plan is stale because the available repairs changed. No files were changed; run beignet doctor --fix --dry-run again.");
|
|
52
|
+
}
|
|
53
|
+
return applyPlannedDoctorFixes(plan, options.fixIds);
|
|
54
|
+
}
|
|
36
55
|
export async function applyDoctorFixes(options = {}) {
|
|
56
|
+
return (await applyDoctorFixesWithResult(options)).fixes;
|
|
57
|
+
}
|
|
58
|
+
export async function applyDoctorFixesWithResult(options = {}) {
|
|
59
|
+
const plan = await buildDoctorFixPlan(options);
|
|
60
|
+
return applyPlannedDoctorFixes(plan);
|
|
61
|
+
}
|
|
62
|
+
export function createDoctorFixInspectionResult(inspected, applied) {
|
|
63
|
+
return {
|
|
64
|
+
...inspected,
|
|
65
|
+
planId: applied.planId,
|
|
66
|
+
operationIds: applied.operationIds,
|
|
67
|
+
fixes: applied.fixes,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async function buildDoctorFixPlan(options = {}) {
|
|
37
71
|
const targetDir = path.resolve(options.cwd ?? process.cwd());
|
|
38
72
|
await assertDirectory(targetDir);
|
|
39
73
|
const files = await listFiles(targetDir);
|
|
@@ -43,28 +77,41 @@ export async function applyDoctorFixes(options = {}) {
|
|
|
43
77
|
const convention = inspectConvention(files, config);
|
|
44
78
|
const contracts = await readContracts(targetDir, files, config);
|
|
45
79
|
const matchedRoutes = await inspectRouteSurface(targetDir, files, config, contracts);
|
|
46
|
-
const
|
|
47
|
-
const packageFix = await
|
|
80
|
+
const operations = [];
|
|
81
|
+
const packageFix = await planPackageScripts(targetDir, files, convention);
|
|
48
82
|
if (packageFix)
|
|
49
|
-
|
|
50
|
-
const routeGroupFix = await
|
|
83
|
+
operations.push(packageFix);
|
|
84
|
+
const routeGroupFix = await planUnregisteredRouteGroups(targetDir, files, config);
|
|
51
85
|
if (routeGroupFix)
|
|
52
|
-
|
|
86
|
+
operations.push(routeGroupFix);
|
|
53
87
|
const drift = await workflowRegistrationDrift(targetDir, files, config);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
88
|
+
if (config.paths.schedules === config.paths.tasks) {
|
|
89
|
+
const workflowFix = await planCoLocatedScheduleAndTaskRegistries(targetDir, files, drift, config);
|
|
90
|
+
if (workflowFix)
|
|
91
|
+
operations.push(workflowFix);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
const scheduleFix = await planUnregisteredSchedules(targetDir, files, drift, config);
|
|
95
|
+
if (scheduleFix)
|
|
96
|
+
operations.push(scheduleFix);
|
|
97
|
+
const taskFix = await planUnregisteredTasks(targetDir, files, drift, config);
|
|
98
|
+
if (taskFix)
|
|
99
|
+
operations.push(taskFix);
|
|
100
|
+
}
|
|
101
|
+
const outboxFix = await planUnregisteredOutboxEntries(targetDir, files, drift, config);
|
|
102
|
+
if (outboxFix)
|
|
103
|
+
operations.push(outboxFix);
|
|
104
|
+
const listenerFix = await planUnregisteredListeners(targetDir, files, drift, config);
|
|
62
105
|
if (listenerFix)
|
|
63
|
-
|
|
64
|
-
const openApiFix = await
|
|
106
|
+
operations.push(listenerFix);
|
|
107
|
+
const openApiFix = await planDirectOpenApiArrayDrift(targetDir, files, config, convention, contracts, matchedRoutes);
|
|
65
108
|
if (openApiFix)
|
|
66
|
-
|
|
67
|
-
return
|
|
109
|
+
operations.push(openApiFix);
|
|
110
|
+
return createDoctorFixPlan({
|
|
111
|
+
targetDir,
|
|
112
|
+
strict: Boolean(options.strict),
|
|
113
|
+
operations,
|
|
114
|
+
});
|
|
68
115
|
}
|
|
69
116
|
/**
|
|
70
117
|
* Format inspected routes as a CLI table.
|
|
@@ -88,10 +135,54 @@ export function formatRoutes(result) {
|
|
|
88
135
|
...rows.map((row) => [row.method, row.path, row.contract, row.handler]),
|
|
89
136
|
]);
|
|
90
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Format a read-only doctor fix plan with exact patches and apply commands.
|
|
140
|
+
*/
|
|
141
|
+
export function formatDoctorFixPlan(plan, options = {}) {
|
|
142
|
+
const paint = createPainter(options.color);
|
|
143
|
+
const lines = [];
|
|
144
|
+
if (plan.operations.length === 0) {
|
|
145
|
+
lines.push(`No applicable Beignet fixes found in ${plan.targetDir}.`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
const strictFlag = plan.strict ? " --strict" : "";
|
|
149
|
+
lines.push(`Planned ${plan.operations.length} Beignet fix operation${plan.operations.length === 1 ? "" : "s"} in ${plan.targetDir}:`, "");
|
|
150
|
+
for (const operation of plan.operations) {
|
|
151
|
+
lines.push(`${paint("PLAN", "cyan")} ${operation.id}`, ...operation.fixes.map((fix) => ` ${fix.code} ${fix.file}\n ${fix.message}`), ...operation.changes.flatMap((change) => [
|
|
152
|
+
` ${change.kind === "create" ? "Create" : "Update"} ${change.file}`,
|
|
153
|
+
change.patch.trimEnd(),
|
|
154
|
+
]), "");
|
|
155
|
+
}
|
|
156
|
+
lines.push(`Plan ID: ${plan.planId}`, "", "Apply the complete plan:", ` beignet doctor --fix${strictFlag} --plan ${plan.planId}`, "", "Apply selected operations:", ` beignet doctor --fix${strictFlag} --plan ${plan.planId} --only ${plan.operations.map((operation) => operation.id).join(",")}`);
|
|
157
|
+
}
|
|
158
|
+
lines.push("", formatDoctorResult({
|
|
159
|
+
targetDir: plan.targetDir,
|
|
160
|
+
strict: plan.strict,
|
|
161
|
+
diagnostics: plan.diagnostics,
|
|
162
|
+
fixes: [],
|
|
163
|
+
}, options));
|
|
164
|
+
return lines.join("\n");
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Format planned fixes as GitHub Actions notice annotations.
|
|
168
|
+
*/
|
|
169
|
+
export function formatDoctorFixPlanGithub(plan) {
|
|
170
|
+
return [
|
|
171
|
+
...plan.operations.map((operation) => formatGithubAnnotation({
|
|
172
|
+
severity: "notice",
|
|
173
|
+
file: operation.changes[0]?.file,
|
|
174
|
+
message: `${operation.id}: ${operation.fixes.map((fix) => fix.message).join(" ")} Plan ${plan.planId}.`,
|
|
175
|
+
})),
|
|
176
|
+
...formatDoctorDiagnosticsGithub(plan.diagnostics),
|
|
177
|
+
].join("\n");
|
|
178
|
+
}
|
|
91
179
|
/**
|
|
92
180
|
* Format doctor diagnostics and applied fixes for CLI output.
|
|
93
181
|
*/
|
|
94
182
|
export function formatDoctor(result, options = {}) {
|
|
183
|
+
return formatDoctorResult(result, options);
|
|
184
|
+
}
|
|
185
|
+
function formatDoctorResult(result, options = {}) {
|
|
95
186
|
const paint = createPainter(options.color);
|
|
96
187
|
const fixLines = result.fixes.length === 0
|
|
97
188
|
? []
|
|
@@ -193,13 +284,14 @@ function formatDoctorFooter(result) {
|
|
|
193
284
|
* Format doctor diagnostics as GitHub Actions annotations.
|
|
194
285
|
*/
|
|
195
286
|
export function formatDoctorGithub(result) {
|
|
196
|
-
return result.diagnostics
|
|
197
|
-
|
|
287
|
+
return formatDoctorDiagnosticsGithub(result.diagnostics).join("\n");
|
|
288
|
+
}
|
|
289
|
+
function formatDoctorDiagnosticsGithub(diagnostics) {
|
|
290
|
+
return diagnostics.map((diagnostic) => formatGithubAnnotation({
|
|
198
291
|
severity: githubAnnotationSeverity(diagnostic.severity),
|
|
199
292
|
file: diagnostic.file,
|
|
200
293
|
message: `${diagnostic.code}: ${diagnostic.message}`,
|
|
201
|
-
}))
|
|
202
|
-
.join("\n");
|
|
294
|
+
}));
|
|
203
295
|
}
|
|
204
296
|
function githubAnnotationSeverity(severity) {
|
|
205
297
|
if (severity === "hint")
|
|
@@ -308,7 +400,7 @@ function parseContracts(source, file) {
|
|
|
308
400
|
const exportRegex = /(?:^|\n)export const\s+([A-Za-z_$][\w$]*)\s*=([\s\S]*?)(?=\nexport const\s+[A-Za-z_$][\w$]*\s*=|$)/g;
|
|
309
401
|
for (const exportMatch of source.matchAll(exportRegex)) {
|
|
310
402
|
const block = exportMatch[2];
|
|
311
|
-
const methodMatch = /([A-Za-z_$][\w$]*)(?:\s*\.\s*[A-Za-z_$][\w$]*\([^)]*\))*\s*\.\s*(get|post|put|patch|delete|head|options)\(\s*["']([^"']+)["']
|
|
403
|
+
const methodMatch = /([A-Za-z_$][\w$]*)(?:\s*\.\s*[A-Za-z_$][\w$]*\([^)]*\))*\s*\.\s*(get|post|put|patch|delete|head|options)\(\s*["']([^"']+)["'](?:\s*,\s*["'][^"']+["'])?\s*\)/.exec(block);
|
|
312
404
|
if (methodMatch) {
|
|
313
405
|
const receiver = methodMatch[1];
|
|
314
406
|
const pathPrefix = groupPrefixes.get(receiver) ?? "";
|
|
@@ -911,7 +1003,7 @@ async function inspectDiagnostics(targetDir, files, config, convention, contract
|
|
|
911
1003
|
diagnostics.push(unmatchedRouteDiagnostic(routeHandler));
|
|
912
1004
|
}
|
|
913
1005
|
}
|
|
914
|
-
diagnostics.push(...(await inspectPackageScripts(targetDir, files, convention)), ...(await inspectOpenApiDrift(targetDir, files, config, convention, contracts, matchedRoutes)), ...(await inspectFeatureRouteRegistration(targetDir, files, config, convention, contracts)), ...(await inspectRouteErrorCatalogDrift(targetDir, files, config, convention, contracts, strict)), ...(await inspectCanonicalConformance(targetDir, files, config, convention, strict)), ...(await inspectVersionSkew(targetDir, files)), ...(await inspectProviderMetadataDrift(targetDir, files, convention)), ...(await inspectPortProviderDrift(targetDir, files, config, convention)), ...(await inspectProviderRegistrationDrift(targetDir, files, config, convention)), ...(await inspectDatabaseLifecycleDrift(targetDir, files, config, convention, strict)), ...(await inspectAuthzAuditDrift(targetDir, files, config, convention, contracts)), ...(await inspectServerlessFootguns(targetDir, files, config, convention)), ...(await inspectProductionReadiness(targetDir, files, config, convention)), ...(await inspectFeatureArtifactDrift(targetDir, files, config, convention)), ...(await inspectWorkflowRegistrationDrift(targetDir, files, config, convention)), ...(await inspectRuntimeManifestDrift(targetDir, files, config, convention)), ...(await inspectResourceSlices(targetDir, files, config, convention, strict)));
|
|
1006
|
+
diagnostics.push(...(await inspectPackageScripts(targetDir, files, convention)), ...(await inspectOpenApiDrift(targetDir, files, config, convention, contracts, matchedRoutes)), ...(await inspectFeatureRouteRegistration(targetDir, files, config, convention, contracts)), ...(await inspectRouteErrorCatalogDrift(targetDir, files, config, convention, contracts, strict)), ...(await inspectCanonicalConformance(targetDir, files, config, convention, strict)), ...(await inspectVersionSkew(targetDir, files)), ...(await inspectProviderMetadataDrift(targetDir, files, convention)), ...(await inspectPortProviderDrift(targetDir, files, config, convention)), ...(await inspectProviderRegistrationDrift(targetDir, files, config, convention)), ...(await inspectInngestProductionPath(targetDir, files, config)), ...(await inspectDatabaseLifecycleDrift(targetDir, files, config, convention, strict)), ...(await inspectAuthzAuditDrift(targetDir, files, config, convention, contracts)), ...(await inspectServerlessFootguns(targetDir, files, config, convention)), ...(await inspectProductionReadiness(targetDir, files, config, convention)), ...(await inspectFeatureArtifactDrift(targetDir, files, config, convention)), ...(await inspectWorkflowRegistrationDrift(targetDir, files, config, convention)), ...(await inspectRuntimeManifestDrift(targetDir, files, config, convention)), ...(await inspectResourceSlices(targetDir, files, config, convention, strict)));
|
|
915
1007
|
return dedupeDiagnostics(diagnostics);
|
|
916
1008
|
}
|
|
917
1009
|
async function inspectFrameworkAdapter(targetDir, files, config) {
|
|
@@ -1900,6 +1992,56 @@ async function inspectProviderRegistrationDrift(targetDir, files, config, conven
|
|
|
1900
1992
|
}
|
|
1901
1993
|
return diagnostics;
|
|
1902
1994
|
}
|
|
1995
|
+
async function inspectInngestProductionPath(targetDir, files, config) {
|
|
1996
|
+
const packageJson = await readPackageJson(targetDir, files);
|
|
1997
|
+
if (!installedPackageNames(packageJson).has("@beignet/provider-jobs-inngest")) {
|
|
1998
|
+
return [];
|
|
1999
|
+
}
|
|
2000
|
+
const diagnostics = [];
|
|
2001
|
+
const serverDir = directoryPath(path.dirname(config.paths.server));
|
|
2002
|
+
const registryFile = `${serverDir}/inngest.ts`;
|
|
2003
|
+
const routeFile = `${directoryPath(config.paths.routes)}/inngest/route.ts`;
|
|
2004
|
+
if (!files.includes(registryFile)) {
|
|
2005
|
+
diagnostics.push({
|
|
2006
|
+
severity: "warning",
|
|
2007
|
+
code: "BEIGNET_INNGEST_REGISTRY_MISSING",
|
|
2008
|
+
file: registryFile,
|
|
2009
|
+
message: `@beignet/provider-jobs-inngest is installed, but ${registryFile} is missing. Run beignet provider add jobs-inngest or create a central createInngestJobFunctions(...) registry so dispatched jobs can execute.`,
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
else {
|
|
2013
|
+
const registrySource = await readFile(path.join(targetDir, registryFile), "utf8");
|
|
2014
|
+
const registry = arrayInitializerInfo(registrySource, "inngestJobs");
|
|
2015
|
+
const registeredJobs = registry
|
|
2016
|
+
? identifiersFromArrayExpression(registry.text)
|
|
2017
|
+
: new Set();
|
|
2018
|
+
const featureRoot = directoryPath(config.paths.features);
|
|
2019
|
+
const jobIndexes = files.filter((file) => new RegExp(`^${escapeRegExp(featureRoot)}/[^/]+/jobs/index\\.ts$`).test(file));
|
|
2020
|
+
for (const jobIndex of jobIndexes) {
|
|
2021
|
+
const source = await readFile(path.join(targetDir, jobIndex), "utf8");
|
|
2022
|
+
const match = source.match(/export const\s+([A-Za-z_$][\w$]*Jobs)\s*=/);
|
|
2023
|
+
const registryName = match?.[1];
|
|
2024
|
+
if (!registryName || registeredJobs.has(registryName)) {
|
|
2025
|
+
continue;
|
|
2026
|
+
}
|
|
2027
|
+
diagnostics.push({
|
|
2028
|
+
severity: "warning",
|
|
2029
|
+
code: "BEIGNET_INNGEST_JOB_REGISTRATION_MISSING",
|
|
2030
|
+
file: registryFile,
|
|
2031
|
+
message: `${jobIndex} exports ${registryName}, but it is not registered in inngestJobs in ${registryFile}. Add ...${registryName} so Inngest can execute the job.`,
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
if (config.framework === "next" && !files.includes(routeFile)) {
|
|
2036
|
+
diagnostics.push({
|
|
2037
|
+
severity: "warning",
|
|
2038
|
+
code: "BEIGNET_INNGEST_ROUTE_MISSING",
|
|
2039
|
+
file: routeFile,
|
|
2040
|
+
message: `@beignet/provider-jobs-inngest is installed in a Next.js app, but ${routeFile} is missing. Expose the shared Inngest client and function registry with serve(...) so Inngest can discover and invoke jobs.`,
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
return diagnostics;
|
|
2044
|
+
}
|
|
1903
2045
|
function isFeatureUploadDefinition(file, config) {
|
|
1904
2046
|
const featuresPath = directoryPath(config.paths.features);
|
|
1905
2047
|
return new RegExp(`^${escapeRegExp(featuresPath)}/[^/]+/uploads/[^/]+\\.ts$`).test(file);
|
|
@@ -2672,7 +2814,7 @@ function callReferencesCentralListenerRegistry(args) {
|
|
|
2672
2814
|
}
|
|
2673
2815
|
for (const identifier of args.callIdentifiers) {
|
|
2674
2816
|
const imported = args.namedImports.get(identifier);
|
|
2675
|
-
if (
|
|
2817
|
+
if (imported?.importedName !== "listeners")
|
|
2676
2818
|
continue;
|
|
2677
2819
|
const importedFile = sourceFileFromImport(imported.sourcePath, args.importerFile, args.files);
|
|
2678
2820
|
if (importedFile === args.listenerRegistryFile)
|
|
@@ -3674,7 +3816,7 @@ async function inspectPackageScripts(targetDir, files, convention) {
|
|
|
3674
3816
|
severity: "warning",
|
|
3675
3817
|
code: "BEIGNET_PACKAGE_TEST_SCRIPT_MISSING",
|
|
3676
3818
|
file: "package.json",
|
|
3677
|
-
message:
|
|
3819
|
+
message: `package.json does not define a test script. Add "test": "${currentGeneratedPackageScripts.test}" or run beignet doctor --fix.`,
|
|
3678
3820
|
});
|
|
3679
3821
|
}
|
|
3680
3822
|
const legacyScripts = hasDirectBunTestImports
|
|
@@ -3685,7 +3827,7 @@ async function inspectPackageScripts(targetDir, files, convention) {
|
|
|
3685
3827
|
severity: "warning",
|
|
3686
3828
|
code: "BEIGNET_PACKAGE_SCRIPT_LEGACY",
|
|
3687
3829
|
file: "package.json",
|
|
3688
|
-
message: `package.json still uses legacy generated
|
|
3830
|
+
message: `package.json still uses legacy generated scripts for ${legacyScripts.join(", ")}. Run beignet doctor --fix to migrate them to the current package-manager-neutral scripts.`,
|
|
3689
3831
|
});
|
|
3690
3832
|
}
|
|
3691
3833
|
const needsTsx = hasCurrentGeneratedTsxScript(scripts) &&
|
|
@@ -3711,7 +3853,7 @@ async function inspectPackageScripts(targetDir, files, convention) {
|
|
|
3711
3853
|
}
|
|
3712
3854
|
return diagnostics;
|
|
3713
3855
|
}
|
|
3714
|
-
async function
|
|
3856
|
+
async function planPackageScripts(targetDir, files, convention) {
|
|
3715
3857
|
if (!convention.resourceGenerator || !files.includes("package.json")) {
|
|
3716
3858
|
return undefined;
|
|
3717
3859
|
}
|
|
@@ -3727,8 +3869,8 @@ async function fixPackageScripts(targetDir, files, convention) {
|
|
|
3727
3869
|
updatedScripts.push("test");
|
|
3728
3870
|
}
|
|
3729
3871
|
if (!hasDirectBunTestImports) {
|
|
3730
|
-
for (const
|
|
3731
|
-
if (packageJson.scripts[name]
|
|
3872
|
+
for (const name of Object.keys(legacyGeneratedPackageScripts)) {
|
|
3873
|
+
if (isLegacyGeneratedPackageScript(name, packageJson.scripts[name])) {
|
|
3732
3874
|
packageJson.scripts[name] = currentGeneratedPackageScripts[name];
|
|
3733
3875
|
if (!updatedScripts.includes(name))
|
|
3734
3876
|
updatedScripts.push(name);
|
|
@@ -3744,47 +3886,65 @@ async function fixPackageScripts(targetDir, files, convention) {
|
|
|
3744
3886
|
packageJson.devDependencies.tsx = externalVersions.tsx;
|
|
3745
3887
|
}
|
|
3746
3888
|
const next = `${JSON.stringify(packageJson, null, "\t")}\n`;
|
|
3747
|
-
const createdSupportFiles =
|
|
3889
|
+
const createdSupportFiles = packageJson.scripts.test === currentGeneratedPackageScripts.test
|
|
3890
|
+
? missingTestSupportFiles(files)
|
|
3891
|
+
: [];
|
|
3748
3892
|
if (next === original && createdSupportFiles.length === 0)
|
|
3749
3893
|
return undefined;
|
|
3750
|
-
|
|
3751
|
-
await writeFile(filePath, next);
|
|
3894
|
+
let fix;
|
|
3752
3895
|
if (addedTestScript) {
|
|
3753
|
-
|
|
3896
|
+
fix = {
|
|
3754
3897
|
code: "BEIGNET_PACKAGE_TEST_SCRIPT_MISSING",
|
|
3755
3898
|
file: "package.json",
|
|
3756
3899
|
message: createdSupportFiles.length > 0
|
|
3757
|
-
? `Added "test": "
|
|
3758
|
-
:
|
|
3900
|
+
? `Added "test": "${currentGeneratedPackageScripts.test}" and ${createdSupportFiles.join(", ")}.`
|
|
3901
|
+
: `Added "test": "${currentGeneratedPackageScripts.test}".`,
|
|
3759
3902
|
};
|
|
3760
3903
|
}
|
|
3761
|
-
if (updatedScripts.length > 0) {
|
|
3762
|
-
|
|
3904
|
+
else if (updatedScripts.length > 0) {
|
|
3905
|
+
fix = {
|
|
3763
3906
|
code: "BEIGNET_PACKAGE_SCRIPT_LEGACY",
|
|
3764
3907
|
file: "package.json",
|
|
3765
3908
|
message: packageScriptFixMessage(updatedScripts, createdSupportFiles, addedTsx),
|
|
3766
3909
|
};
|
|
3767
3910
|
}
|
|
3768
|
-
if (createdSupportFiles.length > 0) {
|
|
3769
|
-
|
|
3911
|
+
else if (createdSupportFiles.length > 0) {
|
|
3912
|
+
fix = {
|
|
3770
3913
|
code: "BEIGNET_PACKAGE_TEST_SUPPORT_MISSING",
|
|
3771
3914
|
file: "package.json",
|
|
3772
3915
|
message: `Restored ${createdSupportFiles.join(", ")}.`,
|
|
3773
3916
|
};
|
|
3774
3917
|
}
|
|
3775
|
-
if (addedTsx) {
|
|
3776
|
-
|
|
3918
|
+
else if (addedTsx) {
|
|
3919
|
+
fix = {
|
|
3777
3920
|
code: "BEIGNET_PACKAGE_TSX_DEPENDENCY_MISSING",
|
|
3778
3921
|
file: "package.json",
|
|
3779
3922
|
message: 'Added "tsx" to devDependencies.',
|
|
3780
3923
|
};
|
|
3781
3924
|
}
|
|
3782
|
-
|
|
3925
|
+
if (!fix)
|
|
3926
|
+
return undefined;
|
|
3927
|
+
return {
|
|
3928
|
+
id: "package.repair-generated-support",
|
|
3929
|
+
fixes: [fix],
|
|
3930
|
+
changes: [
|
|
3931
|
+
...(next === original
|
|
3932
|
+
? []
|
|
3933
|
+
: [
|
|
3934
|
+
planDoctorFixFileChange({
|
|
3935
|
+
file: "package.json",
|
|
3936
|
+
before: original,
|
|
3937
|
+
after: next,
|
|
3938
|
+
}),
|
|
3939
|
+
]),
|
|
3940
|
+
...testSupportTemplateFiles
|
|
3941
|
+
.filter((file) => createdSupportFiles.includes(file.path))
|
|
3942
|
+
.map((file) => planDoctorFixFileChange({ file: file.path, after: file.content })),
|
|
3943
|
+
],
|
|
3944
|
+
};
|
|
3783
3945
|
}
|
|
3784
3946
|
function legacyGeneratedScriptNames(scripts) {
|
|
3785
|
-
return Object.
|
|
3786
|
-
.filter(([name, command]) => scripts[name] === command)
|
|
3787
|
-
.map(([name]) => name);
|
|
3947
|
+
return Object.keys(legacyGeneratedPackageScripts).filter((name) => isLegacyGeneratedPackageScript(name, scripts[name]));
|
|
3788
3948
|
}
|
|
3789
3949
|
async function appUsesDirectBunTestImports(targetDir, files) {
|
|
3790
3950
|
for (const file of files) {
|
|
@@ -3799,27 +3959,14 @@ async function appUsesDirectBunTestImports(targetDir, files) {
|
|
|
3799
3959
|
return false;
|
|
3800
3960
|
}
|
|
3801
3961
|
function hasCurrentGeneratedTsxScript(scripts) {
|
|
3802
|
-
return Object.values(currentGeneratedPackageScripts).some((command) => command.startsWith("tsx ")
|
|
3962
|
+
return Object.values(currentGeneratedPackageScripts).some((command) => (command.startsWith("tsx ") || command.includes("--import tsx")) &&
|
|
3963
|
+
Object.values(scripts).includes(command));
|
|
3803
3964
|
}
|
|
3804
3965
|
function missingTestSupportFiles(files) {
|
|
3805
3966
|
return testSupportTemplateFiles
|
|
3806
3967
|
.map((file) => file.path)
|
|
3807
3968
|
.filter((file) => !files.includes(file));
|
|
3808
3969
|
}
|
|
3809
|
-
async function createMissingTestSupportFiles(targetDir, files, shouldCreate) {
|
|
3810
|
-
if (!shouldCreate)
|
|
3811
|
-
return [];
|
|
3812
|
-
const createdSupportFiles = [];
|
|
3813
|
-
for (const file of testSupportTemplateFiles) {
|
|
3814
|
-
if (files.includes(file.path))
|
|
3815
|
-
continue;
|
|
3816
|
-
const destination = path.join(targetDir, file.path);
|
|
3817
|
-
await mkdir(path.dirname(destination), { recursive: true });
|
|
3818
|
-
await writeFile(destination, file.content);
|
|
3819
|
-
createdSupportFiles.push(file.path);
|
|
3820
|
-
}
|
|
3821
|
-
return createdSupportFiles;
|
|
3822
|
-
}
|
|
3823
3970
|
function packageScriptFixMessage(updatedScripts, createdSupportFiles, addedTsx) {
|
|
3824
3971
|
const parts = [
|
|
3825
3972
|
`Updated generated package scripts: ${updatedScripts.join(", ")}.`,
|
|
@@ -4157,7 +4304,7 @@ function findRouteGroupContract(contracts, routeGroupContract) {
|
|
|
4157
4304
|
contract.file ===
|
|
4158
4305
|
routeGroupContract.file.replace(/\.ts$/, "/index.ts")));
|
|
4159
4306
|
}
|
|
4160
|
-
async function
|
|
4307
|
+
async function planDirectOpenApiArrayDrift(targetDir, files, config, convention, contracts, matchedRoutes) {
|
|
4161
4308
|
const routePath = path.join(targetDir, config.paths.openapiRoute);
|
|
4162
4309
|
let source;
|
|
4163
4310
|
try {
|
|
@@ -4181,16 +4328,27 @@ async function fixDirectOpenApiArrayDrift(targetDir, files, config, convention,
|
|
|
4181
4328
|
const next = `${source.slice(0, firstArg.start)}${nextArray}${source.slice(firstArg.end)}`;
|
|
4182
4329
|
if (next === source)
|
|
4183
4330
|
return undefined;
|
|
4184
|
-
await writeFile(routePath, next);
|
|
4185
4331
|
return {
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4332
|
+
id: "openapi.register-missing",
|
|
4333
|
+
fixes: [
|
|
4334
|
+
{
|
|
4335
|
+
code: "BEIGNET_OPENAPI_MISSING",
|
|
4336
|
+
file: config.paths.openapiRoute,
|
|
4337
|
+
message: `Added ${missingContracts
|
|
4338
|
+
.map((contract) => contract.exportName)
|
|
4339
|
+
.join(", ")} to the direct createOpenAPIHandler contract array.`,
|
|
4340
|
+
},
|
|
4341
|
+
],
|
|
4342
|
+
changes: [
|
|
4343
|
+
planDoctorFixFileChange({
|
|
4344
|
+
file: config.paths.openapiRoute,
|
|
4345
|
+
before: source,
|
|
4346
|
+
after: next,
|
|
4347
|
+
}),
|
|
4348
|
+
],
|
|
4191
4349
|
};
|
|
4192
4350
|
}
|
|
4193
|
-
async function
|
|
4351
|
+
async function planUnregisteredRouteGroups(targetDir, files, config) {
|
|
4194
4352
|
if (!files.includes(config.paths.server))
|
|
4195
4353
|
return undefined;
|
|
4196
4354
|
const routeGroups = await readFeatureRouteGroups(targetDir, files, config);
|
|
@@ -4233,19 +4391,30 @@ async function fixUnregisteredRouteGroups(targetDir, files, config) {
|
|
|
4233
4391
|
next = `${next.slice(0, nextFirstArg.start)}${nextArray}${next.slice(nextFirstArg.end)}`;
|
|
4234
4392
|
if (next === original)
|
|
4235
4393
|
return undefined;
|
|
4236
|
-
await writeFile(targetPath, next);
|
|
4237
4394
|
return {
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4395
|
+
id: "routes.register-missing",
|
|
4396
|
+
fixes: [
|
|
4397
|
+
{
|
|
4398
|
+
code: "BEIGNET_ROUTE_GROUP_UNREGISTERED",
|
|
4399
|
+
file: targetFile,
|
|
4400
|
+
message: `Registered ${groupsToAppend
|
|
4401
|
+
.map((routeGroup) => routeGroup.name)
|
|
4402
|
+
.join(", ")} in the central route list.`,
|
|
4403
|
+
},
|
|
4404
|
+
],
|
|
4405
|
+
changes: [
|
|
4406
|
+
planDoctorFixFileChange({
|
|
4407
|
+
file: targetFile,
|
|
4408
|
+
before: original,
|
|
4409
|
+
after: next,
|
|
4410
|
+
}),
|
|
4411
|
+
],
|
|
4243
4412
|
};
|
|
4244
4413
|
}
|
|
4245
|
-
async function
|
|
4414
|
+
async function planUnregisteredSchedules(targetDir, files, drift, config) {
|
|
4246
4415
|
if (!drift.schedules.centralExists)
|
|
4247
4416
|
return undefined;
|
|
4248
|
-
return
|
|
4417
|
+
return planWorkflowRegistryOperation("schedules.register-missing", {
|
|
4249
4418
|
targetDir,
|
|
4250
4419
|
files,
|
|
4251
4420
|
centralFile: config.paths.schedules,
|
|
@@ -4256,10 +4425,10 @@ async function fixUnregisteredSchedules(targetDir, files, drift, config) {
|
|
|
4256
4425
|
append: (source, entry, importLine) => appendToNamedArray(source, "schedules", entry, importLine),
|
|
4257
4426
|
});
|
|
4258
4427
|
}
|
|
4259
|
-
async function
|
|
4428
|
+
async function planUnregisteredTasks(targetDir, files, drift, config) {
|
|
4260
4429
|
if (!drift.tasks.centralExists)
|
|
4261
4430
|
return undefined;
|
|
4262
|
-
return
|
|
4431
|
+
return planWorkflowRegistryOperation("tasks.register-missing", {
|
|
4263
4432
|
targetDir,
|
|
4264
4433
|
files,
|
|
4265
4434
|
centralFile: config.paths.tasks,
|
|
@@ -4270,6 +4439,70 @@ async function fixUnregisteredTasks(targetDir, files, drift, config) {
|
|
|
4270
4439
|
append: (source, entry, importLine) => appendToNamedArray(source, "tasks", entry, importLine),
|
|
4271
4440
|
});
|
|
4272
4441
|
}
|
|
4442
|
+
async function planCoLocatedScheduleAndTaskRegistries(targetDir, files, drift, config) {
|
|
4443
|
+
if (!drift.schedules.centralExists || !drift.tasks.centralExists) {
|
|
4444
|
+
return undefined;
|
|
4445
|
+
}
|
|
4446
|
+
const centralFile = config.paths.schedules;
|
|
4447
|
+
let original;
|
|
4448
|
+
try {
|
|
4449
|
+
original = await readFile(path.join(targetDir, centralFile), "utf8");
|
|
4450
|
+
}
|
|
4451
|
+
catch {
|
|
4452
|
+
return undefined;
|
|
4453
|
+
}
|
|
4454
|
+
let next = original;
|
|
4455
|
+
const fixes = [];
|
|
4456
|
+
const repairedKinds = [];
|
|
4457
|
+
const scheduleEdit = planWorkflowRegistryEdit({
|
|
4458
|
+
files,
|
|
4459
|
+
centralFile,
|
|
4460
|
+
source: next,
|
|
4461
|
+
unregistered: drift.schedules.unregistered,
|
|
4462
|
+
code: "BEIGNET_SCHEDULE_UNREGISTERED",
|
|
4463
|
+
listName: "the schedules array",
|
|
4464
|
+
importSpecifier: (indexFile) => `@/${modulePath(indexFile)}`,
|
|
4465
|
+
append: (source, entry, importLine) => appendToNamedArray(source, "schedules", entry, importLine),
|
|
4466
|
+
});
|
|
4467
|
+
if (scheduleEdit) {
|
|
4468
|
+
next = scheduleEdit.after;
|
|
4469
|
+
fixes.push(scheduleEdit.fix);
|
|
4470
|
+
repairedKinds.push("schedules");
|
|
4471
|
+
}
|
|
4472
|
+
const taskEdit = planWorkflowRegistryEdit({
|
|
4473
|
+
files,
|
|
4474
|
+
centralFile,
|
|
4475
|
+
source: next,
|
|
4476
|
+
unregistered: drift.tasks.unregistered,
|
|
4477
|
+
code: "BEIGNET_TASK_UNREGISTERED",
|
|
4478
|
+
listName: "defineTasks([...])",
|
|
4479
|
+
importSpecifier: (indexFile) => relativeModule(centralFile, indexFile),
|
|
4480
|
+
append: (source, entry, importLine) => appendToNamedArray(source, "tasks", entry, importLine),
|
|
4481
|
+
});
|
|
4482
|
+
if (taskEdit) {
|
|
4483
|
+
next = taskEdit.after;
|
|
4484
|
+
fixes.push(taskEdit.fix);
|
|
4485
|
+
repairedKinds.push("tasks");
|
|
4486
|
+
}
|
|
4487
|
+
if (fixes.length === 0 || next === original)
|
|
4488
|
+
return undefined;
|
|
4489
|
+
const id = repairedKinds.length === 2
|
|
4490
|
+
? "workflows.register-missing"
|
|
4491
|
+
: repairedKinds[0] === "schedules"
|
|
4492
|
+
? "schedules.register-missing"
|
|
4493
|
+
: "tasks.register-missing";
|
|
4494
|
+
return {
|
|
4495
|
+
id,
|
|
4496
|
+
fixes,
|
|
4497
|
+
changes: [
|
|
4498
|
+
planDoctorFixFileChange({
|
|
4499
|
+
file: centralFile,
|
|
4500
|
+
before: original,
|
|
4501
|
+
after: next,
|
|
4502
|
+
}),
|
|
4503
|
+
],
|
|
4504
|
+
};
|
|
4505
|
+
}
|
|
4273
4506
|
/**
|
|
4274
4507
|
* Wire fully unregistered feature listener registries into the central
|
|
4275
4508
|
* listener registry and ensure the providers file registers that central
|
|
@@ -4281,7 +4514,7 @@ async function fixUnregisteredTasks(targetDir, files, drift, config) {
|
|
|
4281
4514
|
* register against, or when the registry name is already imported from another
|
|
4282
4515
|
* file. The diagnostic stays in every bail case.
|
|
4283
4516
|
*/
|
|
4284
|
-
async function
|
|
4517
|
+
async function planUnregisteredListeners(targetDir, files, drift, config) {
|
|
4285
4518
|
const candidates = drift.listeners.unregistered.filter((entry) => entry.fullyUnregistered);
|
|
4286
4519
|
if (candidates.length === 0)
|
|
4287
4520
|
return undefined;
|
|
@@ -4357,60 +4590,93 @@ async function fixUnregisteredListeners(targetDir, files, drift, config) {
|
|
|
4357
4590
|
nextProviders === originalProviders) {
|
|
4358
4591
|
return undefined;
|
|
4359
4592
|
}
|
|
4360
|
-
await mkdir(path.dirname(listenersPath), { recursive: true });
|
|
4361
|
-
await writeFile(listenersPath, nextListeners);
|
|
4362
|
-
if (nextProviders !== originalProviders) {
|
|
4363
|
-
await writeFile(providersPath, nextProviders);
|
|
4364
|
-
}
|
|
4365
4593
|
return {
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4594
|
+
id: "listeners.register-missing",
|
|
4595
|
+
fixes: [
|
|
4596
|
+
{
|
|
4597
|
+
code: "BEIGNET_LISTENER_UNREGISTERED",
|
|
4598
|
+
file: listenersFile,
|
|
4599
|
+
message: `Registered ${candidateNames.join(", ")} in ${listenersFile} and wired listeners with registerListeners(...) in ${providersFile}.`,
|
|
4600
|
+
},
|
|
4601
|
+
],
|
|
4602
|
+
changes: [
|
|
4603
|
+
...(nextListeners === originalListeners
|
|
4604
|
+
? []
|
|
4605
|
+
: [
|
|
4606
|
+
planDoctorFixFileChange({
|
|
4607
|
+
file: listenersFile,
|
|
4608
|
+
before: originalListeners,
|
|
4609
|
+
after: nextListeners,
|
|
4610
|
+
}),
|
|
4611
|
+
]),
|
|
4612
|
+
...(nextProviders === originalProviders
|
|
4613
|
+
? []
|
|
4614
|
+
: [
|
|
4615
|
+
planDoctorFixFileChange({
|
|
4616
|
+
file: providersFile,
|
|
4617
|
+
before: originalProviders,
|
|
4618
|
+
after: nextProviders,
|
|
4619
|
+
}),
|
|
4620
|
+
]),
|
|
4621
|
+
],
|
|
4369
4622
|
};
|
|
4370
4623
|
}
|
|
4371
|
-
async function
|
|
4624
|
+
async function planUnregisteredOutboxEntries(targetDir, files, drift, config) {
|
|
4372
4625
|
if (!drift.outbox.exists)
|
|
4373
|
-
return
|
|
4626
|
+
return undefined;
|
|
4627
|
+
const outboxPath = path.join(targetDir, config.paths.outbox);
|
|
4628
|
+
let original;
|
|
4629
|
+
try {
|
|
4630
|
+
original = await readFile(outboxPath, "utf8");
|
|
4631
|
+
}
|
|
4632
|
+
catch {
|
|
4633
|
+
return undefined;
|
|
4634
|
+
}
|
|
4635
|
+
let next = original;
|
|
4374
4636
|
const fixes = [];
|
|
4375
|
-
const eventFix =
|
|
4376
|
-
targetDir,
|
|
4637
|
+
const eventFix = planWorkflowRegistryEdit({
|
|
4377
4638
|
files,
|
|
4378
4639
|
centralFile: config.paths.outbox,
|
|
4640
|
+
source: next,
|
|
4379
4641
|
unregistered: drift.outbox.events,
|
|
4380
4642
|
code: "BEIGNET_OUTBOX_EVENT_UNREGISTERED",
|
|
4381
4643
|
listName: "the outbox events list",
|
|
4382
4644
|
importSpecifier: (indexFile) => `@/${modulePath(indexFile)}`,
|
|
4383
4645
|
append: (source, entry, importLine) => appendToOutboxRegistryArray(source, "events", entry, importLine),
|
|
4384
4646
|
});
|
|
4385
|
-
if (eventFix)
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4647
|
+
if (eventFix) {
|
|
4648
|
+
next = eventFix.after;
|
|
4649
|
+
fixes.push(eventFix.fix);
|
|
4650
|
+
}
|
|
4651
|
+
const jobFix = planWorkflowRegistryEdit({
|
|
4389
4652
|
files,
|
|
4390
4653
|
centralFile: config.paths.outbox,
|
|
4654
|
+
source: next,
|
|
4391
4655
|
unregistered: drift.outbox.jobs,
|
|
4392
4656
|
code: "BEIGNET_OUTBOX_JOB_UNREGISTERED",
|
|
4393
4657
|
listName: "the outbox jobs list",
|
|
4394
4658
|
importSpecifier: (indexFile) => `@/${modulePath(indexFile)}`,
|
|
4395
4659
|
append: (source, entry, importLine) => appendToOutboxRegistryArray(source, "jobs", entry, importLine),
|
|
4396
4660
|
});
|
|
4397
|
-
if (jobFix)
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
}
|
|
4401
|
-
|
|
4402
|
-
* Append `...<feature><Kind>` registry spreads to an existing central
|
|
4403
|
-
* registry file.
|
|
4404
|
-
*
|
|
4405
|
-
* Bails without writing when an import for the registry name resolves to a
|
|
4406
|
-
* different file, when the registry anchor is missing or unparseable, or when
|
|
4407
|
-
* some members are already individually registered (appending the registry
|
|
4408
|
-
* spread would run those members twice). The diagnostic stays in that case.
|
|
4409
|
-
*/
|
|
4410
|
-
async function applyWorkflowRegistryFix(options) {
|
|
4411
|
-
const candidates = options.unregistered.filter((entry) => entry.fullyUnregistered);
|
|
4412
|
-
if (candidates.length === 0)
|
|
4661
|
+
if (jobFix) {
|
|
4662
|
+
next = jobFix.after;
|
|
4663
|
+
fixes.push(jobFix.fix);
|
|
4664
|
+
}
|
|
4665
|
+
if (fixes.length === 0 || next === original)
|
|
4413
4666
|
return undefined;
|
|
4667
|
+
return {
|
|
4668
|
+
id: "outbox.register-missing",
|
|
4669
|
+
fixes,
|
|
4670
|
+
changes: [
|
|
4671
|
+
planDoctorFixFileChange({
|
|
4672
|
+
file: config.paths.outbox,
|
|
4673
|
+
before: original,
|
|
4674
|
+
after: next,
|
|
4675
|
+
}),
|
|
4676
|
+
],
|
|
4677
|
+
};
|
|
4678
|
+
}
|
|
4679
|
+
async function planWorkflowRegistryOperation(id, options) {
|
|
4414
4680
|
const centralPath = path.join(options.targetDir, options.centralFile);
|
|
4415
4681
|
let original;
|
|
4416
4682
|
try {
|
|
@@ -4419,7 +4685,26 @@ async function applyWorkflowRegistryFix(options) {
|
|
|
4419
4685
|
catch {
|
|
4420
4686
|
return undefined;
|
|
4421
4687
|
}
|
|
4422
|
-
|
|
4688
|
+
const edit = planWorkflowRegistryEdit({ ...options, source: original });
|
|
4689
|
+
if (!edit)
|
|
4690
|
+
return undefined;
|
|
4691
|
+
return {
|
|
4692
|
+
id,
|
|
4693
|
+
fixes: [edit.fix],
|
|
4694
|
+
changes: [
|
|
4695
|
+
planDoctorFixFileChange({
|
|
4696
|
+
file: options.centralFile,
|
|
4697
|
+
before: original,
|
|
4698
|
+
after: edit.after,
|
|
4699
|
+
}),
|
|
4700
|
+
],
|
|
4701
|
+
};
|
|
4702
|
+
}
|
|
4703
|
+
function planWorkflowRegistryEdit(options) {
|
|
4704
|
+
const candidates = options.unregistered.filter((entry) => entry.fullyUnregistered);
|
|
4705
|
+
if (candidates.length === 0)
|
|
4706
|
+
return undefined;
|
|
4707
|
+
let next = options.source;
|
|
4423
4708
|
const registeredNames = [];
|
|
4424
4709
|
for (const { registry } of candidates) {
|
|
4425
4710
|
const imported = parseNamedImportSources(next).get(registry.registryName);
|
|
@@ -4440,13 +4725,15 @@ async function applyWorkflowRegistryFix(options) {
|
|
|
4440
4725
|
registeredNames.push(registry.registryName);
|
|
4441
4726
|
}
|
|
4442
4727
|
}
|
|
4443
|
-
if (next ===
|
|
4728
|
+
if (next === options.source || registeredNames.length === 0)
|
|
4444
4729
|
return undefined;
|
|
4445
|
-
await writeFile(centralPath, next);
|
|
4446
4730
|
return {
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4731
|
+
after: next,
|
|
4732
|
+
fix: {
|
|
4733
|
+
code: options.code,
|
|
4734
|
+
file: options.centralFile,
|
|
4735
|
+
message: `Registered ${registeredNames.join(", ")} in ${options.listName}.`,
|
|
4736
|
+
},
|
|
4450
4737
|
};
|
|
4451
4738
|
}
|
|
4452
4739
|
function routeRegistryFileFromServerSource(source, serverFile, files) {
|