@mandujs/core 0.53.3 → 0.54.1
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 +654 -654
- package/package.json +1 -1
- package/src/a11y/__tests__/run-audit.test.ts +333 -333
- package/src/a11y/fix-hints.ts +76 -76
- package/src/a11y/index.ts +18 -18
- package/src/a11y/run-audit.ts +394 -394
- package/src/a11y/types.ts +125 -125
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +122 -122
- package/src/auth/__tests__/tokens.test.ts +274 -274
- package/src/auth/__tests__/verification.test.ts +274 -274
- package/src/auth/index.ts +76 -76
- package/src/auth/login.ts +225 -225
- package/src/auth/password.ts +120 -120
- package/src/auth/reset.ts +243 -243
- package/src/auth/tokens.ts +612 -612
- package/src/auth/verification.ts +253 -253
- package/src/brain/__tests__/redactor.test.ts +94 -94
- package/src/brain/adapters/__tests__/_helpers.ts +83 -83
- package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
- package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
- package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
- package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
- package/src/brain/adapters/anthropic-oauth.ts +1 -1
- package/src/brain/adapters/chatgpt-auth.ts +300 -300
- package/src/brain/adapters/index.ts +319 -319
- package/src/brain/adapters/oauth-flow.ts +439 -439
- package/src/brain/consent.ts +240 -240
- package/src/brain/credentials.ts +396 -396
- package/src/bundler/__tests__/build-runner.ts +113 -113
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
- package/src/bundler/__tests__/extended-watch.test.ts +711 -711
- package/src/bundler/__tests__/fast-refresh.test.ts +10 -10
- package/src/bundler/__tests__/hdr.test.ts +353 -353
- package/src/bundler/__tests__/hmr-client.test.ts +532 -532
- package/src/bundler/__tests__/manifest-schema.test.ts +266 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +519 -519
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +179 -179
- package/src/bundler/build.ts +55 -55
- package/src/bundler/dev.ts +42 -42
- package/src/bundler/manifest-schema.ts +301 -301
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +263 -263
- package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
- package/src/bundler/plugins/block-generated-imports.ts +155 -155
- package/src/bundler/plugins/index.ts +83 -83
- package/src/bundler/plugins/react-compiler-config.ts +108 -108
- package/src/bundler/plugins/react-compiler-lint.ts +253 -253
- package/src/bundler/plugins/react-compiler.ts +162 -162
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +201 -201
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +10 -10
- package/src/bundler/vendor-cache-types.ts +130 -130
- package/src/bundler/vendor-cache.ts +526 -526
- package/src/change/snapshot.ts +18 -18
- package/src/client/Form.tsx +105 -105
- package/src/client/Link.tsx +9 -9
- package/src/client/__tests__/use-sse.test.ts +153 -153
- package/src/client/globals.ts +1 -1
- package/src/client/hooks.ts +362 -362
- package/src/client/hydrate.ts +340 -340
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/router.ts +11 -11
- package/src/client/runtime.ts +47 -47
- package/src/client/serialize.ts +404 -404
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-head.ts +197 -197
- package/src/client/use-sse.ts +378 -378
- package/src/client/window-state.ts +101 -101
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +3 -3
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +8 -8
- package/src/content/content-layer.ts +7 -7
- package/src/content/data-store.ts +245 -245
- package/src/content/frontmatter.ts +189 -189
- package/src/content/loader-context.ts +171 -171
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +172 -172
- package/src/content/loaders/glob.ts +253 -253
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/prebuild.test.ts +571 -571
- package/src/content/prebuild.ts +636 -636
- package/src/content/schema.ts +20 -20
- package/src/content/sidebar.ts +630 -630
- package/src/content/slug.ts +110 -110
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/define.ts +11 -11
- package/src/contract/index.ts +1 -1
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +410 -410
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/route-helpers.ts +1 -1
- package/src/contract/rpc.ts +443 -443
- package/src/contract/schema.ts +48 -48
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +482 -482
- package/src/db/index.ts +138 -138
- package/src/db/migrations/history-table.ts +345 -345
- package/src/db/migrations/index.ts +3 -3
- package/src/db/migrations/lock.ts +324 -324
- package/src/db/migrations/runner.ts +650 -650
- package/src/deploy/cache.ts +140 -140
- package/src/deploy/compile/vercel.ts +344 -344
- package/src/deploy/index.ts +87 -87
- package/src/deploy/inference/brain.ts +268 -268
- package/src/deploy/inference/context.ts +182 -182
- package/src/deploy/inference/filling-extract.ts +245 -245
- package/src/deploy/inference/heuristic.ts +182 -182
- package/src/deploy/intent.ts +173 -173
- package/src/deploy/plan.ts +178 -178
- package/src/design/__tests__/agents-link.test.ts +109 -109
- package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
- package/src/design/__tests__/lint.test.ts +110 -110
- package/src/design/__tests__/parser.test.ts +195 -195
- package/src/design/__tests__/tailwind-theme.test.ts +229 -229
- package/src/design/agents-link.ts +165 -165
- package/src/design/diff.ts +138 -138
- package/src/design/extract.ts +285 -285
- package/src/design/index.ts +102 -102
- package/src/design/lint.ts +209 -209
- package/src/design/parser.ts +555 -555
- package/src/design/patch.ts +241 -241
- package/src/design/scaffold.ts +147 -147
- package/src/design/tailwind-theme.ts +441 -441
- package/src/design/types.ts +210 -210
- package/src/desktop/__tests__/webview-fallback.test.ts +254 -254
- package/src/desktop/__tests__/window.test.ts +248 -248
- package/src/desktop/__tests__/worker.test.ts +266 -266
- package/src/desktop/index.ts +43 -43
- package/src/desktop/types.ts +158 -158
- package/src/desktop/worker.ts +180 -180
- package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
- package/src/dev-error-overlay/index.ts +30 -30
- package/src/dev-error-overlay/overlay-injector.ts +243 -243
- package/src/dev-error-overlay/overlay-styles.ts +52 -52
- package/src/dev-error-overlay/types.ts +66 -66
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +25 -25
- package/src/devtools/client/catchers/error-catcher.ts +344 -344
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/mandu-character.tsx +331 -331
- package/src/devtools/client/components/overlay.tsx +368 -368
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
- package/src/devtools/client/components/panel/islands-panel.tsx +320 -320
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +500 -500
- package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/client/persistence.ts +335 -335
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +265 -265
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/server/source-context.ts +450 -450
- package/src/devtools/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/devtools/worker/worker-manager.ts +410 -410
- package/src/diagnose/__tests__/checks.test.ts +451 -451
- package/src/diagnose/checks.ts +832 -832
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +93 -93
- package/src/diagnose/types.ts +53 -53
- package/src/email/__tests__/email.test.ts +355 -355
- package/src/email/index.ts +282 -282
- package/src/email/smtp.ts +64 -64
- package/src/error/domains.ts +265 -265
- package/src/error/result.ts +60 -60
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/__tests__/session-sqlite.test.ts +454 -454
- package/src/filling/auth.ts +308 -308
- package/src/filling/cookie-codec.ts +299 -299
- package/src/filling/deps.ts +265 -265
- package/src/filling/session-sqlite.ts +617 -617
- package/src/filling/sse.ts +5 -5
- package/src/filling/ws.ts +78 -78
- package/src/generator/index.ts +3 -3
- package/src/guard/__tests__/design-inline-class.test.ts +219 -219
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/define-rule.ts +243 -243
- package/src/guard/design-inline-class.ts +393 -393
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/reporter.ts +442 -442
- package/src/guard/rule-presets.ts +379 -379
- package/src/guard/semantic-slots.ts +1 -1
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/tsgolint-bridge.ts +512 -512
- package/src/guard/types.ts +348 -348
- package/src/guard/watcher.ts +405 -405
- package/src/i18n/define.ts +126 -126
- package/src/i18n/index.ts +52 -52
- package/src/i18n/message-registry.ts +173 -173
- package/src/i18n/types.ts +112 -112
- package/src/id/index.ts +105 -105
- package/src/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -544
- package/src/kitchen/api/errors-grouping.ts +126 -0
- package/src/kitchen/kitchen-handler.ts +192 -63
- package/src/kitchen/kitchen-ui.ts +842 -464
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/middleware/bridge.ts +147 -147
- package/src/middleware/compose.ts +134 -134
- package/src/middleware/compress.ts +62 -62
- package/src/middleware/cors.ts +47 -47
- package/src/middleware/csrf.ts +328 -328
- package/src/middleware/define.ts +132 -132
- package/src/middleware/jwt.ts +134 -134
- package/src/middleware/logger.ts +58 -58
- package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
- package/src/middleware/oauth/index.ts +505 -505
- package/src/middleware/oauth/providers.ts +115 -115
- package/src/middleware/rate-limit/index.ts +522 -522
- package/src/middleware/rate-limit/sqlite-store.ts +382 -382
- package/src/middleware/scheduler-cron.ts +96 -96
- package/src/middleware/secure/__tests__/secure.test.ts +360 -360
- package/src/middleware/secure/csp.ts +193 -193
- package/src/middleware/session.ts +174 -174
- package/src/middleware/timeout.ts +55 -55
- package/src/observability/logger-adapter.ts +36 -36
- package/src/observability/sqlite-store.ts +254 -254
- package/src/openapi/generator.ts +1 -1
- package/src/openapi/openapi.test.ts +43 -43
- package/src/perf/__tests__/user-marks.test.ts +354 -354
- package/src/perf/index.ts +133 -133
- package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
- package/src/plugins/__tests__/runner.test.ts +409 -409
- package/src/plugins/define.ts +124 -124
- package/src/plugins/examples/dep-check-plugin.ts +80 -80
- package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
- package/src/plugins/examples/sitemap-plugin.ts +65 -65
- package/src/plugins/runner.ts +361 -361
- package/src/plugins/types.ts +368 -368
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +32 -32
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +823 -823
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +559 -559
- package/src/resource/ddl/persistence-types.ts +218 -218
- package/src/resource/ddl/type-map.ts +223 -223
- package/src/resource/ddl/types.ts +232 -232
- package/src/resource/generator-repo.ts +630 -630
- package/src/resource/schema.ts +1 -1
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.ts +6 -6
- package/src/routes/index.ts +74 -74
- package/src/routes/metadata-routes.ts +427 -427
- package/src/routes/types.ts +341 -341
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
- package/src/runtime/__tests__/hdr-client.test.ts +223 -223
- package/src/runtime/__tests__/http-errors.test.ts +117 -117
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/adapter.ts +47 -47
- package/src/runtime/boundary.tsx +252 -252
- package/src/runtime/cache.ts +494 -494
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +300 -300
- package/src/runtime/http-errors.ts +113 -113
- package/src/runtime/image-handler.ts +1 -1
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/middleware.ts +264 -264
- package/src/runtime/not-found.ts +93 -93
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/router.ts +105 -105
- package/src/runtime/server.ts +67 -67
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/validate.ts +169 -169
- package/src/seo/index.ts +219 -219
- package/src/seo/integration/ssr.ts +306 -306
- package/src/seo/render/basic.ts +435 -435
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +197 -197
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +127 -127
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +104 -104
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +588 -588
- package/src/slot/validator.ts +39 -39
- package/src/storage/s3/__tests__/s3.test.ts +479 -479
- package/src/storage/s3/index.ts +412 -412
- package/src/testing/__tests__/assertions.test.ts +632 -632
- package/src/testing/__tests__/reporter.test.ts +454 -454
- package/src/testing/assertions.ts +986 -986
- package/src/testing/db.ts +157 -157
- package/src/testing/mocks.ts +203 -203
- package/src/testing/session.ts +190 -190
- package/src/types/branded.ts +56 -56
- package/src/types/index.ts +1 -1
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/watcher.ts +18 -18
|
@@ -1,182 +1,182 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deploy inference context — the bundle of facts an inferer (heuristic
|
|
3
|
-
* or brain) consumes when deciding a route's `DeployIntent`.
|
|
4
|
-
*
|
|
5
|
-
* Issue #250 — Phase 1.
|
|
6
|
-
*
|
|
7
|
-
* The context is intentionally narrow. Anything that's not statically
|
|
8
|
-
* derivable from the route source and manifest entry stays out — we
|
|
9
|
-
* want inference to be deterministic enough that two identical inputs
|
|
10
|
-
* always yield the same intent.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { promises as fs } from "fs";
|
|
14
|
-
import { createHash } from "crypto";
|
|
15
|
-
import path from "path";
|
|
16
|
-
import type { RouteSpec } from "../../spec/schema";
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* The dependency import classes the heuristic recognises. Mapped from
|
|
20
|
-
* raw import specifiers in `extractImports()` below. The key is the
|
|
21
|
-
* coarsest signal a route exposes about whether it can run at the
|
|
22
|
-
* edge — DB drivers and file IO push the route to `node`/`bun`, while
|
|
23
|
-
* stateless `fetch`/transform code happily runs at the edge.
|
|
24
|
-
*/
|
|
25
|
-
export type DependencyClass =
|
|
26
|
-
| "db" // bun:sqlite, postgres, drizzle, prisma, mongodb, etc.
|
|
27
|
-
| "node-fs" // node:fs, fs/promises, fs
|
|
28
|
-
| "node-net" // node:net, http, dgram
|
|
29
|
-
| "node-child" // node:child_process, worker_threads
|
|
30
|
-
| "bun-native" // bun:sqlite, bun:ffi, Bun.s3, Bun.serve
|
|
31
|
-
| "ai-sdk" // @anthropic-ai/sdk, openai, ai (long latency)
|
|
32
|
-
| "heavy" // sharp, playwright, puppeteer, large native deps
|
|
33
|
-
| "fetch-only" // only http fetch / minor transforms
|
|
34
|
-
| "unknown";
|
|
35
|
-
|
|
36
|
-
export interface DeployInferenceContext {
|
|
37
|
-
/** Stable id from the manifest entry. */
|
|
38
|
-
routeId: string;
|
|
39
|
-
/** URL pattern (`/api/embed`, `/[lang]/page`). */
|
|
40
|
-
pattern: string;
|
|
41
|
-
/** `page` | `api` | `metadata`. */
|
|
42
|
-
kind: RouteSpec["kind"];
|
|
43
|
-
/** Pattern contains `[param]` or `[...rest]`. */
|
|
44
|
-
isDynamic: boolean;
|
|
45
|
-
/**
|
|
46
|
-
* Page route exports `generateStaticParams` AND has at least one
|
|
47
|
-
* static-param entry in the manifest. Adapters use this with
|
|
48
|
-
* `runtime: "static"` to know the route is genuinely prerenderable.
|
|
49
|
-
*/
|
|
50
|
-
hasGenerateStaticParams: boolean;
|
|
51
|
-
/** Top-level imports from the handler module (deduped, sorted). */
|
|
52
|
-
imports: string[];
|
|
53
|
-
/** Coarse classification derived from `imports`. */
|
|
54
|
-
dependencyClasses: ReadonlySet<DependencyClass>;
|
|
55
|
-
/** Whether the handler exports a `default` Mandu.filling() instance. */
|
|
56
|
-
exportsFilling: boolean;
|
|
57
|
-
/**
|
|
58
|
-
* SHA-256 of the route source. Used by the cache to skip re-
|
|
59
|
-
* inference when nothing relevant changed.
|
|
60
|
-
*/
|
|
61
|
-
sourceHash: string;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Build the inference context for a single route. `rootDir` is the
|
|
66
|
-
* project root; `route.module` is resolved relative to it.
|
|
67
|
-
*/
|
|
68
|
-
export async function buildDeployInferenceContext(
|
|
69
|
-
rootDir: string,
|
|
70
|
-
route: RouteSpec,
|
|
71
|
-
): Promise<DeployInferenceContext> {
|
|
72
|
-
const modulePath = path.resolve(rootDir, route.module);
|
|
73
|
-
let source = "";
|
|
74
|
-
try {
|
|
75
|
-
source = await fs.readFile(modulePath, "utf8");
|
|
76
|
-
} catch {
|
|
77
|
-
// A missing module file is unusual but not fatal — the inferer
|
|
78
|
-
// still gets the manifest metadata and falls back to defaults.
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const imports = extractImports(source);
|
|
82
|
-
const dependencyClasses = classifyImports(imports);
|
|
83
|
-
// Mandu's manifest patterns use `:param` / `*` (path-pattern style),
|
|
84
|
-
// not bracket-form. Detect both shapes so the heuristic doesn't
|
|
85
|
-
// misclassify dynamic routes as prerenderable. Examples:
|
|
86
|
-
// "/blog/:slug" → dynamic
|
|
87
|
-
// "/docs/*" → dynamic
|
|
88
|
-
// "/[lang]/blog" → dynamic (legacy / hand-written)
|
|
89
|
-
const isDynamic =
|
|
90
|
-
/\[(\.\.\.)?[^\]]+\]/.test(route.pattern) ||
|
|
91
|
-
/\/:[^/]+/.test(route.pattern) ||
|
|
92
|
-
/\*/.test(route.pattern);
|
|
93
|
-
const hasGenerateStaticParams =
|
|
94
|
-
route.kind === "page" &&
|
|
95
|
-
Array.isArray(route.staticParams) &&
|
|
96
|
-
route.staticParams.length > 0;
|
|
97
|
-
const exportsFilling = /\bMandu\.filling\b|\bfilling\(\)/m.test(source);
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
routeId: route.id,
|
|
101
|
-
pattern: route.pattern,
|
|
102
|
-
kind: route.kind,
|
|
103
|
-
isDynamic,
|
|
104
|
-
hasGenerateStaticParams,
|
|
105
|
-
imports,
|
|
106
|
-
dependencyClasses,
|
|
107
|
-
exportsFilling,
|
|
108
|
-
sourceHash: hashSource(source),
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ─── Internals ────────────────────────────────────────────────────────
|
|
113
|
-
|
|
114
|
-
/** SHA-256 hex digest. Empty input still produces a stable hash. */
|
|
115
|
-
export function hashSource(source: string): string {
|
|
116
|
-
return createHash("sha256").update(source).digest("hex");
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Extract bare import specifiers (`from "..."` / `import("...")`).
|
|
121
|
-
*
|
|
122
|
-
* Best-effort — a TS AST would be more precise but pulling in a parser
|
|
123
|
-
* just for this is heavy. False positives (matches in comments or
|
|
124
|
-
* strings) only mis-classify the route in the heuristic, never break
|
|
125
|
-
* deploys, and the brain inferer can override.
|
|
126
|
-
*/
|
|
127
|
-
export function extractImports(source: string): string[] {
|
|
128
|
-
const out = new Set<string>();
|
|
129
|
-
const staticImport = /^\s*import\b[^"']*?["']([^"']+)["']/gm;
|
|
130
|
-
const dynamicImport = /\bimport\(\s*["']([^"']+)["']\s*\)/g;
|
|
131
|
-
for (const re of [staticImport, dynamicImport]) {
|
|
132
|
-
let m: RegExpExecArray | null;
|
|
133
|
-
while ((m = re.exec(source)) !== null) {
|
|
134
|
-
const spec = m[1]!;
|
|
135
|
-
if (!spec.startsWith(".")) out.add(spec);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return [...out].sort();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** Map import specifiers to coarse dependency classes. */
|
|
142
|
-
export function classifyImports(imports: string[]): ReadonlySet<DependencyClass> {
|
|
143
|
-
const classes = new Set<DependencyClass>();
|
|
144
|
-
for (const spec of imports) {
|
|
145
|
-
const cls = classifyOne(spec);
|
|
146
|
-
if (cls) classes.add(cls);
|
|
147
|
-
}
|
|
148
|
-
if (classes.size === 0) classes.add("fetch-only");
|
|
149
|
-
return classes;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function classifyOne(spec: string): DependencyClass | null {
|
|
153
|
-
const s = spec.toLowerCase();
|
|
154
|
-
if (
|
|
155
|
-
s === "bun:sqlite" ||
|
|
156
|
-
s === "bun:ffi" ||
|
|
157
|
-
s.startsWith("bun:")
|
|
158
|
-
) {
|
|
159
|
-
return "bun-native";
|
|
160
|
-
}
|
|
161
|
-
if (s === "fs" || s === "fs/promises" || s === "node:fs" || s === "node:fs/promises" || s === "node:path") {
|
|
162
|
-
return "node-fs";
|
|
163
|
-
}
|
|
164
|
-
if (s === "net" || s === "node:net" || s === "node:dgram" || s === "node:tls") {
|
|
165
|
-
return "node-net";
|
|
166
|
-
}
|
|
167
|
-
if (s === "node:child_process" || s === "child_process" || s === "node:worker_threads" || s === "worker_threads") {
|
|
168
|
-
return "node-child";
|
|
169
|
-
}
|
|
170
|
-
if (
|
|
171
|
-
/^(postgres|pg|mysql2?|drizzle-orm(\/.*)?|@prisma\/client|prisma|mongodb|mongoose|@neondatabase\/.+|kysely|sqlite3|better-sqlite3|@planetscale\/.+)$/.test(s)
|
|
172
|
-
) {
|
|
173
|
-
return "db";
|
|
174
|
-
}
|
|
175
|
-
if (/^(@anthropic-ai\/sdk|openai|ai|@ai-sdk\/.+|@google\/generative-ai|cohere-ai)$/.test(s)) {
|
|
176
|
-
return "ai-sdk";
|
|
177
|
-
}
|
|
178
|
-
if (/^(sharp|playwright|playwright-core|puppeteer|@sparticuz\/.+|canvas|jimp)$/.test(s)) {
|
|
179
|
-
return "heavy";
|
|
180
|
-
}
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Deploy inference context — the bundle of facts an inferer (heuristic
|
|
3
|
+
* or brain) consumes when deciding a route's `DeployIntent`.
|
|
4
|
+
*
|
|
5
|
+
* Issue #250 — Phase 1.
|
|
6
|
+
*
|
|
7
|
+
* The context is intentionally narrow. Anything that's not statically
|
|
8
|
+
* derivable from the route source and manifest entry stays out — we
|
|
9
|
+
* want inference to be deterministic enough that two identical inputs
|
|
10
|
+
* always yield the same intent.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { promises as fs } from "fs";
|
|
14
|
+
import { createHash } from "crypto";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import type { RouteSpec } from "../../spec/schema";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The dependency import classes the heuristic recognises. Mapped from
|
|
20
|
+
* raw import specifiers in `extractImports()` below. The key is the
|
|
21
|
+
* coarsest signal a route exposes about whether it can run at the
|
|
22
|
+
* edge — DB drivers and file IO push the route to `node`/`bun`, while
|
|
23
|
+
* stateless `fetch`/transform code happily runs at the edge.
|
|
24
|
+
*/
|
|
25
|
+
export type DependencyClass =
|
|
26
|
+
| "db" // bun:sqlite, postgres, drizzle, prisma, mongodb, etc.
|
|
27
|
+
| "node-fs" // node:fs, fs/promises, fs
|
|
28
|
+
| "node-net" // node:net, http, dgram
|
|
29
|
+
| "node-child" // node:child_process, worker_threads
|
|
30
|
+
| "bun-native" // bun:sqlite, bun:ffi, Bun.s3, Bun.serve
|
|
31
|
+
| "ai-sdk" // @anthropic-ai/sdk, openai, ai (long latency)
|
|
32
|
+
| "heavy" // sharp, playwright, puppeteer, large native deps
|
|
33
|
+
| "fetch-only" // only http fetch / minor transforms
|
|
34
|
+
| "unknown";
|
|
35
|
+
|
|
36
|
+
export interface DeployInferenceContext {
|
|
37
|
+
/** Stable id from the manifest entry. */
|
|
38
|
+
routeId: string;
|
|
39
|
+
/** URL pattern (`/api/embed`, `/[lang]/page`). */
|
|
40
|
+
pattern: string;
|
|
41
|
+
/** `page` | `api` | `metadata`. */
|
|
42
|
+
kind: RouteSpec["kind"];
|
|
43
|
+
/** Pattern contains `[param]` or `[...rest]`. */
|
|
44
|
+
isDynamic: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Page route exports `generateStaticParams` AND has at least one
|
|
47
|
+
* static-param entry in the manifest. Adapters use this with
|
|
48
|
+
* `runtime: "static"` to know the route is genuinely prerenderable.
|
|
49
|
+
*/
|
|
50
|
+
hasGenerateStaticParams: boolean;
|
|
51
|
+
/** Top-level imports from the handler module (deduped, sorted). */
|
|
52
|
+
imports: string[];
|
|
53
|
+
/** Coarse classification derived from `imports`. */
|
|
54
|
+
dependencyClasses: ReadonlySet<DependencyClass>;
|
|
55
|
+
/** Whether the handler exports a `default` Mandu.filling() instance. */
|
|
56
|
+
exportsFilling: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* SHA-256 of the route source. Used by the cache to skip re-
|
|
59
|
+
* inference when nothing relevant changed.
|
|
60
|
+
*/
|
|
61
|
+
sourceHash: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build the inference context for a single route. `rootDir` is the
|
|
66
|
+
* project root; `route.module` is resolved relative to it.
|
|
67
|
+
*/
|
|
68
|
+
export async function buildDeployInferenceContext(
|
|
69
|
+
rootDir: string,
|
|
70
|
+
route: RouteSpec,
|
|
71
|
+
): Promise<DeployInferenceContext> {
|
|
72
|
+
const modulePath = path.resolve(rootDir, route.module);
|
|
73
|
+
let source = "";
|
|
74
|
+
try {
|
|
75
|
+
source = await fs.readFile(modulePath, "utf8");
|
|
76
|
+
} catch {
|
|
77
|
+
// A missing module file is unusual but not fatal — the inferer
|
|
78
|
+
// still gets the manifest metadata and falls back to defaults.
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const imports = extractImports(source);
|
|
82
|
+
const dependencyClasses = classifyImports(imports);
|
|
83
|
+
// Mandu's manifest patterns use `:param` / `*` (path-pattern style),
|
|
84
|
+
// not bracket-form. Detect both shapes so the heuristic doesn't
|
|
85
|
+
// misclassify dynamic routes as prerenderable. Examples:
|
|
86
|
+
// "/blog/:slug" → dynamic
|
|
87
|
+
// "/docs/*" → dynamic
|
|
88
|
+
// "/[lang]/blog" → dynamic (legacy / hand-written)
|
|
89
|
+
const isDynamic =
|
|
90
|
+
/\[(\.\.\.)?[^\]]+\]/.test(route.pattern) ||
|
|
91
|
+
/\/:[^/]+/.test(route.pattern) ||
|
|
92
|
+
/\*/.test(route.pattern);
|
|
93
|
+
const hasGenerateStaticParams =
|
|
94
|
+
route.kind === "page" &&
|
|
95
|
+
Array.isArray(route.staticParams) &&
|
|
96
|
+
route.staticParams.length > 0;
|
|
97
|
+
const exportsFilling = /\bMandu\.filling\b|\bfilling\(\)/m.test(source);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
routeId: route.id,
|
|
101
|
+
pattern: route.pattern,
|
|
102
|
+
kind: route.kind,
|
|
103
|
+
isDynamic,
|
|
104
|
+
hasGenerateStaticParams,
|
|
105
|
+
imports,
|
|
106
|
+
dependencyClasses,
|
|
107
|
+
exportsFilling,
|
|
108
|
+
sourceHash: hashSource(source),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ─── Internals ────────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
/** SHA-256 hex digest. Empty input still produces a stable hash. */
|
|
115
|
+
export function hashSource(source: string): string {
|
|
116
|
+
return createHash("sha256").update(source).digest("hex");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Extract bare import specifiers (`from "..."` / `import("...")`).
|
|
121
|
+
*
|
|
122
|
+
* Best-effort — a TS AST would be more precise but pulling in a parser
|
|
123
|
+
* just for this is heavy. False positives (matches in comments or
|
|
124
|
+
* strings) only mis-classify the route in the heuristic, never break
|
|
125
|
+
* deploys, and the brain inferer can override.
|
|
126
|
+
*/
|
|
127
|
+
export function extractImports(source: string): string[] {
|
|
128
|
+
const out = new Set<string>();
|
|
129
|
+
const staticImport = /^\s*import\b[^"']*?["']([^"']+)["']/gm;
|
|
130
|
+
const dynamicImport = /\bimport\(\s*["']([^"']+)["']\s*\)/g;
|
|
131
|
+
for (const re of [staticImport, dynamicImport]) {
|
|
132
|
+
let m: RegExpExecArray | null;
|
|
133
|
+
while ((m = re.exec(source)) !== null) {
|
|
134
|
+
const spec = m[1]!;
|
|
135
|
+
if (!spec.startsWith(".")) out.add(spec);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return [...out].sort();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Map import specifiers to coarse dependency classes. */
|
|
142
|
+
export function classifyImports(imports: string[]): ReadonlySet<DependencyClass> {
|
|
143
|
+
const classes = new Set<DependencyClass>();
|
|
144
|
+
for (const spec of imports) {
|
|
145
|
+
const cls = classifyOne(spec);
|
|
146
|
+
if (cls) classes.add(cls);
|
|
147
|
+
}
|
|
148
|
+
if (classes.size === 0) classes.add("fetch-only");
|
|
149
|
+
return classes;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function classifyOne(spec: string): DependencyClass | null {
|
|
153
|
+
const s = spec.toLowerCase();
|
|
154
|
+
if (
|
|
155
|
+
s === "bun:sqlite" ||
|
|
156
|
+
s === "bun:ffi" ||
|
|
157
|
+
s.startsWith("bun:")
|
|
158
|
+
) {
|
|
159
|
+
return "bun-native";
|
|
160
|
+
}
|
|
161
|
+
if (s === "fs" || s === "fs/promises" || s === "node:fs" || s === "node:fs/promises" || s === "node:path") {
|
|
162
|
+
return "node-fs";
|
|
163
|
+
}
|
|
164
|
+
if (s === "net" || s === "node:net" || s === "node:dgram" || s === "node:tls") {
|
|
165
|
+
return "node-net";
|
|
166
|
+
}
|
|
167
|
+
if (s === "node:child_process" || s === "child_process" || s === "node:worker_threads" || s === "worker_threads") {
|
|
168
|
+
return "node-child";
|
|
169
|
+
}
|
|
170
|
+
if (
|
|
171
|
+
/^(postgres|pg|mysql2?|drizzle-orm(\/.*)?|@prisma\/client|prisma|mongodb|mongoose|@neondatabase\/.+|kysely|sqlite3|better-sqlite3|@planetscale\/.+)$/.test(s)
|
|
172
|
+
) {
|
|
173
|
+
return "db";
|
|
174
|
+
}
|
|
175
|
+
if (/^(@anthropic-ai\/sdk|openai|ai|@ai-sdk\/.+|@google\/generative-ai|cohere-ai)$/.test(s)) {
|
|
176
|
+
return "ai-sdk";
|
|
177
|
+
}
|
|
178
|
+
if (/^(sharp|playwright|playwright-core|puppeteer|@sparticuz\/.+|canvas|jimp)$/.test(s)) {
|
|
179
|
+
return "heavy";
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|