@nifrajs/cli 3.3.0 → 4.0.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/README.md +42 -3
- package/dist/app-files.d.ts +11 -0
- package/dist/app-files.d.ts.map +1 -0
- package/dist/app-files.js +34 -0
- package/dist/app-files.js.map +1 -0
- package/dist/assure-hydration.d.ts +7 -1
- package/dist/assure-hydration.d.ts.map +1 -1
- package/dist/assure-hydration.js +128 -43
- package/dist/assure-hydration.js.map +1 -1
- package/dist/assure.d.ts.map +1 -1
- package/dist/assure.js +16 -2
- package/dist/assure.js.map +1 -1
- package/dist/capabilities-tool.d.ts +5 -1
- package/dist/capabilities-tool.d.ts.map +1 -1
- package/dist/capabilities-tool.js +32 -10
- package/dist/capabilities-tool.js.map +1 -1
- package/dist/cdn-check.d.ts +41 -0
- package/dist/cdn-check.d.ts.map +1 -0
- package/dist/cdn-check.js +207 -0
- package/dist/cdn-check.js.map +1 -0
- package/dist/check-diagnostics.d.ts +7 -15
- package/dist/check-diagnostics.d.ts.map +1 -1
- package/dist/check-diagnostics.js +162 -713
- package/dist/check-diagnostics.js.map +1 -1
- package/dist/check-scan.d.ts +58 -7
- package/dist/check-scan.d.ts.map +1 -1
- package/dist/check-scan.js +323 -62
- package/dist/check-scan.js.map +1 -1
- package/dist/check.d.ts +1 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +145 -54
- package/dist/check.js.map +1 -1
- package/dist/cli.d.ts +42 -16
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +306 -87
- package/dist/cli.js.map +1 -1
- package/dist/command-catalog.d.ts +60 -5
- package/dist/command-catalog.d.ts.map +1 -1
- package/dist/command-catalog.js +298 -33
- package/dist/command-catalog.js.map +1 -1
- package/dist/config-target.d.ts +8 -0
- package/dist/config-target.d.ts.map +1 -0
- package/dist/config-target.js +45 -0
- package/dist/config-target.js.map +1 -0
- package/dist/contracts.d.ts +3 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +56 -16
- package/dist/contracts.js.map +1 -1
- package/dist/db-audit.d.ts +37 -0
- package/dist/db-audit.d.ts.map +1 -0
- package/dist/db-audit.js +99 -0
- package/dist/db-audit.js.map +1 -0
- package/dist/db-child.d.ts +64 -0
- package/dist/db-child.d.ts.map +1 -0
- package/dist/db-child.js +213 -0
- package/dist/db-child.js.map +1 -0
- package/dist/db-config.d.ts +60 -0
- package/dist/db-config.d.ts.map +1 -0
- package/dist/db-config.js +176 -0
- package/dist/db-config.js.map +1 -0
- package/dist/db-tool.d.ts +84 -0
- package/dist/db-tool.d.ts.map +1 -0
- package/dist/db-tool.js +537 -0
- package/dist/db-tool.js.map +1 -0
- package/dist/dev-bun-config.d.ts +20 -13
- package/dist/dev-bun-config.d.ts.map +1 -1
- package/dist/dev-bun-config.js +98 -41
- package/dist/dev-bun-config.js.map +1 -1
- package/dist/dev-feed-tool.d.ts +106 -0
- package/dist/dev-feed-tool.d.ts.map +1 -0
- package/dist/dev-feed-tool.js +713 -0
- package/dist/dev-feed-tool.js.map +1 -0
- package/dist/dev-server-client.d.ts +67 -0
- package/dist/dev-server-client.d.ts.map +1 -0
- package/dist/dev-server-client.js +195 -0
- package/dist/dev-server-client.js.map +1 -0
- package/dist/diagnostics.d.ts +86 -0
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +85 -0
- package/dist/diagnostics.js.map +1 -1
- package/dist/diff-tool.d.ts +3 -3
- package/dist/diff-tool.d.ts.map +1 -1
- package/dist/diff-tool.js +6 -5
- package/dist/diff-tool.js.map +1 -1
- package/dist/docs-search.d.ts.map +1 -1
- package/dist/docs-search.js +12 -4
- package/dist/docs-search.js.map +1 -1
- package/dist/doctor.d.ts +17 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +76 -15
- package/dist/doctor.js.map +1 -1
- package/dist/env-file.d.ts +15 -0
- package/dist/env-file.d.ts.map +1 -1
- package/dist/env-file.js +73 -1
- package/dist/env-file.js.map +1 -1
- package/dist/fix-recipes.d.ts.map +1 -1
- package/dist/fix-recipes.js +41 -21
- package/dist/fix-recipes.js.map +1 -1
- package/dist/frontend-guidance.js +5 -5
- package/dist/frontend-guidance.js.map +1 -1
- package/dist/i18n-check.d.ts +16 -0
- package/dist/i18n-check.d.ts.map +1 -0
- package/dist/i18n-check.js +154 -0
- package/dist/i18n-check.js.map +1 -0
- package/dist/init-agents.d.ts +49 -13
- package/dist/init-agents.d.ts.map +1 -1
- package/dist/init-agents.js +239 -34
- package/dist/init-agents.js.map +1 -1
- package/dist/internal/code-unit-order.d.ts +4 -0
- package/dist/internal/code-unit-order.d.ts.map +1 -0
- package/dist/internal/code-unit-order.js +6 -0
- package/dist/internal/code-unit-order.js.map +1 -0
- package/dist/internal/script-kind.d.ts +7 -0
- package/dist/internal/script-kind.d.ts.map +1 -0
- package/dist/internal/script-kind.js +12 -0
- package/dist/internal/script-kind.js.map +1 -0
- package/dist/internal/source-facts.d.ts +5 -0
- package/dist/internal/source-facts.d.ts.map +1 -1
- package/dist/internal/source-facts.js +61 -5
- package/dist/internal/source-facts.js.map +1 -1
- package/dist/internal/typescript-import.d.ts +49 -24
- package/dist/internal/typescript-import.d.ts.map +1 -1
- package/dist/internal/typescript-import.js +507 -67
- package/dist/internal/typescript-import.js.map +1 -1
- package/dist/introspect.d.ts +6 -4
- package/dist/introspect.d.ts.map +1 -1
- package/dist/introspect.js +93 -74
- package/dist/introspect.js.map +1 -1
- package/dist/learn.js +3 -3
- package/dist/learn.js.map +1 -1
- package/dist/levels-tool.d.ts +10 -0
- package/dist/levels-tool.d.ts.map +1 -1
- package/dist/levels-tool.js +33 -3
- package/dist/levels-tool.js.map +1 -1
- package/dist/load.d.ts +71 -16
- package/dist/load.d.ts.map +1 -1
- package/dist/load.js +78 -18
- package/dist/load.js.map +1 -1
- package/dist/manifest-tool.d.ts.map +1 -1
- package/dist/manifest-tool.js +3 -2
- package/dist/manifest-tool.js.map +1 -1
- package/dist/mcp-app-tool.d.ts.map +1 -1
- package/dist/mcp-app-tool.js +2 -0
- package/dist/mcp-app-tool.js.map +1 -1
- package/dist/mcp-context.d.ts.map +1 -1
- package/dist/mcp-context.js +5 -6
- package/dist/mcp-context.js.map +1 -1
- package/dist/mcp-delegate.d.ts +57 -0
- package/dist/mcp-delegate.d.ts.map +1 -0
- package/dist/mcp-delegate.js +130 -0
- package/dist/mcp-delegate.js.map +1 -0
- package/dist/mcp-docs-tools.d.ts.map +1 -1
- package/dist/mcp-docs-tools.js +23 -5
- package/dist/mcp-docs-tools.js.map +1 -1
- package/dist/mcp-exec.d.ts +3 -0
- package/dist/mcp-exec.d.ts.map +1 -1
- package/dist/mcp-exec.js +263 -131
- package/dist/mcp-exec.js.map +1 -1
- package/dist/mcp-http.d.ts.map +1 -1
- package/dist/mcp-http.js +20 -5
- package/dist/mcp-http.js.map +1 -1
- package/dist/mcp-io.d.ts +26 -0
- package/dist/mcp-io.d.ts.map +1 -1
- package/dist/mcp-io.js +113 -0
- package/dist/mcp-io.js.map +1 -1
- package/dist/mcp-isolate.d.ts +48 -0
- package/dist/mcp-isolate.d.ts.map +1 -0
- package/dist/mcp-isolate.js +222 -0
- package/dist/mcp-isolate.js.map +1 -0
- package/dist/mcp-path.d.ts +10 -0
- package/dist/mcp-path.d.ts.map +1 -0
- package/dist/mcp-path.js +51 -0
- package/dist/mcp-path.js.map +1 -0
- package/dist/mcp-project-child.d.ts +74 -0
- package/dist/mcp-project-child.d.ts.map +1 -0
- package/dist/mcp-project-child.js +130 -0
- package/dist/mcp-project-child.js.map +1 -0
- package/dist/mcp-reflect.d.ts.map +1 -1
- package/dist/mcp-reflect.js +2 -2
- package/dist/mcp-reflect.js.map +1 -1
- package/dist/mcp-render.d.ts.map +1 -1
- package/dist/mcp-render.js +64 -45
- package/dist/mcp-render.js.map +1 -1
- package/dist/mcp-root.d.ts +37 -9
- package/dist/mcp-root.d.ts.map +1 -1
- package/dist/mcp-root.js +124 -25
- package/dist/mcp-root.js.map +1 -1
- package/dist/mcp-run.d.ts +18 -1
- package/dist/mcp-run.d.ts.map +1 -1
- package/dist/mcp-run.js +159 -55
- package/dist/mcp-run.js.map +1 -1
- package/dist/mcp-ws.js +4 -1
- package/dist/mcp-ws.js.map +1 -1
- package/dist/mcp.d.ts +18 -5
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +165 -82
- package/dist/mcp.js.map +1 -1
- package/dist/migrate-layout.d.ts +44 -0
- package/dist/migrate-layout.d.ts.map +1 -0
- package/dist/migrate-layout.js +982 -0
- package/dist/migrate-layout.js.map +1 -0
- package/dist/openapi-tool.d.ts +2 -2
- package/dist/openapi-tool.d.ts.map +1 -1
- package/dist/openapi-tool.js.map +1 -1
- package/dist/openapi-types.d.ts +2 -1
- package/dist/openapi-types.d.ts.map +1 -1
- package/dist/openapi-types.js +240 -12
- package/dist/openapi-types.js.map +1 -1
- package/dist/pipeline-report.d.ts +3 -2
- package/dist/pipeline-report.d.ts.map +1 -1
- package/dist/pipeline-report.js +19 -16
- package/dist/pipeline-report.js.map +1 -1
- package/dist/port.d.ts +5 -4
- package/dist/port.d.ts.map +1 -1
- package/dist/port.js +46 -29
- package/dist/port.js.map +1 -1
- package/dist/project-facts.d.ts +28 -4
- package/dist/project-facts.d.ts.map +1 -1
- package/dist/project-facts.js +12 -2
- package/dist/project-facts.js.map +1 -1
- package/dist/recipes/4.0.0.d.ts +9 -0
- package/dist/recipes/4.0.0.d.ts.map +1 -0
- package/dist/recipes/4.0.0.js +34 -0
- package/dist/recipes/4.0.0.js.map +1 -0
- package/dist/recipes/index.d.ts.map +1 -1
- package/dist/recipes/index.js +2 -0
- package/dist/recipes/index.js.map +1 -1
- package/dist/release-verification.d.ts +11 -1
- package/dist/release-verification.d.ts.map +1 -1
- package/dist/release-verification.js +47 -4
- package/dist/release-verification.js.map +1 -1
- package/dist/review.d.ts +21 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +823 -0
- package/dist/review.js.map +1 -0
- package/dist/route-types.d.ts +28 -0
- package/dist/route-types.d.ts.map +1 -0
- package/dist/route-types.js +106 -0
- package/dist/route-types.js.map +1 -0
- package/dist/rules/codes.d.ts +9 -0
- package/dist/rules/codes.d.ts.map +1 -1
- package/dist/rules/codes.js +9 -0
- package/dist/rules/codes.js.map +1 -1
- package/dist/rules/data-guard.d.ts +12 -0
- package/dist/rules/data-guard.d.ts.map +1 -0
- package/dist/rules/data-guard.js +213 -0
- package/dist/rules/data-guard.js.map +1 -0
- package/dist/rules/index.d.ts +3 -0
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +17 -5
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/islands.d.ts.map +1 -1
- package/dist/rules/islands.js +7 -4
- package/dist/rules/islands.js.map +1 -1
- package/dist/rules/legacy.d.ts +3 -1
- package/dist/rules/legacy.d.ts.map +1 -1
- package/dist/rules/legacy.js +829 -34
- package/dist/rules/legacy.js.map +1 -1
- package/dist/rules/nano.d.ts.map +1 -1
- package/dist/rules/nano.js +15 -6
- package/dist/rules/nano.js.map +1 -1
- package/dist/rules/pages.d.ts +15 -0
- package/dist/rules/pages.d.ts.map +1 -0
- package/dist/rules/pages.js +88 -0
- package/dist/rules/pages.js.map +1 -0
- package/dist/rules/routes.d.ts +11 -0
- package/dist/rules/routes.d.ts.map +1 -1
- package/dist/rules/routes.js +180 -2
- package/dist/rules/routes.js.map +1 -1
- package/dist/rules/secrets.d.ts +10 -0
- package/dist/rules/secrets.d.ts.map +1 -0
- package/dist/rules/secrets.js +169 -0
- package/dist/rules/secrets.js.map +1 -0
- package/dist/rules/security.d.ts +7 -0
- package/dist/rules/security.d.ts.map +1 -1
- package/dist/rules/security.js +171 -65
- package/dist/rules/security.js.map +1 -1
- package/dist/rules/typescript.d.ts +16 -0
- package/dist/rules/typescript.d.ts.map +1 -0
- package/dist/rules/typescript.js +59 -0
- package/dist/rules/typescript.js.map +1 -0
- package/dist/rules/zones.d.ts +17 -0
- package/dist/rules/zones.d.ts.map +1 -0
- package/dist/rules/zones.js +174 -0
- package/dist/rules/zones.js.map +1 -0
- package/dist/scaffold.d.ts +25 -12
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +154 -90
- package/dist/scaffold.js.map +1 -1
- package/dist/sdk.d.ts +17 -3
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +941 -153
- package/dist/sdk.js.map +1 -1
- package/dist/search-terms.d.ts +2 -0
- package/dist/search-terms.d.ts.map +1 -1
- package/dist/search-terms.js +23 -8
- package/dist/search-terms.js.map +1 -1
- package/dist/smoke.d.ts +79 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +494 -0
- package/dist/smoke.js.map +1 -0
- package/dist/sync-manifest.d.ts.map +1 -1
- package/dist/sync-manifest.js +2 -1
- package/dist/sync-manifest.js.map +1 -1
- package/dist/sync-routes.d.ts +4 -3
- package/dist/sync-routes.d.ts.map +1 -1
- package/dist/sync-routes.js +4 -3
- package/dist/sync-routes.js.map +1 -1
- package/dist/test-tool.d.ts.map +1 -1
- package/dist/test-tool.js +30 -11
- package/dist/test-tool.js.map +1 -1
- package/dist/types-search.d.ts.map +1 -1
- package/dist/types-search.js +11 -5
- package/dist/types-search.js.map +1 -1
- package/dist/upgrade.d.ts +36 -6
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +167 -29
- package/dist/upgrade.js.map +1 -1
- package/dist/verification-plan.d.ts.map +1 -1
- package/dist/verification-plan.js +8 -0
- package/dist/verification-plan.js.map +1 -1
- package/dist/verification.d.ts.map +1 -1
- package/dist/verification.js +45 -12
- package/dist/verification.js.map +1 -1
- package/dist/web-app-options.d.ts +19 -0
- package/dist/web-app-options.d.ts.map +1 -0
- package/dist/web-app-options.js +49 -0
- package/dist/web-app-options.js.map +1 -0
- package/dist/work-graph.d.ts +3 -0
- package/dist/work-graph.d.ts.map +1 -1
- package/dist/work-graph.js +33 -16
- package/dist/work-graph.js.map +1 -1
- package/docs/examples.json +185 -29
- package/docs/llms-full.txt +4123 -548
- package/docs/types.json +14391 -10060
- package/package.json +19 -11
- package/src/app-files.ts +40 -0
- package/src/assure-hydration.ts +139 -46
- package/src/assure.ts +17 -2
- package/src/capabilities-tool.ts +35 -9
- package/src/cdn-check.ts +268 -0
- package/src/check-diagnostics.ts +199 -797
- package/src/check-scan.ts +394 -63
- package/src/check.ts +146 -58
- package/src/cli.ts +347 -92
- package/src/command-catalog.ts +407 -45
- package/src/config-target.ts +47 -0
- package/src/contracts.ts +66 -15
- package/src/db-audit.ts +128 -0
- package/src/db-child.ts +285 -0
- package/src/db-config.ts +224 -0
- package/src/db-tool.ts +690 -0
- package/src/dev-bun-config.ts +116 -40
- package/src/dev-feed-tool.ts +912 -0
- package/src/dev-server-client.ts +267 -0
- package/src/diagnostics.ts +179 -0
- package/src/diff-tool.ts +6 -5
- package/src/docs-search.ts +10 -3
- package/src/doctor.ts +98 -15
- package/src/env-file.ts +78 -1
- package/src/fix-recipes.ts +44 -31
- package/src/frontend-guidance.ts +5 -5
- package/src/i18n-check.ts +179 -0
- package/src/init-agents.ts +301 -38
- package/src/internal/code-unit-order.ts +5 -0
- package/src/internal/script-kind.ts +11 -0
- package/src/internal/source-facts.ts +96 -22
- package/src/internal/typescript-import.ts +663 -68
- package/src/introspect.ts +93 -82
- package/src/learn.ts +3 -3
- package/src/levels-tool.ts +47 -2
- package/src/load.ts +144 -32
- package/src/manifest-tool.ts +3 -2
- package/src/mcp-app-tool.ts +2 -0
- package/src/mcp-context.ts +5 -7
- package/src/mcp-delegate.ts +162 -0
- package/src/mcp-docs-tools.ts +23 -5
- package/src/mcp-exec.ts +301 -161
- package/src/mcp-http.ts +22 -5
- package/src/mcp-io.ts +130 -0
- package/src/mcp-isolate.ts +287 -0
- package/src/mcp-path.ts +50 -0
- package/src/mcp-project-child.ts +182 -0
- package/src/mcp-reflect.ts +3 -2
- package/src/mcp-render.ts +72 -42
- package/src/mcp-root.ts +130 -24
- package/src/mcp-run.ts +190 -56
- package/src/mcp-ws.ts +4 -1
- package/src/mcp.ts +193 -91
- package/src/migrate-layout.ts +1121 -0
- package/src/openapi-tool.ts +7 -3
- package/src/openapi-types.ts +315 -13
- package/src/pipeline-report.ts +22 -18
- package/src/port.ts +53 -30
- package/src/project-facts.ts +42 -8
- package/src/recipes/4.0.0.ts +35 -0
- package/src/recipes/index.ts +2 -0
- package/src/release-verification.ts +74 -5
- package/src/review.ts +988 -0
- package/src/route-types.ts +133 -0
- package/src/rules/codes.ts +9 -0
- package/src/rules/data-guard.ts +230 -0
- package/src/rules/index.ts +18 -5
- package/src/rules/islands.ts +12 -4
- package/src/rules/legacy.ts +947 -35
- package/src/rules/nano.ts +18 -6
- package/src/rules/pages.ts +99 -0
- package/src/rules/routes.ts +181 -2
- package/src/rules/secrets.ts +174 -0
- package/src/rules/security.ts +198 -70
- package/src/rules/typescript.ts +77 -0
- package/src/rules/zones.ts +195 -0
- package/src/scaffold.ts +175 -98
- package/src/sdk.ts +1215 -158
- package/src/search-terms.ts +24 -7
- package/src/smoke.ts +607 -0
- package/src/sync-manifest.ts +2 -1
- package/src/sync-routes.ts +4 -3
- package/src/test-tool.ts +32 -11
- package/src/types-search.ts +11 -4
- package/src/upgrade.ts +205 -25
- package/src/verification-plan.ts +27 -0
- package/src/verification.ts +50 -12
- package/src/web-app-options.ts +57 -0
- package/src/work-graph.ts +40 -16
package/src/openapi-tool.ts
CHANGED
|
@@ -54,7 +54,7 @@ function toYaml(value: unknown, depth = 0): string {
|
|
|
54
54
|
* worth the risk of emitting a `$ref` with no target.
|
|
55
55
|
*/
|
|
56
56
|
function renderOpenApiWithResponses(
|
|
57
|
-
app: LoadedApp,
|
|
57
|
+
app: Pick<LoadedApp, "cwd" | "backend">,
|
|
58
58
|
format: OpenApiFormat,
|
|
59
59
|
pathPrefix: string | undefined,
|
|
60
60
|
inferredResponses: InferredOpenAPIResponses | undefined,
|
|
@@ -81,7 +81,11 @@ function renderOpenApiWithResponses(
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/** Render the current runtime route table without loading the TypeScript compiler. */
|
|
84
|
-
export function renderOpenApi(
|
|
84
|
+
export function renderOpenApi(
|
|
85
|
+
app: Pick<LoadedApp, "cwd" | "backend">,
|
|
86
|
+
format: OpenApiFormat,
|
|
87
|
+
pathPrefix?: string,
|
|
88
|
+
): string {
|
|
85
89
|
return renderOpenApiWithResponses(app, format, pathPrefix, undefined)
|
|
86
90
|
}
|
|
87
91
|
|
|
@@ -90,7 +94,7 @@ export function renderOpenApi(app: LoadedApp, format: OpenApiFormat, pathPrefix?
|
|
|
90
94
|
* only in this explicit CLI path; the server and schema runtime packages remain compiler-free.
|
|
91
95
|
*/
|
|
92
96
|
export async function renderOpenApiWithTypes(
|
|
93
|
-
app: LoadedApp,
|
|
97
|
+
app: Pick<LoadedApp, "cwd" | "backend">,
|
|
94
98
|
format: OpenApiFormat,
|
|
95
99
|
pathPrefix?: string,
|
|
96
100
|
): Promise<string> {
|
package/src/openapi-types.ts
CHANGED
|
@@ -8,11 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
import { existsSync } from "node:fs"
|
|
10
10
|
import { mkdtemp, rm, writeFile } from "node:fs/promises"
|
|
11
|
-
import {
|
|
12
|
-
import { dirname, join, resolve } from "node:path"
|
|
11
|
+
import { basename, dirname, join, resolve } from "node:path"
|
|
13
12
|
import type { JsonSchema } from "@nifrajs/core/reflection"
|
|
14
13
|
import type * as TSApi from "typescript"
|
|
15
|
-
import {
|
|
14
|
+
import { BACKEND_APP_FILE } from "./app-files.ts"
|
|
15
|
+
import {
|
|
16
|
+
loadProjectTypeScript,
|
|
17
|
+
type TypeScriptApi,
|
|
18
|
+
type TypeScriptSemanticSession,
|
|
19
|
+
type TypeScriptSession,
|
|
20
|
+
} from "./internal/typescript-import.ts"
|
|
16
21
|
|
|
17
22
|
export interface InferredOpenAPIResponse {
|
|
18
23
|
readonly description?: string
|
|
@@ -137,6 +142,254 @@ function propertyType(checker: TypeChecker, type: Type, name: string): Type | un
|
|
|
137
142
|
: checker.getTypeOfSymbolAtLocation(property, declaration)
|
|
138
143
|
}
|
|
139
144
|
|
|
145
|
+
interface TypeScript7Node {
|
|
146
|
+
readonly kind: number
|
|
147
|
+
readonly pos: number
|
|
148
|
+
readonly end: number
|
|
149
|
+
getStart(sourceFile?: TypeScript7SourceFile): number
|
|
150
|
+
getText(sourceFile?: TypeScript7SourceFile): string
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
interface TypeScript7SourceFile extends TypeScript7Node {
|
|
154
|
+
readonly fileName: string
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
interface TypeScript7Symbol {
|
|
158
|
+
readonly name: string
|
|
159
|
+
readonly flags: number
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface TypeScript7Type {
|
|
163
|
+
readonly flags: number
|
|
164
|
+
readonly value?: string | number | boolean | bigint
|
|
165
|
+
getTypes(): Promise<readonly TypeScript7Type[] | undefined>
|
|
166
|
+
getSymbol(): Promise<TypeScript7Symbol | undefined>
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
interface TypeScript7Checker {
|
|
170
|
+
getTypeAtPosition(file: string, position: number): Promise<TypeScript7Type | undefined>
|
|
171
|
+
getTypeOfSymbol(symbol: TypeScript7Symbol): Promise<TypeScript7Type | undefined>
|
|
172
|
+
getTypeOfSymbolAtLocation(
|
|
173
|
+
symbol: TypeScript7Symbol,
|
|
174
|
+
location: TypeScript7Node,
|
|
175
|
+
): Promise<TypeScript7Type>
|
|
176
|
+
getPropertiesOfType(type: TypeScript7Type): Promise<readonly TypeScript7Symbol[]>
|
|
177
|
+
getPropertyOfType(type: TypeScript7Type, name: string): Promise<TypeScript7Symbol | undefined>
|
|
178
|
+
getDeclaredTypeOfSymbol(symbol: TypeScript7Symbol): Promise<TypeScript7Type>
|
|
179
|
+
getApparentType(type: TypeScript7Type): Promise<TypeScript7Type | undefined>
|
|
180
|
+
getSignaturesOfType(type: TypeScript7Type, kind: number): Promise<readonly unknown[]>
|
|
181
|
+
getTypeArguments(type: TypeScript7Type): Promise<readonly TypeScript7Type[]>
|
|
182
|
+
getNonNullableType(type: TypeScript7Type): Promise<TypeScript7Type | undefined>
|
|
183
|
+
typeToString(type: TypeScript7Type): Promise<string>
|
|
184
|
+
isTupleType(type: TypeScript7Type): Promise<boolean>
|
|
185
|
+
isArrayType(type: TypeScript7Type): Promise<boolean>
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const semanticChecker = async (
|
|
189
|
+
semantic: TypeScriptSemanticSession,
|
|
190
|
+
file: string,
|
|
191
|
+
): Promise<TypeScript7Checker> =>
|
|
192
|
+
((await semantic.getCheckerForFile(file)) ?? semantic.checker) as TypeScript7Checker
|
|
193
|
+
|
|
194
|
+
const semanticSource = async (
|
|
195
|
+
semantic: TypeScriptSemanticSession,
|
|
196
|
+
file: string,
|
|
197
|
+
): Promise<TypeScript7SourceFile | undefined> =>
|
|
198
|
+
(await semantic.getSourceFile(file)) as TypeScript7SourceFile | undefined
|
|
199
|
+
|
|
200
|
+
function semanticFlag(semantic: TypeScriptSemanticSession, name: string): number {
|
|
201
|
+
const value = semantic.typeFlags[name]
|
|
202
|
+
if (typeof value !== "number") throw new Error(`TypeScript 7 TypeFlags.${name} is unavailable`)
|
|
203
|
+
return value
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function semanticSymbolFlag(semantic: TypeScriptSemanticSession, name: string): number {
|
|
207
|
+
const value = semantic.symbolFlags[name]
|
|
208
|
+
if (typeof value !== "number") throw new Error(`TypeScript 7 SymbolFlags.${name} is unavailable`)
|
|
209
|
+
return value
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function semanticSignatureKind(semantic: TypeScriptSemanticSession, name: string): number {
|
|
213
|
+
const value = semantic.signatureKind[name]
|
|
214
|
+
if (typeof value !== "number")
|
|
215
|
+
throw new Error(`TypeScript 7 SignatureKind.${name} is unavailable`)
|
|
216
|
+
return value
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function schemaForType7(
|
|
220
|
+
semantic: TypeScriptSemanticSession,
|
|
221
|
+
checker: TypeScript7Checker,
|
|
222
|
+
input: TypeScript7Type,
|
|
223
|
+
seen: Set<TypeScript7Type>,
|
|
224
|
+
depth: number,
|
|
225
|
+
): Promise<JsonSchema | undefined> {
|
|
226
|
+
if (depth > 20 || seen.has(input)) return undefined
|
|
227
|
+
const anyFlag = semanticFlag(semantic, "Any")
|
|
228
|
+
const unknownFlag = semanticFlag(semantic, "Unknown")
|
|
229
|
+
const neverFlag = semanticFlag(semantic, "Never")
|
|
230
|
+
const undefinedFlag = semanticFlag(semantic, "Undefined")
|
|
231
|
+
const voidFlag = semanticFlag(semantic, "Void")
|
|
232
|
+
const stringLiteralFlag = semanticFlag(semantic, "StringLiteral")
|
|
233
|
+
const numberLiteralFlag = semanticFlag(semantic, "NumberLiteral")
|
|
234
|
+
const booleanLiteralFlag = semanticFlag(semantic, "BooleanLiteral")
|
|
235
|
+
const stringLikeFlag = semanticFlag(semantic, "StringLike")
|
|
236
|
+
const numberLikeFlag = semanticFlag(semantic, "NumberLike")
|
|
237
|
+
const booleanLikeFlag = semanticFlag(semantic, "BooleanLike")
|
|
238
|
+
const bigintLikeFlag = semanticFlag(semantic, "BigIntLike")
|
|
239
|
+
const nullFlag = semanticFlag(semantic, "Null")
|
|
240
|
+
const unionFlag = semanticFlag(semantic, "Union")
|
|
241
|
+
const objectFlag = semanticFlag(semantic, "Object")
|
|
242
|
+
|
|
243
|
+
if ((input.flags & (anyFlag | unknownFlag)) !== 0) return undefined
|
|
244
|
+
if ((input.flags & neverFlag) !== 0) return undefined
|
|
245
|
+
if ((input.flags & (undefinedFlag | voidFlag)) !== 0) return undefined
|
|
246
|
+
if ((input.flags & stringLiteralFlag) !== 0 && typeof input.value === "string") {
|
|
247
|
+
return { const: input.value }
|
|
248
|
+
}
|
|
249
|
+
if ((input.flags & numberLiteralFlag) !== 0 && typeof input.value === "number") {
|
|
250
|
+
return { const: input.value }
|
|
251
|
+
}
|
|
252
|
+
if ((input.flags & booleanLiteralFlag) !== 0) {
|
|
253
|
+
return { const: (await checker.typeToString(input)) === "true" }
|
|
254
|
+
}
|
|
255
|
+
if ((input.flags & stringLikeFlag) !== 0) return { type: "string" }
|
|
256
|
+
if ((input.flags & numberLikeFlag) !== 0) return { type: "number" }
|
|
257
|
+
if ((input.flags & booleanLikeFlag) !== 0) return { type: "boolean" }
|
|
258
|
+
if ((input.flags & bigintLikeFlag) !== 0) return { type: "integer" }
|
|
259
|
+
if ((input.flags & nullFlag) !== 0) return { type: "null" }
|
|
260
|
+
|
|
261
|
+
if ((input.flags & unionFlag) !== 0) {
|
|
262
|
+
const members = (await input.getTypes()) ?? []
|
|
263
|
+
const schemas = (
|
|
264
|
+
await Promise.all(
|
|
265
|
+
members.map((member) => schemaForType7(semantic, checker, member, seen, depth + 1)),
|
|
266
|
+
)
|
|
267
|
+
).filter((schema): schema is JsonSchema => schema !== undefined)
|
|
268
|
+
if (schemas.length === 0) return undefined
|
|
269
|
+
if (schemas.length === 1) return schemas[0]
|
|
270
|
+
return { anyOf: schemas }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (await checker.isTupleType(input)) {
|
|
274
|
+
const elements = (
|
|
275
|
+
await Promise.all(
|
|
276
|
+
(
|
|
277
|
+
await checker.getTypeArguments(input)
|
|
278
|
+
).map((member) => schemaForType7(semantic, checker, member, seen, depth + 1)),
|
|
279
|
+
)
|
|
280
|
+
).filter((schema): schema is JsonSchema => schema !== undefined)
|
|
281
|
+
return elements.length === 0 ? undefined : { type: "array", prefixItems: elements }
|
|
282
|
+
}
|
|
283
|
+
if (await checker.isArrayType(input)) {
|
|
284
|
+
const [element] = await checker.getTypeArguments(input)
|
|
285
|
+
const items =
|
|
286
|
+
element === undefined
|
|
287
|
+
? undefined
|
|
288
|
+
: await schemaForType7(semantic, checker, element, seen, depth + 1)
|
|
289
|
+
return items === undefined ? { type: "array" } : { type: "array", items }
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const symbol = await input.getSymbol()
|
|
293
|
+
if (symbol?.name === "Date") return { type: "string", format: "date-time" }
|
|
294
|
+
if ((input.flags & objectFlag) === 0) return undefined
|
|
295
|
+
|
|
296
|
+
seen.add(input)
|
|
297
|
+
try {
|
|
298
|
+
const properties: Record<string, JsonSchema> = {}
|
|
299
|
+
const required: string[] = []
|
|
300
|
+
for (const property of await checker.getPropertiesOfType(input)) {
|
|
301
|
+
const propertyType = await checker.getTypeOfSymbol(property)
|
|
302
|
+
if (propertyType === undefined) return undefined
|
|
303
|
+
if (
|
|
304
|
+
(await checker.getSignaturesOfType(propertyType, semanticSignatureKind(semantic, "Call")))
|
|
305
|
+
.length > 0
|
|
306
|
+
)
|
|
307
|
+
continue
|
|
308
|
+
const propertySchema = await schemaForType7(semantic, checker, propertyType, seen, depth + 1)
|
|
309
|
+
if (propertySchema === undefined) return undefined
|
|
310
|
+
properties[property.name] = propertySchema
|
|
311
|
+
if ((property.flags & semanticSymbolFlag(semantic, "Optional")) === 0) {
|
|
312
|
+
required.push(property.name)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (Object.keys(properties).length === 0) return { type: "object" }
|
|
316
|
+
return {
|
|
317
|
+
type: "object",
|
|
318
|
+
properties,
|
|
319
|
+
...(required.length > 0 ? { required } : {}),
|
|
320
|
+
}
|
|
321
|
+
} finally {
|
|
322
|
+
seen.delete(input)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function inferOpenAPIResponses7(
|
|
327
|
+
semantic: TypeScriptSemanticSession,
|
|
328
|
+
probePath: string,
|
|
329
|
+
warnings: string[],
|
|
330
|
+
): Promise<OpenAPITypeInferenceResult> {
|
|
331
|
+
const source = await semanticSource(semantic, probePath)
|
|
332
|
+
if (source === undefined) {
|
|
333
|
+
warnings.push("could not create the TypeScript 7 response-inference probe")
|
|
334
|
+
return { responses: {}, warnings }
|
|
335
|
+
}
|
|
336
|
+
const checker = await semanticChecker(semantic, probePath)
|
|
337
|
+
const registryPosition = source.getText().indexOf("__nifra_registry")
|
|
338
|
+
if (registryPosition < 0) {
|
|
339
|
+
warnings.push("backend does not expose a typed Nifra server registry")
|
|
340
|
+
return { responses: {}, warnings }
|
|
341
|
+
}
|
|
342
|
+
const registry = await checker.getTypeAtPosition(probePath, registryPosition)
|
|
343
|
+
if (registry === undefined) {
|
|
344
|
+
warnings.push("backend does not expose a typed Nifra server registry")
|
|
345
|
+
return { responses: {}, warnings }
|
|
346
|
+
}
|
|
347
|
+
const responses: Record<string, Record<string, InferredOpenAPIResponse>> = {}
|
|
348
|
+
for (const pathSymbol of await checker.getPropertiesOfType(registry)) {
|
|
349
|
+
const methods = await checker.getTypeOfSymbol(pathSymbol)
|
|
350
|
+
if (methods === undefined) continue
|
|
351
|
+
for (const methodSymbol of await checker.getPropertiesOfType(methods)) {
|
|
352
|
+
if (methodSymbol.name === "WS") continue
|
|
353
|
+
const info = await checker.getTypeOfSymbol(methodSymbol)
|
|
354
|
+
if (info === undefined) continue
|
|
355
|
+
const responseSymbol = await checker.getPropertyOfType(info, "responses")
|
|
356
|
+
if (responseSymbol === undefined) continue
|
|
357
|
+
const responseType = await checker.getTypeOfSymbol(responseSymbol)
|
|
358
|
+
if (responseType === undefined) continue
|
|
359
|
+
const nonNullable = await checker.getNonNullableType(responseType)
|
|
360
|
+
if (nonNullable === undefined) continue
|
|
361
|
+
const routeResponses: Record<string, InferredOpenAPIResponse> = {}
|
|
362
|
+
const responseMap = (await checker.getApparentType(nonNullable)) ?? nonNullable
|
|
363
|
+
for (const property of await checker.getPropertiesOfType(responseMap)) {
|
|
364
|
+
if (!/^[1-5][0-9]{2}$/.test(property.name)) continue
|
|
365
|
+
const bodyType = await checker.getTypeOfSymbol(property)
|
|
366
|
+
if (bodyType === undefined) continue
|
|
367
|
+
const schema = await schemaForType7(semantic, checker, bodyType, new Set(), 0)
|
|
368
|
+
if (schema === undefined) {
|
|
369
|
+
const bodylessStatus = ["204", "205", "304"].includes(property.name)
|
|
370
|
+
const bodylessType =
|
|
371
|
+
(bodyType.flags &
|
|
372
|
+
(semanticFlag(semantic, "Undefined") | semanticFlag(semantic, "Void"))) !==
|
|
373
|
+
0
|
|
374
|
+
if (bodylessStatus && bodylessType) {
|
|
375
|
+
routeResponses[property.name] = {}
|
|
376
|
+
continue
|
|
377
|
+
}
|
|
378
|
+
warnings.push(
|
|
379
|
+
`response ${property.name} has an unsupported or opaque TypeScript body (${await checker.typeToString(bodyType)})`,
|
|
380
|
+
)
|
|
381
|
+
continue
|
|
382
|
+
}
|
|
383
|
+
routeResponses[property.name] = { schema }
|
|
384
|
+
}
|
|
385
|
+
if (Object.keys(routeResponses).length > 0) {
|
|
386
|
+
responses[`${methodSymbol.name.toUpperCase()} ${pathSymbol.name}`] = routeResponses
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return { responses, warnings }
|
|
391
|
+
}
|
|
392
|
+
|
|
140
393
|
function responseEntries(
|
|
141
394
|
ts: TypeScriptApi,
|
|
142
395
|
checker: TypeChecker,
|
|
@@ -168,29 +421,75 @@ function responseEntries(
|
|
|
168
421
|
return output
|
|
169
422
|
}
|
|
170
423
|
|
|
171
|
-
/** Infer response schemas from `<root>/backend.ts` without affecting runtime application
|
|
424
|
+
/** Infer response schemas from `<root>/backend/app.ts` without affecting runtime application
|
|
425
|
+
* loading. */
|
|
172
426
|
export async function inferOpenAPIResponses(root: string): Promise<OpenAPITypeInferenceResult> {
|
|
173
427
|
const warnings: string[] = []
|
|
174
|
-
const backendPath = resolve(root,
|
|
428
|
+
const backendPath = resolve(root, BACKEND_APP_FILE)
|
|
175
429
|
if (!existsSync(backendPath)) return { responses: {}, warnings }
|
|
176
430
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const temp = await mkdtemp(join(tmpdir(), "nifra-openapi-"))
|
|
184
|
-
const probePath = join(temp, "probe.ts")
|
|
431
|
+
// TypeScript 7's project checker only resolves node handles inside a configured project's root.
|
|
432
|
+
// Keep both the probe and a short-lived extending config at the project root, then remove them.
|
|
433
|
+
const temp = await mkdtemp(join(root, ".nifra-openapi-"))
|
|
434
|
+
const tempName = basename(temp)
|
|
435
|
+
const probePath = join(root, `.${tempName}.ts`)
|
|
436
|
+
const probeConfigPath = join(root, `.${tempName}.tsconfig.json`)
|
|
185
437
|
const probe = [
|
|
186
438
|
'import type { Server as __NifraServer } from "@nifrajs/core/server"',
|
|
187
439
|
`import { backend as __nifra_backend } from ${JSON.stringify(backendPath)}`,
|
|
188
440
|
"type __NifraRegistry = typeof __nifra_backend extends __NifraServer<infer R, any, any> ? R : never",
|
|
189
441
|
"declare const __nifra_registry: __NifraRegistry",
|
|
190
442
|
].join("\n")
|
|
443
|
+
let session: TypeScriptSession | undefined
|
|
191
444
|
|
|
192
445
|
try {
|
|
193
446
|
await writeFile(probePath, probe, "utf8")
|
|
447
|
+
await writeFile(
|
|
448
|
+
probeConfigPath,
|
|
449
|
+
JSON.stringify(
|
|
450
|
+
existsSync(join(root, "tsconfig.json"))
|
|
451
|
+
? {
|
|
452
|
+
extends: "./tsconfig.json",
|
|
453
|
+
compilerOptions: { allowImportingTsExtensions: true },
|
|
454
|
+
include: [BACKEND_APP_FILE, basename(probePath)],
|
|
455
|
+
}
|
|
456
|
+
: {
|
|
457
|
+
compilerOptions: {
|
|
458
|
+
module: "ESNext",
|
|
459
|
+
moduleResolution: "Bundler",
|
|
460
|
+
allowImportingTsExtensions: true,
|
|
461
|
+
skipLibCheck: true,
|
|
462
|
+
},
|
|
463
|
+
include: [BACKEND_APP_FILE, basename(probePath)],
|
|
464
|
+
},
|
|
465
|
+
),
|
|
466
|
+
"utf8",
|
|
467
|
+
)
|
|
468
|
+
const backendContent = await Bun.file(backendPath).text()
|
|
469
|
+
const contents = new Map<string, string>([
|
|
470
|
+
[backendPath, backendContent],
|
|
471
|
+
[probePath, probe],
|
|
472
|
+
])
|
|
473
|
+
const loaded = await loadProjectTypeScript(root, undefined, {
|
|
474
|
+
files: [backendPath, probePath],
|
|
475
|
+
read: (file) => contents.get(resolve(file)),
|
|
476
|
+
projectConfig: probeConfigPath,
|
|
477
|
+
})
|
|
478
|
+
session = loaded.session
|
|
479
|
+
if (loaded.unsupported !== undefined) {
|
|
480
|
+
warnings.push(
|
|
481
|
+
`TypeScript ${loaded.unsupported.version} is outside the supported OpenAPI reflection API`,
|
|
482
|
+
)
|
|
483
|
+
return { responses: {}, warnings }
|
|
484
|
+
}
|
|
485
|
+
const ts = loaded.compiler
|
|
486
|
+
if (ts === undefined) {
|
|
487
|
+
warnings.push("TypeScript is not installed; response inference was skipped")
|
|
488
|
+
return { responses: {}, warnings }
|
|
489
|
+
}
|
|
490
|
+
if (loaded.session?.semantic !== undefined) {
|
|
491
|
+
return await inferOpenAPIResponses7(loaded.session.semantic, probePath, warnings)
|
|
492
|
+
}
|
|
194
493
|
const configPath = ts.findConfigFile(root, ts.sys.fileExists, "tsconfig.json")
|
|
195
494
|
let options: TSApi.CompilerOptions = {
|
|
196
495
|
noEmit: true,
|
|
@@ -256,6 +555,9 @@ export async function inferOpenAPIResponses(root: string): Promise<OpenAPITypeIn
|
|
|
256
555
|
}
|
|
257
556
|
return { responses, warnings }
|
|
258
557
|
} finally {
|
|
558
|
+
await session?.close()
|
|
559
|
+
await rm(probePath, { force: true })
|
|
560
|
+
await rm(probeConfigPath, { force: true })
|
|
259
561
|
await rm(temp, { recursive: true, force: true })
|
|
260
562
|
}
|
|
261
563
|
}
|
package/src/pipeline-report.ts
CHANGED
|
@@ -10,15 +10,16 @@
|
|
|
10
10
|
* Text, not types, so the rules are chosen for the ones that survive being read rather than run:
|
|
11
11
|
*
|
|
12
12
|
* - Which pipeline the app is on. `chooseBuildPipeline` decides from whether the plugin slots are
|
|
13
|
-
* empty, and "does this file declare a non-empty `vitePlugins`" is a question the source
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
13
|
+
* empty, and "does this file declare a non-empty `vitePlugins`" is a question the source
|
|
14
|
+
* answers.
|
|
15
|
+
* - The adapter-entry hazard. `nifra build` imports the adapter from `backend/framework.ts`, or
|
|
16
|
+
* from `nifra.config.ts` when there is no `backend/framework.ts`, and bundles everything that
|
|
17
|
+
* file imports into the production server. A Vite plugin or an SFC compiler reached that way
|
|
18
|
+
* builds cleanly and then dies at startup on a missing native binding. Nothing runtime-side
|
|
19
|
+
* catches it: by then the build has already succeeded.
|
|
20
|
+
* - A plugin in the wrong slot, for the subset whose import specifier names its pipeline. The
|
|
21
|
+
* load guard classifies by hook shape and is authoritative; this catches the same thing
|
|
22
|
+
* earlier, before anything is installed or started.
|
|
22
23
|
* - `conditions` on the Bun pipeline, which reach SSR and cannot reach the dev client bundle.
|
|
23
24
|
*
|
|
24
25
|
* Everything here degrades to silence rather than guessing. A config that computes its plugins, or
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
*/
|
|
28
29
|
import { existsSync } from "node:fs"
|
|
29
30
|
import { join } from "node:path"
|
|
31
|
+
import { CONFIG_FILE, FRAMEWORK_FILE } from "./app-files.ts"
|
|
30
32
|
import { codePositionMask, stripComments } from "./check.ts"
|
|
31
33
|
import type { BuildPipeline } from "./pipeline-guard.ts"
|
|
32
34
|
|
|
@@ -52,7 +54,8 @@ export interface PipelineFinding {
|
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
export interface PipelineReport {
|
|
55
|
-
/** `false` when the directory holds no `nifra.config.ts` or `framework.ts` - nothing to
|
|
57
|
+
/** `false` when the directory holds no `nifra.config.ts` or `backend/framework.ts` - nothing to
|
|
58
|
+
* report on. */
|
|
56
59
|
readonly ran: boolean
|
|
57
60
|
/** The bundler `nifra dev` and `nifra build` will use, or `unknown` when the source can't say. */
|
|
58
61
|
readonly pipeline: BuildPipeline | "unknown"
|
|
@@ -65,7 +68,7 @@ export interface PipelineReport {
|
|
|
65
68
|
readonly certain: boolean
|
|
66
69
|
/** The config file read, project-relative. */
|
|
67
70
|
readonly configFile?: string
|
|
68
|
-
/** The file `nifra build` imports the adapter from - `framework.ts`, else the config. */
|
|
71
|
+
/** The file `nifra build` imports the adapter from - `backend/framework.ts`, else the config. */
|
|
69
72
|
readonly adapterEntry?: string
|
|
70
73
|
readonly findings: readonly PipelineFinding[]
|
|
71
74
|
}
|
|
@@ -227,13 +230,13 @@ async function readSource(path: string): Promise<string | undefined> {
|
|
|
227
230
|
* Total: a missing or unreadable config yields `ran: false`, never a throw.
|
|
228
231
|
*/
|
|
229
232
|
export async function collectPipelineReport(cwd: string): Promise<PipelineReport> {
|
|
230
|
-
const configFile = existsSync(join(cwd,
|
|
233
|
+
const configFile = existsSync(join(cwd, CONFIG_FILE)) ? CONFIG_FILE : FRAMEWORK_FILE
|
|
231
234
|
const source = await readSource(join(cwd, configFile))
|
|
232
235
|
if (source === undefined) {
|
|
233
236
|
return {
|
|
234
237
|
ran: false,
|
|
235
238
|
pipeline: "unknown",
|
|
236
|
-
reason: "no nifra.config.ts or framework.ts here",
|
|
239
|
+
reason: "no nifra.config.ts or backend/framework.ts here",
|
|
237
240
|
certain: false,
|
|
238
241
|
findings: [],
|
|
239
242
|
}
|
|
@@ -284,8 +287,9 @@ export async function collectPipelineReport(cwd: string): Promise<PipelineReport
|
|
|
284
287
|
? "default: this app declares Bun plugins, so both phases stay on Bun"
|
|
285
288
|
: "default: no transforms to place, so both phases stay on Bun"
|
|
286
289
|
|
|
287
|
-
// The adapter entry is bundled into every `nifra build` server entry. Whatever it imports goes
|
|
288
|
-
|
|
290
|
+
// The adapter entry is bundled into every `nifra build` server entry. Whatever it imports goes
|
|
291
|
+
// with it.
|
|
292
|
+
const adapterEntry = existsSync(join(cwd, FRAMEWORK_FILE)) ? FRAMEWORK_FILE : configFile
|
|
289
293
|
const entrySource =
|
|
290
294
|
adapterEntry === configFile ? source : await readSource(join(cwd, adapterEntry))
|
|
291
295
|
if (entrySource !== undefined) {
|
|
@@ -302,11 +306,11 @@ export async function collectPipelineReport(cwd: string): Promise<PipelineReport
|
|
|
302
306
|
`${adapterEntry} imports "${specifier}", and \`nifra build\` imports the adapter from ${adapterEntry} - ` +
|
|
303
307
|
"so the dev toolchain is bundled into the production server entry. The build succeeds and the server then fails at startup on a dependency of the bundler (a missing native binding, typically), which reads like a broken install rather than a config split.",
|
|
304
308
|
fix:
|
|
305
|
-
adapterEntry ===
|
|
309
|
+
adapterEntry === FRAMEWORK_FILE
|
|
306
310
|
? 'move "' +
|
|
307
311
|
specifier +
|
|
308
|
-
'" to `nifra.config.ts`, which only the CLI imports, and keep `framework.ts` to the adapter'
|
|
309
|
-
: 'add a `framework.ts` exporting just the adapter, re-export it from `nifra.config.ts` (`export { adapter } from "./framework"`), and leave the toolchain imports in the config',
|
|
312
|
+
'" to `nifra.config.ts`, which only the CLI imports, and keep `backend/framework.ts` to the adapter'
|
|
313
|
+
: 'add a `backend/framework.ts` exporting just the adapter, re-export it from `nifra.config.ts` (`export { adapter } from "./backend/framework"`), and leave the toolchain imports in the config',
|
|
310
314
|
})
|
|
311
315
|
}
|
|
312
316
|
}
|