@beignet/cli 0.0.39 → 0.0.41
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 +18 -0
- package/README.md +110 -16
- 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/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/db.d.ts +32 -7
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +182 -14
- package/dist/db.js.map +1 -1
- package/dist/explain.d.ts +98 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +512 -0
- package/dist/explain.js.map +1 -0
- package/dist/framework.d.ts +3 -0
- package/dist/framework.d.ts.map +1 -0
- package/dist/framework.js +6 -0
- package/dist/framework.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +79 -2
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +4 -2
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +173 -36
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +4 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +2 -0
- package/dist/lib.js.map +1 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +2 -0
- package/dist/make/payments.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +157 -3
- 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 +69 -1
- 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 +17 -4
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +10 -4
- 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/shadcn.js +1 -1
- package/dist/templates/shared.js +1 -1
- package/package.json +2 -2
- package/skills/app-structure/SKILL.md +38 -5
- 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/config.ts +7 -3
- package/src/db.ts +232 -14
- package/src/explain.ts +786 -0
- package/src/framework.ts +13 -0
- package/src/index.ts +113 -2
- package/src/inspect.ts +259 -40
- package/src/lib.ts +36 -0
- package/src/make/payments.ts +7 -0
- package/src/make.ts +232 -2
- package/src/mcp.ts +107 -1
- 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 +17 -4
- package/src/templates/base.ts +22 -4
- package/src/templates/index.ts +18 -1
- package/src/templates/server.ts +58 -1
- package/src/templates/shadcn.ts +1 -1
- package/src/templates/shared.ts +1 -1
- package/src/test-helpers/generated-app.ts +1 -1
package/dist/inspect.js
CHANGED
|
@@ -19,10 +19,7 @@ export async function inspectApp(options = {}) {
|
|
|
19
19
|
: await loadBeignetConfig(targetDir, files);
|
|
20
20
|
const convention = inspectConvention(files, config);
|
|
21
21
|
const contracts = await readContracts(targetDir, files, config);
|
|
22
|
-
const
|
|
23
|
-
file.endsWith("/route.ts"));
|
|
24
|
-
const routeExports = await readRouteExports(targetDir, routeFiles, config);
|
|
25
|
-
const matchedRoutes = matchRoutes(contracts, routeExports);
|
|
22
|
+
const matchedRoutes = await inspectRouteSurface(targetDir, files, config, contracts);
|
|
26
23
|
const diagnostics = await inspectDiagnostics(targetDir, files, config, convention, contracts, matchedRoutes, Boolean(options.strict));
|
|
27
24
|
return {
|
|
28
25
|
schemaVersion: 1,
|
|
@@ -45,10 +42,7 @@ export async function applyDoctorFixes(options = {}) {
|
|
|
45
42
|
: await loadBeignetConfig(targetDir, files);
|
|
46
43
|
const convention = inspectConvention(files, config);
|
|
47
44
|
const contracts = await readContracts(targetDir, files, config);
|
|
48
|
-
const
|
|
49
|
-
file.endsWith("/route.ts"));
|
|
50
|
-
const routeExports = await readRouteExports(targetDir, routeFiles, config);
|
|
51
|
-
const matchedRoutes = matchRoutes(contracts, routeExports);
|
|
45
|
+
const matchedRoutes = await inspectRouteSurface(targetDir, files, config, contracts);
|
|
52
46
|
const fixes = [];
|
|
53
47
|
const packageFix = await fixPackageScripts(targetDir, files, convention);
|
|
54
48
|
if (packageFix)
|
|
@@ -85,7 +79,9 @@ export function formatRoutes(result) {
|
|
|
85
79
|
contract: route.exportName,
|
|
86
80
|
handler: route.handlerSource === "missing"
|
|
87
81
|
? "missing"
|
|
88
|
-
:
|
|
82
|
+
: route.handlerSource === "web-server"
|
|
83
|
+
? `${route.handlerFile ?? "unknown"}:registry`
|
|
84
|
+
: `${route.handlerFile ?? "unknown"}:${route.handlerExport ?? route.method}`,
|
|
89
85
|
}));
|
|
90
86
|
return table([
|
|
91
87
|
["METHOD", "PATH", "CONTRACT", "HANDLER"],
|
|
@@ -265,6 +261,10 @@ function inspectConvention(files, config) {
|
|
|
265
261
|
`${directoryPath(config.paths.routes)}/`,
|
|
266
262
|
config.paths.server,
|
|
267
263
|
]);
|
|
264
|
+
const missingWebLayout = missingRequirements(files, [
|
|
265
|
+
`${directoryPath(config.paths.contracts)}/`,
|
|
266
|
+
config.paths.server,
|
|
267
|
+
]);
|
|
268
268
|
const missingResourceGenerator = missingRequirements(files, [
|
|
269
269
|
config.paths.appContext,
|
|
270
270
|
config.paths.portWiring,
|
|
@@ -273,13 +273,22 @@ function inspectConvention(files, config) {
|
|
|
273
273
|
config.paths.useCaseBuilder,
|
|
274
274
|
]);
|
|
275
275
|
const nextLayout = missingNextLayout.length === 0;
|
|
276
|
+
const webLayout = missingWebLayout.length === 0;
|
|
276
277
|
const resourceGenerator = missingResourceGenerator.length === 0;
|
|
277
278
|
return {
|
|
278
|
-
kind: resourceGenerator
|
|
279
|
+
kind: resourceGenerator
|
|
280
|
+
? "standard"
|
|
281
|
+
: config.framework === "web" && webLayout
|
|
282
|
+
? "web"
|
|
283
|
+
: nextLayout
|
|
284
|
+
? "next"
|
|
285
|
+
: "custom",
|
|
279
286
|
nextLayout,
|
|
287
|
+
webLayout,
|
|
280
288
|
resourceGenerator,
|
|
281
289
|
configFile: config.configFile,
|
|
282
290
|
missingNextLayout,
|
|
291
|
+
missingWebLayout,
|
|
283
292
|
missingResourceGenerator,
|
|
284
293
|
};
|
|
285
294
|
}
|
|
@@ -299,7 +308,7 @@ function parseContracts(source, file) {
|
|
|
299
308
|
const exportRegex = /(?:^|\n)export const\s+([A-Za-z_$][\w$]*)\s*=([\s\S]*?)(?=\nexport const\s+[A-Za-z_$][\w$]*\s*=|$)/g;
|
|
300
309
|
for (const exportMatch of source.matchAll(exportRegex)) {
|
|
301
310
|
const block = exportMatch[2];
|
|
302
|
-
const methodMatch = /([A-Za-z_$][\w$]*)(?:\s*\.\s*[A-Za-z_$][\w$]*\([^)]*\))*\s*\.\s*(get|post|put|patch|delete|head|options)\(\s*["']([^"']+)["']
|
|
311
|
+
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);
|
|
303
312
|
if (methodMatch) {
|
|
304
313
|
const receiver = methodMatch[1];
|
|
305
314
|
const pathPrefix = groupPrefixes.get(receiver) ?? "";
|
|
@@ -801,6 +810,42 @@ function matchRoutes(contracts, routeExports) {
|
|
|
801
810
|
unmatchedRouteHandlers,
|
|
802
811
|
};
|
|
803
812
|
}
|
|
813
|
+
async function inspectRouteSurface(targetDir, files, config, contracts) {
|
|
814
|
+
if (config.framework === "next") {
|
|
815
|
+
const routeFiles = files.filter((file) => file.startsWith(`${directoryPath(config.paths.routes)}/`) &&
|
|
816
|
+
file.endsWith("/route.ts"));
|
|
817
|
+
const routeExports = await readRouteExports(targetDir, routeFiles, config);
|
|
818
|
+
return matchRoutes(contracts, routeExports);
|
|
819
|
+
}
|
|
820
|
+
if (!files.includes(config.paths.server)) {
|
|
821
|
+
return {
|
|
822
|
+
routes: contracts.map((contract) => ({
|
|
823
|
+
...contract,
|
|
824
|
+
handlerSource: "missing",
|
|
825
|
+
})),
|
|
826
|
+
unmatchedRouteHandlers: [],
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
const serverSource = await readFile(path.join(targetDir, config.paths.server), "utf8");
|
|
830
|
+
const routeGroups = await readFeatureRouteGroups(targetDir, files, config);
|
|
831
|
+
const registeredGroups = await registeredRouteGroupsForServer(targetDir, files, config, serverSource);
|
|
832
|
+
const registeredContracts = contractsForRegisteredRouteGroups(contracts, routeGroups, registeredGroups);
|
|
833
|
+
const registeredKeys = new Set(registeredContracts.map(contractKey));
|
|
834
|
+
const handlerFile = routeRegistryFileFromServerSource(serverSource, config.paths.server, files) ?? config.paths.server;
|
|
835
|
+
return {
|
|
836
|
+
routes: contracts.map((contract) => registeredKeys.has(contractKey(contract))
|
|
837
|
+
? {
|
|
838
|
+
...contract,
|
|
839
|
+
handlerFile,
|
|
840
|
+
handlerSource: "web-server",
|
|
841
|
+
}
|
|
842
|
+
: {
|
|
843
|
+
...contract,
|
|
844
|
+
handlerSource: "missing",
|
|
845
|
+
}),
|
|
846
|
+
unmatchedRouteHandlers: [],
|
|
847
|
+
};
|
|
848
|
+
}
|
|
804
849
|
function findContract(contracts, routeExport) {
|
|
805
850
|
return findContracts(contracts, routeExport)[0];
|
|
806
851
|
}
|
|
@@ -828,30 +873,66 @@ function contractPathMatchesCatchAll(contractPath, catchAllPrefix) {
|
|
|
828
873
|
}
|
|
829
874
|
async function inspectDiagnostics(targetDir, files, config, convention, contracts, matchedRoutes, strict) {
|
|
830
875
|
const diagnostics = [];
|
|
831
|
-
|
|
876
|
+
const frameworkLayout = config.framework === "next" ? convention.nextLayout : convention.webLayout;
|
|
877
|
+
const missingFrameworkLayout = config.framework === "next"
|
|
878
|
+
? convention.missingNextLayout
|
|
879
|
+
: convention.missingWebLayout;
|
|
880
|
+
if (!frameworkLayout) {
|
|
881
|
+
const expectedLayout = config.framework === "next"
|
|
882
|
+
? `${directoryPath(config.paths.contracts)}/, ${directoryPath(config.paths.routes)}/, and ${config.paths.server}`
|
|
883
|
+
: `${directoryPath(config.paths.contracts)}/ and ${config.paths.server}`;
|
|
884
|
+
const layoutName = config.framework === "next"
|
|
885
|
+
? "standard Beignet app layout"
|
|
886
|
+
: "standard Beignet web app layout";
|
|
832
887
|
diagnostics.push({
|
|
833
888
|
severity: "warning",
|
|
834
889
|
code: "BEIGNET_APP_LAYOUT_NOT_FOUND",
|
|
835
|
-
message: `This directory does not match the
|
|
890
|
+
message: `This directory does not match the ${layoutName}. CLI inspection expects ${expectedLayout}. Missing: ${missingFrameworkLayout.join(", ")}. Add the missing app files or configure their paths in beignet.config.ts.`,
|
|
836
891
|
});
|
|
837
892
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
893
|
+
diagnostics.push(...(await inspectFrameworkAdapter(targetDir, files, config)));
|
|
894
|
+
if (config.framework === "next" || !convention.resourceGenerator) {
|
|
895
|
+
for (const route of matchedRoutes.routes) {
|
|
896
|
+
if (route.handlerSource !== "missing")
|
|
897
|
+
continue;
|
|
898
|
+
diagnostics.push({
|
|
899
|
+
severity: "error",
|
|
900
|
+
code: "BEIGNET_ROUTE_MISSING",
|
|
901
|
+
file: route.file,
|
|
902
|
+
contract: route.exportName,
|
|
903
|
+
message: config.framework === "next"
|
|
904
|
+
? `${route.exportName} (${route.method} ${route.path}) has no matching Next route handler. Add ${methodArticle(route.method)} ${route.method} export to ${directoryPath(config.paths.routes)}/[[...path]]/route.ts or a matching route file, or remove the unused contract.`
|
|
905
|
+
: `${route.exportName} (${route.method} ${route.path}) is not registered in the route surface passed to createFetchServer(...). Register the contract through defineRoutes(...) or remove the unused contract.`,
|
|
906
|
+
});
|
|
907
|
+
}
|
|
848
908
|
}
|
|
849
|
-
|
|
850
|
-
|
|
909
|
+
if (config.framework === "next") {
|
|
910
|
+
for (const routeHandler of matchedRoutes.unmatchedRouteHandlers) {
|
|
911
|
+
diagnostics.push(unmatchedRouteDiagnostic(routeHandler));
|
|
912
|
+
}
|
|
851
913
|
}
|
|
852
|
-
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)));
|
|
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 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)));
|
|
853
915
|
return dedupeDiagnostics(diagnostics);
|
|
854
916
|
}
|
|
917
|
+
async function inspectFrameworkAdapter(targetDir, files, config) {
|
|
918
|
+
if (!files.includes(config.paths.server))
|
|
919
|
+
return [];
|
|
920
|
+
const source = await readFile(path.join(targetDir, config.paths.server), "utf8");
|
|
921
|
+
const expectedFactory = config.framework === "next" ? "createNextServer" : "createFetchServer";
|
|
922
|
+
const otherFactory = config.framework === "next" ? "createFetchServer" : "createNextServer";
|
|
923
|
+
if (containsCallExpression(source, expectedFactory) ||
|
|
924
|
+
!containsCallExpression(source, otherFactory)) {
|
|
925
|
+
return [];
|
|
926
|
+
}
|
|
927
|
+
return [
|
|
928
|
+
{
|
|
929
|
+
severity: "error",
|
|
930
|
+
code: "BEIGNET_FRAMEWORK_MISMATCH",
|
|
931
|
+
file: config.paths.server,
|
|
932
|
+
message: `${config.configFile ?? "beignet.config.*"} selects framework: "${config.framework}", but ${config.paths.server} calls ${otherFactory}(...). Use ${expectedFactory}(...) or update the configured framework profile.`,
|
|
933
|
+
},
|
|
934
|
+
];
|
|
935
|
+
}
|
|
855
936
|
async function inspectCanonicalConformance(targetDir, files, config, convention, strict) {
|
|
856
937
|
if (!strict || !convention.resourceGenerator)
|
|
857
938
|
return [];
|
|
@@ -967,11 +1048,12 @@ async function inspectCanonicalConformance(targetDir, files, config, convention,
|
|
|
967
1048
|
});
|
|
968
1049
|
}
|
|
969
1050
|
if (!importsAppContext(serverSource, config.paths.server, config, files)) {
|
|
1051
|
+
const serverFactory = config.framework === "next" ? "createNextServer" : "createFetchServer";
|
|
970
1052
|
diagnostics.push({
|
|
971
1053
|
severity: "warning",
|
|
972
1054
|
code: "BEIGNET_SERVER_APP_CONTEXT",
|
|
973
1055
|
file: config.paths.server,
|
|
974
|
-
message: `${config.paths.server} should import type { AppContext } from ${config.paths.appContext} and thread that context through
|
|
1056
|
+
message: `${config.paths.server} should import type { AppContext } from ${config.paths.appContext} and thread that context through ${serverFactory} and hooks.`,
|
|
975
1057
|
});
|
|
976
1058
|
}
|
|
977
1059
|
// Apps may keep the context blueprint in a sibling server/context.ts
|
|
@@ -1427,7 +1509,7 @@ async function inspectProductionReadiness(targetDir, files, config, convention)
|
|
|
1427
1509
|
}
|
|
1428
1510
|
}
|
|
1429
1511
|
if (installedPackages.has("@beignet/provider-auth-better-auth")) {
|
|
1430
|
-
if (!hasBetterAuthRoute(files, config)) {
|
|
1512
|
+
if (config.framework === "next" && !hasBetterAuthRoute(files, config)) {
|
|
1431
1513
|
diagnostics.push({
|
|
1432
1514
|
severity: "warning",
|
|
1433
1515
|
code: "BEIGNET_AUTH_ROUTE_MISSING",
|
|
@@ -1573,7 +1655,8 @@ async function inspectPaymentsProductionReadiness(targetDir, files, config, sour
|
|
|
1573
1655
|
message: "@beignet/provider-payments-stripe is installed and Stripe env/config is present, but server/providers.ts still registers memory payments. Register createStripePaymentsProvider() before deploying Stripe-backed billing, or remove the Stripe provider/config until the app is ready to switch.",
|
|
1574
1656
|
});
|
|
1575
1657
|
}
|
|
1576
|
-
if (
|
|
1658
|
+
if (config.framework === "next" &&
|
|
1659
|
+
!(await hasPaymentWebhookRoute(targetDir, files, config, sourceCache))) {
|
|
1577
1660
|
diagnostics.push({
|
|
1578
1661
|
severity: "warning",
|
|
1579
1662
|
code: "BEIGNET_PAYMENTS_WEBHOOK_ROUTE_MISSING",
|
|
@@ -1817,6 +1900,56 @@ async function inspectProviderRegistrationDrift(targetDir, files, config, conven
|
|
|
1817
1900
|
}
|
|
1818
1901
|
return diagnostics;
|
|
1819
1902
|
}
|
|
1903
|
+
async function inspectInngestProductionPath(targetDir, files, config) {
|
|
1904
|
+
const packageJson = await readPackageJson(targetDir, files);
|
|
1905
|
+
if (!installedPackageNames(packageJson).has("@beignet/provider-jobs-inngest")) {
|
|
1906
|
+
return [];
|
|
1907
|
+
}
|
|
1908
|
+
const diagnostics = [];
|
|
1909
|
+
const serverDir = directoryPath(path.dirname(config.paths.server));
|
|
1910
|
+
const registryFile = `${serverDir}/inngest.ts`;
|
|
1911
|
+
const routeFile = `${directoryPath(config.paths.routes)}/inngest/route.ts`;
|
|
1912
|
+
if (!files.includes(registryFile)) {
|
|
1913
|
+
diagnostics.push({
|
|
1914
|
+
severity: "warning",
|
|
1915
|
+
code: "BEIGNET_INNGEST_REGISTRY_MISSING",
|
|
1916
|
+
file: registryFile,
|
|
1917
|
+
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.`,
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
else {
|
|
1921
|
+
const registrySource = await readFile(path.join(targetDir, registryFile), "utf8");
|
|
1922
|
+
const registry = arrayInitializerInfo(registrySource, "inngestJobs");
|
|
1923
|
+
const registeredJobs = registry
|
|
1924
|
+
? identifiersFromArrayExpression(registry.text)
|
|
1925
|
+
: new Set();
|
|
1926
|
+
const featureRoot = directoryPath(config.paths.features);
|
|
1927
|
+
const jobIndexes = files.filter((file) => new RegExp(`^${escapeRegExp(featureRoot)}/[^/]+/jobs/index\\.ts$`).test(file));
|
|
1928
|
+
for (const jobIndex of jobIndexes) {
|
|
1929
|
+
const source = await readFile(path.join(targetDir, jobIndex), "utf8");
|
|
1930
|
+
const match = source.match(/export const\s+([A-Za-z_$][\w$]*Jobs)\s*=/);
|
|
1931
|
+
const registryName = match?.[1];
|
|
1932
|
+
if (!registryName || registeredJobs.has(registryName)) {
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
diagnostics.push({
|
|
1936
|
+
severity: "warning",
|
|
1937
|
+
code: "BEIGNET_INNGEST_JOB_REGISTRATION_MISSING",
|
|
1938
|
+
file: registryFile,
|
|
1939
|
+
message: `${jobIndex} exports ${registryName}, but it is not registered in inngestJobs in ${registryFile}. Add ...${registryName} so Inngest can execute the job.`,
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
if (config.framework === "next" && !files.includes(routeFile)) {
|
|
1944
|
+
diagnostics.push({
|
|
1945
|
+
severity: "warning",
|
|
1946
|
+
code: "BEIGNET_INNGEST_ROUTE_MISSING",
|
|
1947
|
+
file: routeFile,
|
|
1948
|
+
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.`,
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
return diagnostics;
|
|
1952
|
+
}
|
|
1820
1953
|
function isFeatureUploadDefinition(file, config) {
|
|
1821
1954
|
const featuresPath = directoryPath(config.paths.features);
|
|
1822
1955
|
return new RegExp(`^${escapeRegExp(featuresPath)}/[^/]+/uploads/[^/]+\\.ts$`).test(file);
|
|
@@ -2589,7 +2722,7 @@ function callReferencesCentralListenerRegistry(args) {
|
|
|
2589
2722
|
}
|
|
2590
2723
|
for (const identifier of args.callIdentifiers) {
|
|
2591
2724
|
const imported = args.namedImports.get(identifier);
|
|
2592
|
-
if (
|
|
2725
|
+
if (imported?.importedName !== "listeners")
|
|
2593
2726
|
continue;
|
|
2594
2727
|
const importedFile = sourceFileFromImport(imported.sourcePath, args.importerFile, args.files);
|
|
2595
2728
|
if (importedFile === args.listenerRegistryFile)
|
|
@@ -2615,7 +2748,8 @@ async function inspectServerlessFootguns(targetDir, files, config, convention) {
|
|
|
2615
2748
|
const isProviderFile = /(^|\/)provider(s)?\.ts$/.test(file) ||
|
|
2616
2749
|
/(^|\/)[^/]*provider[^/]*\.ts$/.test(file);
|
|
2617
2750
|
const source = await readFile(path.join(targetDir, file), "utf8");
|
|
2618
|
-
if (
|
|
2751
|
+
if (config.framework === "next" &&
|
|
2752
|
+
convention.resourceGenerator &&
|
|
2619
2753
|
isNextRouteFile(file, config) &&
|
|
2620
2754
|
importsEagerServer(source, file, files, config)) {
|
|
2621
2755
|
diagnostics.push({
|
|
@@ -4039,13 +4173,15 @@ function registeredRouteGroups(source) {
|
|
|
4039
4173
|
}
|
|
4040
4174
|
async function registeredRouteGroupsForServer(targetDir, files, config, serverSource) {
|
|
4041
4175
|
const registeredGroups = registeredRouteGroups(serverSource);
|
|
4042
|
-
const imports =
|
|
4176
|
+
const imports = parseNamedImportSources(serverSource);
|
|
4043
4177
|
for (const identifier of routeOptionIdentifiers(serverSource)) {
|
|
4044
4178
|
const imported = imports.get(identifier);
|
|
4045
|
-
if (!imported
|
|
4179
|
+
if (!imported)
|
|
4046
4180
|
continue;
|
|
4047
|
-
|
|
4048
|
-
|
|
4181
|
+
const importedFile = sourceFileFromImport(imported.sourcePath, config.paths.server, files);
|
|
4182
|
+
if (!importedFile || !files.includes(importedFile))
|
|
4183
|
+
continue;
|
|
4184
|
+
const source = await readFile(path.join(targetDir, importedFile), {
|
|
4049
4185
|
encoding: "utf8",
|
|
4050
4186
|
});
|
|
4051
4187
|
for (const routeGroup of registeredRouteGroups(source)) {
|
|
@@ -4661,7 +4797,8 @@ async function inspectResourceSlices(targetDir, files, config, convention, stric
|
|
|
4661
4797
|
if (!hasRequirement(files, infrastructureDir)) {
|
|
4662
4798
|
diagnostics.push(resourceDiagnostic("BEIGNET_RESOURCE_INFRASTRUCTURE_MISSING", resource, infrastructureDir));
|
|
4663
4799
|
}
|
|
4664
|
-
if (
|
|
4800
|
+
if (config.framework === "next" &&
|
|
4801
|
+
!hasRequirement(files, routeFile) &&
|
|
4665
4802
|
!hasRequirement(files, catchAllRouteFile)) {
|
|
4666
4803
|
diagnostics.push(resourceDiagnostic("BEIGNET_RESOURCE_ROUTE_MISSING", resource, routeFile));
|
|
4667
4804
|
}
|